pax_global_header00006660000000000000000000000064144044436740014524gustar00rootroot0000000000000052 comment=532c39a124d5d12fc23bd01f22a3c52109772c48 flippercloud-flipper-532c39a/000077500000000000000000000000001440444367400162065ustar00rootroot00000000000000flippercloud-flipper-532c39a/.codeclimate.yml000066400000000000000000000000721440444367400212570ustar00rootroot00000000000000exclude_patterns: - "lib/flipper/ui/public" - "spec/" flippercloud-flipper-532c39a/.github/000077500000000000000000000000001440444367400175465ustar00rootroot00000000000000flippercloud-flipper-532c39a/.github/dependabot.yml000066400000000000000000000001661440444367400224010ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" flippercloud-flipper-532c39a/.github/workflows/000077500000000000000000000000001440444367400216035ustar00rootroot00000000000000flippercloud-flipper-532c39a/.github/workflows/ci.yml000066400000000000000000000037051440444367400227260ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: test: name: Test on ruby ${{ matrix.ruby }} and rails ${{ matrix.rails }} runs-on: ubuntu-latest services: redis: image: redis ports: ['6379:6379'] options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 strategy: matrix: ruby: ['2.6', '2.7', '3.0', '3.1', '3.2'] rails: ['5.2', '6.0.0', '6.1.0', '7.0.0'] exclude: - ruby: "2.6" rails: "7.0.0" - ruby: "3.0" rails: "5.2" - ruby: "3.1" rails: "5.2" - ruby: "3.1" rails: "6.0.0" - ruby: "3.2" rails: "5.2" - ruby: "3.2" rails: "6.0.0" - ruby: "3.2" rails: "6.1.0" env: SQLITE3_VERSION: 1.4.1 REDIS_URL: redis://localhost:6379/0 CI: true RAILS_VERSION: ${{ matrix.rails }} steps: - name: Setup memcached uses: KeisukeYamashita/memcached-actions@v1 - name: Start MongoDB uses: supercharge/mongodb-github-action@1.9.0 with: mongodb-version: 4.0 - name: Check out repository code uses: actions/checkout@v3 - name: Do some action caching uses: actions/cache@v3 with: path: vendor/bundle key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}- - name: Install libpq-dev run: sudo apt-get -yqq install libpq-dev - name: Set up Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true # 'bundle install' and cache gems - name: Run Rake with Rails ${{ matrix.rails }} run: bundle exec rake flippercloud-flipper-532c39a/.github/workflows/examples.yml000066400000000000000000000036601440444367400241510ustar00rootroot00000000000000name: Examples on: [push, pull_request] jobs: test: if: github.repository_owner == 'jnunemaker' name: Test on ruby ${{ matrix.ruby }} and rails ${{ matrix.rails }} runs-on: ubuntu-latest services: redis: image: redis ports: ['6379:6379'] options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 strategy: matrix: ruby: ['2.6', '2.7', '3.0', '3.1'] rails: ['5.2', '6.0.0', '6.1.0', '7.0.0'] exclude: - ruby: "2.6" rails: "7.0.0" - ruby: "3.0" rails: "5.2" - ruby: "3.1" rails: "5.2" - ruby: "3.1" rails: "6.0.0" env: SQLITE3_VERSION: 1.4.1 REDIS_URL: redis://localhost:6379/0 CI: true RAILS_VERSION: ${{ matrix.rails }} steps: - name: Setup memcached uses: KeisukeYamashita/memcached-actions@v1 - name: Start MongoDB uses: supercharge/mongodb-github-action@1.9.0 with: mongodb-version: 4.0 - name: Check out repository code uses: actions/checkout@v3 - name: Do some action caching uses: actions/cache@v3 with: path: vendor/bundle key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}- - name: Install libpq-dev run: sudo apt-get -yqq install libpq-dev - name: Set up Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true # 'bundle install' and cache gems - name: Run Examples with Rails ${{ matrix.rails }} env: FLIPPER_CLOUD_TOKEN: ${{ secrets.FLIPPER_CLOUD_TOKEN }} run: script/examples flippercloud-flipper-532c39a/.gitignore000066400000000000000000000003171440444367400201770ustar00rootroot00000000000000*.gem *.rbc .bundle .config .ruby-version .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp log *.pstore .sass-cache bin .DS_Store flippercloud-flipper-532c39a/.rspec000066400000000000000000000000261440444367400173210ustar00rootroot00000000000000--require spec_helper flippercloud-flipper-532c39a/.tool-versions000066400000000000000000000000131440444367400210240ustar00rootroot00000000000000ruby 3.1.2 flippercloud-flipper-532c39a/CODE_OF_CONDUCT.md000066400000000000000000000062201440444367400210050ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nunemaker@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ flippercloud-flipper-532c39a/Changelog.md000066400000000000000000000645431440444367400204330ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. ## 0.26.2 * Improve Active Record Adapter get/get_multi/get_all performance by 5-10x when dealing with thousands of gate values (https://github.com/jnunemaker/flipper/pull/707). ## 0.26.1 * Improve `Flipper#enabled?` performance by ~37%-55% (https://github.com/jnunemaker/flipper/pull/706) * Make Memory adapter threadsafe (https://github.com/jnunemaker/flipper/pull/702 and https://github.com/jnunemaker/flipper/pull/703) * ActiveRecord adapter: wrap all reads/writes in `with_connection` (https://github.com/jnunemaker/flipper/pull/705) * Improve performance of background polling (https://github.com/jnunemaker/flipper/pull/699) * Remove executables directive from gem (https://github.com/jnunemaker/flipper/pull/693) ## 0.26.0 * Cloud Background Polling (https://github.com/jnunemaker/flipper/pull/682) * Changed default branch from master to main * Allow configuring railtie via ENV vars (https://github.com/jnunemaker/flipper/pull/681) * flipper-ui: Fix issue preventing feature flags being enabled when confirm_fully_enable is on and feature_removal_enabled is off (https://github.com/jnunemaker/flipper/pull/680) ## 0.25.4 * Added read_only UI config option (https://github.com/jnunemaker/flipper/pull/679) ## 0.25.3 * Added configurable confirm warning for fully enabling a feature (https://github.com/jnunemaker/flipper/pull/665) * Update rack protection to < 4 (https://github.com/jnunemaker/flipper/pull/675) * Check sadd_returns_boolean on the actual client class rather than ::Redis (https://github.com/jnunemaker/flipper/pull/677) ## 0.25.2 * Fix deprecation warnings for Redis >= 4.8.0 (https://github.com/jnunemaker/flipper/pull/660) ## 0.25.1 ### Additions/Changes * ActiveRecord: use provided `gate_class` option when calling `#get_all` (https://github.com/jnunemaker/flipper/pull/647) * Relaxed the rack-protection version to support latest (https://github.com/jnunemaker/flipper/commit/f4a41c541ccf14c535a61c6bc6fe7eeabbfc7e71). * Configure ActiveRecord adapter immediately upon require of flipper-active_record (https://github.com/jnunemaker/flipper/pull/652) ## 0.25.0 ### Additions/Changes * Added a prompt in Flipper UI for the 'Delete' button to prevent accidental delete of features (https://github.com/jnunemaker/flipper/pull/625) * Added failsafe adapter (https://github.com/jnunemaker/flipper/pull/626) * Removed previously deprecated options and settings. Those upgrading from `<0.21` should upgrade to `~>0.24` first and fix any deprecation warnings when initializing Flipper. (https://github.com/jnunemaker/flipper/pull/627) * ActiveRecord: base class for internal models (https://github.com/jnunemaker/flipper/pull/629) * Remove use of `Rack::BodyProxy` in the memoizer middleware (https://github.com/jnunemaker/flipper/pull/631) ## 0.24.1 ### Additions/Changes * flipper-api: `exclude_gates` parameter to exclude gate data in GETs (https://github.com/jnunemaker/flipper/pull/572). * Make it possible to disable internal memoization (https://github.com/jnunemaker/flipper/pull/612). * Add Flipper::Actor#hash so actors can be hash keys (https://github.com/jnunemaker/flipper/pull/616). * Pretty Up `rails routes` again and make rack-protection dependency less strict (https://github.com/jnunemaker/flipper/pull/619). * Add kwargs for method_missing using ruby 3.0 (https://github.com/jnunemaker/flipper/pull/620). * Relax the rack-protection dependency (https://github.com/jnunemaker/flipper/commit/c1cb9cd78140c2b09123687642558101e6e5d37d). ## 0.24.0 ### Additions/Changes * Add Ruby 3.0 and 3.1 to the CI matrix and fix groups block arity check for ruby 3 (https://github.com/jnunemaker/flipper/pull/601) * Removed support for Ruby 2.5 (which was end of line 9 months ago) * Add (alpha) client side instrumentation of events to cloud (https://github.com/jnunemaker/flipper/pull/602) * Fix deprecated uses of Redis#pipelined (https://github.com/jnunemaker/flipper/pull/603). redis-rb >= 3 now required. * Fix Flipper UI Rack application when `Rack::Session::Pool` is used to build it (https://github.com/jnunemaker/flipper/pull/606). ## 0.23.1 ### Additions/Changes * Relax dalli version constraint (https://github.com/jnunemaker/flipper/pull/596) ### Bug Fixes * Fix railtie initialization to mount middleware after config/intializers/* (https://github.com/jnunemaker/flipper/pull/586) ## 0.23.0 ### Additions/Changes * Allow some HTML in banner and descriptions (https://github.com/jnunemaker/flipper/pull/570). * Moved some cloud headers to http client (https://github.com/jnunemaker/flipper/pull/567). * Update flipper-ui jquery and bootstrap versions (https://github.com/jnunemaker/flipper/issues/565 and https://github.com/jnunemaker/flipper/pull/566). * Moved docs to www.flippercloud.io/docs (https://github.com/jnunemaker/flipper/pull/574). * PStore adapter now defaults to thread safe and no longer supports `.thread_safe` (https://github.com/jnunemaker/flipper/commit/4048704fefe41b716015294a19a0b94546637630). * Add failover adapter (https://github.com/jnunemaker/flipper/pull/584). * Improve http adapter error message (https://github.com/jnunemaker/flipper/pull/587). * Rails 7 support (mostly in https://github.com/jnunemaker/flipper/pull/592). ## 0.22.2 ### Additions/Changes * Allow adding multiple actors at once in flipper-ui via comma separation (configurable via `Flipper::UI.configuration.actors_separator`) (https://github.com/jnunemaker/flipper/pull/556) ### Bug Fixes * Fix railtie initialization to avoid altering middleware order (https://github.com/jnunemaker/flipper/pull/563) ## 0.22.1 ### Additions/Changes * Remove Octicons and replace with a pure CSS status circle (https://github.com/jnunemaker/flipper/pull/547) * Rescue unique errors in AR and Sequel when setting value (https://github.com/jnunemaker/flipper/commit/87f5a98bce7baad7a27b75b5bce3256967769f27) * Add a Content-Security-Policy to flipper-ui (https://github.com/jnunemaker/flipper/pull/552) * Fix Synchronizer issue that occurs for ActiveRecord adapter (https://github.com/jnunemaker/flipper/pull/554) ## 0.22.0 ### Additions/Changes * Enable log subscriber by default in Rails (https://github.com/jnunemaker/flipper/pull/525) * Remove memoizer from API and UI (https://github.com/jnunemaker/flipper/pull/527). If you are using the UI or API without configuring the default instance of Flipper, you'll need to enable memoization if you want it. For examples, see the examples/ui and examples/api directories. * Fix SQL reserved word use in get_all for ActiveRecord and Sequel (https://github.com/jnunemaker/flipper/pull/536). * Handle spaces in names gracefully in UI (https://github.com/jnunemaker/flipper/pull/541). ## 0.21.0 ### Additions/Changes * Default to using memory adapter (https://github.com/jnunemaker/flipper/pull/501) * Adapters now configured on require when possible (https://github.com/jnunemaker/flipper/pull/502) * Added cloud recommendation to flipper-ui. Can be disabled with `Flipper::UI.configure { |config| config.cloud_recommendation = false }`. Just want to raise awareness that more is available if people want it (https://github.com/jnunemaker/flipper/pull/504) * Added default `flipper_id` implementation via `Flipper::Identifier` and automatically included it in ActiveRecord and Sequel models (https://github.com/jnunemaker/flipper/pull/505) * Deprecate superflous sync_method setting (https://github.com/jnunemaker/flipper/pull/511) * Flipper is now pre-configured when used with Rails. By default, it will [memoize and preload all features for each request](https://flippercloud.io/docs/optimization#memoization). (https://github.com/jnunemaker/flipper/pull/506) ### Upgrading You should be able to upgrade to 0.21 without any breaking changes. However, if you want to simplify your setup, you can remove some configuration that is now handled automatically: 1. Adapters are configured when on require, so unless you are using caching or other customizations, you can remove adapter configuration. ```diff # config/initializers/flipper.rb - Flipper.configure do |config| - config.default { Flipper.new(Flipper::Adapters::ActiveRecord.new) } - end ``` 2. `Flipper::Middleware::Memoizer` will be enabled by default -- including preloading. **Note**: You may want to disable preloading (see below) if you have > 100 features. ```diff # config/initializers/flipper.rb - Rails.configuration.middleware.use Flipper::Middleware::Memoizer, - preload: [:stats, :search, :some_feature] + Rails.application.configure do + # Uncomment to configure which features to preload on all requests + # config.flipper.preload = [:stats, :search, :some_feature] + # + # Or, you may want to disable preloading entirely: + # config.flipper.preload = false + end ``` 3. `#flipper_id`, which is used to enable features for specific actors, is now defined by [Flipper::Identifier](lib/flipper/identifier.rb) on all ActiveRecord and Sequel models. You can remove your implementation if it is in the form of `ModelName;id`. 4. When using `flipper-cloud`, The `Flipper::Cloud.app` webhook receiver is now mounted at `/_flipper` by default. ```diff # config/routes.rb - mount Flipper::Cloud.app, at: "/_flipper" ``` ## 0.20.4 ### Additions/Changes * Allow actors and time gates to deal with decimal percentages (https://github.com/jnunemaker/flipper/pull/492) * Change Flipper::Cloud::Middleware to receive webhooks at / in addition to /webhooks. * Add `write_through` option to ActiveSupportCacheStore adapter to support write-through caching (https://github.com/jnunemaker/flipper/pull/512) ## 0.20.3 ### Additions/Changes * Changed the internal structure of how the memory adapter stores things. ## 0.20.2 ### Additions/Changes * Http adapter now raises error when enable/disable/add/remove/clear fail. * Cloud adapter sends some extra info like hostname, ruby version, etc. for debugging and decision making. ## 0.20.1 ### Additions/Changes * Just a minor tweak to cloud webhook middleware to provide more debugging information about why a hook wasn't successful. ## 0.20.0 ### Additions/Changes * Add support for webhooks to `Flipper::Cloud` (https://github.com/jnunemaker/flipper/pull/489). ## 0.19.1 ### Additions/Changes * Bump rack-protection version to < 2.2 (https://github.com/jnunemaker/flipper/pull/487) * Add memoizer_options to Flipper::Api.app (https://github.com/jnunemaker/flipper/commit/174ad4bb94046a25c432d3c53fe1ff9f5a76d838) ## 0.19.0 ### Additions/Changes * 100% of actors is now considered conditional. Feature#on?, Feature#conditional?, Feature#state would all be affected. See https://github.com/jnunemaker/flipper/issues/463 for more. * Several doc updates. ## 0.18.0 ### Additions/Changes * Add support for feature descriptions to flipper-ui (https://github.com/jnunemaker/flipper/pull/461). * Remove rubocop (https://github.com/jnunemaker/flipper/pull/469). * flipper-ui redesign (https://github.com/jnunemaker/flipper/pull/470). * Removed support for ruby 2.4. * Added support for ruby 2.7. * Removed support for Rails 4.x.x. * Removed support for customizing actors, groups, % of actors and % of time text in flipper-ui in favor of automatic and more descriptive text. ## 0.17.2 ### Additions/Changes * Avoid errors on import when there are no features and shared specs/tests for get all with no features (https://github.com/jnunemaker/flipper/pull/441 and https://github.com/jnunemaker/flipper/pull/442) * ::ActiveRecord::RecordNotUnique > ActiveRecord::RecordNotUnique (https://github.com/jnunemaker/flipper/pull/444) * Clear gate values on enable (https://github.com/jnunemaker/flipper/pull/454) * Remove use of multi from redis adapter (https://github.com/jnunemaker/flipper/pull/451) ## 0.17.1 * Fix require in flipper-active_record (https://github.com/jnunemaker/flipper/pull/437) ## 0.17.0 ### Additions/Changes * Allow shorthand block notation on group types (https://github.com/jnunemaker/flipper/pull/406) * Relax active record/support constraints to support Rails 6 (https://github.com/jnunemaker/flipper/pull/409) * Allow disabling fun (https://github.com/jnunemaker/flipper/pull/413) * Include thing_value in payload of Instrumented#enable and #disable (https://github.com/jnunemaker/flipper/pull/417) * Replace Erubis with Erubi (https://github.com/jnunemaker/flipper/pull/407) * Allow customizing Rack::Protection middleware list (https://github.com/jnunemaker/flipper/pull/385) * Allow setting write_timeout for ruby 2.6+ (https://github.com/jnunemaker/flipper/pull/433) * Drop support for Ruby 2.1, 2.2, and 2.3 (https://github.com/jnunemaker/flipper/commit/cf58982e70de5e6963b018ceced4f36a275f5b5d) * Add support for Ruby 2.6 (https://github.com/jnunemaker/flipper/commit/57888311449ec81184d3d47ba9ae5cb1ad4a2f45) * Remove support for Rails 3.2 (https://github.com/jnunemaker/flipper/commit/177c48c4edf51d4e411e7c673e30e06d1c66fb40) * Add write_timeout for flipper http adapter for ruby 2.6+ (https://github.com/jnunemaker/flipper/pull/433) * Relax moneta version to allow for < 1.2 (https://github.com/jnunemaker/flipper/pull/434). * Improve active record idempotency (https://github.com/jnunemaker/flipper/pull/436). * Allow customizing add actor placeholder text (https://github.com/jnunemaker/flipper/commit/5faa1e9cf66b68f8227d2f8408fb448a14676c45) ## 0.16.2 ### Additions/Changes * Bump rollout redis dependency to < 5 (https://github.com/jnunemaker/flipper/pull/403) * Bump redis dependency to < 5 (https://github.com/jnunemaker/flipper/pull/401) * Bump sequel dependency to < 6 (https://github.com/jnunemaker/flipper/pull/399 and https://github.com/jnunemaker/flipper/commit/edc767e69b4ce8daead9801f38e0e8bf6b238765) ## 0.16.1 ### Additions/Changes * Add actors API endpoint (https://github.com/jnunemaker/flipper/pull/372). * Fix rack body proxy require for those using flipper without rack (https://github.com/jnunemaker/flipper/pull/376). * Unescapes feature_name in FeatureNameFromRoute (https://github.com/jnunemaker/flipper/pull/377). * Replace delete_all with destroy_all in ActiveRecord adapter (https://github.com/jnunemaker/flipper/pull/395) * Target correct bootstrap breakpoints in flipper UI (https://github.com/jnunemaker/flipper/pull/396) ## 0.16.0 ### Bug Fixes * Support slashes in feature names (https://github.com/jnunemaker/flipper/pull/362). ### Additions/Changes * Re-order gates for improved performance in some cases (https://github.com/jnunemaker/flipper/pull/370). * Add Feature#exist?, DSL#exist? and Flipper#exist? (https://github.com/jnunemaker/flipper/pull/371). ## 0.15.0 * Move Flipper::UI configuration options to Flipper::UI::Configuration (https://github.com/jnunemaker/flipper/pull/345). * Bug fix in adapter synchronizing and switched DSL#import to use Synchronizer (https://github.com/jnunemaker/flipper/pull/347). * Fix AR adapter table name prefix/suffix bug (https://github.com/jnunemaker/flipper/pull/350). * Allow feature names to end with "features" in UI (https://github.com/jnunemaker/flipper/pull/353). ## 0.14.0 * Changed sync_interval to be seconds instead of milliseconds. ## 0.13.0 ### Additions/Changes * Update PStore adapter to allow setting thread_safe option (https://github.com/jnunemaker/flipper/pull/334). * Update Flipper::UI to Bootstrap 4 (https://github.com/jnunemaker/flipper/pull/336). * Add Flipper::UI configuration to add a banner with customizeable text and background color (https://github.com/jnunemaker/flipper/pull/337). * Add sync adapter (https://github.com/jnunemaker/flipper/pull/341). * Make cloud use sync adapter (https://github.com/jnunemaker/flipper/pull/342). This makes local flipper operations resilient to cloud failures. ## 0.12.2 ### Additions/Changes * Improvements/fixes/examples for rollout adapter (https://github.com/jnunemaker/flipper/pull/332). ## 0.12.1 ### Additions/Changes * Added rollout adapter documentation (https://github.com/jnunemaker/flipper/pull/328). ### Bug Fixes * Fixed ActiveRecord and Sequel adapters to include disabled features for `get_all` (https://github.com/jnunemaker/flipper/pull/327). ## 0.12 ### Additions/Changes * Added Flipper.instance= writer method for explicitly setting the default instance (https://github.com/jnunemaker/flipper/pull/309). * Added Flipper::UI configuration instance for changing text and things (https://github.com/jnunemaker/flipper/pull/306). * Delegate memoize= and memoizing? for Flipper and Flipper::DSL (https://github.com/jnunemaker/flipper/pull/310). * Fixed error when enabling the same group or actor more than once (https://github.com/jnunemaker/flipper/pull/313). * Fixed redis cache adapter key (and thus cache misses) (https://github.com/jnunemaker/flipper/pull/325). * Added Rollout adapter to make it easy to import rollout data into Flipper (https://github.com/jnunemaker/flipper/pull/319). * Relaxed redis gem dependency constraint to allow redis-rb 4 (https://github.com/jnunemaker/flipper/pull/317). * Added configuration option for Flipper::UI to disable feature removal (https://github.com/jnunemaker/flipper/pull/322). ## 0.11 ### Backwards Compatibility Breaks * Set flipper from env for API and UI (https://github.com/jnunemaker/flipper/pull/223 and https://github.com/jnunemaker/flipper/pull/229). It is documented, but now the memoizing middleware requires that the SetupEnv middleware is used first, unless you are configuring a Flipper default instance. * Drop support for Ruby 2.0 as it is end of lined (https://github.com/jnunemaker/flipper/commit/c2c81ed89938155ce91acb5173ac38580f630e3d). * Allow unregistered groups (https://github.com/jnunemaker/flipper/pull/244). Only break in compatibility is that previously unregistered groups could not be enabled and now they can be. * Removed support for metriks (https://github.com/jnunemaker/flipper/pull/291). ### Additions/Changes * Use primary keys with sequel adapter (https://github.com/jnunemaker/flipper/pull/210). Should be backwards compatible, but if you want it to work this way you will need to migrate your database to the new schema. * Add redis cache adapter (https://github.com/jnunemaker/flipper/pull/211). * Finish API and HTTP adapter that speaks to API. * Add flipper cloud adapter (https://github.com/jnunemaker/flipper/pull/249). Nothing to see here yet, but good stuff soon. ;) * Add importing (https://github.com/jnunemaker/flipper/pull/251). * Added Adapter#get_all to allow for more efficient preload_all (https://github.com/jnunemaker/flipper/pull/255). * Added :unless option to Flipper::Middleware::Memoizer to allow skipping memoization and preloading for certain requests. * Made it possible to instrument Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f). * Made it possible to wrap Http adapter when using Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f). * Instrument get_multi in instrumented adapter (https://github.com/jnunemaker/flipper/commit/951d25c5ce07d3b56b0b2337adf5f6bcbe4050e7). * Allow instrumenting Flipper::Cloud http adapter (https://github.com/jnunemaker/flipper/pull/253). * Add DSL#preload_all and Adapter#get_all to allow for making even more efficient loading of features (https://github.com/jnunemaker/flipper/pull/255). * Allow setting debug output of http adapter (https://github.com/jnunemaker/flipper/pull/256 and https://github.com/jnunemaker/flipper/pull/258). * Allow setting env key for middleware (https://github.com/jnunemaker/flipper/pull/259). * Added ActiveSupport cache store adapter for use with Rails.cache (https://github.com/jnunemaker/flipper/pull/265 and https://github.com/jnunemaker/flipper/pull/297). * Added support for up to 3 decimal places in percentage based rollouts (https://github.com/jnunemaker/flipper/pull/274). * Removed Flipper::GroupNotRegistered error as it is now unused (https://github.com/jnunemaker/flipper/pull/270). * Added get_all to all adapters (https://github.com/jnunemaker/flipper/pull/298). * Added support for Rails 5.1 (https://github.com/jnunemaker/flipper/pull/299). * Added Flipper default instance generation (https://github.com/jnunemaker/flipper/pull/279). ## 0.10.2 * Add Adapter#get_multi to allow for efficient loading of more than one feature at a time (https://github.com/jnunemaker/flipper/pull/198) * Add DSL#preload for efficiently loading several features at once using get_mutli (https://github.com/jnunemaker/flipper/pull/198) * Add :preload and :preload_all options to memoizer as a way of efficiently loading several features for a request in one network call instead of N where N is the number of features checked (https://github.com/jnunemaker/flipper/pull/198) * Strip whitespace out of feature/actor/group values posted by UI (https://github.com/jnunemaker/flipper/pull/205) * Fix bug with dalli adapter where deleting a feature using the UI or API was not clearing the cache in the dalli adapter which meant the feature would continue to use whatever cached enabled state was present until the TTL was hit (1cd96f6) * Change cache keys for dalli adapter. Backwards compatible in that it will just repopulate new keys on first check with this version, but old keys are not expired, so if you used the default ttl of 0, you'll have to expire them on your own. The primary reason for the change was safer namespacing of the cache keys to avoid collisions. ## 0.10.1 * Add docker compose support for contributing * Add sequel adapter * Show confirmation dialog when deleting a feature in flipper-ui ## 0.10.0 * Added feature check context (https://github.com/jnunemaker/flipper/pull/158) * Do not use mass assignment for active record adapter (https://github.com/jnunemaker/flipper/pull/171) * Several documentation improvements * Make Flipper::UI.app.inspect return a String (https://github.com/jnunemaker/flipper/pull/176) * changes boolean gate route to api/v1/features/boolean (https://github.com/jnunemaker/flipper/pull/175) * add api v1 percentage_of_actors endpoint (https://github.com/jnunemaker/flipper/pull/179) * add api v1 percentage_of_time endpoint (https://github.com/jnunemaker/flipper/pull/180) * add api v1 actors gate endpoint (https://github.com/jnunemaker/flipper/pull/181) * wait for activesupport to tell us when active record is loaded for active record adapter (https://github.com/jnunemaker/flipper/pull/192) ## 0.9.2 * GET /api/v1/features * POST /api/v1/features - add feature endpoint * rack-protection 2.0.0 support * pretty rake output ## 0.9.1 * bump flipper-active_record to officially support rails 5 ## 0.9.0 * Moves SharedAdapterTests module to Flipper::Test::SharedAdapterTests to avoid clobbering anything top level in apps that use Flipper * Memoizable, Instrumented and OperationLogger now delegate any missing methods to the original adapter. This was lost with the removal of the official decorator in 0.8, but is actually useful functionality for these "wrapping" adapters. * Instrumenting adapters is now off by default. Use Flipper::Adapters::Instrumented.new(adapter) to instrument adapters and maintain the old functionality. * Added dalli cache adapter (https://github.com/jnunemaker/flipper/pull/132) ## 0.8 * removed Flipper::Decorator and Flipper::Adapters::Decorator in favor of just calling methods on wrapped adapter * fix bug where certain versions of AR left off quotes for key column which caused issues with MySQL https://github.com/jnunemaker/flipper/issues/120 * fix bug where AR would store multiple gate values for percentage gates for each enable/disable and then nondeterministically pick one on read (https://github.com/jnunemaker/flipper/pull/122 and https://github.com/jnunemaker/flipper/pull/124) * added readonly adapter (https://github.com/jnunemaker/flipper/pull/111) * flipper groups now match for truthy values rather than explicitly only true (https://github.com/jnunemaker/flipper/issues/110) * removed gate operation instrumentation (https://github.com/jnunemaker/flipper/commit/32f14ed1fb25c64961b23c6be3dc6773143a06c8); I don't think it was useful and never found myself instrumenting it in reality * initial implementation of flipper api - very limited functionality right now (get/delete feature, boolean gate for feature) but more is on the way * made it easy to remove a feature (https://github.com/jnunemaker/flipper/pull/126) * add minitest shared tests for adapters that work the same as the shared specs for rspec (https://github.com/jnunemaker/flipper/pull/127) ## 0.7.5 * support for rails 5 beta/ rack 2 alpha * fix uninitialized constant in rails generators * fix adapter test for clear to ensure that feature is not deleted, only gates ## 0.7.4 * Add missing migration file to gemspec for flipper-active_record ## 0.7.3 * Add Flipper ActiveRecord adapter ## 0.7.2 * Add Flipper::UI.application_breadcrumb_href for setting breadcrumb back to original app from Flipper UI ## 0.7.1 * Fix bug where features with names that match static file routes were incorrectly routing to the file action (https://github.com/jnunemaker/flipper/issues/80) ## 0.7 * Added Flipper.groups and Flipper.group_names * Changed percentage_of_random to percentage_of_time * Added enable/disable convenience methods for all gates (enable_group, enable_actor, enable_percentage_of_actors, enable_percentage_of_time) * Added value convenience methods (boolean_value, groups_value, actors_value, etc.) * Added Feature#gate_values for getting typecast adapter gate values * Added Feature#enabled_gates and #disabled_gates for getting the gates that are enabled/disabled for the feature * Remove Feature#description * Added Flipper::Adapters::PStore * Moved memoizable decorator to instance variable storage from class level thread local stuff. Now not thread safe, but we can make a thread safe version later. UI * Totally new. Works like a charm. Mongo * Updated to latest driver (~> 2.0) ## 0.6.3 * Minor bug fixes ## 0.6.2 * Added Flipper.group_exists? ## 0.6.1 * Added statsd support for instrumentation. ## 0.4.0 * No longer use #id for detecting actors. You must now define #flipper_id on anything that you would like to behave as an actor. * Strings are now used instead of Integers for Actor identifiers. More flexible and the only reason I used Integers was to do modulo for percentage of actors. Since percentage of actors now uses hashing, integer is no longer needed. * Easy integration of instrumentation with AS::Notifications or anything similar. * A bunch of stuff around inspecting and getting names/descriptions out of things to more easily figure out what is going on. * Percentage of actors hash is now also seeded with feature name so the same actors don't get all features instantly. flippercloud-flipper-532c39a/Dockerfile000066400000000000000000000006321440444367400202010ustar00rootroot00000000000000FROM ruby:3.0 RUN apt-get update && apt-get install -y \ # build-essential \ # for postgres # libpq-dev \ # postgresql-client-9.4 \ # for nokogiri # libxml2-dev \ # libxslt1-dev \ # for a JS runtime # imagemagick \ # ghostscript \ # debug tools vim ENV APP_HOME /srv/app ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \ BUNDLE_JOBS=8 \ BUNDLE_PATH=/bundle_cache WORKDIR $APP_HOME flippercloud-flipper-532c39a/Gemfile000066400000000000000000000014661440444367400175100ustar00rootroot00000000000000source 'https://rubygems.org' gemspec name: 'flipper' Dir['flipper-*.gemspec'].each do |gemspec| plugin = gemspec.scan(/flipper-(.*)\.gemspec/).flatten.first gemspec(name: "flipper-#{plugin}", development_group: plugin) end gem 'debug' gem 'rake', '~> 12.3.3' gem 'shotgun', '~> 0.9' gem 'statsd-ruby', '~> 1.2.1' gem 'rspec', '~> 3.0' gem 'rack-test', '~> 0.6.3' gem 'sqlite3', "~> #{ENV['SQLITE3_VERSION'] || '1.4.1'}" gem 'rails', "~> #{ENV['RAILS_VERSION'] || '7.0.0'}" gem 'minitest', '~> 5.8' gem 'minitest-documentation' gem 'webmock', '~> 3.0' gem 'ice_age' gem 'redis-namespace' gem 'webrick' gem 'stackprof' gem 'benchmark-ips' gem 'stackprof-webnav' gem 'flamegraph' group(:guard) do gem 'guard', '~> 2.15' gem 'guard-rspec', '~> 4.5' gem 'guard-bundler', '~> 2.2' gem 'rb-fsevent', '~> 0.9' end flippercloud-flipper-532c39a/Guardfile000066400000000000000000000011471440444367400200360ustar00rootroot00000000000000# A sample Guardfile # More info at https://github.com/guard/guard#readme guard 'bundler' do watch('Gemfile') watch(/^.+\.gemspec/) end rspec_options = { all_after_pass: false, all_on_start: false, failed_mode: :keep, cmd: 'bundle exec rspec', } guard 'rspec', rspec_options do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch('lib/flipper/ui/middleware.rb') { 'spec/flipper/ui_spec.rb' } watch('lib/flipper/api/middleware.rb') { 'spec/flipper/api_spec.rb' } watch(/shared_adapter_specs\.rb$/) { 'spec' } watch('spec/helper.rb') { 'spec' } end flippercloud-flipper-532c39a/LICENSE000066400000000000000000000020561440444367400172160ustar00rootroot00000000000000Copyright (c) 2012 John Nunemaker MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.flippercloud-flipper-532c39a/README.md000066400000000000000000000113701440444367400174670ustar00rootroot00000000000000[![Flipper Mark](docs/images/banner.jpg)](https://www.flippercloud.io) [Website](https://flippercloud.io) | [Documentation](https://flippercloud.io/docs) | [Examples](examples) | [Twitter](https://twitter.com/flipper_cloud) # Flipper > Beautiful, performant feature flags for Ruby. Flipper gives you control over who has access to features in your app. * Enable or disable features for everyone, specific actors, groups of actors, a percentage of actors, or a percentage of time. * Configure your feature flags from the console or a web UI. * Regardless of what data store you are using, Flipper can performantly store your feature flags. * Use [Flipper Cloud](#flipper-cloud) to cascade features from multiple environments, share settings with your team, control permissions, keep an audit history, and rollback. Control your software — don't let it control you. ## Installation Add this line to your application's Gemfile: gem 'flipper' You'll also want to pick a storage [adapter](https://flippercloud.io/docs/adapters), for example: gem 'flipper-active_record' And then execute: $ bundle Or install it yourself with: $ gem install flipper ## Subscribe & Ship [💌  Subscribe](https://buttondown.email/flipper) - I'll send you short and sweet emails when we release new versions. ## Getting Started Use `Flipper#enabled?` in your app to check if a feature is enabled. ```ruby # check if search is enabled if Flipper.enabled? :search, current_user puts 'Search away!' else puts 'No search for you!' end ``` All features are disabled by default, so you'll need to explicitly enable them. ```ruby # Enable a feature for everyone Flipper.enable :search # Enable a feature for a specific actor Flipper.enable_actor :search, current_user # Enable a feature for a group of actors Flipper.enable_group :search, :admin # Enable a feature for a percentage of actors Flipper.enable_percentage_of_actors :search, 2 ``` Read more about [getting started with Flipper](https://flippercloud.io/docs) and [enabling features](https://flippercloud.io/docs/features). ## Flipper Cloud Like Flipper and want more? Check out [Flipper Cloud](https://www.flippercloud.io), which comes with: * **everything in one place** — no need to bounce around from different application UIs or IRB consoles. * **permissions** — grant access to everyone in your organization or lockdown each project to particular people. * **multiple environments** — production, staging, enterprise, by continent, whatever you need. * **personal environments** — no more rake scripts or manual enable/disable to get your laptop to look like production. Every developer gets a personal environment that inherits from production that they can override as they please ([read more](https://www.johnnunemaker.com/flipper-cloud-environments/)). * **no maintenance** — we'll keep the lights on for you. We also have handy webhooks for keeping your app in sync with Cloud, so **our availability won't affect yours**. All your feature flag reads are local to your app. * **audit history** — every feature change and who made it. * **rollbacks** — enable or disable a feature accidentally? No problem. You can roll back to any point in the audit history with a single click. [![Flipper Cloud Screenshot](docs/images/flipper_cloud.png)](https://www.flippercloud.io) Cloud is super simple to integrate with Rails ([demo app](https://github.com/fewerandfaster/flipper-rails-demo)), Sinatra or any other framework. ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Run the tests (`bundle exec rake`). Check out [Docker-Compose](docs/DockerCompose.md) if you need help getting all the adapters running. 4. Commit your changes (`git commit -am 'Added some feature'`) 5. Push to the branch (`git push origin my-new-feature`) 6. Create new Pull Request ## Releasing 1. Update the version to be whatever it should be and commit. 2. `script/release` 3. Profit. ## Brought To You By | pic | @mention | area | |---|---|---| | ![@jnunemaker](https://avatars3.githubusercontent.com/u/235?s=64) | [@jnunemaker](https://github.com/jnunemaker) | most things | | ![@bkeepers](https://avatars3.githubusercontent.com/u/173?s=64) | [@bkeepers](https://github.com/bkeepers) | most things | | ![@dpep](https://avatars3.githubusercontent.com/u/918804?s=64) | [@dpep](https://github.com/dpep) | tbd | | ![@alexwheeler](https://avatars3.githubusercontent.com/u/3260042?s=64) | [@alexwheeler](https://github.com/alexwheeler) | api | | ![@thetimbanks](https://avatars1.githubusercontent.com/u/471801?s=64) | [@thetimbanks](https://github.com/thetimbanks) | ui | | ![@lazebny](https://avatars1.githubusercontent.com/u/6276766?s=64) | [@lazebny](https://github.com/lazebny) | docker | flippercloud-flipper-532c39a/Rakefile000066400000000000000000000025741440444367400176630ustar00rootroot00000000000000#!/usr/bin/env rake $LOAD_PATH.push File.expand_path('../lib', __FILE__) require 'rake/testtask' require 'flipper/version' # gem install pkg/*.gem # gem uninstall flipper flipper-ui flipper-redis desc 'Build gem into the pkg directory' task :build do FileUtils.rm_rf('pkg') Dir['*.gemspec'].each do |gemspec| system "gem build #{gemspec}" end FileUtils.mkdir_p('pkg') FileUtils.mv(Dir['*.gem'], 'pkg') end desc 'Tags version, pushes to remote, and pushes gem' task release: :build do sh 'git', 'tag', "v#{Flipper::VERSION}" sh 'git push origin main' sh "git push origin v#{Flipper::VERSION}" puts "\nWhat OTP code should be used?" otp_code = STDIN.gets.chomp sh "ls pkg/*.gem | xargs -n 1 gem push --otp #{otp_code}" end require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = %w(--color --format documentation) end namespace :spec do desc 'Run specs for UI queue' RSpec::Core::RakeTask.new(:ui) do |t| t.rspec_opts = %w(--color) t.pattern = ['spec/flipper/ui/**/*_spec.rb', 'spec/flipper/ui_spec.rb'] end end Rake::TestTask.new do |t| t.libs = %w(lib test) t.pattern = 'test/**/*_test.rb' t.options = '--documentation' t.warning = false end Rake::TestTask.new(:test_rails) do |t| t.libs = %w(lib test_rails) t.pattern = 'test_rails/**/*_test.rb' t.warning = false end task default: [:spec, :test, :test_rails] flippercloud-flipper-532c39a/benchmark/000077500000000000000000000000001440444367400201405ustar00rootroot00000000000000flippercloud-flipper-532c39a/benchmark/active_record_adapter_ips.rb000066400000000000000000000005541440444367400256550ustar00rootroot00000000000000require 'bundler/setup' require_relative './active_record_setup' require 'flipper' require 'flipper/adapters/active_record' require 'benchmark/ips' flipper = Flipper.new(Flipper::Adapters::ActiveRecord.new) 2000.times do |i| flipper.enable_actor :foo, Flipper::Actor.new("User;#{i}") end Benchmark.ips do |x| x.report("get_all") { flipper.preload_all } end flippercloud-flipper-532c39a/benchmark/active_record_ips.rb000066400000000000000000000010401440444367400241440ustar00rootroot00000000000000require 'bundler/setup' require_relative './active_record_setup' require 'flipper' require 'flipper/adapters/active_record' require 'benchmark/ips' flipper = Flipper.new(Flipper::Adapters::ActiveRecord.new) 2000.times do |i| flipper.enable_actor :foo, Flipper::Actor.new("User;#{i}") end Benchmark.ips do |x| x.report("all") { Flipper::Adapters::ActiveRecord::Gate.where(feature_key: "foo".freeze).load } x.report("pluck") { Flipper::Adapters::ActiveRecord::Gate.where(feature_key: "foo".freeze).pluck(:key, :value) } x.compare! end flippercloud-flipper-532c39a/benchmark/active_record_setup.rb000066400000000000000000000013711440444367400245200ustar00rootroot00000000000000require 'bundler/setup' require 'active_record' ActiveRecord::Base.establish_connection({ adapter: 'sqlite3', database: ':memory:', }) ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_features ( id integer PRIMARY KEY, key text NOT NULL UNIQUE, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_gates ( id integer PRIMARY KEY, feature_key text NOT NULL, key text NOT NULL, value text DEFAULT NULL, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value) SQL flippercloud-flipper-532c39a/benchmark/enabled_ips.rb000066400000000000000000000003641440444367400227350ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'benchmark/ips' actor = Flipper::Actor.new("User;1") Benchmark.ips do |x| x.report("with actor") { Flipper.enabled?(:foo, actor) } x.report("without actor") { Flipper.enabled?(:foo) } end flippercloud-flipper-532c39a/benchmark/enabled_profile.rb000066400000000000000000000007051440444367400236010ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'stackprof' require 'benchmark/ips' flipper = Flipper.new(Flipper::Adapters::Memory.new) feature = flipper.feature(:foo) actor = Flipper::Actor.new("User;1") profile = StackProf.run(mode: :wall, interval: 1_000) do 2_000_000.times do feature.enabled?(actor) end end result = StackProf::Report.new(profile) puts result.print_text puts "\n\n\n" result.print_method(/Flipper::Feature#enabled?/) flippercloud-flipper-532c39a/benchmark/instrumentation_ips.rb000066400000000000000000000012141440444367400246010ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'active_support/notifications' require 'active_support/isolated_execution_state' require 'benchmark/ips' class FlipperSubscriber def call(name, start, finish, id, payload) end ActiveSupport::Notifications.subscribe(/flipper/, new) end actor = Flipper::Actor.new("User;1") bare = Flipper.new(Flipper::Adapters::Memory.new) instrumented = Flipper.new(Flipper::Adapters::Memory.new, instrumenter: ActiveSupport::Notifications) Benchmark.ips do |x| x.report("with instrumentation") { instrumented.enabled?(:foo, actor) } x.report("without instrumentation") { bare.enabled?(:foo, actor) } end flippercloud-flipper-532c39a/benchmark/typecast_ips.rb000066400000000000000000000016601440444367400231770ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'benchmark/ips' Benchmark.ips do |x| x.report("Typecast.to_boolean true") { Flipper::Typecast.to_boolean(true) } x.report("Typecast.to_boolean 1") { Flipper::Typecast.to_boolean(1) } x.report("Typecast.to_boolean 'true'") { Flipper::Typecast.to_boolean('true'.freeze) } x.report("Typecast.to_boolean '1'") { Flipper::Typecast.to_boolean('1'.freeze) } x.report("Typecast.to_boolean false") { Flipper::Typecast.to_boolean(false) } x.report("Typecast.to_integer 1") { Flipper::Typecast.to_integer(1) } x.report("Typecast.to_integer '1'") { Flipper::Typecast.to_integer('1') } x.report("Typecast.to_float 1") { Flipper::Typecast.to_float(1) } x.report("Typecast.to_float '1'") { Flipper::Typecast.to_float('1'.freeze) } x.report("Typecast.to_float 1.01") { Flipper::Typecast.to_float(1) } x.report("Typecast.to_float '1.01'") { Flipper::Typecast.to_float('1'.freeze) } end flippercloud-flipper-532c39a/docker-compose.yml000066400000000000000000000014241440444367400216440ustar00rootroot00000000000000version: "2.4" services: # postgres: # container_name: flipper_postgres # image: postgres:9.4 redis: container_name: flipper_redis image: redis:6.2.5 mongo: container_name: flipper_mongo image: mongo:4.4.8 memcached: container_name: flipper_memcached image: memcached:1.4.33 app: container_name: flipper_app build: context: . dockerfile: Dockerfile volumes: - .:/srv/app volumes_from: - bundle_cache links: # - postgres - redis - mongo - memcached environment: - REDIS_URL=redis://redis:6379 - MONGODB_HOST=mongo - MEMCACHED_URL=memcached:11211 bundle_cache: container_name: flipper_bundle_cache image: busybox volumes: - /bundle_cache flippercloud-flipper-532c39a/docs/000077500000000000000000000000001440444367400171365ustar00rootroot00000000000000flippercloud-flipper-532c39a/docs/DockerCompose.md000066400000000000000000000012771440444367400222240ustar00rootroot00000000000000# Docker Compose for contributors This gem includes different adapters which require specific tools instaled on local machine. With docker this could be achieved inside container and new contributor could start working on code with a minumum efforts. ## Steps: 1. Install Docker Compose https://docs.docker.com/compose/install 1. Build the app container `docker-compose build` 1. Install gems `docker-compose run --rm app bundle install` 1. Run specs `docker-compose run --rm app bundle exec rspec` 1. Run tests `docker-compose run --rm app bundle exec rake test` 1. Optional: log in to container an using a `bash` shell for running specs ```sh docker-compose run --rm app bash bundle exec rspec ``` flippercloud-flipper-532c39a/docs/README.md000066400000000000000000000001101440444367400204050ustar00rootroot00000000000000See [flippercloud.io](https://flippercloud.io/docs) for extensive docs. flippercloud-flipper-532c39a/docs/images/000077500000000000000000000000001440444367400204035ustar00rootroot00000000000000flippercloud-flipper-532c39a/docs/images/banner.jpg000066400000000000000000001127301440444367400223560ustar00rootroot00000000000000ÿØÿàJFIFÿÛC  !"$"$ÿÛCÿÀôÜ"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?û Š(« (¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÍQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Ph¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¨/îí¬,g¾¼™ ¶·¥šG8TEf>ÀS×+ñsI¾×~x‹IÓUšòæÂE…à»c;ûØÇã@J¿þ!x¿Q»? <¡¥Ú¹Fº½ w÷ìèã¹cÈõ¯Fø9ã?x¶ËP_øFç@¼°”BìÀ¬s62B«|ÃzŽG5æß³ÄhßbðÆ·©ÚèšžŸ<ÞrÞ7”%Üåƒ<3´Ž¿/Jõßü@ð§®u êBñôùMû¶PAèˑʒÏ·¦ HlꨢŠb åþ&øßHð…¥×µ}î¡„PA7Ï!„éÀ$žÀ¥uà_¶ž¨]ø?FÖm¡iìô˶7h2@W+vÊíÏût0EHþ-|k¼Óá#°ø_nÚ _5Yf®áó‚F9ÈëÔ~üGÒ>$xuõ->'µº·aå£¶æ…ˆÈ ÿœ‡ŠÍÓ>6|3ŸÃêçÄv–а†k7Èž2(#$Ž™{â¼ÿöE³»¾ñŒüc›Ùhú•Ñ[HÈÀ$Èî@ö@À~'ÐÒôMQLA\'Çoêžøyqâ" 9îâž(Õ.‘š<3`ð¬§?wuäµïü‘KÞßévÿú ¿‚lþ#xM¯ Ãm¬Ú·öˆN±Ã¨$Ž3Ђ2q“Kösø‡­üFðΣ©ë–º}¼Ö·žB-œnŠWb·;™Žr}kÉ5 jßü?á/‹Þ „˜ŸG³å’ä#«B›œü-ÆïFÃzã«ýˆ?äC×p0?µö’Rô QLA^}ñkXø¡¥Üéëð÷ÃZn³Šæðݰ2ÚïS¯ÍëÓµz ó7„¾/üqñd—ñø{Á¾½}=ÄwJ“ËbNÍp3÷OLô¯¤t׺“N¶’ú5Šé¡C:/Erà9<žæ¼ ö>9Õ¾ ØF/ý zú’QE0 ùâ_Šÿµˆ^%ðσ|/áíI4kÉbýèdqÈQY‹N “ŽÃð¯¡ëäO ØüE½øÛñü9Ö¬4Û¸¯î^ä]"·š¾{aWtn3Ÿ\}i1£ÕþüYñ«ñoøïÃqhºç”d„ÀÇcáwÁ-ü9 †#‚+Ù+æÙ~ßþŸˆº×‹¼[¬Ý]xÃOg4B3°ÉÇÌ›@sÎw }1B (¢˜ß][ØÙO{y2AmoK,Žp¨Š2Xû x'ü.¯ø»P»ÿ…]àHõ2Õö5ÝðoÞ½N9Û’pG®+Ö>-é7ÚïÃ?é:h-ysa"€à»c;?àXÇã^Cû1üIðf‹ðò? k𕶉©éóÍæ­ßî„Ûœ¶àÇŒŒí óòô¤ÆŽ«á?Æ)|Gây|âí ü=âXÃl„ä$Ä r§oÌ ŽA¯]¯˜/5[O‰µNƒ¨ø<4ö41›»äBÄlìÍÏc¸ =þ˜¯§é¡0¢Š(;—1ÛË"€J!aŸa^iû:|BÖ¾#x[PÕu»]>Þkkß³¢ÙÆè¥v+dîf9Ëõéßñå?ýsoå_7þÈ+ðƃà}ZÛ\ñ•¦M&¤^8îîÒ&uònˆÈÈ?•è}/EgèZÖ¯Yµæ‰ªÙjVÊæ6–Öu•€ \©#8 ãÜV… (¢€ (¢€ (¢€+jbøé×#L{t½ò›ìíp…£cåÜ‚W8Î5埾,Ýë×^ Ðüuoa¢ëº!ygŽ Éœ;|ÌÇå=NpC)ë×+åÿÛ7ÃVVÚ¾‡â[{[íIžÆðÇÀ•@'ðJŸQÚ“zWÁˆ^)ø‰¯ëZ€Ó´û?ZJÐÙJb“í3¶xËoÛÂò~^7(õ5êÕ‘àïi¾ð͇ô˜¼»K8‚.z¹êÌެĒ}ÍkÓ¢Š(ø·ñ Gøsá¯í}MâiŸÊ´´Œ€ó¾3ÔôP9-ÛÜ•?Åpéÿð‘Ü|1µ]/šFÙ<á]ÇçÈçw—ŒsGía{Ï„(ð6­áÍ3Âúv™{s¬<‘„¼G9pѪ€C /ßô®WWø¿ñsÁ/k}ñ Àm¾‘4â&–ÊO˜d€D² Ø€qœuïU¿kè¯æñ·ÃØ´©ãƒP{™VÖYV9L°lcÁà6;¡®GÇ6ž>Ô¾"è~øÅâýºMÌÉ4WVpF°HÄ0B'9ÊeÚ[8Ç%1Ÿ\XÜÃ{eå³ï‚xÖXÛûÊà þF¦¨líá³´†ÒÝpÁÇáUò51Q@Q@Q@þ#Ö4ÿèWšÖ«8‚ÊÎ#,ÎFp`;“ÐäŠð‹?ŒŸ¼Z'Ôüðâ '*²Ýîg“„:}BîÇ©¯Bý¤tmG]ø7®Yiq<× ±Ïä É‘c‘]€Î w W'ð ⯀mþé:V¡®Xè÷šm¿“</åî ŸIûÛºñÎI¤3¾øEâÝkÆ}G]ðÅׇîâ h¦È•ಆÃFê:žqÙ×7àøoÇ:}Í÷†ïÔ6Ó˜eÝ#ØàŒàŽAþ¹ÒSQEÄ|aø‘¤|7ðôzŽ¡Ý]ܱŽÎÍkLÀd’…FFNQÁÍymÇůš~œ|G©|0¶]WÍpE™#ë–ùÉQŽæ< oíqky¦ø«Á^4–Íït}6è ¨ÀÈJ’`úo Güé^ƒªüløg…¥Õ‡ˆ¬ï¡%l—&y pÆFA=9À÷Å!Ã/é?ð´Zö]±Šx$Æø%‡x ƒÜô®¢¼ö,ÑuêÚµÔO ¦§x­hŒܨ.=‰8Ïû5ï”ИQEæ_´WÄ káׄ¬5mÖÂâ{‹ñlëy²1»de9ÊŽþµÄ^üUøÕá‹EÖ|aðãNmå2Kc! Šz1"I¨EXý·N>hçÿ‰Âÿè™k ø‰ñOÀ? /í×Ä:^©sq¦µ´vv“¬Ìîñ•‚“´dòN?ñ>•ã YøƒG‘žÖåI ã†Fˆ Ôdb¶ëÇÿdMQÑþÇ&£‘h^Éy8Á2¢©Ç¡ØXz†½{4 ¢Š(Èh‰>*ð>µá­/ÂÚv—}q¬¼‘ì¼G$¸hÕ‘"’ýýºV~›âOÚBMFÙ/üᨭZTÈ“¦U7 Ä¥@Ïc\ÏíâïÅý¥ý—¾i—í»¶ý›2@<ÌäcoÞÎGN¢¯x#FµÒɨæð}”¶í«$«ë˜m˜öÆ+»±´µ°³ŠÎÆÚkhT$PÂt©è Š( ±ü_áź#è¾!²ûmƒº»Eæ¼ye9(AýkbŠ£g¤éÖš:6‰ý› ªÚ%»å×É °!Ý’ÃhÇ9ÏzÍðGƒ<5à«‹ é¿`·¸—Ε<ù$Üø9v$pŠè( Š( Š( wÁþ ðÏ„&Ô&ðî™ö'ÔdÝŸ>I<ư~v8ûÍÓk¢¢Š(¢Š+ð÷‚¼1 x‡Tñ‘¦}›RÕ\½ìÞ|æ±bÄíf*¼’xº*(˜·ð„­üm'ŒíôŸ#]—>eÌW |®Óº0ÛF3•ëÏ^k§¢Š(¢Š+‹ñ_¿‡Þ)Ô[QÖü1iqvÇ/4nð»ŸV1²î>ç5ÚQ@Þ𿇼+`l|;¤Zé°1ˈSÏ«1å¹&¶h¢€ (¢€"«£# «ö¯3ÿ…ð—þ…?ü¨ÝñÊôê( Á^ð÷ƒ4©4¯ iÿa³’s;GçI&\…åØžŠ8Î8­Ú( Š( Š( Š( ¹ïø+Ã>3†Ò/é¿nK9 °>Hö1àŸ‘†zw®†Š(¢Š(¢Š†öÖÚöÒ[KËx®mæR’Å*GSÔx"¸$ø'ð±5·Yù»·m2ÊcÏýs-³ØÅz´Û[Çoo pÄŽ8Ô*¢ŽpIEQE í­µõœ¶w–ñ\ÛL…%ŠT ާ¨ ðEp–ß¾[ß‹èüde;]äxóÿ\ÙŠ~•è4PQUQ*¨Â¨zS¨¢€ (¢€9ßx'Ã(Õ4½O]Ó>×w¤ÉæÙIçÈžSnV΀nQzƒÒx#Âþ6´·µñ>’š„VÒ!̯#ƒ†FÓ8àz 訠­ ŽÖÒXwùp¢Æ›ägl–bI>ä’jZ( Š( Š( Š( ¸={àÿÃ]sR}GQð›Ü»vŠI!Ç©+($÷$s]埠hºN¦G¦hºu¶Ÿg݆®{ž:“êy5¡EQE_P³´Ô,¥²¿µ†êÖeÙ,3 tuô ðEp|øY /ÓÁÖfPÛ¶¼²¼yÿ®eŠcÛètP!Š8aHa#*".@è>Š(¢Š(Æþðç4Øtïißoµ†aÇ ·‰tAÖùcý*{]kJ¹`°ßÀXð›i?Åy“¯$Ÿz–Ÿ±ˆ½£=†Šó-_¿ÒT9šØu…ϲ{*ô=2úßQ³K«WÜØõSÜØÖS¦à\f¤Z¢Š+2Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š( Š(4QEQEQEQEQEQEWŸøã ºmSJBÛ‰y {²ÿ<~^•èUÂnè™EIYŸ>É&*rsü«ºø¡á±fͭئ ‘±q#Œ=‰ý~¼pöp›-Ä`þuèFjJèä”y]˜ûh^cò€»V倚^ÞÞI‚JG˜ÈQÒ›glÌÐ1ß°«Šë”€c=Xõoð©“ÇúÓ–þâÿSR‰¨kè¿ãQFµ:%C- +žqÍ.Î*eLÓ¶qš›ŽÅr•¨ÇNjÓ-1—< ’})¦"› t­êma«¤Çì÷$#Fþùñô5˜ëÆ{g¼Õw®A‚*šRVìî{ ”¾}œ3ž<ÈÕÿ1šš¸ ¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠŽæh­­ä¸™ÂG–v=€ë@¾7Ôí,49¢¹E•î‘¢Ž"~öG'è3ü«Ë¬­Ã(ÉÙOôoYÔg×õ©.œ•¤j嚟õ5:°Æ1ô¾õßN‘·S–ræd¥÷€ˆ6Æ:/õ54kúÔP¯­Z‰zqM‚$D«£‘¡Ç5þõܺU®"Šh^ cIctu ¬;‚QºË|áMSB°ð¿Š-µ‹YíµÙŒSXÇËÇbÄç Wo< §'5ßÈ=ºŠ4ÿéz=ÓæÎïñ´ÿZáeÆp:UI²:qT¨Á‹ÚIÅ¡ëzv³5”Ùuûñ¸Ã¯ÔQÅiW‡hÚ„Úf© ì,A‰Á`?‰{ƒõî5ÏZŸ#ÐÖœùQE‰ QEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEQEW ñOU1ÛŤBØiy6?ºÊ?Ïà+¸b$êMxŸˆ/›SÖ®o[8’C°È8Qù]xsJýŒªÊÊÅx•YAÀlgêÜ¥V„}E[„{W\ŽtY‰xâ®D¼ ¯ö«‘/”™¢&ˆdÖ–Ÿ§ÝÞso2ÿ|ð¿™©¼)”º€Šî!#0Ì[˸uwãùWoò¢vUQôW4çgchÆêç;ká£Á¹¹Õb_ê´aдØÇÍ H}]Éý:USÅúM¡hàv¼qˆ~è>ìxü³XW4Ôe$[Û[@½·eÏô¥oO‰ª¯k/=?àžV#=Ëðï•Ï™ùkøíø¢iÚz -•°ÿ¶B‡ÓtçkCÿl–¸Aâmq¿åñGÒÿ •"]ýšw‡íº'™ »XèÛþe8È8ä^‹Eyšü\'?ñl>%‚=t/þÙZ>ø‘ý¿¯[i?ðøëJó÷¥jZG“oÔ-ó¾óŒãŽI»º(®­yýŸ¥]ßý–êïìÐ<ÞE¬{æ—j“±ø˜ãw$Wž‹Ùÿš_ñ4Üÿ³¯M¢€<Èü^ÇüÒÿ‰¿ø!ÿí•ÓxÅÿð•ý³þ)è_eÙÿ!›³yÛ·«ùŽìmçÓ#Öºz( ÆÞ#ÿ„cKŠÿû]ÖüÉÄ>F‘gö‰—*Ç{.FmÁ>¤z×~/ãþiÄÓÿpþν:Šóø\õK¾'àƒÿ³®ïºÇöþƒm«eêšWŸ»ýR·òn#Úå~tÉÆq‘Ï ƒZ”PãÂ/ªÅ§ÿÂãoÌ€MöKûD+–a°¶á†ùsB=kþÿ…[ñ?ÿýz}æ?ð¸:ÿÅ®øÿ‚þν:Š(ÎõoŠÙú­å‡ü+ˆ—eáóítMð˵ˆÞ¼nCŒƒÜUQñ?óK¾'®ÿÙ×§Q@'…¾#ÿokÖúOü ~:Ò¼ýßéz–‘äÛǵK|ϼã8Àã’@®ÇW¼þÎÒo5²Ý]ýš›ìö±ïš]ªNÄ_âcŒÜ‘V¨ 1ò3ÿ »âwÓûÿ³ |_Éÿ’]ñ8}tþÙ^Erþñ‡ü%mÿŠ[ÄúÙvÈfÃìÞvíßêþc»yôܾµoÆÞ$ÿ„_J‹PþÁ×u¿2qÙô‹O´L¹V;Êäa~\gÔZÝ¢€<Ëþ÷ ¯ø›ÿ‚þΓþÿ®øÿ‚þν:ŠËð®±ý¿ ÛjßÙZ¦•çîÿDÔ­ü›ˆö¹_2qœdsÈ ÷¬/øóþ}V-?þÏë~do´igÚ!\³ …· 0Ûœzë]æãGü’ï‰ßø ÿìéGÅÿú¥ÿ¿ðAÿÙ×§Q@yÖ­ñSû;U¼Óÿá\|E»û4ïÚ-tMð˵ˆÞ¿æSŒƒÜ^‹Ey‡ü.ú¥ß¿ðAÿÙÖ§…¾$oköÚGü ^:Òüýßéz–‘äÛǵ |ï¼ã8Àã’@®îŠ««ÞgiWš‡Ùn®þÍÍö{X÷Í.Õ'b/ñ1ÆîH¯;ÿ…ÁÆáW|NÿÁÿg^Eyü-ÿú¥ß¿ðAÿÙ×OàOÂWößø¥¼O¡}—ËÿΟöo;vïõ1ݼúdz×OEaxÛÄðŒiQßÿ`ëºß™8‡ìúE§Ú&\«år0¿.3êG­qÇâþ ð«¾'Ôÿ¶W§Q@aÿ ƒþ©wÄïüöÊîü+¬oè6Ú·öV©¥yû¿Ñ5+&â=¬WçLœgò5©EqÞ7ñßü"ú¤Vð†xÇ[ó yúF™öˆW,Ãamà òç„zÖü.@ÿ…[ñ;Ÿú€öÊôê(ÌÆ ­øœîÿÛ+Ó袀<ëWø©ýªÞX¸ø‹yöiÞ>×Dß »XèÛÆä8È=ÅUÿ…ÁÿT·âþ?û:ôú(„ð·Äíí~ÛHÿ„ ÇzWŸ¿ý/RÒ<›xö¡o÷œgrHë±Õï?³´›ÍCì·Wfæû=¬{æ—j“±ø˜ãw$Uª(Ì?ápÿÕ-øŸÿ‚þÙR\|\8Uøgñ.PFršÇÓ—étP/àOÂYöÏø¥¼O ý—Ëÿ͇ټíÛ¿ÕüÇv6óé¹}jß¼Iÿ¾•¡ýƒ®ë~dâ³éh™r¬w•ÈÂü¸Ï©µ»Ey‡ü.ú¥¿ÿðAÿÙÑÿ ƒþ©wÄïüöuéôP_…uíýÛVþËÕ4¯?wú&¥oäÜGµÊü铌ã#žA°¼oãÏøEõh´ÿøCbDÜœg Ö’%aÍ\ˆUkxÜÛÇqåÈ!“î¹RíVáê+Í.Z–ŠT‘ÖF ¡âMsRÔåü¨ʈ#?/Ï÷¿=«FéXšìa5F#øÑXýzJíÊÔ]g̵¶‡ÍqgµX(¸I¥Íf»¦žÿqZ1ÍXŒT1õ©£¯zGÁÒDè*Äuv«׫†ñŽ¥9P“Ö;zÀ™=Q^YôÁEPEP ⟈ÿØ:íΓÿ޵_#oúV›¤yÖòn@ß#ïÆì8 ŽÕ–~0cþiwÄïüöuéôPi?´u[M?þÇÄKO´Îù÷Z'— [˜ îÛþUÉ=€5è´Q@aÿ ƒþ©wÄÿüöt£ãýRï‰ßø ÿìëÓ¨ ;Á><ÿ„£V—Oÿ„3Æ:'—›í¾™öx[ £`mÇ-óg€úVì çVþËÔõ_#oú&›oç\I¹Âü‰‘œg'ž&µ( 0ÿ…ÁÿT»âwþ?û:Sñƒþ©wÄïüöÊôê(Á$ÿ„£J—PþÀ×tO.sÙõ{?³ÌØU;ÂäåNìgÔJ«ã¿Â)ö/ø¥¼O®ý«Ìÿ5‡Ú|»Ö|Ãnwqëµ½+¨¢€<ÃþýRï‰ßø ÿí”§âþ?æ—üM?÷ÿìëÓ¨  ºMçöŽ•i¨}–êÓí0$ßgºdÑnPv:ÿ àŽÄã¼Uñ#û^¹ÒáñÞ«ö}¿ézn‘ç[ɹC|¼gÁã‚í]Ýæð¸?ê–üNÿÁÿl«ZGÅOíVÓOÿ…oñÓí3¤?hºÑ<¸bÜÀovßò¨ÎIì¯E¢€ óø\õK~'àƒÿ¶W§Ñ@aÿ ƒþ©oÄÿüöÊÞðO?á(Õ¥Óÿá ñމåÀfûF¯¦}žÃ(ØqËÙÐJìh  ¿kØ έý—ªj¾FßôM6ßθ“s…ù#8ÎO<Ojá?ápÿÕ-øŸÿ‚þÙ^ŸEy‡ü.ú¥¿ÿðAÿÙ×càÂQ¤Ë¨`kº'—9‡ìúÅŸÙæl*árr¿63ê¥oQ@ÇŽüaÿ§Ø¿â–ñ>»ö­ÿò°ûO“·oúϘmÎî=pÞ•ËÿÂàÿª[ñ;ÿý²½>ŠóñåÝÿ ¿âo\cûŸý½H¼þÑÒ¬õ²ÝZ}¦›ì÷Qìš-ÊÇ^v°ÎìA«TP ⯉ØíΓÿ޵_#oúV›¤yÖònPß#ïÆì:‚;V_ü.3ÿ »âwþ?ûez}çZGÅOíZÓOÿ…qñÓí3¤?hºÑ6Cæ{¶ÿ•FrO` z-P˜Âáÿª[ñ?ÿý²ø\õK¾'àƒÿ³¯O¢€8ïxóþV]?þÏèž\o´júgÙál2·±Ý=ô­ßkØ έý—ªj¾FßôM6ßθ“s…ù#8ÎO<MjQ@aÿ ƒœ­øÿ‚þÙKÿ œ¯ø›ÿ‚þν:ŠÁðG‰?á(ÒeÔ?°5íËœÃö}bÏìó6Nð¹9S»õÒ«øçÅÿðŠ‹Oø¦¯göy› §x\œ¯ÍŒúƒé[ÔP/ã¿Â'ö/ø¥¼O¯}«ü´ÿ´ù;vÿ¬ù†Üîã×kzW1ÿ ƒþ©oÄïüöÊôú(ÌáoÿÕ.øÓþ€ýz&‘yý£¤Ùêeº´ûL 7Ùî£Ù4[”Ž¿ÂÃ8#±­Q@'о#ÿ`k×:Oü ^:Õ|¿ézn‘ç[ɹU¾GÞ3Ø•»â­cûA¹Õ¿²õ=WÈÛþ‰¦Ûù×n`¿"dgÉç€ ­J(ÌápÕ.øÿ‚þΓþ?òK~'ÿàƒÿ³¯O¢€0|âOøJ4©uì wDòç0ýŸW³û<Í…S¼.NTîÆ}Aôª¾;ñ‡ü"bÿŠ[Äú÷Ú·ÿÈOûO“·oúϘmÎî=v·¥uP˜Âàÿª[ñ?ÿýãÿ…]ñ;ÿýz}WH¼þÑÒ¬õ²ÝZ}¦›ì÷Qìš-ÊÇ_áaœ؃\wо$`k×Oü ^;Õ|¿ézn‘ç[ɹC|¯¼gÁã‚®îŠóñ€áW|NéŸùöÊOø\õK~'àƒÿ¶W§Ñ@7x‡þƒú®ëîOñ ø“Ä=N¿ªý>×'øÖPöïG=ÀÎkõ¯«ÑþU÷#ð­â?ýìÕÿ„“Ä<ÄûT?ö÷'øÒÂGâ3ý¿ª“Ÿùü“ük+µ/8ÈÍW£ü«îAõºÿÎþöjÂIâ/újŸø'øÒÂGâ.Úþ«ÿ’fu¯GùW܃ëuÿýìÕ>$ñ8þßÕð.Oñ¤ÿ„“Ä]?·µ_ü “ük/ƒÉ8ô¤ïGÕèÿ"û}n¿ó¿½šÃÄž"ÿ ö©ÿrð’x‹kÚ¯þIþ5“Ú•¸4}^ò/¹ÖñÎþöjÂGâ>Úþ«ÿr â_äÿÄûTßk“ük/§4„õ£êô‘}È>·_ùßÞÍCâOÿÐTÿÀÉ?Æ•¼Gâ!ÿ1íW¿l“ük)@$’ÀqšL{â«ÑþE÷ ëxç{5á$ñýõ_ü “ühÿ„“ÄGŸííWÿ$ÿÊæ×$âŸÕèÿ"û¾·ˆþw÷³Px“Ä_ôÕð2Oñ¥ÿ„“Ä_ôÕ?ð2Oñ¬®3ÖŽý(ú½ä_r­â?ýìÕÿ„“ÄY?ñ>Õð2Oñ¥ÿ„‹Ä`ãûUÏý~Iþ5”¹Ï‘Ÿ~i}^—ò¯¹ëuÿýìÕ>$ñ?ñ>Õý¾Iþ4‡Äž"ÿ þ©ÿ’eŸÎ’«ÑþU÷ úæ#ùßÞÍ_øI|EŒÿoj¿ø'øÒÿÂGâ<È{UÿÀÉ?ƲGN™¥Ï¨ú½ä_r­â?ýìÔÿ„“Äcþcú¯?ôù'øÑÿ 'ˆ¿è?ªÿàdŸãY}Eð3ƒj>¯Kù܃ëuÿçãûÙ«ÿ ˆ¿è?ªàdŸãGü$ž!Ï:þ«úü“ük(A¥3GÕé"û}n¿ó¿½š‡Äž"Çö«Çý=Éþ4‡Äž"ÿ ö©ÿ’eޤæŸåIåyÆ6òÁÆìpMW¢¾Êûþµˆ{MýìÑ>#ñ_íýSÿ$ÿ?á$ñýõ_ü “ük/·_ùßÞÍOøI·ˆþw÷³Tø“Ä8Ïöþ«ÿ’ð’x‹þƒú§þIþ5”ãG&—Õèÿ"ûþ·ˆþw÷³Wþ_Ð{UÿÀ¹?Æ“þOÐUÿÀÉ?Ʋۯ4cŒÑõz?Ⱦä[Ä;ûÙ«ÿ /ˆ³ÿ!íWÿ$ÿ‰Ý(ú½ä_r­â?ýìÔÿ„“Ä_ôÕð2Oñ£þ?“‘¯j¼ÿÓäŸãYX4v§õz?ʾä/­â?ýìÔÿ„“ÄXÿþ«ÿ’ð’ø‹þƒÚ¯þIþ5—IGÕèÿ"û}oüïïf·ü$ž"Ïö«ÿ’ð’ø‹ŸøŸê¿ø'øÖOzrŸZ>­Gù܃ëxç{5?á$ñ?ä?ªÿàdŸãGü$ž"ÿ þ©ÿ’esÍ%V£ü«îAõ¼G󿽚ÿð’x‡kú¯þIþ4‡Äž"í¯ê¿ø'øÖPâŽ)}^ò¯¹ëxç{5á$ñýõ_ü “üi?á$ñýõ_ü “ük,Òv§õz?ʾä/­â?ýìÕ$ñ}UÿÀÉ?ÆøI|Eùê¿ø'øÖ_LÑGÕèÿ"û}oüïïf¯ü$ž"Æ·õOü “ühÿ„“ÄgþcÚ¯þÉþ5”z`zõ§w và÷ô¥õz_Ⱦä?­â?çãûÙ§ÿ 'ˆóÿ!ýSÿ$ÿ?á$ñ?ä?ªÿàdŸãY4½éý^ò/¹ ëxç{5G‰æîâ[‰ŸÍÝ$®Y›¸'ž€ ùôŒÈæ½÷á7ü“ý3þÚÿèׯâjTá„‹ŒR÷—O&}wW«S597î=ßœNªŠ(¯†?L (¢€ (¢€8‹­óiŠ=%?úp°c5Üü]_Þi‡ÔJ?ô á Åz4†ŽJŸ.CÁ§ÏÜÏ(>f¯ÉÀ¦EÚ¶ü/ Íâ9qÿ-Ã~C?ҔݕÂ'{„p[%¢ 1Æ‚0¤d+Š–æÍµ«Ë;@Á “j’r½`r+½ñ ÈÓ4;ÍCÐÂ̾íŒ/êExµœ²[È’«t9äõ=ÿ>kl³õšs›ô^¿×æ|ÿqöf"…$®ž²ÿÚ~/äw0šÆñ¨¨V%ÏæMkZȲF’'*Àô5Ï_Î./¦˜r¥°¿AÀ­rªm×o²2âÌD OâjÞ‰^ÿ—Þ Ö§CUäT:ñ²XØEæo$›Å{®.NÈø:sQWf²*xÏ¥déÚµÙ¬R_ùæýOÓÖ´ãa a8µ£=5ÕY]ÚÝnû5Ì3íûÞ[†Ç×mµxý¼ÒÁ*Ío+E"Ÿ•ÔàŠî|3âU¾d´¾ÛÑáp²ÿƒ{wª¯„pWZ¢ð¹„fùe£:˜íQ—®kÆ^,·ÐÐÛB}A†V2~XÇfè:Ÿ¥sS¥)Ë–+S¶¶&4áÍ'dl꺖›oö‹û¨­ã= ž[اð®NñŽ©ëPé–v÷MæîÄΨ“Ӯ8ö¯0Ô¯¯5¶»¾æº»žÞ€v·~ÿ„ª=ØÈ†]¿]¿ášõ~£tܤîì|ûÌçZªŒU•ÏNs]§.<Ý*Krraàz)çù渗5Ñü<—·Pÿz0ß‘ÇþÍ^6aO›ßcèòþÏ0‚ïuøŽÒŠ(¯™?H (¢€ (¢€¯Gù܃ëxç{5á$ñýµ_ü “üh>#ñ l{UÓí’eqKßÔžô}^ò/¹ÖñÎþöjÿÂIâ/új¿ø'øÓO‰%ñýµQÿo’ð’x‹òÕGý¾Iþ5”~¢Ž(ú½ä_r­â?ýìÕ%ñä=ªàdŸãGü$ž"ÿ þ«ÿ’etïA£êÔ‘}Áõ¼G󿽚£Äž"Çü‡µ_ü “üi?á%ñýõ_ü “ük,zP2xö£êô‘}È>·ˆþw÷³SþOÐ{UÿÀÉ?Æ”x“ÄDøŸê¿ø'øÖPéG«ÑþE÷ úÝç{5?á$ñýõ_ü “ühÿ„—ÄXÿþ«ÿ’ev¢«ÑþE÷ úÞ#ùßÞÍ_øI·ˆþw÷³Wþ_ÐTÿÀÉ?Æ“þOÿÐUÿÀÉ?ƲÀÍéýZò/¹ ëxç{5—Äž!Áÿ‰þ«ÿr!ñ'ˆ»ëÚ¯ôù'øÖRÒãš_W£ü‹îCúÞ#ùßÞÍAâOuþßÕ?ð2Oñ¥>$ñsý¿ªû|“ük'Žô6:”}^ò/¹Öëÿ;ûÙ«ÿ 'ˆ³Æ¿ªàdŸãJ|Iâ¿Ûú©ÿ·É?Ʋ-V¥ü«îAõºÿÎþökx‹òÕð2Oñ x—Ä ƒý½ªŸ­Ü˜þu•œðdj>¯Gù܃ëxç{5á#ñkÚ¯þIþ4ßøI$ñýµ_ü “ük.ŒqGÕèÿ"û}oüïïf§ü$ž"ÿ þ«ÿrð’ø‹þƒú¯þIþ5•EW£ü‹îAõ¼G󿽚¿ð’x‹þƒú¯þIþ4«âOûTöù'øÖW~h>Ý(ú½ä_r­â?ýìÔÿ„“ÄYÿö«ÿ’8ø—ÄCííWÿäÿÈÅ/>ô}^ò¯¹Æb?ýìúžŠóøBþ-gþKOþZö¿üU)ðgÅœ`|iüáµÿâ«òC÷£Óh¬¿ Yjú~ƒmg®ë_ÛzŒ{üëï²­¿›—%v¤…¼uÆ{Ö¼?ã}WUŠçÃ_‡,Öký Þù712orÈ*1ÓåÏzìh¯2> ø³ÛãIÿÂ^×ÿЦÿÂñk?òZ¿òÖµÿâ¨Ó袼ïVð—ÄëVîâÃâá±³–wx-á¶“ÈŒ±+òrÛFãÉÆhÑ(¯2 ø³ÿE§ÿ-{_þ*µ<-ᯈ:~¹mw®|MþÛ°weý…¿›•!x§+†!¸ëŒw 披«Cws¥]Ûé÷¿a¼–K{¯(IäHT…}‡†Úppx8ÅyÙðgÅœÉiçþÅ{_þ*€=:ŠóøBþ-gþKWþZÖ¿üUuÑ|[£‹ÏøJ|kÿ ?›³ìÿñ+ŠÏÈÆíßêÉÝ»+צßzéè¬iž"Õ´¨­ü3âøG/pïuöî÷ǵMŽ@%Nzü¸ï\wü!óÿ%¯ÿ-k_þ*€=>ŠóøBþ-ÿÑkÿËZ×ÿŠ®ï¶Z¾ ÛYëºßöÞ£ï:ûì©oæåÉ_ݧʸRޏÏzÔ¢¸ïxǶ«dž¾!Â9f°*=¯ö47{ä ľ÷ Œ‚£>\÷¬#à¿‹]¾5cþå{_þ*€=:ŠóøBþ-ÑjÿËZ×ÿНN Šó­_Â_îu[Ë?âïØ,åÞÞ×þ»i|ˆË±ï-–Ú02y8ÍVÿ„/â×ý¡ÿ„µ¯ÿ@EpžðÇÄM;]¶¼×~'ÿniñîó¬° ·ór„/ﲸbޏÇzìux.ît«»}>÷ì’Àéouå <‰ ²l<6ÓƒƒÁÆ(Õæ?ð…üZÿ¢Õÿ–½¯ÿH<ñk¿Æ²îWµÿâ¨Óè®cÀº/‹t¶ÂQã_øI¼ÝŸgÿ‰\VgÆíßêÉÝ»+צßz³ãm3ÄZ¶•¿†|QÿåâÎî¾ÁÞøö°1ìrÉ*s×åÇzÞ£ ø·ÿE¯ÿ-k_þ*øBþ-ÿÑkÿËZ×ÿŠ O¢Šó­_Â?.uk»?âïØ,åÞÞ×þ»i|ˆË±ï-–Ú02y8Íz-æ?ð…üZÀÿ‹Ô}ÿâ—µÿâ«O¾ø‰§kÖךïÅí½:=þuö½¿›•!x‡ráˆn:ã転ªêÐÝÜéWvö¿`»–H.¼¡'‘!RM‡†Úppx8ÅyÛx3âÑ<|iÇýÊö¿üUzuæð…ü[ÿ¢Õÿ–µ¯ÿ]?4_hÿmÿ„§Æ¿ð“ùÛ>Ïÿ¸¬þÏÛ¿Õ“»vW¯M¾ôÔQX^6Óø…ÿÝšÀí±¡»ß&æ&MîAF:|¹ïXGÁŽqñ¨û•­øªôê+Ì?á ø·ÿE¯ÿ-k_þ*½>€ +εo |O¹Õ¯.l>.ý†ÎYÝííá¶“ÈŒ±+òÙm£'“ŒÕcà¿‹Yÿ’Õÿ–½¯ÿ@EpžðÇÄM?_¶¼×>'ÿméÑîó¬°`·ór„/ïåpÄ7qŽõØêÐÝÜéWvö¿a»–H.¼¡'‘!RöiÁÁàãjŠóà¿‹Xÿ’Õÿ–½¯ÿIÿ_ůú-Gÿ {_þ*€=>Šæ< ¢ø·Hûoü%>5ÿ„›ÎÙöø•Ågö|nÝ÷ Ý»+צßz³ã}/ÄZ¶•·†|QÿÝâÎî¾ÁÞøÂ°1ìrÉ*s×åÇzÞ¢¼Ãþ¿‹ôZ¿òÖµÿâ¨ÿ„/âßý¿üµ­øªôú+/¶Z¾ ÛYëºßöæ£ï:ûì©oæå‰_ݧʸRޏÏzÂñ¿‡¼s«jÑ\øgâü#vkGµþƆï|˜™7¹dz|¹ï@æCÁŸqƒñ§'×þ{_þ*›ÿ_Å¿ú-ùkZÿñTéôQ^u«øGâ}Ϋws§ü]ûœ³»ÛÚÿÂ7m'‘bV=å²ÛFãÉÆhÑh¯0ÿ„/â×ý¯üµ­øªÕð·†>!éúíµæ»ñ?ûoOwcýƒooçe_Þ!ܸbŽ»qÞ€;ª*®¯ ÝΕwo§Þý‚òX-'‘!RM‡†Úppx8Åy×ü!ÿèµÿå­kÿÅP§Ñ^`|ñoþ‹_þZÖ¿üUtþÑ|[£ý³þŸÿÂOæìû?üJâ³ò1»wú²wnÊõé·Þ€:Š+Æú_ˆµm&+o ø£þ»Åœ;Ý}‚;½ñ…`cØä’Tç¯ËŽõÇÂñoþ‹_þZÖ¿üUz}æ§ÁÿþÎ#ÿ…Ⱦ`92Â1m’=1»ÛxVËWÓ´k=w[þÛÔcÝç_}•müܹ+û´áp¤/qžô©EPEPEPñfévw8ÿW9Oûésÿ²×ž@3øW­xöÓí~¼ ¹h”L¾ÛNOéšòH:àsšïûÂÇ-Ui"­ÿɳÄZ{ùë·U#ú×?´4Û³^ÛÜÿÏ)RCø0&ªjêĦw_f1xEãtñF}þmßû-yLgžµèß6½ÄŽ8nárÇ ËmÿÙ…xÅ–«(G4w ØÝúŽkÝÈ­õ];¿ÐüÓhNy’•þÊüÙÛÛjrC§ES¿+碟ëUã `úV$Ä=#ôÜ+B ûGÆÛ˜¾…°ZïSmÅo«< ¸¼MuÖwPV^KúùšJ@Æ fx†@|ˆÉåÏ·aýiÓjvñ¯îÙeo@xüMdOqæHÒË"în§5pƒæ»)KKÁϸ­8õù ÓæK€^]…b~ä‘€óÍbIu<çÛ¥TšF‘÷?ƒ°­gÍYŽ”Ó£Ú†ýi•T³ªKdßê æ;RG¬‡©úëV“fnI-NºïÇ7šI´P$Ô‡Ê'8*«¼Gwý;ŸJá$‘噥–Fy‹3±Ë1=I=ÍA»¿ZPGÿªµ¥JïÊ·1¯^¥ks=‰*ÿ‡ïÿ³5«[ã÷"oÿtðß¡5šÒîù¹­ZRVfn-4{y`@*Á—¨ ðG­oü=$ë“׳èK^Sà¯B M*þO,§Ëo+žpžÄvü«Ö~Æ[Sº— Sø°?Ò¾w1¦éPšgÕäU=®>—/Ðî¨¢Šøóõ¢Š(¢Š(¢¸Oxc⣯\^h_ÿ±4ù6ùV?Ø6÷VAýãÍ– ÜôÝŽÕ—ÿ_Å¿ú-ùkZÿñTéôWi>ø¡m«Z\ê¾ßgè÷¿ðÛEçÆxl®á‘‘ÈÎkÑh¢¼Çþ¿‹8?ñzò×µÿâ©?á ø·ÿE«ÿ-k_þ*€=>Šã¼áÿé:´·>&ø…ÿ ›@Q-±¡´Ù&å"MèI8†:|Ùí[Þ(²Õµ âÏCÖ¿±u 6ùWßeK+ ~íþVÊ‚¼ôÎ{Pæ'Á»|jÇýÊö¿üU'ü!¿èµÿå¯kÿÅP§ÑX>ÒüE¤éRÛx›Åð’^4åÒëìÚlŒªÄ$Ç=~lvªÞ;Ñ|[¬}þoÿÂ1äïûGüJâ¼ûFvíÿXFݸnw{POEyð_Ŭññ«ÿ-{_þ*øBþ-ÿÑkÿËZ×ÿŠ O¢ªé]ÛiVvÚ…ïÛï"âëÊyò¤Ø8]Ç'œW⯠|DÔuë›Í â‡ö&&ß&ÇûÞãÊÂß¼s¹²À·=7cµwtW˜Âñoþ‹_þZÖ¿üUZÑü%ñ>ÛU´¸Ô>.}¾Î)Ñî-á¶‹ÏŒ0,›Ãew ŒŽFs@‹EæÁóÇÆ¿üµ­øªôú+Ì?á ø·ÿE«ÿ-k_þ*·|áïé:¬·>&ø‡ÿ %›@Q-±a´Ù!e!÷¡$àéógµvTV_Ьµ}GA¹³Ðµ¿ìMFM¾M÷ÙVãÊÂ߻|+eA^zg=«„ÿ„/âßý¿üµ­øªôú+Ìá øµù-_ùkÚÿñUØx'Lñ“¤ÉmâoÂGxÓ—K¯°Gi²2ªlBAÁ s׿ÇjÞ¢¹è¾-ÕþÇÿ·áò|Ï´Ä®+Ï´gnß¾Fݸnw{W/ÿ_Å¿ú-ùkZÿñTéôW˜ü[Ïñÿrµ¯ÿ^‹¤Awm¤ÙÛj¿o¼ŠK‹¯(EçÈ“`áwœq@¨®Å>ø‡¨ë×7šÅìM:M¾Mö ½Ç•„¿xç-–¹éœv¬ÏøBþ-ÑjÿË^×ÿŠ N¢¼ëIð—ÄûmZÒæÿâïÛìâ{_øFí£óã G¼6WpÈÜ9Íz-W˜Âñoþ‹_þZÖ¿üUð…ü[Ïü–¾?ìVµÿâ¨Óè®;Á^ñÆ•«Ësâ_ˆ_ð‘Ù4 ‘ÚcCi²BÊCïBIÀ 1ÓæÏjÝñU–¯¨è76z·ý‡¨É·É¾û*\yXp[÷oò¶Tç¦sÚ€5(¯0ÿ„/âßý¿üµ­øªQà¿‹\çãV}?â—µãÿ N¢°¼¦x‹IÒd¶ñ7Š?á#¼iË¥×Ø#´ÙU6! à†9ëócµUñÞ‹âÝ_ì_ð‹x×þŸ'ÌûGüJâ¼ûFvíÿXFݸnw{POEy‡ü!¿èµå­kÿÅRÿÂñkòZ¿òÖµÿâ¨Ó¨ªºDvÚU¶¡{öûÈ D¸ºò„^|@i6qÉÀàgÇx¯ÃõvâïAøŸý‡§É·É²þÁ‚ãÊ€߼r enzg¨»¢¼Àø/âßý¿üµ­øªµ£øKâ}¶«iq¨|]öqN=¯ü#vÑyñ†£Þ+¸ddr3šôZ(¯1ÿ„3âÖÒ?átŒúÿÂ/mÿÅP§Q^aÿ_ŬÉjÿË^×ÿŠ­ïxÇN«-lj¾!ÂIfÐK_ìhm6HYH}èI8†:|Ùí@—â«-_QÐnlô-kûQ“o“}öU¸ò°À·îۆʂ¼ôÎ{W ÿ_Å¿ú-ùkZÿñTéôW˜ÿÂñkþ‹WþZö¿üUv Óßiè÷ßðŽ[GçÆxl®á‘‘ÈÎkÑ(¢¼Çþ¿‹_ôZ¿ò×µÿâ¨> ø³Ž>4÷ÿ¡^×ÿŠ N¢¸ïøÇN«-lj¾!ÂIfÐK_ìhm6I¹H“zNaŽŸ6{Vµ}GA¹³Ðµ¿ìMFM¾M÷ÙRãÊÂ߻•² ¯=3žÔ©Eyü!³ÿ%«ÿ-k_þ*“þ¿‹_ôZ¿òÖµÿâ¨Óè¬iž"Òt©müMâøI/réuöí6Gµ@bcž¿6;Uoè¾-ÕþÅÿ·áòwý£þ%q^}£;vÿ¬#nÜ7N»½¨§¢¼Àx/â×_ùkZÿñTÂñoþ‹_þZÖ¿üUz}WH‚îÛJ³¶Ô/~ßy—^P‹Ï( &ÁÂî98 â¸ïxcâ&£¯ÜÞh_?±4é6ù6?Ø6÷VýãÍ–¹éœv îŠóøBþ-ÿÑkÿËZ×ÿŠ£þ¿‹ôZ¿òÖµÿâ¨Ó袊(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Šd¨’ÄñÈ¡‘ԫܵâZ›éúÍ”‡˜\®OqØþ#½Â¼ÿâ–”Rxux“å|E6Cü'òÈütaçi[¹hÝ\ãâ8«p‚êò®OÓ¥QˆÕë7P€À©ükªF(ì¸U5êä•ye:/Õ~¿¡ñü_…rTñ ü/ó_©—¡{ ÔÃÐ'ùÕ¸õÛõ8fŠAÓæŒLV8sô§y™} χ±¼ž “¤–±þËþ5:ø‚µ_w§àÜjw'”Ÿw$qŠ÷ÿÙÚ¯øE//.&•Ò[Ÿ.vÈ Š9Ùb? ùò0Í DÜœ*¨äŸJúçÁ:8Ð<)§i™¨Ëep>xÛìñÄS#<}kÒük  bÈMn ^–zoÜ'ù{ýkÌpÈåJ²œ2‘‚õßN|èä”yY¤’9 s•–22Tòè³þ8éÅžƒÅ¶Q©é åê¨Éh;‡²œ°ö/éSA)ƒ Ä„zVŽŸw-Àº¶ÃÆÀ¤‘¸Êºž¨ã¸«§RTj*‘ÝV9ñxhbèJŒöÒgͪÜuç­(vç·jê¾'øZ=Sû~–¬tKÇ>@'&Ùú˜éÕIûËêA®F7‹Ðqõ¯­¥Z5`§™ùf' <5WJ¢³DÁ™GÖœ¤°Í@Ðg4õ`N=qZóܤÁ†1’M[Ò4ëÍVõmla29cÑPz±ì)š—u¬ê‘ØÚ€¥¾gsÒ4XÿžxëÚF™g£X-¥’az»·ß‘½XÿN‚¸±˜åAr­d{9FI,t¹ç¤ãä¿Ìòßih—ñÙÍ<33Ä$ @$c‘ê+7w­Ý}£ÅW ¶V"°¯¶ÕýsX…‹{JêÃÎR§-Ú<Ì}tñ3…%î¦ÒùS÷ct¨²qÏáK¿§5½Î.RpÞ´ ñQ÷}ªö…¦ÞëZµ¶—§@eº¹pˆ¿©'Ð’O` j*ì#IÎJ1Wlô€~:׊Ƨ<@ÙidHIèóü1»ðµôaøÖžðݶ‘i‡(7M.Üd?yÏò€Ú·+ásgÖ«9-–ˆýw#Ë]„TßÄõ~½¾AEWì…Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@r¾2ðÀÔw_Ø*­à:tñ®ªŠ¨ÉÅÝ Å5fx“Ú9‘ÔÊÃCRÁ3!ÈÁ‚C^—â_ Úk eÈ»‰@ûÞÌ;ýz×êšeö“så^ÀTíqÊ?Ðÿ“]°¨¦sJ$7öVº”¶ÓB·Ò.%…ÿÏcÈ#‘^UâÏ]ؼÑ÷ÞÚL]eŒ}?ˆ{Ž}»×¨+°`TàŽ„TÆhÜfUe“?}?¨®¬>&¥îíØó±Ùu tmQkÑ­ÿ¯#ç^:1R€‚2OOJö?xgEÖY¥¹·ò®ü¼@v9úöoÄgÞ¸ëÿ‡wÑ’lu iÓÒe1·é‘ú×·K0¥5®ŒøÜWbè¿qs/-þïò¹Ðü2Ó…—‡Íë¦Ù¯›w=DjHQùäþUÓ–PO⢂5¶µ†Ú!ˆácAìòªZÕâÙiWwdåBÌ3ÇÍŒ(üȯ¤jŽ]ϵÃÒ†tŠ×õgêwhÕ.®IÏ›;¾~¬MBÒ¢ ƒÏjEÈ85õ EcòÙûòr}I÷1JF™9ùIõÐx/Âÿ‹o…®fÏœKpãl1¼Øëì2}«.i; §Bud¡vûÚ]­Ýýü6VIqs3ìŽ\–5ôÿÂ?‡öþÓÕØIu‹”ÄòDK×ËSéê{‘ì*×Ão‡º?‚íwÂÞ¥"âkÇ\uð¯ê{ž;:ù|Ë4uÿwOáüÿà d\>°v¯_Yô]¿à…Q^)õAEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPQ\Á Ì- ÄI,mÕ\d–Šã5Á!i4»!ü²—%?Ô~µÈêz¯§’nle?å¢ Ë¨éø×°Ñ[Ƽ£¾¦r¤žÇ…“ùÔeýëÚo´}.ô“u§ÛHÇ«ÀoÌsY7ðü¿rÞXÜ™¿®ke‰TdèË¡å IéPÝ[ÛÝ[½½ÔI4N0ÈãƒþÞºßxN} ›«=÷8å/ÿkG¿à}ù~«¢M^&3‡Ù’<«ÄÞ¸ÒõE·¶Yn!Ÿ&Ü…,ÇÕN;׊ÖðïÂßën¾V‹5”$ó5ïîT\˜ 5êÕgÑõ8ï!åGË"gï§qþ{^ɧ^[ßÙEwk x¤\©þ‡ÜW]\Úµ8¤’õ<ð¾¥W7&—eþzžGàßE‘ŽçÄ·¯©Ì96ðæ8Aô'ï7þ;ô¯\°³´Óí#´±¶†ÖÞ1„Š$«ôбExõñUk»Ô•Ï¢Âeø|mF6üþð¢Š+œì (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€€A rZï´ëÖi¬ì2žJªæ3øvü8ö®ºŠ¨ÍÅÝ⥹å7~× 'ˆ‘ë èX­[ø“GÕ’Ýôû±ÌàL¬>Uÿl€Ôqé^IZºòjÍ©$î…¢Š+P¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢¼ÇâwÆÏ ü=ñè:ÖŸ­\\µºÜ´†&M¬X–‘N~SÚ³<+ûF|;×õ{}3þ&ºd—ã’úÝ2Ç€ #¶2{œ .=†Š( Š( Š( Šæü㯠øÑ¯—ÃZ§Û̓ª\ÿ£Ë–[vΣ?uºg¥ei¿4 ÿŠ7á³Ô×UµVg™ãAÂ8`ûºü4ÜÑ^YãÿŽþðg‰fðþ º¥ÝäDÿcY"$‚Y—'tÍz’0e :‘@ EÄx;âfƒâŸkžÓí5(¯ôW‘.dž49I<³°‡$óÓ q@½Q@Q@Q@W•é| «øæ i«ª\\Ü\ýš+•E»?Ô¾ìq×mz¥Q@Q@Q@Q@U-sT°Ñ4{½_SŸìöVq4Ó˱›b(É8PIü]¢±ü!âmź$zׇ¯~Ûa#²,¾SÇ’§aÀ=}«b€ +Êô>Õüs„´ÕÕ..n.~ÍÊÀ¢ÝŸê_v8ë¶½R€ (¢€ +Æ>(м!¢¶³â+ï±X¬‹—Êy>fè0€ŸÒ®èÚ•–±¤ZjÚlÞ}•ä)<m+½eN2B3@(¢Š(¢Š(¢³&Ks¾s™†Ñ…cß=Jæ¾2x–ïÇ߇"ñ_î|¦EpRkû‹YYʶ3ƒå&p<œ×¤ülðgÄ«ºgü C¨ (Ò9&¹…SÌL‚­"±áÅdxÇôÄÛ}ÄúN‰£é«p³<‹4xÈv<Œp àb‘GKûM|O¿ðäZ‡ü7«G¦¾®‚yµP ‚bB†\Ny$€NŽMy–ñ>ÿÁ^2ÑäÓ~*^xïHº”&¥å­ÄF$WÎÏ© ©þ޾±ñwàæ¡¬xs³xSP5ß [Eol÷-ÂFnN ägŽH>´ÏYüpÔüY¦Iâ]ÂÚhù¼X¡ŽCv¸ÁÀWr¸9P$”OÚ7P6úÖ ø¼Þ ƒìÄËio’Lí¸âLCó€FG'/ë—ý¾ kíñVçÁ7þ.>-Òg·i-/Ü>íÊ¡ò ãÀ«gqï³ñáÿ,>8·Ä_ hš_‰!¸‰Ú^ʉä0‰c8ÞÀt\‚ <‘X|à/ˆ~Ðëãohºuµ”¶ïæËa4bËA´ MÛÉœrrzs@~Ëzþ½¬xÛÇ–ú¾·©j0ÚÜ ·ŽêéåXG›0Â$/tô—ðVûÅ>.ñ'Å ok6^亖O±, 4cpÛ€Ý# ¨ìþ|að?ĽnûÀ_Ù“išÌÌÆââDÙ3–]èÇväÜFTWCû4|>ñƒ¡k%í¬%öê+½Î×ÃŒ#C}óøú…õ^OÛ]ÒdÕ/ŸNŽÝŠZ5ØTù„Î$žêŸÂ¯üYø}ñúÛM²Ó¥ðÖ¥¨F÷—2mÑØåWpu}®ÃHÏç]‡¼ â«OÚXñ¥Æ—³A¹’+¯´Dwk·ŽTŽGj@xσþëگǯxVj–z•”2¼ºÊ+™î1‚çpêÇîשéúö½ÿ “{áö×5&ÒRÙJØ›§û8?bFÈ;~ñ'§SšÓð|U¥þÒ~'ñ}ö—äè—ÐL–×?h‰·–hˆuºÒ²~,ü:ø‹mñš?‰ãµ»¸–4E,¨†6ˆ˜äB€t9Îz``H5ý{þ,è?Ûz—öGÙ·}ƒíOö|ýv|¼íûÜôëÍfþÏòrŸÿë½çþ•ÔÞøyñ6Ûö†·ñ·‹m­î¡’k›Ûy¢£5±A¦íøS„ÎÞqŸzÝø;à_xãwŽ5j²üñ6¹â[Í[Ã{#ÞWože;bÞ}ùŽÃÖ¹/„p|pµøM`¾þͽÑõ?<ÆÒº$ö %tm¥ÙF ]ÆäžGøiðF=á&¹áM~ñÿ_®¥ƒæX ó\ãvÖù»dœ{ÒâðŸë·ÚÞ(ºøÝ}gâ!ºX´8ì®Dá7(ò# ÚG#'®;ωµý{ögÐüSe©]éZ»jéiy5ŒívT—<¡ …ltÏÒ¥Ð|)ñïÂ#x;FѼ5¨X#:Ûj²™áV$äo`z’pÈØéÈÅuŸ¼ ã_|Ñô [K-SÄÞÃ5ïÙ vñ1ÈÆò£«/¦z€•ð›Cø±ã ï[ñ‹nt­?UÑVÛK\HÓ@wFRãh#,ʬw—Üwž€×žþÉ~¾¾»¿ñD~*½Òì4[ˆå»±…XÇx¡Y°øp8Áê­Ö¾«ðE•Æ™à½M»ˆCqi§[Á,`ƒ±Ö5R288 ô⼟öVð‰¼¡ø†Ëź@²ût±yhgŠQ"`ßêÙ±×½;Î;Âw?þ8ßêÚæ™ã9ü'¢YÜy6°Z»©Î7; – %˜õ< p:¿€ž8ñt5Ÿ…þ:º]CPÓ‘¤·½êÌ®Tœ À«Œõ¶0´ß‡ÿ¾ë:” FŸ­hWòy‰ Ôˆ.À°v_˜2¤†;ÃàgÃAñ6«ãÏßCuâMQY qË ±²@Æãµ@ Àœð⟠õ/Œž;¸Õ4ø³PXc™'»¾½Ô%&‚Ưó2îùŽs·“]‡Žµ_ˆ‡íI{á? x®óNIàED–áÚÚ6ŠÎâ,íÝÔŽ>ñÞ»Ù_Àž)ðL^%'Ò…ƒ_O [ÿ¤E.õ_3?q›Ü:ã­,~ñPý«¤ñÁÒÿâŸ0lh‹¯ÙxÙ»Þãîþ”æz5ïÆ=;â½ÿÂx¼w,÷W\5ýÓ´ÞJybo22Ùe%>\ç¶Ÿàֽ㿠ü{½ømâŸ\ëÐ4,ÂIåipÞP•Yþa•8+œdû],~ñPý«eñÁÒÿâŸk}‚ïíuû 7oûÜ}ßÒˆ¼ â¥ý«¥ñÁÒÿâŸh6 ¿´E×ì‚#_|2Ö|ãë[-6ÌéCMÓ .’H«±Ð»”bÌtèh/án½®^~Êšö³y­j7œVz‹Gy-Ó¼èUiNàGnx®[KñG‰Ÿö?Õu×ñ®Úªj*‰|od3ªùñ 3¸ 1ž„ÔøsñßHð^·à;tÓaÑ'Šwó<è§%ÕDID„— âr+{Nøkãh¿e½KÁRh»ué¯ÖXí>ÕÌžtmûötRzö ~j_.ô¸>"xŸ[ü%¤é—$[Ití5à2‘ƒ¼î ó9Ïq\õ5þ»­üdºð…ÚÊßaÒmm.š- eC4Ch²w "¾™øaá{‹/ƒzg„üEgäKýžö·° °pa¹I†ê ¯Ð| ñ›á«ßèžÒ¼?âMæs,žXxInÃ:pG̹u4è³Ä OÇžµ·jzmÇ‘,ÁBùÊTrõ™ïTl *ò÷áÖ¡­qiŸ,Oqj™ÙxXÑUðÀaYƒŒƒÊŽk½ø]§x›Mð…´/ŸO¸ÖI&i,á¸ÏÊ`:úõ9Ÿü?«ø§á&·¡hVŸkÔn¼&1vۈݾg!FIäö£ u8Ýxžî9ÔOc47˜ç²å{`óùz¿Ç_‰þ#þ Ó|%2éÚ‡‹-âœÜ±,‚=¨¤Ž2d庀¼u®gžøÇâ?†6^“ÁÓãÓ%E¶žqs´ÜýŠ;· “ŒµÓ|_ð6‰uðÁþ(×íü-®iv–öV×щ¼•ß”ÎcÎðp1ß<€stŠ<šŠüTÕõ[{‹˜ãºI"š$äÞ[o‘ÛMo‹¾4ñ†‹ð;áõÖ™ª_Ú¦©eoý«ªÇ™'SäÆGÌNw1.ÙÈ$¯^My§Æ;n_Ú[ø›â¾•âÛ¤E•†“2ÌŠ¸9–WU\° Ù?1ç­{Öµ¢üM´øiàÛC£\}‹K‚SMÔâí$D‹°îã†Èʜ㚧û;Ïyy«]ÏmñvOi"Û?cº‰’ê) ™Ä…œ(Ÿjõ¯é×Z¿‡5 .ËS›K¹º·x¢¼‡;àb0pAÈëÁZñ‚ß ü_eñVox‡BѼ-Û¼I¦éŽ6HÌ¡sµY•W©<õÇô 4&|wû+øKSÕ|U«jðx¶ûK·Ðïmå¼·‰X® »¤;dÃŽ>CÔ7Þ?Iá½GâoÇ-sXÔt_Oá=Âa´vÌÊÌNJƒ°©c€ Ø—ìÁàOø6çÅ­â}(Y&£< m™â—ÌU3nûŒØûë×kµøwñkáo‰5I>.Ÿ«èš‹ï·R"˜±»ƒ²ò ‘•nGQè†k|ñ¯Œt߉ڧ¯ß RîÖ3%­îrç ¯‚Ø•‘ƒyÁöò߇úŸÆ/øƒÄ^ðÏŠµCqæÏyy)±£È#|Å7 ÉØ;^ËðOá—‰4Ïj_ü{Lj¯Ð¢C °ƒ€I#ŒáB€¹w9âÙ›Àž)ðv¹ã+¯iBÆ=FxZѾÑžb«L[î1#ï¯\uö k…YaEvÜÁ@'Ôâ¾Rñgˆt þØÚ®³âG ¦Å k!0™ykÂü y"¾°¯½øc¬j¿µ ÷еŸ Z_øRx”y—M±» DA˜˜–áÔŽW¶}è`6øÕ¯økâ¼/¦|5Ó¤“TYYe»ŽÓÉÎY6ç€HL3F?ZôO‹šþ½cûOø#G²Öõ+m6æ c=¤7NÊMÄÀ–@v¶@‘Ð e÷Ãoü?øÉ‹~h©¡_5 6;ˆ )#z(‘”c£&:Ž[ß´?Ãëþ.Ðr#{hæàE"ïVÛ× TOœÓ_àNµ¯øë㋼PúÞ¦þ²‘ ±³ûTŸfbÇj0;xD,xêà׿W†øÂÞ3ð‡ìáioà‹e+½hï¤W™IÍÂäDsÜõ?‡‡Äà½0ø½5Óûh0qÿž/Lt¡ ›ôQE0 (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ †úÚ Û)ìîP¼ÆÑH¡ˆÊ°ÁŽjšŠÈ𧆴/ éCKðö™Ÿi¼ÈR<’Ìz³1$±à ’O§ñ‹¦x‹DºÑu‹o´Ø]¦ÉâÞɸdeH#:Т€2¼)áý#ÂÚ ¾… Ú}N¶ÝäÃæ<›w1vùœ–9f'“Þµh¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ Æñ_…¼=⻲ñ‘i©@‡r “%r­ÕOЊ٢€8?|øk êqêZg…-#º·ÆòÉ$Ût*$fŽÄ+¼¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š+ŒÖ>øXñ9ñ.§á«k­Pº»M#¾€MÛ€:ƒÒ»:()h¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¢€EPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPA¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢Š(¢ŠÿÙflippercloud-flipper-532c39a/docs/images/flipper_cloud.png000066400000000000000000007442251440444367400237560ustar00rootroot00000000000000‰PNG  IHDR(_Úf3Ë âiCCPICC ProfileH‰•—TSY€ï{é!!@B¤„Þéz(Ò«¨„$PBLvepGPGpTDÁѱ ¬(*`AAY XPÙ,aföìîÙÿœûî÷þü÷/÷Ü›ó?ÈA±8V C”% ÷ódÄÆÅ3pD€2Àr¸R1+44y3ó_e¬@“ó]‹I_ÿþû_ÊJ@8‰'åf ÜŠŒ×\±$ Ô1D¯¿3,É —ÇJ–x±f˜#™+K‹’ë|¶Üž 2f†³…Ñ gXš8kã%×Kdáòüù"?ÏÙ¸¾òÚ3¤ªWÈ–¯ÍDúËkçÌæÏ±f}Jcå¹ñøÞ>³6Qr{q–§<–8=TnÏO÷“ë¥ÙòµYÈáœ]*ßÃTN@è OÀ"Ààrª²ø9Y“ÅxeŠs%ÂAƒ…Ü8>ƒ-âZÎcØXÙX0y§Ä»ð©{ ÑÏÎê2÷#Gy ¹3ųº¤Rš P{8«3Ø%€Æ6®L’=­CO>0ÈÿЀ:ÐúÀX Ù9à|@‘ ,\ @–ƒ•`(E`Ø*À^P£à8h§Áyp\·AxzÁxFÀ‡ ‘!*¤é@†9d1!7È ‚¡8(JD Z m€Š ¨ÚÕB?C§ óÐU¨zõACÐ[è3Œ‚I0 Ö‚àù0fÁp$¼N—Áyp>¼.‡«á#p#|¾wÁ½ð+xP (:Jeb¢¼P!¨xT2J‚Z*D•¡ªQõ¨T;ê.ª5Œú„Æ¢©hÚí‚öGG¡¹èeèÕèÍè ô!t#ú"ú.º=‚þ†!c41æg ‹IÁ,Ç`Ê00'1—0]˜Ì‹¥c±ŽXl6»»»Û€mÅvbû±£8NgŽsÅ…à8¸,\nîîîn÷¯€×ÁÛà}ññx~=¾ ÿ?NP"œ !!—°•°ŸÐB¸E Œ•‰ÆDWb$1•¸ŽXN¬'^">&¾SPPÐSpRS*¬U(W8¦pE¡OáI…dFò"%d¤-¤ƒ¤VÒÒ;2™lDö Ç“³È[ȵä ä§äŠTEKE¶"Oqb¥b£âÅ×Å¢,¡äQÊ('(·(ÃJ%#%/%ŽÒj¥J¥SJ=J£ÊTekåå åÍʇ•¯*ªàTŒT|Tx*ù*5*Tú©(ª>Õ‹Ê¥n î§^¢а4c›–J+¢¥uÐFTUTíT£UsT+UϨöÒQt#:›žNßJ?N咽£5‡5‡?gÓœú9wæ|P›«æ¡ÆW+TkPëRû¬ÎP÷QOS/VoR¢Ö0ÓÓX®±Gã’Æð\Ú\—¹Ü¹…sÏ}¨ kši†k®Ð¬Ñ¼¡9ª¥­å§%ÖÚ¥uAkX›®í¡ª]ª}V{H‡ªã¦#Ô)Õ9§ó’¡Ê`1Ò匋Œ]M]]™î>ÝÝq=c½(½õz zOô‰úLýdýRý6ýƒ`ƒ•u †LCáNÃvÃFÆF1FšŒÕŒÙÆyÆuÆMÈ&î&ËLªMî™bM™¦i¦»Mo›Áföf³J³[æ°¹ƒ¹Ð|·yç<Ì<§y¢yÕóz,H,‹l‹:‹>KºeåzË&Ë×ó æÇÏ/žß>ÿ›•½UºÕ~«GÖ*ÖÖë­[¬ßÚ˜Ùpm*mîÙ’m}mר6Û¾±3·ãÛí±»oOµ¶ßhßfÿÕÁÑAâPï0ähà˜èXåØÃ¤1C™›™Wœ0NžNkœN;}rvpÎr>îü‡‹…KšËa—ÁÆ ø ö/èwÕså¸îsíuc¸%ºýèÖë®ëÎq¯væ¡ïÁó8àñ‚eÊJea½ö´ò”xžôüàåìµÊ«Õåíç]èÝá£âåSáóÔWÏ7Å·ÎwÄÏÞo…_«?Æ?пؿ‡­Åæ²kÙ#Ž«.’#+Ÿ™I‚Z‚áà€àíÁ.-l !ìí!OBC—…þ† « {n¾2¼=‚±4âpÄX¤gäÖÈGQ&Q²¨¶hJtBtmô‡˜ÞØù±«b¯ÇiÄ ãšãqñÑñâGù,Ú±h Á>¡ ¡{±ñâœÅW—h,I_rf)e)gé‰DLbLâáÄ/œN5g4‰T•4Âõâîä¾âyðJyC|W~ ÿE²krIò`ŠkÊö”!» L0,ôVߤú§îMý’v0m"=&½!Ÿ‘˜qJ¤"J]ÌÔÎÌÉ웋 Ľ˜—íX6" ”BÒÅÒæ,Ò(ݙȾ“õe»eWf\½üDŽrŽ(çF®Yî¦Üy¾y?­@¯à®h[©»rÝʾU¬UûVC«“V·­Ñ_“¿f`­ßÚCëˆëÒÖÝ\oµ¾dýû 1Zòµò׿÷ç÷]]b¤ g£Ëƽߣ¿~ß±ÉvÓ®Mß y…׊¬ŠÊоlæn¾öƒõå?LlIÞÒ±ÕaëžmØm¢mÝÅîŇJ”KòJú·oo,e”–¾ß±tÇÕ2»²½;‰;e;{˃ʛwìÚ¶ëK… ¢«Ò³²¡J³jSՇݼÝwöxì©ß«µ·hïç…?Þßç·¯±Ú¨º¬[“]ó|ôþöŸ˜?ÕÐ8PtàëAÑÁÞCá‡.Ö:ÖÖÖ<¼µ®“Õ I8rû¨÷Ñæz‹ú} ô†¢cà˜ìØËŸî>x¼íóDý/†¿T¤ž,l„sGšM½ÍqͧNµµ¸´œüÕò׃§uOWžQ=³õ,ñlþÙ‰syçF[Å­ÃçSÎ÷·-m{t!ö½‹a;.^ºrÙ÷ò…vVû¹+®WN_u¾zêóZÓu‡ë7ìoœ¼iód‡CGã-Ç[Í·n·t.è<{ÇýÎù»Þw/ßc߻޵°«³;ªû~OBOï}ÞýÁéÞ<Ì~8þhícÌãÂ'JOÊžj>­þÍô·†^‡Þ3}Þ}7žE<{ÔÏíõ»ô÷/ùÏÉÏË^輨´<=ä;tû増¯Ä¯Æ‡ þ¡üª×&¯ùÃã#±#o$o&Þn~§þîà{»÷m£¡£OÇ2ÆÆ?~TÿxèóSûç˜Ï/Æ—Á})ÿjúµå[à·ÇbŽ„3Õ  ''ðö ÒÇ@½ qÑt=%Ðô7ÁÿÄÓ=ø”8PÓ@ä ‚n°«iiÿä» ”‚è]lk+ÿi²­Í´/’;Òš<™˜xg®€¯Åã5_kdК;Ý×O ¡€œ.ħO·þØø›L÷üªñï3˜ÌÀü}þ'±în‹>VeXIfMM*‡i’†D ( _ASCIIScreenshot«¶Ú«×iTXtXML:com.adobe.xmp 1320 Screenshot 863 Øý@IDATxì½ ¸eGU°½ow' é$„™ºÃ<Š ƒ "“ ¢D ˆ‚矢1"ú#‚*2üPüQF•€ ˜€Ì„yJBBFdêî¿ÞÚç=gÝê}Î=÷ô½Ý·“µž»OU­±jíUµk¯»÷9]—H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤Òéô@z =H¤ÒéôÀy`óéI5»{`SA-•ÃrW`G›€'Þ6¥ÐÒS—uxá£m™öñJøDŸÁO4!ÖÁÅ6u}ëò¥ÿ3þÚ¸062þ&s)ç_®?¹þ²2ôs¢½Æô”þ³¥åõgr-×ú‘v®¿“=Šq„Oòú“×׿ N¼mJ¡¥9çÀ[‡×¶eÞèÁÞ79ÿ&q–ñ—óïš°þÄ5n2Û³¶°âÅfa%×`A&XJýeb—ÒÅU%ø£cG)øÀo)G d/T”NRdÛÔµM[Ð[¹´ŸþÏøËù—ëO¹þrõÈëO^sÿ‘û¯Üæþ;ï?òþ«ßð™÷Ÿýýt{oòþ/ôù‡>Nfå_ð”ù—‘Ûj.î?+ð 3< £f°\kH‹.mOݶuüF=8ŽÀÒ#/uøb ¾´?ñ)>ròR·m=ý?ñ±düåüËõ'×ß¼þL®ýʸ|­ä’×ßÜäþkòO<æIî?'ë„ûMüâžSÿ¸~PFÈýWî¿rÿ•û¯ÜåþË܅ׇx­ðú1ïþƒxJ¸–x€ ÁC X·MéQmiQŽ@ÔapÚ–¦Œ%xëòªßväQ´(—öñLúߘ!6ÛÖKðÖå¥ô€ÈC]š8Úxfâo|àº>³„f]^Jè€<Ô¥‰£þÇ3ãßÔõ™%4ëòRz@ä¡.Míô?ž™øßøF ®Ï,¡Y——Ò: uiâh§ÿñÌÄßøÀ7u}f ͺ¼”Ðy¨KG;ýg&þÆ7¾¨ë3KhÖå¥ô€ÈC]š8Úé<3ñ7¾ð@]ŸYB³./¥t@êÒÄÑNÿ㙉¿ñ €oêúÌšuy)= òP—&ŽvúÏLüo|#P×g–ЬËK釺4q´Óÿxfâo|àº>³„f]^Jè€<Ô¥‰£þÇ3ãßÔõ™%4ëòRz@ä¡.Mí}áúr­{M,D01N2×´ý/‡ÙlKù Kå£ Ÿ^Z[Â? }l¦ýÞéÿI<;Ëç ¥s+ç_®?¹þ2#z`^87Ú޼þô~Êëo¿ÿÉýGî¿rÿ™ûOÖÃÜçþÛý4{‡¼ÿÈû÷“qßÈʽ¥ñßz,©G=ä_rÿ±vû|=ñ¾¨ 6ª8ŒË\˜1¹þ®!®3àŒ|$žºxe,¥)K™ëO®?ćqc¹·Öâ2aƒxÀ“?­$(<à‰‡C€ÞÊL”[lË;$'Ípñ(Í CzÀ”i?ýã!Æ–õi%r1öä7Æl[F{1bŒXŸV{”ðăØ¤QWO´—ñ—ñã!ƈõi%rðăx ·òÑ^Æ_Æ_Œce(n¤YÂã.¥YaH8€2ã/ã/ÆCŒ-ëÓJä­4ö(ቱH£®žh/ã/ã/ÆCŒëÓJä<à‰ñ@o壽ý9þê÷׿¿‚Aãë T|TqxðòÁ‹“zl—fi¶)áC‡º•SoœòñK²âõ9rü¢ x62BkžØw}];”i¿÷AúebÈù—ëO®¿yýÉëo¿ò™ûÜåþs2rÿÝï“ôHÞôžÈû¯¼ÿÌûï~ïÈšù‡~ß0”Პù§•ò_¬U®Ûýêµ}š¬Øº`–}äà×®%2âÕ—ö‹S D_ÓNÿ/÷‰ñ’ñGtäüËõ§¡õ?×ßÉuÆke^&~q=Íëo]NóúÛ»¡Îª¹ÿÈýGÜ“º^äþ«Ÿ(¹ÿÈý1û¯~OÁ¬Àì'rÿ9Ùgí¯ûOÖ{×ÿRçmˆw÷ŒóÆ?òȸf¶uèÚ‚À¾×šŠØŸ>ÌþÐgœìIõÄÐoÇàIfCH¼|–â ©ÒÜ,€'R;à”¥O]оòاîÓ9âcŸE‡}…—:úáSÚOÿ ÆDÆ_ÎâšñüwžåüËõ'×ß¼þäõ·_+]sÿ1ÙrÝÈýWî?sÿÝï-óþ#ï¿òþ3ï¿3ÿ0¹×f°/ò/Øå<³ì³ŸÌ?ÑFÎkš¹.síþÙ ñF£vÖý£m¿9€8Ûâ<)”ÒbR’“ªÊØVŽ0ID]{Ê* 2ØQ‡WPOÛ ‚_ÝêQ‡òꬵ}ôr¤ýIxíI³=}x£}eÀS¨£_ö¼ö¤ÙV׬ñ§ýôÆßdþ;grþåúC,ÔsýÍëר¼þNöWî7ôm×ÒÜô{4×üÿ(óú›×߸ž'Ä…ñRªµž×Ÿ¼þ¸ÆÆx!Vi¶sýíçM^–¯'ÄG^ÖîúË\߮ݥ:®Ç¹ ~^ÿ;Õ…~çxÔ)Ÿ4ÛÎyÕC¹a`£%(ã‰ÔI8T<8ë8Z<1Ð3ÅÑùŽœàIód©›RÀ†vÀÉÃcÀèlíÓžfÙh¿4+€÷@žºíË…Uû´¥M³ïÒ>ÞZúŽ2ýŸñG9ÿ&ëŸóßäú“ëo^&›BçF^‡÷?¹ÿ诫¹ÿòÊ:);”¹ÿÊýq äþ+÷_Þ»N¹ÿÌýç¾ÚrZ$ÿCÜÆë?zã:^ÿ¤ÅõO^h&ÖåSmdÔ… €¯€!û=e~Úé}Ý5 èì¾Õ·¡áÔÈCÝAÉÉðQÜx¢¨ËË U¦T—ñGÝꃻø(ÚwaD·yê”ÚÒÇèWªc~êè Ïk~lɯýyÇ/?zÒ>^HÿgüMæñ0kþCÏù7ñ—ëI®?“;0FÚëþ"ŽrýÅ ¹þæú;YOˆ‡\§ï?ñk ë‡ëI®¿¹þÞ#yýé}‚?ç õ¼þâ…¼þæõw²nyý~ýõšK̰¦¸žPwÍ_P5¡(/mù©­?®áðqÐĺvì mê”ðYÇ.mJpÀ4ûÐà‘ö>’ztª¥NâÂJÝO8tÀeÀyB¤[zÒ´k@ °M[ÔÚÊÒF Þ!ûâb©ŽhCà¦ÙGûC[ÙÖF|‚šv‘¡d-Å¥ýáó€ôïâ Èø›,ðúßäü›¬ã¹þäú›×Ÿ~>°fz­Íëoî?rÿ5ÙCà‹Üæþ“8àzAÉäþ»÷ƒþÈýwï÷ÜćõÜçýÇjæ?ä‚xR’9æÞ =BÜÃBŒ9ãOÙYö‘“/柢ý¸þ9×ç±neíS,Õ¡}KxäCÇ>°¯!:‚ºÎ×9öQGJ/N^JlYªµéʃsóëQº©-å=‘”ê¢_âÐëa€+¯Mø©;ZúJöÕ¯}lƒ£h;qZýò§ýÞg­Òÿ}áâHÿ7ÆøŒ¿åó>ç_ Šäú3™+Ο8g¨çú›ë/kFyý鑼þäõ—uÒù‘ûÉ5%^Kð íÜôëŸøÃ½ªñ}F=¯¿yýÍëïd}u~\÷Œƒ5#þ°q\Sø¨{ÿÛæ i¬ Þ•Öù)Ñ9ËÿQ—ë}±Oè Î!=ÊPŸEGÆ<[©î} sût&'àdp¡°_”:”~œ¦td)q¤zhô†è|ÚÚÄþUå@?Í“(?6æ±o_¦Ùw<óا/km}Žc¥ñ§ýôÆßdqÞ0w§­?+Íÿœ¹þG¹þö×Úi×ß¼þäõ'¯?yýqﺙ×ßÜäþkøþ7÷Ÿìfßÿ»Žäþ+÷_\Kfí?™Ok}ýÁ&ÇJñG®h¥ý±ìœ÷ºXP»å¿ÀñþS¹•òo½ä^üÄáû°‹q@ë4“‚àMä!cÝ@@–:4õY/¨Š“îÂ4d;öyë­}ƒJ[Ø€W›Ž‘6àøä§²ùä7d½tƦ>qÈsꢄ߶4íZB—\Ú_œÿ¤ÿ3þrþ9#&k kàZBÉ|±-ÍuǺ<àrýÉõ‡5ÖØÉõ—™Ó>ÉëO^òú㌘¬\C¯%”®!úKmh”à”—ן¼þäõg2wòú[…äõ÷Ú³ÿà:ÀuÁ¤ž×B!^7äƒÎºáµD~ñÈ?ÆeÔC]^(vþSŽ:z°ÁPJ‡Ð i—Òþ‚wÍ_P{ìüÞ³Ø;ƒA š>p²c_¢c xf]9e(q|t¨<ÈÑ>|³ìÈϲ¾!û=Ñ>r+ÙGÉ"öµÓvB<:Ó~ú?ãoúúã… ç_¿Š°vD_Äuívýí%'Ÿ¹þô¾Èõ7¯?yýÍëo^óúË: xMåúûÞ+qÏëú*–¹ÿX~ÿk\YæþkSÄM®¿¹þ‚kɼë/ë2”ó®?ðrÌŠ?Þèâšg½µÛÞAçÿ<öé/ lßZçOºÎfÆê£=2ÁÑ:fð8‚’ÇN̓C‡t³Ê´u6uŽh«4k[”èÕùÐ# y€’ï hí{’ÐAÝ~a3¶­ô2û´ÑéæÛÈRr´ö±cÿsAÕ1hùÖ><²ÈEŽ_›‘–öñÚäü§ÿ3þrþõs"ן~Ç+­ÿ½ÇrýÍëO^sÿ‘û¯¸ÇÌýç䯔õ1ú&÷ßý•Óû¯Üçþ;÷ßýœÈý÷ÞÛ“bíÁç€yÖ%êà­³~sÄ|LiVpÿk ƒDÖÄd\ÿ¡G%ù'׿Ö>xí¡ƒƒþph :àõ¥oMú.Ÿ%tÇ„}ú‰, ý¾µ>uü^056Á =9 qŒN¤¤ÍáI)Õetƒ< cÕnœ¬cKµê7ÚWŸ4Jx¢<8lȯ=ùí;tíS¶ô‚ªc3ˆà·®íSÚ¢²y «“RÀ€Ž8~l¤ýþ<¥ÿ'ñjì3–ýrN¶s4ç_®?¹þî~ýÍë+h^sÿ‘û¯Üö7Ÿ¬¹ÿÎû¼ÿÊû/Ö¼ÿܘ÷Ÿ®Óœs*ñþ/ž;è\ߘÓò–ê8ÇBÜ–sÞ{…!â´í»Ÿ†€FC”â䇯½þFýòÝÿѪO~Úë tboƒtžÔâ哎Œ'R^p8Þ o&™:²±T=Èqž@ñÒÔŸöå…F=ÚŒ‡Œz,éC´ïøÔIiùÖ~¤O³¯Œtô ¤ýôÆ_?˜ÌÀ9çxçõè7×øÛù¯LοɺƒŸ€\–ÇQÆ_9ÿ&{"}g\K¨çú3Y·sýíc#¯?yýÍýÇdŒ¾ÈýWî¿ÜwZæþsù>"÷Ÿì¬ÖçþýZܧ`g(þ\§ §Ôtqÿν¡k8ê­]ù ê±oà´©}p€ñaû¦^òo‘Þ5:¶ÞààÌÈêHðØw>îJ;òê<èÖáA |ßš‡mJlñ¨,ò­ý‚ªtíCç„Û7úÂ!:€]OmÇI Í~ÑF×£ýŽC]ð8>ä†ì+§Ì4ûèŽöÓÿ9ÿrýé××Qæ„ki®¿yýÉëo??rÿ‘û¯ÜNæ×¯^3rÿ=|ÿ“÷ñî«÷Qî¿sÿûïkîþ›k°Òúgièþƒû®3ñú ‡ûS®?؃oÐa,Á‘ÿR?%zåáëmSB‡&ö¬ƒŸf¿Ö¼Ø®Æåš°N€ ŽÁa§:‚Ò 3"œ²ð¢ ˆõ8¦HGÐ>A€}dð­ý¨Ëþhu*Nù¡qÈ Ú/Õ Ð£åGäeÁ, Î(—ö'¾ñœ¦ÿ'±’ñ—ó/ן\óú3Ùx-×ѼþNöWúÇëiî?&{ j1nrÿ5ññ’û¯Üy¯’ûÏÜæþ3÷Ÿ×„ýg¥›ïú/ïÐúçõ1î#àçú)°~r8wÀÓF&ÊyÍ…nmúARY@]î_ÄQFeÁ!³®;³Ö†Ð­ã(qŒNÀm@¼üòàhò©ÃL²mtè(x‘£ÍоN•OYÚØµ,Õjº<­}õGÈË/ÝñPFÞÒ¬íj_Ñ>4@šúi«c%û‘·* ²ê Ô¥õ´ß{L¤ÿ'óØØÉø›ÌãÃùKç¥ïbiŒåü›øSŸéýK[ßeüMü¥bÜE_õ«ÙÄwúP~®wfüõ“ó¯ß:¿âœÊõg2_ôëI,KDT\wÀË—ëO®¿yý™Ì'çL®¿¹þÆüF^&×}qýå:ç5l%û‘—k ,ç´½þÁï|W·m¯¯”ÊÂc[º×Q¯«”zbìÀÈíQç¤Ñ¶?Ô#í5;½f E ÂAQtŽOOÆÁEç*G©)­‹Wº‘gLð€÷¤•jmSfï£,úäGÞ6%:µGiÝ“ª£<º9ÔUªUŽRûÔãxàW7%ú(§Ùwœ+Ù/**  Hû“§gÒÿ}L9ÿˆ׬\rýÍëO^sÿ±üºà™û¯~ÏûÏ~5mÿßSû¢žûïÜ{ÿ›÷ýìÈû¼ÿº¶ÝšbÿÔãýí¸ÿ¢ ÀÃÁ5'^Ñ£.ýYPã{{p\|zàï÷Zû´KêØ ­}u@[3ÀÈz€Âq>ñíè8KhÔåGž:8åuDAULj‡‡gq /87â j ö„uí«+Ú×üб 6ö9´ÞðkS<8íCÃ8lj>p@´¸iö•+,àOû½ßF.©>LÿOâš˜ÉøËù—ëO¿¶æú›×¯£”^ÿóú›ûÜåþ“5!÷ß½Úû×͸×Îû¼ÿ`ݨçý×d_ÁœÁ'¹ÿÞ¸ûob—sÄ1mý÷»áà,­£Ã}%uÖÑxþY3hèu E×Jd5ÿ¤\AU€¦ìUù§Í?yÕûéäQßš”th­!Ghƒ:ƒq@”|qgrÊÃGPFŽË“G‰>íëùЫ}ê€öÑ Ž íS¶ö‘7Ë>´´ßû)ýŸñ—ó¯,rýéýëïäz“ןþ:‘×ßÝ÷?¹ÿÈýWî?ûk†ûÿÜçýGÞåýgÞ÷÷ø¡Í€»&å˜ï+­ÿWxØG:~î·Úüº¼ÿ „œy#øå±nÛ“:)ý¯Þiö±È×ÚgŒÚ„†]š}‡ í—êž ×„΢Ó;>èàt©Žë´ºpmÞ¨œv£ |´¡]¯•ƒ§6ÁiSû5¶‹tûn¿iÒ­ÛÇcŸ)±G ¨‡Ÿ6:8ËhŸñÙGÇ,ûú½iOø#ý¿|þw–“xÉù—ëO®¿¬šýõ&^Ãòú3ÙÄ5“ëK^û5”¸ÉýÇäz‚?rÿ‘ûÖ‹Üÿ÷×÷–q-ÍýWî¿rÿÅU#÷_¬eÿɺ4kÿëÆy‹ûdâõŸºë%G”% \QŽËÊqy9À!ÇaþË[ôÍý'ú´_ª´OC›ÔÁÚWºuQ—ðÚ‡¶Ç€Òµt©ÏÐyñÔé¼4ë8ÀÒ(xqòÛ¦ÔùÒ(ìbóÀë\ç:7yÙË^ö„ÛÝîvؾ}û:è ›ìܹ³[ZZê6oÆlaµwíÚUñ”<ÔÅÓõÕWWù(+ ~tC£¾iÓ¦Z§¤­LÚOÿ3ƃqFcI|Æ_Î?ã"ן\½Æx26X/¤QÏëO^¹Æäþ#÷_¬î7rÿ™ûO®ƃûLÊx-ŸûÏܹÿÌý§{ÌÜöy"ç륾¡¾ûïË.»ì¼³Ï>ûãguÖûžñŒg¼æòË/¿¨Ø#aÉ¢Ô/Øt Ï{#·FÉa.ÎRóo¶-‹Hô‚¼ÀÅyóoðÈÄþTä"*\D¶•19èlÇic€:}È‚ƒNÝv©Ž¬ rÔȧàm!9ùº×½îwŽ;]uÕUÇŽ;jâ±JæGz =H¤Òéô@z =H¤Òéô@z`x€0IÏ8 ;í´Ó^~ 'üéW\q~éIJóm¼VNÃÜù1òbæâ ™'‹‰Dsrð)óm=ÎÍ!§Mxc¾5|{ªŒ9(:ÊA§„N*ÕšPĦ4%NY†Ny¤!k¿¥É‡4ê×å+_yâñÇÿ¿¾ÿýïwüǧÿÏ ìš(Õ ©þç°Ð‡¸y˜²ÿGãr¡ÞFTÚÖ—ó/£6¤´_žhMÿgü-›$}#ç_®?¹þ2–¯yýºZÇd¹¿"¥qeÝ/LãÎõ'ן\rýÉõwù ™×Ÿ¼þ,»¦îÖX/ËÈ )ïóþ· ‰q¸\«ö_e£Á\àáº+¯¼²;öØcï¶}ûö‹Þüæ7´8„W½q¹1Þ*&ÿE²ð;*©›‹ù7ói”æÓ\Àl#K]Ôá±iâ zÏEktÖÇÁ£ßÎ:2¼ßÍ0›K=´©+‹Ni–Uù´MÉ € ¾µœÈ\Yžšä€“¼kפ^‘#|­£%@}tyÔîeû@éëËõÁë´ ¢â'¸´¯/ª£ÊGõSEŠéËô‰‘Kú˜Ëø›ø!ç_ï —¼\ðG®¿yý©—’z­Ïëoî?rÿź8™¹ÿÔÕ)åCÿŒ7[#Bî?sÿ™ûï~2ô{ͼÿ˜ø!ï?z_äýG¼Ž¬÷ý¶ÊëÝÝît§û–*¿­âeþ‹6uògäÙh›7#ÿF<@Ýü[EŒÚà9O°v,‘£î=xiSçØ#`{ è C Ǝʼns°v::2Ò,ÅÑŽzà÷ c,2~/å¡'tÒÿ)sPÙR%©»)¡ÂGê@äë1Ë?¡«WÊ.¾ßrÔˆòò¥ýôÿP\2ƈ±ùÄÅ2ã/ç_3¹þäú›×Ÿ~•Œë§ó$¯¿yýŠ "ÆñùÄÅ2¯¿yýmc&¯¿yýÍëo¿JÆõÓy’×ß¼þÅcŒx|âbymºþ^ïz×Ûú’—¼äÿ–ñ_:òÃ2Yh® œù:½m£Ëü<´Õc ^}à´@{¥{&Ñ;cG)±Á p'Èc§&¡ëêö)âÕçÀ±Ûòj«j_ÈßìßøÆ'x$–/)Áëâ,#®2„y,i·ª<–0ĺâ,§ñE|äUO[Êcå#¯tËi|y£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|Q>ꌺb]Ë(¿_”:£®X—Ç2Ê/Âå£Î¨+Ö屌ò‹ðEù¨3êŠuy,£ü"|ÊlÙ²¥;úè£ïRÚg—ƒü90€:97òeæÉÀq˜ó¡¿‚óQ‡_=ÊǶ<‘œ<ÖÕŸy;h«„÷Z´ÅÑ1:Ng©›¨¤‡Ã(‡@¼r”>©óÑM]{ÚF–ãfçž{îÇʉ–ja ¯^Ûö¿7±¬Ìᣕ ¤Z5ðä‹ôøéâ¢\Úïÿ‹¤o¢[¿EõèÇ–õµz¢\ú?ýo<´1ÔÆÍÝøii“ëVëGýÝ–­[¹!zúùáõQÆ_ÆŸ±ÐΣvÞÙ–ß²•o©œ|â)3þ2þŒã؈qãú-MÊV.Ò¤+ßÒ¢¾VOÚïŸvmýÐú°õÛ=ýŸ×_ã$ÆGο\ÿãØh×Ûò[¶râ-•“O<¥¶¨KåÀÙ†7B+iÔ•“/Òµ%¥¸(·ˆý<°;ꨣ~ ¨üj9̵™ Ãù5¿ƒ’:¹2ÚæÇ¬#+X‡‡:‡uäÕCIpG{¥YÛâL¨«~{tœØQ;CÇè$mPÒF€ÀI¾'/]yœ@ÑÅ^›¥Zi”ÊQ‡\µÏ—Š |e "EzÛ^ o€)gñ<¹)~³Ï“§$w'?.¤GúÒq.ÇÇ^sgÜqüðñ T×½îuk©_°Ãw*L³Óê·­Û–Øä×®Ê/¸×C;óø¥1kƒrš}ñíøiCkA~uòŸ}ÎþO““n©>ÚÑÿ󌿵‘öÓÿÆU1ÎbœDüJñwá…vïyÏ{º/ùËu­imd;=H¤Òé½ãöèÇsLw¯{ݫ۶m[Ç~tâuºû„ˆ_éúõ*qQo‹v"_Ä_›ísïvUùý|ËoZÐ_-~¥ö4¹ˆ'‰Â}15ÏýÇþxÿ…Ÿã}²ãÇß1þÖËÿØ‹÷ŒÓìGÿG?sžéÛjA;­œøYã}&F€i}û N;‹Ø2ÓôDê±ÏijröÏ2âãùþGŸüÔ¶Ýc§ãÑ æ»PSógͭŇû"ù7iæ×LšÑ&o‡nð&+KµÒà1ïfiŸväGfU°ûjµ*ñÚA;í¢N:GÛL-Ú0àðÊÇ@8b»4Ç€dÐéû:QzAUÜ-¾úÕ¯~ÄÀ p€iP‰åãM}'“AbCGW ÂÀ6®ÂƒžýÉþw¿ûÝzQ9òÈ#»ò]uŒÈqئÂE:uüšú7¿ùÍZ?è ƒºË.»l™i›¡¨kèüEç;Ú¹þõ¯?—ÿéKì×ZÔ[mݺõZžOâêÚ8ÿ® ã'9yÊ)§ÔNÌš;\âúyñÓJkÏ<ûÙÏŽcn(¦¦á†ðÚÈøËùã,ן\\b\ˆ³ZS¦á†ðꡌñ—ñglĸg9SÓpCxõPFØ“øÛ¶m[÷Ä'>qpíEkihœŽ-í¿¢ï“ëqÿ¦ïç-½—ñ>̧åÚóº?Þáçõº׿òégï«£ŸãÜ$B{#ÝçÆØhç4ãÛÈ~ŽORƾ{^(£ÿ#>òÃÌ‹ƒ—·Do~ó›“ <§æÑȃ‘(4/GÝ<8.^$§ø¾3½ù7@¼v(Ñ‹~yqÑ8iÔW.ÊÙ!:ïÀu…ò•jí<8ø98Jp€t““؇>œÂ!/°R×ÉÚ—¯ˆ`Yø—NDò0&èÄ! ضì±=r”Qƺ ui”ûÂ>‹›Üä&µß޾¬èþãÄkÝ<¡©¯ÖB¿:´Ã8°ƒ ÀÒº>_ϾTëø°ïøˆ Šç öÝþÇRðq¨ÇvË ÞñÇzäÛñ—ö'qzMö?¯uœóX'§á*¡|Ècñà<À·<â¢ÍX—í¨Gœmm«Ç²å‹úc]þ!œ:䱌xpöIº%ô¨?ÖÕ9„‹òCºµk9Ä#.êõ´ŸñOŒ ÅxÀ±ì±=ÞØ“f)òQ¬Ë?„S‡<–ÎC[Ò-¡Gý±®Î!\”Ò­]Ë!qQ¬§ýœÄO’åþ“[ËÉzS¡í\‰xpûjÿͽÌzÝ¿9ÆyK}@LB*‹\söÇû/úÌ=šçÚqí‹R?{_mŸ(=è×Fì³÷èÆý¤Ï8ކܨ~¦ö•: ¿='¶¥U¦Ÿc†Çz䇞ˆ«ÉGÌ‘#†À8ÿ5a­y6óræÌbþüއÆA.²?“ü|âÕ‡Me´ßæÿ Ëêå‹Æéˆ‰EÛêƒÆÀ°á 0<8@” YeáÐ4í#G[=:o'Ùw ‘õxò­Có¿Q.PHòHЄX×fÔ©MpÖ7‚}eæµîØgÇ´V%ú±Ãw-î ;цu}Θø®ÒõîËj}‡HPú(9ý¥ï–è£}M‹?Çãø,·¥xù¯)óÏñ8>KÇm)^þýmüÄvbÙøO{Dþvü³ä¤EýÔ£>x"]™XFþ´¿üúý4­ýK=ú™HÒùÓÿéÖÆÕ@Œ/ê1žÐéCz#Æ_Æß5-þH*9ÜwXŠß_÷Œƒ9 Xg>;Çgé¸-ÅËßÎø„¸ŽX×&<Ö×Â>÷¼{rÿö… »îMŸÙÔñµ¥î¼ËÊW“•þ¹uWw¯›íê}»Ý¶8ªùKúÓÞ‹;f´¬Õý×9çœÓ½ä%/éžùÌgòúëüà\éþkµ~þ~É~\oÁŒË¼²ú™8Š¿½qo?àÊ™(úÌùgþ ÅÿFí3çŸ>ïËù_ËÆ%²È¯äøq‚æwPÂc š¹5åh¶áW²ÊÁÍü›¼ýb µ§ÉO åª`ÁéR;Fqêt„ÃÎâ´è ø8p|€ƒ¥.?uxp° ,%:‘×±à}(ÕB,ÁÆÊaÒzˆ“¸½PE>ëQÝà[x^õ+^~yhÍ>“n=ýNîõ¶ã¹ÀÎ,ÿ¯÷˜W;N>äâŒ'ÆâxÄiC~éâcÚÀF‹¿ØOëŽÑ|Ksœ9þýký‰ç”ºç•óëž_ñÐåk#´[Ù¡v”—n -Öi à9€´Ïåwú£õÝP©–?òÅúÄB^ Eyé­>ñ­®V^º%r±®ñ­¼ô´ßÇC뻡6>kýùb]ÿR‚甯Ðne‡ÚQ^º%´X§-€çÒ~Îã"ÆC;Cí!þÈëÑxŽ(/ÝxleìÇ~¬;í´Óºï|ç;•µ•—ß2êS§%<ò‰kù¥‹mdh¹ÿìo—ñ¾ˆ~²®-Á·4ý¼È½ÌÕ»–º¾ÔúåMÝ wÜÙ=çøÝÍí5žsI×~ö¦îWÞ²¹{È­vu¿rÏÝ–UÜ"¶÷2hmÏÿ"}v¼”_|q÷⿸Æ6IÊßÿýßï;ì°È²ªzÛgýn‰²ÕôyÑä$v敵?ôQÿÆþ®¶Ïð·ð½ï}¯;óÌ3»O~ò“Ý oxÃî‡ø‡»íÛ··ls·é31oÛøG‘ãš[é:3ÚŸió±|îsŸÏO»#lS÷I·„ë´»Ìw™ÿG…ÅÕ‡ö¤ƒç0ÿò‘£­ókà¯L©Žy­÷ zÏ N½È¬MPê:ƒaJ;o)9ä-ÕÊC[€ÎpxtÑ'ð::=ê’šüÈxTdß#âhs€t&ɦ‹.ì–Îø@·ô‰vÝ—¿Ø‹l¿e×Ýå»÷¼w·ë†‡e¢ü>—®8·[:ÿÝÒ§uK—~ª—=äŽEÏ}»¥›<¬ÛuþugC“´ „o\ñ­îíÚ½ï’ÿî>yÙg+åNݶ»ß!÷ê~ì†ìŽ:°M;ˆŒÇ'nÚø¥¯¦ä¿­\ nzÓ›Öï­Yì,Þ³Î:«^t~è‡~¨&œgñ®DóüÊçømÏ[>ç9Ïé>ÿùÏÏË>æ»ÍmnS/œcÄ •Yýs,”óĦ”Ѭú£<´yâO]i?ýã§cÒ˜7­ñ±®Ü®!\+;o;òÅzÚŸx`È/-nÑv”‹õ‰õÝc)Ïÿî>i}7o;òÅzúâ!¿´¸EÛQ.Ö'Öw?×ÿ»û¤õݼíÈëû»ÿyªìf7»Y÷Á~°ûØÇ>V÷yCcŠcŽõi¼{rýG”ÇFî?—')£ß­{^Zÿá»ÕÉÉ_{ë¦îëvÝÿ}ÜÕÝ;¾°Ô=ÿ´ÍÝ—.êï‹=lWILîìþñ„ÝŸüÿ~çæîÝÑmîÉs›³Ï ÐnqÒVSrïù¾°{øÃ^xññ|÷¢½¨û½ßû½úþ«ÑygõoOûý¹ ºî6} !šÜãz<ÿmÛöjŒ‘|ßûÞ×}èCªOÃrÈ!ݽï}ïîÏþìϺƒ>¸»ÿýïßýÈüH~Q°qþ‹[T'r+ݯ¯ö¾ôË#®_àhó= $pWù#Ÿ8ËH³Î÷_Ž€\˜ù7P.æÌȉæÔ(¹>/@Þ šy9JåÀ+G ^]Ú/¯ü…uõ°H‚ÒÄ jCè0ƒœÇ4Wª»ƒ®< È;hǪ“¶ü¨ŸÃOL"Àɯ™p D€Í_ü|·ô¦ëÿêë+¶KGY©».¾ »ìmoì.8ë“ÝÎGýD·ã–·_\TªéÛ}76]úÑnéì¿ïßñ±¢ok·é†7®l;/?»»ì‚³º .>³ënþ ÝÎCîZñ¸Á¹sW¹Xã¢×þ‘Ë>ÕýÝ7_Ó}xÓYÝAÔ~ã›T—/ÿfIVþcwÆw>Ò=ýÈ'vwÛzç‚ïG§¾^ÃôñÃÇ!àÖþ˜Vøè+*¾¤÷GôGw›¸ð·öñOµƒ­Q‚X½±<솇u_ùÊWº3Î8£»ç=ï¹,IuÆz´ÞÃ…Dúøüx¢]ëCã'9ù7ó7¿üäîþáÛm¼üzÚ/ÿò/÷c.\ãþÎ?ý:ÿë(ˆYñäµ=?ˆ+ÅŸ¬QgÚ_þŠ[œÿø+úÊöµÝÿu>Έÿê§9ÖýÙÎï¹Ú{Ñ>ç›/¯?ꨣº÷¿ÿýÝínw»î‚o»;÷[ߪÿ‰.?êÖ}öÙ g CëÄS±}îöÀøùa®øè>þñwß¿üû+®ÿóÚ»ÓîTŸt`œãþû[Ë—µý¿ÿû¿;þCahüüò$ÿ㺠Œõs·ƯNÊ!ûÒ±Áùäé .¸`¼¨r…¶HüáûmÛ¶u_úÒ—êÀJöµ1÷xG'y’ê»åÀÿ|ÏÓùçŸß]z饯–ëeÜß=ôÿ´ñzè¡õf‰×þ€±½Úš´·–_ã<¢|öÐØ«Ü^X½Á êôyç7êݤXÔÿ‡~x?ßõ[Ç1-×Ðÿ7ãàí¥cŽ9¦~•¿ä;Íÿ…0sÿ·èø§Ú¹vLoì³¶}ôѵߜcnzL÷õ¯}½î;ã¼l}:Ö×ê_©Ýر Æ¿iiSõß9 sš}w»ÛÝøA…ºq^H,pSþÎw¾³»èÂòNoÙgNƒÜî~ÿ¥¯¢Ï7Òþ—~žÑß©íÂûÂÓ7w[ÜÙýúïêžùÖÍÝç¾=a­|ò[]÷ÉomêÞRžƒ!1ùçï_ê^zÆRy’rÇø7UÿHþG >ÞÿŽú+-–óÌÿ|àÝï|çîøã¯ Ê>ðݷ˾ üð€ªnjÿèÓ”øçÚ2-þÑÏùTý£ÁDúsNÝÔ}ɦîå.iŒögü•¯èñZH{èþƒ>p,ãŸÃþ+_ñÊî½ï}o•óƒ_8ç´Hä#éN?ýôî oxC}‡½Yï2{#cú¨Oö 2ãŽÿ#m$^ yhÄzÛö~=þ¹üüû/ýÒ/M·1åú|<ÿ±Ž}Ú½«C,€ü8þ{Ôø³ÿPþý#À€ ¸ù3päÈē̈́FN€®\,©ÈQ'G‰¬ºh›ØßÊô  Q¸K­È8b ƒ±£v˜’xЦÓè×Â9ÊÈ ]2¥ZmAǦ|ê -Ž’¤¤‡ú6ïÜÙŸƒ‘ˆíSÑýÇß®ÉÉc¾õõnëMŽè–ʶÛTº[êà mzóº¥ /¨º–é »‹ä"ON–ää1|¶;ø&Gv›®Wô-}å <ð¢«î¾ôôÛwžœ$9yÖõ¾Ôqã#º­×ßÚmYÚRêà ½ìÜì¾Qô±m»?¼ñhíÛª¸|œä¿U$'™èl†fÙ‹ök]E£2Ú¦~Dñ÷þàv]tQýÏï´§bÔÊÛŽö µíEÇÏb÷ªW½ª{å+_9êyïçiöYÐxè0Ë~]„F ±ïê©ò£óåxáC®o÷k„ö¢Ž1TFFò–v0ÊFðc}i?ý?güÕøÁ4Š?cªÆÜñŠ1¡MkW¢íµ¶ÿ¨G=ªûßùî¡}hÇðžð„î¾å¿Î$¥žúÔ§v{Xy’~4Ÿ,ç™t-Ç£ݨû…_ø…š´ZKû?õS?ÕÝ÷¾÷ŸúÍé½Ë]îR}AÂÖq[Ù'ñqŸûܧnðàÖrüÚ®ºgÄçWœîxÇ;.lÿÀrî?ÿìÉZ@nhü•qô±ÈøÙsGëå5¸ŸüÉŸìî~÷»×>ı¯4~º°ˆ}d8_U¾~N>ÖÊ>O‹Ýã÷èmŒìÙWí“PûÉÇ7ìqŽHׯ<þgl·½ÍmÇýR¶"FÑ>q|ÛÛÞ¶ÆÄ#ñˆî~÷»_ý.8ý-¯íª/*+uh«µ¯œú)«)ç¿Ò*Ç䃧°ì7{¶ûßïþÉnç%‰üÚпöÁ6¼ÓúSiQÙˆWUwB«+ŠTž‚GYÚ$%O8ᄎ›ð7½éMµȲgç‰(þ±2ZÞÆ*•·”A;– Èíçþsßî¿9'žÎÓ´6´/”§$y­ûÿÜog÷¿ÞÖ''o^¾gòyÙÑýÇS¯®up$.û›+ïÛ¿°©ûúw&i¯‡Yö+/ ”·\)þøá—{Ýë^{+dJ®i¬Ü/Ò¶Òi×zýœ|h—rÞû/¤Õ¯¼mhÑþ‹Þ¿¹ûã÷né¾páR÷ênê^ý±Í}Yêÿò©MÝÕ;'ër+¿ò4öçµo|°ÿ–Øg­˜üÃëì¯ÿú¯w·¿ýíë?V‘6~ôH¯õF14úo¹ÈøÕO ¼úÕ¯ÿ–méÔÝwQ ÅcûêSn^ÿWþÞìøÜ<㇯ALþ‹|šù/Ú€96êäß̣іß8zІýQΤg”ƒG[âÑcßJµæþÔG{.`@« rv‚6Æqð£ãd )鼄/thô‰Òš©…_±ò©OYô”sØ[©atbk‚¬møhÍ|ðŒúää–’«Ã©„‘úò(|·´«ÛrÈ¡•çÛ…÷ê‡>|œ@… m^ëæÉÉ/Oa–ÅgYT/íê8äÝáç}¬üÿíåå)ª¨¥Ý~¿ãâ÷Ö''8¤ôn©nômZê-ýûðgÕWÀŸzÄOAƒz–•aü0J£Ž}& Þ’º 9ùÙÏ~¶&'y [Z[*ñ­Þj¤ùÀ>¿ôÅ&žÿÔðzJûº·:£¨8Ki¶c¹«ŒÏ¾Èg9müðŸx≕:úÔ Òº¥8í´øªhà£=ÿêA€NëQ> GO]Z-Ãùoÿ[“öûM˜þÒw´õyúñøÓŸ”C`üAóXÊß¶çá2±®N˵´Ï„þð‡»—¾ô¥ª¯%›ì“O>¹ã)ëÖÒ>º«eĵ¶iï û<Ï/Æ~õ«_Ý­ CöyÂéÜsÏ¿t‹ ÄYη<òRÙ—Ÿ›Ÿ·¼å-»ýB)ròX‚l[ò*×6ÆO"'Þ.¹ä’îBžl*0Ë~e(ê²M)ÎRm’,èåɶ[ßúÖÕ¯ýëëÍœ|–ëaݱ_±®]Ëõ´OñåÓÆNV²/IʱŠà˜,¥Ù¶D?Éh^'/åJöá’cNñXüöÈeb±‘8ulòYÊG›~òT.ã ±GRW¿žr†_YˈSŸåjì+c©~ËYxyÚ~ûvÐã¼”¿Õi›RKi¶-#¯<±äŸ íž1ÊN“çÉÉã?¾{ë[ßZ×JÎÀ¹äé}~Whõ‰§4þ"uuæþgñýOõ+þ¬Î.þ^v‹½˜ÿ=?¨¬ú©¿é3[ºÇÞaG}­ûóåµc‘/ôUÝÖvu÷yùUîý?ew·£vv?ÿÆj’’WÀòv;Ê•›º§ß½OVÆð¡]KI¶c¹ÈýúN:é¤z «O¢•öË^ö²ñŸþô§W“Ä&‰ú?üÃ?Ó h¿­W¡ð1-þÑ«Ë 6Hãµù“þ“4F×}àì¥r,OÁr]ÝßvGùÇk†Ê‡ýEæ?}êW« #C|ÕxùpŽ·mDÔ§ù?òS×OÊ£³í'4qËÊçQgþ‹MÍa£‹½¹0ðZÄÁÀÛ_&rà‘!ÿfÞŽÒÝ)Õ Êõ ¢Þ&ð1eì'ô¹ÀNÏÅ*ÁÛ~nÛ$¥²Èèå ª‚¼^tq }s .ƒ\ ŽÚFh´ð“ý£ÀÅ$VKðL€zQ_’”ð\Pxw=äÇÆú&|½>ÚKž^_ë'ñ”:Ù4TO¢ok÷í N¯ JûgŸh³Sþ×¥gÔ׺ûp,“ÀÐÀXé^]ѹÔtýƒºÓ¾sfwâM¥N@ôÕñŽì£¯¶ ][•9|Ø—€ªU’“þç^7<¿ø‹¿XŸúhyb»ÕÓ¶#o¬ËÇæ IÉ&¸MRF™XGžc¥óeb]ûgý”SN©U•â‡ÊYz†øÅÑod=ÿâ)ÕiiœÏÈ³Òø=ÿK%vÔ'.í§ÿ×#þb¼N«‹ÓèóâÑÓŠl\þå_þ¥þ£ãøãïøá€ç>÷¹5)ôŒg<£¾bÌ=±ÿŠW¼¢>eÆS<ò_ÿõ_õ¿ò?þã?Þýë¿þk}:…/cç‰”ßøß¨¯äþÕ_ýU½6üÁüA÷š×¼¦¾&Më·~«Û¾}{ý°ç=ïyݳžõ¬e]Ò“žT_q>õÔSëÆ›W½ÙDqáæ˜ÿó9ýåI%Ö_ô‘ÈzãßX×Eò4ߣýèº)Gcå6é¿ú«¿Z“€ü§›×qxН¦ Á¯²®¾îu¯[Ö/üuòzöÛßþöê?3O{ÚÓjr•W³¹¡g\ý×]íðTO6€áKÃù®¢! ÁÓ÷Ïþóë«¶Óì)KbÉÿêS'ÁÇ÷ýðDx´yJÿ`ÿ£ýh÷™Ï|¦þ·Ÿ§ëˆgøx2êÝï~w=ÿøåV·ºU}5_s#N|Nò'I(’tùŸÿùŸšÐãuI^ÇæÀï-ð%¯RããV7ç§i^Eûâ¿XõPÇ;ÞQÖŒó|Ýë^·ã•SO$²8|@’›ïkæ\»w½ë]ëëç<7q]‹@IDATá@ò9â`̼wì±ÇÖWãà¡ï|ù=úx*ŸÁ>|Ê“¯¯r‘È¡ß|tôÐOâ¹3$Ol´çãÓŸþtÕ?ð/Od0&ÎÑ×¾öµ:/H´Í:ÌìKÄbLÀ ¿]G8¶Î'’¾øžsÆzƒï™gœkæí¶mÛjÒ°†àKùø¯Ç‘ø&Öˆ9¾+×Ô=ß1ž‰9ÇŒ óÝD'vè3±þ¡s‚_ Üo|Ìœ ¶I\·cƒ¿õÿ(xÚ]øž'Á| ž*ÇŽUŸL;ßÌ•Ö>çÿ“øøzÖ ÿ÷ô-OȺ6à7ÆE|烯'ì7o $üœ—Ä8Ogq®ñç–u‘õ…þÑoæ:YW©Çñ† ~@ޝTðkè/ç dN/ ¼uô‰O|¢&'ÑAü3?éój ÷Ÿû×þ»]ç<×Óðg–_ëþÃòƒ8Ï?­¿ÿ¥{\]““ÊY’°„ö¬wméÞYžžüíû^Ý=ÿô-%A)Çì’µ…>pm²/ãö Qy‡XX«þîïþ®^[úË_þòŠb±¯›¥§•íYñùVªcÿ›ßíïɦñn.÷^ç_Öu—\±©»ýúûÕö~ü (ï8¦Ùž†W~}^ü,=5«Rú[ïQî?çµùØ—ø5lOkûÍy`f¿‹ŸyxêØÊ§Å?øiãG{ÿE»'™ ù3Jò^}§ú’:8òcÐiKWPË‘´sjòƒS]fžèƒº´®fºJi]û5 °Zˆµ3àúÕm2x®²‘×ÎQ*Gò‘6¡D‡ƒ.ÕÊÛ Mê×)t€‡•ó»P5`‚æ$^Ö.?ˆ³TnúÞäÑ3ÖE½gôã9:ÛƒÄÙo5R>ÆzFõ‘ x6}ç¬Ú?uÄEE?ˆÃÍÊ8TªÞÐ?ô•<è´>4~mjoZù—ù—7,þ¼’6o-ñlìØüsƒÀ“Hó讃}À?küóèƒGÿ=å)Oé8"n–åfñDšç"ÊźvgÅüü³ÆmÊ« ʶn;í÷›.}K}O >ý—ôèõ¯“pᆑDÀZÂMäîp‡º À,É)nl‰khðÀ Ð&ùAâ¤7É$À#ϺËÍ=4‰*žŒ# Erй#~¿`œU¾pœMÖþçÖ›p^¹ ¡›Ü˜³QÆkÙ|Ï-ýæf”¾üʯüJ}…DãàÆÿmo{[÷©O}ªãK$i°AŠô?øÁv©–êDž~s£ñó?ÿóõÕMn²é77ݯ<ÓÆ.O½ÃG_è'ý– D}ãÆ–=lÀÇê¹ÑF'Iì’0bnñz&}GÒŠ cƒŸsDbŠ:×.üvÑKò…„$ºÐM¢ ¾mÛ¶î+%1I²ˆ¤‰$yÆKB²üÊ"熘›¦·Ih’öŸb‡˜"ÁÁðÍñÇ_ùå#i¸æ: ç•8úBB’ä—@›„ Éb”D ||#þä:K²»~É2ÇÌ? wlcÿO“·¿ñ|ØŸX2Ïð5É|Âü" Ì:œwbŽóʸ¨Ï‚¡±Ï²Í|dŒ|O*ç•:ðþã§ìKÎ)vãò¾Fž1s>Y×X·tùDË´s4úÈ“uÌ tp mÈèr>qÉùs¾ÇC>™æó!ûŒ‡¹&0F|´¾eN36€$/íãŽ;®&“+²ù IØö;²èæ¥uâ”$1çåh ÓGæ ãåŸ Ì'æ.€¯ë kInÆ K?Yˆ€Xø' לßÕ‚×)÷4È[—–û¿»ÿ‹çj¨þ­Ë–ºcÞ5þAœ»Õ'=·ñ|CøñœcÞÙ}«$܆táÔ3kÿ;$7 ‡Næ}äÑŽ8è-NÚJ%}…'ÊÇúJò-ý…¹²&yÑq‡ïìþ÷\ÝtÜäø½\U^ñîºG¿öÀîcçÎïÛÖNlcKˆøÕÔ•oËVô7O[9Ö}Ž\¬Ï£'ò “u5⬻ÞÚÖ¾íÕ”Èî‹õkD€Áü]ñp!Ù¨÷›õ>ž2Jór¥ZÁv?z\¼yStry€¼5ÿÖ£ê'ý€¶ª‹YLð]S«Ñ@ì(Æisè;¯2:f–•:‡üÔ‘ƒNŸ(‘W/t]ÞR­uèôCûð¯AnѶN”â(W ꉺ¨3¹Õ§œºíŸíÕôÑ~(Ûê/Îrýò²É^ìÏjeýOܼrq‘CÆ~kŸÒú¼:å;å”SjõÄOµ¦¥}kû¬‘¶ß´á<ï–ò¶º”iKtL“Hï[“¹D[»–ò¦ýÉÆ?µ~·-²õ8@Ÿö­‰.ÚúÝRÞV—öÚÓd ÒûÖêíÛ—¡2ê‡nÛúJív<ö•Ä 7àÜ’ O⊛IÜ`òjÜtCƒ‡Ä7‹ÈðDÉ$nj}Ê‘þ@'AÉ’?öƒß‹äaöÇyÀ6I¿ß–5'ÜHœÓ¿øÅÕ>zŸûÜçV»Ð¯¬’ åø¹Ÿû¹nÛ¶mã^ž, ùÁéë¿ýÛ¿Õ"vy‹~ à7:y*’¤O‘ò‹™<%äSAòÇÝø˜'JIâð%ëüš€nÏ»8ÊÖOKñ5"ØûJIrNx"ß“¼ÂÆ{Þóžš ÙÇ÷\Âcr˜þrn±G¾ð mà<ÑFb€äO°‘Lƒ ±LR‰>£Ÿ$ ˜´¡“Œ° 6“¾FusíD†$ ÉDú†]d8ô$ I™è¥o$Ì8ðñT# .âÃ~“¸B·8dI„ƒ'ÏùÆ&O‹_$VHÔ ÃXHf2^úIÒ“d+cG¼Ä<%ç†Äò´I¸(ÿèÓi§VaÄçvð;ñG¢'¿LÖl+±KG@<€çÄ ã&ALâÀÏŒýŒÄ €-ÆM2ѱÛ7æO…2|mÿi€,ºˆ=žJ$îH>1'ð ‰*èœ3b“u[ÈÓOhñ€Å.<úûà‰IÇÌö‡à9!YÏ ç§Ñá¸é~!ÎHºÅ±©R [à<‡ô;Ä4ç“yÈÜe-Š>Á>àù&Žˆ |Îù&¹ÖÚ×GÚlÛ<-…o‰M×üž0OóÏ æ:ç„ñ¢Ã>Ç~c›˜‰6bݾ#ƒÖ$!ñ @Lùt)×bß—”ð!Gœ1núÐWŽÚ¥DF˜Õf}'>åQ‡ò´™cüs‹§Ð"_ä‰xuDûÖ)å¥Î9@¥²à¨ÛV¦-ÕGÙÊ€ÔÑ·Ò~ëý²R¹¨ÜJz#í¹y7Jݾµ}Þ“þ½ð¡% Y¾*wu?{—«»ÿï¬ÉzñåKÝ NßÜsqŸ¤|ýO÷¯Ö/bÏygéXѵÞ2ô-ö¹Ïö¯vtôAí;ãâ¸h·±¤ _ôÈG>rlHê¥Üy𺗟3úqœ‚@×(ðjv}ðöG}1ÈïxýÛt_¹üÜúã8uÔÕS#%S-Çåß¿¼ƒPŸuÚÓÆ_æøàuAn’¹‰¦þ·zÑÊâ¹™—>6ì>I“óÂzŒÛ'žxâ¼]XˆOÅó9.Jx—ÈC]>ë´gùÕ—öûͶ~Ñ¿¶)Óÿ«‹?ü%èǶmüEz¬ÃÛò«Ç2ò°~ðª)¯ÃÏ+Ð$vX[¸¡#1ÃÜ ‘@²Œ›Ëþçî~ú§º>QG²ƒ„s‚n’‘$!¸É'AÿöíÛÇIƒ¶ô…cÖø‘ñÆ^nôá÷©0,$A’o$wH¦™0àuòôÙ›en¼ÑÉS=èd¼´#ÔIrÐI`pÃŒ€°|LPªê­I’Ò§?ÊÁC²D{Èúô6ð;I>“ø~ð&(½IG–§­ð²èF/@›„IìÛ·øšpì;¾wýDVÈá”<µ¥Ï9/èÖçðGÝꨊÊmÆa¡Ñ_ñþc~£Ä>c'aF’˜&ç+Þ$f°íصI‰=9t¡›'¾8ÿø—d~Í _@Ò…$¡‰Ä!ùª¸|p®ÐIRÏL‚Æ|#Îñ?Ictj:2€ã¥îyà¼B'©&ã3‘KxrX ùEbWPÛ<5‹m@B'‘ÆšÁZA_™ï>q/<ðãCúCb1öß.&'I"‘0׉*cƒñÁË8œïúϱÈ3ÇHü9pžSâ€C êˆÿ” ‘Ù‚ú¦ùÀq –7êˆ>oð ñ4ë|ÃßêŒmê¶ñþ÷CòÚöƒq$úd9Ÿ¬·‚:é# ~ëÒñ·uÎ þŒmùé>" yB?è+G\K¸®À°n¨¯"ʇ´¡vŒ7lbÃ>¶üðÒ_ø¦étü¹ÿÛ¿÷žû¡òˆƒvÕ»9ö°]ݧÎ[êþç››»ûßÜ[ôåú·Ø]ï׿Sþ1µµÓå\³[Ƴ1w³5̦ºšÍ5uVüïIŸ_ôÐ+»O”_EÏW6wÏ9u8 ÃÓ©?ñO×éÞö³Wtw,O[Î ú7òïI_ÑÏUÔÛÖÙ{ÍËeÛþÙÖÿ‘w­ëôyˆ~¦N_9ì{ÔqÊE?E:r¶Õ7´þ‚+ÐOÈšñ×™¼£,Ð8ÆD…—AJ`$$½O@šs+Õš¯ƒðÔcŽNÞÈSX*  ÔFEÌó1<3¦K:(;Ä€ qhv†:@Ç9ì,8lóCÿHÈD†y‘ÊQbõP¢'Â|ÏîSß{mß¿"W¦ÄÄC#]è¼ì{åi‹Â ¨/ê™6þÈ3«Î ¿ÜõñÝ)£' W“¤líϲ-&'¹Qˆ|%Ù•Æ?D_IçÞ ùȾƸp!“Fߌ?im[Ýðÿ›Âw€ |mD¼6¤ÁÒ¢êißå±÷ ~º6ù?ÆŠ±±³¬1܆xä*[þ¶ 7àÄ ž㦚§îHZòÔ"2Ü4Â÷ =¨&Ifð=m?ñ?Qb¾:G¢’ïŽ$aóæ7¿¹Þ€òš47 |‡à}û=D+¦Ç€NyLLòä À“=ÜìrcM¢‡µ˜D…k"ß™É'@Ò”¤Ž¯ÉVdù0ሒ¶O–²aó&ûØäæš~’$$°ORÃþÑh”B+G?LÀ£¬üâZ¹hq’ˆõ‰1ú Žþ‘Deü$ÓH©/ê'‰@BÈ5‰Ä­@¢ùˆ#Ž,ßo÷Þšàã 0µò¬TÒ^É%YÇ“xºIñ)ÉCuGôëe€"Ì8L"¡Ë:ãàü· >J¾?“ÄcàzÉwÒñt ^ú'oÔqé¥ýÓ_Áo²ˆ:¾àX î~÷šj›µ‚1$ éçWFI-΀O9gÌ=¾6 &9L(Wdù€—˜ éë\düÌ}N'%Ÿ²”Ƹ1Žñä:'ò±^¬¿G}T]WL`;6x§ù€¤7ã "cÓ|ÂúÏ÷%—\Zž2_~¾£}t3¿ɶµGŸÑæ©h}O|’ü%–ÛØCÿGˆú"Þ:þ&)Ð/Ÿ|¤Ïqb¾à€óÍš À«Ö>qãZ\™WøPlü㉧ûù^Ù!€:|Â<×_mÄó⤡~–¦ Khà»6íÖküñDÿN«ß󦻺ÓÏÞÜ=ø–;J‚rK÷Ò37×Äá;'#|÷ª¥îo>ØçCtìÕEfSw£—ŸÃÈßÖéWì[Ä·r«mûÝ“«•â}”¾}Ý\.w=rg÷¯Õ4…ªw+»îÎî 3$»Q‡úyhþ-ÚwöŸ^£†­–dNYÛxË‚ï‰^-د¡ù¿Z]«åç+ØŸí ¸Æ9u?ÒÄYÅÖÐø‘ ‘<“”Ã@"f[âЙ¬à&2#òfà€FiN6tJeÕŸù?hæßZ¾Bª:‘CÇÜ Ò¹#†ˆ³®£è¨BL:ufÛ["ãàá¯J¼6h£½ð@ÛÌÉ4Ú]èuÁ4€<ñWÿÐ=»oæ¬îºç}£þZwMRÖ')‹@µ¾Ô]}é%Ýù7ÛÞÁ«~ôEài_}Ãuç_|fwÝK?_­»&)B%!tÕ¥wç/ݹÛqøƒ–-âÕOÿÐ÷CïßùÝvŸ¾ôËõ׺‘_æ¥Ò¼¤ôï®Wß®ò*û†^@”Žß²ç˜ýɆï×~íׯIJd;î¸ÙB PMNrsÀ““lÜæÆO¿Vÿ¼úö6Ÿç¯xÆ$~Å1¶ôHs ê¤äF…,cýòJhõƒ6Zz¤©Ci?ýoLŒÙqÓ¸c(Ǝ̳èÒ¸‰$ù¦äÔSO­‰6žR#QÇkOGò}n$!H”ðÚ)?øBrŒÍö©“@ã !EBáq{\U<ßû÷øÇ?¾ûû¿ÿûe7‚Úpl–“…¼« %~ŒÛØe 4éHƒï$é@BŒ¹Æëͬ‹ÔIš’ŒâæŸ§ÔøÁž¸çæ”D-7í$ù¡¾/2ÞÔò”:ùUL^&ÙÅºÏøL†D"Äkñýa\|G1¯`Óϸn?á O¨?Üó‚¼`¼ö ÛÚc½×ž¯…úT!%}C7É“mÛ¶Õ„ŽtôÑÎ?Oèñ}€\£ð%7èò`£°Ôd'OòŠ( å)#ĶõûßÿþUž6×*l’pE7@³ÕMòyžð3i¡’x$9ð5ñH¿‰ ¿ÓYåxRŽïÓäœ ç÷áâœ(ì·õ+®¸¼>qūܨ¡¿è!ÎyzŽþðdLÌ#’Z$ë(I¤p.xbkHž°Ú~ÐO’Oø„>´ã³¿–Ä/¾#ñËXIÑO“Tð9ÿ© Úµdþ Ùv^cÄ5öì7~ ô•9JìÑžÚ$yå~…ïomÄü“$TÿÚ-qÄÜD'>…§MHcýÄ<з•ø÷ HÇ$_<'¬ œS^&‰âq0¿YëÀÿŽH°µ>À6ã 6¨·@?Œcâ½ú„¯—€®Ï™sØ¢}æ.± 1Ç9À?®ÅŽWY|F˜ïø„ïd„Ÿ$™TDÆóØË-Ÿãêju‹Çoôd$ xŸ¨…ûöíU7×â‘Ø 6ÿ¡ßØFŽ9ÜŽë?6 3íÓvïûòçÜr-Âoø?Ê¢ƒµ”ëo¨¿×=±AÛsª½ž§ÇÛ?qÑr‘iò«“Ò>”ê2HûýœÒW:§õ/ø!Ë?­|äm®î~ýmvÿïc®èþýó[ʯt/Õ_ëþå{îèÞùäþGÕÞW˜$.Ϲd©»Íºî!·ÜÙ=å t/}Äb_ïe_“ç^üF*gÅßžöóAÇîìþöQË}xÑå›êݶüHΛ~æªîf‡ì¾¦Îc·EbC;|/¹ÿÄK®_§”‡”øgkìjó?ñóriñ˱kעߋ€ý²DGô±xËHÓÞîñÏ:(µ/•—w9µæØH†ÿÌ™#xЃˆ¼ŸOMBk,Žu*+Ò‹ƒ4ÚÚ·^Pµ?à•mó}ð¬«MPÒ:fG4Š޵>dí4Î(ã` ë œöáE—}‰ö‘ƒ.©–F¹°s‚9l×JùW¨E°$-ß;ó°Gtç¼í-ÝÏùrÿªÑhÓÄkÝlzIN^õðGUÞM#ÝèS—uìî<ðˆnÇMŸÖóµWt7>ïEßÖñæðZ7ON’œ¼ê˜§v» ¯‰lÔè?b˺§ÝøñÝËÏ{m÷Ñ+>S­ÛM¯uóä$ÉIxŽ< ÿ­ÕŒûqŒeðþg>ó™õ»Æ˜ìlظa\ °1^#dÃÌ 7jŽe%ý‘®Œö¢_ÅYF¹Yu6Ël<#ÌÒá“ ³x¢®¡z+˹Âßâ-‘¥î8çÿ(þWŠ¿Ø¯´Ÿþ_4þŒUKâª^Fktm—50BäoŒ‹·Œ2Ôñl¶HPR"ÃúB↤›:ÜÅ’D¡2èâÆ’ïc ô5IpÜ„sàÉ$Ö-Ÿæb#Ç¡Ib¢\@ >ŽŸïä¿ÑÜà’ÜùÛ¿ýÛ*‡,7·$žö´§Õ'©øµp@<ô˜Ç<¦>ÝNÒ$k¨OPr“¬}¾W’_3|êS˵gä~¡›~ÌoÆ÷Êò5$/±Çº÷OÿôO5Qƒ_ùŽH^—$iÀÍ0ÀX¸!‡¤ÉTž®â&\ûüWÞ$žkÎÎ"÷Í’X˜f§XÑÃø|MŽë} CÒ–äºt{þITàÏmÛúï¹#ÙâÏI<ݼüȯ›"ÃzíëböO¿1Ç!ðI×ãŽ;®Úæÿ“°ä4†t“háÉ/å$tŸ.}ÐK¬;$vè I8¾š}±GI{þò6r$,èýl¿ó°,ÄÉdÞ„ IWÌ 9’[”¶MPÒ¦/Cò<™ì“~1þ•¡°O"”_œÆ>IRŸ~ƒ®¬cæ€xâ’~ó*=xæI=Ïe ø*ÂG‹í{ÛÆýà|—"7`ô‰$>É&æs»<5 c%!È9ƒFŒÒgì9úÄf¼ã‡ÎüãÜ¡ŸWó9ƳòÈ`Xc^ ›¤&ñ¯Ô‹oðg<§$(Ïýks˜¯¬ˆck}€¯‡v='Ðôñbú„vCç=Ñ>s›9J¢‘0ãe%žø£oXxòµ§Úé ¶X›#Øçªcð‚7>(¹ÎÔ•¡Oè'N˜'}K÷¼½¢ûwØ}þ‚¥š$›<Õë¸õỺÿçG¯¬¼-O\½•¹0[¿TúÇñÔWÓgeW#·¨þY¶öTç­ÛÑÝjòÒI5õµK—º×|lSÇ÷OÞ´üѼþµŸôÉÃø¡‡ÿé3×ø'>ñ‰Ýk_ûÚñ9Ô÷¬küÝ¿ù›¿Y»±ˆÒë©ñ¿ˆ>:Âz×>ež5XTo.CóOZÔ­ÿÅy^àGÕ6ΑÈ}o¿6#rbÐ °ðй€qÈK>ògxft,áé/~=6]“œÈ÷ò^NÞ‚Þ ¢ý݈-C« Óyd]šè¬8õA“ŸR[8AWÎNãh ~tÂáœvÕMÛ¤Ë[” ÍûÜd¾  Xï©ý'ø¥ /è¶|èÌnó§>Þmúê—+aç-¶w;îx—þÉÉÃn8QÏ´ÆMW~«Û|Á»ºÍ ÛüÝOWÙ[o_^ëþáòääƒk"S…ê! ©âä9÷ªó»w^ò¾îôï~°;ëûŸ¯è;\ïÖÝ}·Þ£{è P™­L«gˆÎ—Ùt­Ø¾ìe/«Q³•€ÍâíOŸÿÑgúqZ9Äý鿞þ?˜ÒÚ·í‹Û›åZØçÆÖMǼ}ç»}y’_Ûæé­ñ lQð3?ó3õóä“O®‰ ô³Óh-/6X¿c2žvüð‘PkD‰7~‘Îø¡·rÄ*0ÔwåùÇIá„N¨¯á›\OÉSŠíµ>Ò©ûK»$}+É6’¢îV‰þèíCã0áÓÊNkc9Ï+OCò=‹o}ë[§‰Œñ$r±·ÚØUòÄÇjØà|’ 3NæÕ1ËÇóèÐ6>ÓÿÊA#–cLJÃn‰>2Cö¡s˜ëŽ7Ê Õñ±„¾Õ€ö[’ÅÇ|Gè¥Æ9OD²¯%9j‡ú‰/Ð?­Oœh+­imßÚ6 IžX%±ëúM"›½¥q×ÊØÆþoÿöo×&çhZ_¥M+ÕG)O[âÁü³x#MÝm9¤;ÊEzÄïöIÀÏs_Ç̶üæÛè¾N9ß÷¹ª{×—6wïþÒ–ñ/{ó“¼Öýàcwt/xY—Ë]úŸ>øªŽW•çïÃ<7ÈY_íý—6ùÇ*?Â7”„’‡= ¿—ðªW½JÔÜå¬û¯Eû¼’ñï\¹Ô}§<¸ztùeõE?sÏhìÆ9»§}æºø‘ưîó oÜp­áجY‹€±¡¬qAßùGÓjã=üÇ\ŸöWg[®ö^¼•}¶¿±üJyóŠ'å£ÿÑO¼&·zÛöÃö°ŠR·t|]Öõû•6¯¦ð.ãá¦+ù4òklÌ×Éž<›tä¬3Ë­—ê8 Š@¹¸¨:xt*Sª+‰Ày@åðb€ÅPgÐtÀ²TÇ àÑ“ œºà‹}‚n]d(§ 4é»MÖzÖJð;‰QÒÖw•ä•.ÁÁ1¢S(‚2l@ÀÇH LêõIÊ#ŸØ-õ³ÕQÅ`U£>uTV8u‹ãéÈ'þ˜îÉ7zì¸ïQκú7Ïø‘[ °áâI@;³äᙇï¼æá—7–ÈÍ3þ(3«þ¬g=ky&m5cð\£°=ÿà×<ñ7Ïøµgm§ýôÿZÇ_cÆ2±&ÄøkùáQ&–àuå#½Òza>Çë¹<9úPž&t ÅU}ÍúqøMÝãE>i¼Ù¥ô&RûÒiódU´%8ið«{\öH>«<ÉCi9ú@$^‹£M†À:%ãWNpí*xy)¥Uäè#&‚H’ ‡'¼e(¹‘§¢}Úòñ´ßÁø¸ò£<­ÉSJlºÑ+OL2¡Gˆº#.ÚŠ<êƒ×:}¬ü=²ªÑçÚz%”ÛøÐ˜׫˜\×À·ö‹ö¹Fó”WõRðUÔ¥í6±uË£ÞXB£/1¹鵟0Ͱ?öÛ±ª›ØMo‘û¸Ô•߯®:†x»4m¡'ÆdÔ‹¯iÈq¾\3#4¸Hñ4];¿•‰vÐuÓÔkìÈCâ›#¿ªA>ÏÉÐù—Ökî?û†^^ovÚ¯cð¿>ií#7¯}tà 8®X‚oí³¾E\k?ê¢. #ıEœëŽúáóhç߸ŸEA;Ÿ¥©›ÒX hû¤]KyÔG|ñ­ôZÂÔè“’¯`_"NhàåAÆþZ¯vG6õ¿ò‘Ç:e´¥îˆS>íOüO¢ïñãJ°¹¬H/,?ÜògÐ=é_¯Ó=ö;ºßºÏ•õ IdùAœÓÏÞT^ë¾NITîè~ñîWõ7ú}H®¤~L·_ÓÎÿ˜qÎ O³“|4Ôo¬+$£¤Í©º²)OcZü­Fß<¼|÷çÖòÈÕhªÌ#²Œ‡qzH ­Oñƒzx"üä“Oîxˆròdü³Ÿýìñwuï‰nûˆ-õèÛöcžržÑmÛçiëúå³ýP’Ž£ìñ©ÉÈhÃzê*tò_æÕȳÑfáæ Þo´‹ÅR‡¼%O=:ƒ‘ƒ¼üäÙ¨sx!ˆ<Êhš8øí 6”—n i:˜è›Î±<á(Á(Æ;GgÁ;PJ‘v ; ÝôO©íH‡¼EWƒ§à*xb‡&lOc‚Ãå‚+²”Ê‹C¿ÿ ¥ÛKK“WnÛ ŠòêŽú6Š}üÆ áz›:|¶7ìDÿÎòÿz÷eµ¾Œ7ZQ¶Ï51þ®íóïÚ=þI´»FN0»¯¿Ì‡Èëí\A8ùlGšöä±=ijöO:é¤ñÓX±Øç•áØ¯õ°ïx£qmö–}Öá'ÿVcŸ×~ùÑ žRãúCÂÒWó_;¶4þµð?OŽ Ž-Ž9Öá“GÊ–‡vä‹õ–7ÊËyb^y¨ -íÈë-/:ÄÉg;Ò´%í!žEíóú²_‰ ~žÊˆ6µÏy>í´ÓÆ7ÕêÒ½§ãçi<Æ¡žØgqëiQÿÛÏØÇX§Ïò´ýgyãß8~*<ò±¦ðJ¿IÔ(K]ÊØŽ4ê&l#½å±­.Ú±ÞÊÊù"O¬GêBË“ûÏuÿ·è}Õ–òƒ°¿yï+»Gßv©{Ëç¶t'Ÿz`wÞe帜ø#˯ußã˜ÝKvEw‹C¹õ^x/ƒ”ñcœÚ^äþkž$Ôêz:á¶ÏöOJl¯¦Ïß/Yë-xK=¯,}žä"JR¤ù'0>_MŸs,É <ò‘¬GÄ/Z§ÏBïÛåùh{Úgõ¯Uû¼¯Ö¿Q’ëâ ÇFIþ ÒÚÄ!—<SšƒÜ |胀‡6%@‰|¤SǦºåWHU/%ßÌÅdžébG1†BÀº ¤#Š6‡íØìš´,Õe =:G€Ç¦NA¯Ctø©ƒ§Í1¾`»°P2Q뙸JÑ”GéÂj ..±ùÕ¥MhÖ7’ýÍ›ûïMНEÓ×µþ«ËÓ+¼‚¸ÞvxU ?Ïò?‹úz÷eµþÃGñb£¼qg >Æ\¬C“oÖø7RüÑg ž³Ó:K°q̱M¾]Çk¾Á'øg_œæ¥O*Ñ—!ðœ Ñ".òÅú4ðÓøfÉDZ¬G]±>ü4>h>UE½žök!êŠõÈ×âÛväµ>¼‘/ÖÕÓò µ#¯õiº¤[F¾X—N)ž§©xýfPf%ÞÈëQ®Å·íÈk}x#_¬«§åjG^ëÓtI·Œ|±.²Å·íÈk}žV÷4™ß¶µËyxÖÒ~».j(9ÖêjÛqÜÖçáYËñk×r£Ú§_ñ++b?¹–^SüïÞÙóAé~Ê\ÜsÅzä×G”ÈÖ÷ÅþãšjOïenY^çþµ{‘4š$ŽðÕž@¼—ñœ£ÏúžöyOú6M6ö9ò÷$êVsϸhrÛóÊÆ>;Çì/¾Þ÷ÜÑWóÔcŸ#?ýÝÈ}æüÓ? ®yÖKô?8d<7Ö•QŸ|±=M_Á“3ÿóræßb>Œ“#óéIhxslꃮ¼´‚Z–׋öá…yí—j­ƒÇüäêV:º C–ðÓ €ÌÞš<”—ÏNÃkçáàG´éÚ‘W‡ÙÆ>²Èp€ç8€@ðâg0Eá_t­Sò+O) >A<üÖ¡QG©¾jŸï6àKûù¢w´µ^mA?¯­·^góûI<Cþç;Ö³/«õ!>Š}GÞþÇØŠzÅk´[¹ý!þA4MÖÒéô@z =ØWà‡¶ö¹ÿZ~o§o(7Êþk½ïßâ˜ç©Çû=øÝÏÇûßýñþ‹äýF¼gä;gEüçíF îÑé'`|ØÞˆ}æÞ|èr£¯‰oÚÇØ_Œqòâê@K‹:!—ƒ%90„ÌÍù›.$qÌ«™k3OfnRþR­9;JuË'œ8ôµù?ðôGZ©VÐ~ÌáIÛ­œ'ûdr‘ÒQjêq0’šB.‚ Ièð8˜6uxþöÞÀ®¢Êÿ?é,…BHHØw]ň↌à6Ž:þUpt6qÔ—qfÇm†ñ§ˆˆˆ‚¢‚, Šˆ ›€ì{Ù !dß“N:ÿó©÷¾/Õ·ßÖI'y ç$÷UÕ©SçT}ß½÷Ýûíª{©Ãå<®@ú‡ ºÎ9çœè‹örÑ/e±,½ï)•lµLÑ?åš¾2E›b¹bºãCJò`ÞÔÉÃÅ9AHŠc­Ùg5ÈR¦ió^^zC ðbþ’Ö¬ÐõGyR~‡¿rˆ ,úÍûN˜½ÀóÍ4æžô¥è{sË9F•¾oãï¿[ß#~7Hr…öAé(çû–ôÅ´hS,Wì_¢øóÞÊÊL–L°ïsÑ×M¿ÿ°×’"ÞÅr­vMë#~àû_ÍÃ¥x¼Ë56[Ç_qüÕëþ•qn«ûŸüžQ$p£øL `Oñž»7¿ÿz㇠о¡>ç±É÷p†ôãm¹ØFåFãßþ‡6E^AñŠ)ñgøc„x›:²5ΔÇ{ìIî~®o“p_ðaâ×x³7<™êÄʼnÈ,ڣǡNeù¤½ˆHù–"[Ò¼Nöâò(‹´Q«º‹Œº×lÒ`Æ#‚"@yÊÔaC'Ôôê”g+6¹êUƆXòO’ûVБ—(ŽHPÊü-S¿çä¬$OiXÜ‘¥#Õ&›j)vHòK¦pb¢ ’Ç•ôyž2’·Q¹Z{Ùañ!“]ÿÊ~[‚#ö?p¨vüêrÑq%½ŽÑj©ÚÅñ×;Ç/ü7³ù>û_é¢?Ç!Ž¿qþéóŽ7Ò8ÿ—~óã-ígåk,§…U\9}üúsìØ±ñû›Ý³±oÇþßýwWçðAâ÷'~âüïBvþçåqcÆŒÙt|d¿›Åk‹dTþÐïjѦxbeë1Ç3Õ›Íòk‘ƒ"IáÉT–«ºØS†³£žMyµuUòAÉù=òlÔå)»e‘¢ž­ðˆ¥œR,ô5E`M¯ ÃÐ(“§=ƒÏg.z1u„Nh°ê¥S*û<–ls]²/5â³Ù‘Ê_2òéä}5{騗m®KíÒGŒ„3 'Ré±A¤¦Ò)•½| —m®Kö¥F|Vâ`#;RùKFþ!|¢¯f/õ²Íu©]úˆï„3 'Ré±A¤¦Ò)•½| —m®Kö¥F|Vâ`#;RùKFþ!|¢¯f/õ²Íu©]úزïŸ$‰úVr[ê{1/Ûbš3ï'ú¢ Ó˜òºˆ_ó—¯<øošAû_éœÀþÇ_œòó„ö‰8ÿnºÿ>ùy¶V^¶ÅtKο:_)&©tÄÑw•ëЧ£ÜmÅÏÛ’—>ù‡tò‰^~UG*õ²Íu©]úˆøÀ œsìÈK "0•N©ìs¬e›ë’}©Ÿ•8ØÈŽTþ’‘H'Ÿè«ÙKG½ls]j—>âûá F‰Tzlé„©tJe/èe›ë’}©Ÿ•8ØÈŽTþ’‘H'Ÿè«ÙKG½ls]j—>Zçû/ugë\¸ €÷b¶#)Jq`"&áÒ¨—Ð!è)SWÏ3.”ñ¥:”Ù¨''.O©â¸I¥½ú…¿‡¾¦(@M¯Ð@Ôy•i£Ž„ôä¥G'½:B:숯2)1Щ_*»ª"ÔÑ^1(ËN~IÑ<묳ޯƒÀËI|—I©íè©ÙU6ï’ÐF¢v*û‘å½òú*>º´‹ø ²À¿t«ý(ö?M'ôÊq•eºGo¹ÄñçŸ8ÿÆïO•sH—óFüþ¦³füþÆïoþ{×qý‘ïù¥•ò]Σqý%XJi\Æõg\Æõgùú“Ç+è”]Λ½týÉ’óï}ï{—ùÉg‰oéçÛSG„²6n”áßtà ?&ÂҳɎêØÔFüšø;êr!F‘ÿ£}î y|üa#g»K1Xw‹MÓ ¨Qù!,.ƒÂŽ×™#Ø §>ïyê”z6µ#EO V`IOЍLYÙGbÿspJÇ&8ÅñçŸ8ÿò“V:JÇCüþ$@ü#~7ÍÒLûûI\Äõ—_GèšB©®5ØOâú#®ÿÙ´OTö‘¸þŒëϸþæÐHRºÞŠë/À‹qýYÚ;¶ÿõ§úái‰äò¯Éóðkl츔ED‘¡×#ùdÆ:¥ží¿QF°aÃ/)’·§L1èy¶œ0EO=±š’Ò/u}Ó¼8f“Ž–ÊkùÌHêsÉmÕQùÈØà 6ØS‡ÈVyôèØdK~âƒ>x#?Ä} …ÿÓ±nnHÙ°!­'j‹Mn‹^åb[˜îˆøÇþÇŸÎ!œ/t® çŸ8ÿê7„ߌj¢}‡ºÜVûúb]üþÄïo\ÄõG\Äõ‡~Câú#®¿t­ןqý÷½ÿ¡—äl­ëo^ØãÏ |_âÏâÒ¿¼‰¤,’Te¸5n<õ_¹qiÔaƒ½|x¶’W;R6Ùb#Á‡Øá}^çÅ®‚a#QÇä{Ú©¬Êe‚æ)uòCŠ`éˆÈ&/_Ôå6´Õ&;êñE[Dö¤»>üðÃ7ñ¦*ÝÐé‡C ÉoêB©TíS>ô#_˦XŸÇ‰ø›.s\Õöª’.ö¿Òi¡x|åˆåÇ—ôù~–×çúØÿbÿ+ý¤i¯éžÆñÇ{Eœ¸ô«.ùùUùy6¯ÏõqþóoœkWKñû¿?ìñûSû8É_À Égòú\¿?ñûÓÛ¿?¼$‡·xçûYoî¼$ç¸ãŽ{ƒïâ3}ƒƒDH9H”¢£ŒRˆL¸4é°ÕN êr/Vx?êÚ(.í°—/l¨Gä[©lå§dUø™WPW-ª£ª$‚QG@••G¯Ž°A$2RuR)í±GrŸ*“b›Û“9©¶ªï?}úôôZzí$¤Hµ{íÎ?Ýz/Œ”æçÙÊyÕ+s}nÛÓ¸þŠëOG Ž¿Ò=K~œïr¬¨²æÇY›ö¥A&|”Æõ_\ÿÅõߦc&?NÈÇù§ñõϼyólE¬zãüÓÞÞ®¯þ n þ‹Tü)¼Zÿ9©‰}Ô©­g“­ì(Kð o§#Ú!ŠEtЉ=:6µUIå˳ÕEAª×nÒ*pN.!DQJ§5X:# IÕQòˆÚ(>KûA¾áåßD¾ð§ Û®¾cÜèiH lOÎ]aû޾-£I @ @ @ ôNtžâ¾æø'†À•‘‡ˆÔ’oêáÓ(#Ê“Š$øDð™ò0×!@ @ @ @ @_D ÌmåüÀðªð_ž‡7ƒ?Ëù7ÚˆÃkĿў¶l9ÿæÅŠO‘l¤Š/þO} NýU|WÕ‘…µ-JQ0ìÈD ŠŸÜ—ò¤;DuøÁPÇÆÌHtl¶ØÕŠO½â««6µ‚8B@ @ @ @ ú"en«.ÿåã‚#ƒXÄO–óo¥šMü[Ϋåü>(ÃéÁÛ‘*>m$Ê“Öâÿh§¾¨]·TŽºUTQHÔ ÕiRê Š_Ê ­:‚޵𤹓 >ñ­¶´SLR ˜úœ%Vêœg½¿A;$@ @ @ @ è‹è9–Þwø0qjâ¿Ä¡‰ƒ?C'þ-çäÔVü››%Þ _’œƒo+òoØa#Οl؉„£,ÞPýqUmQÃÚ¥ 8F°×F½!0Aéy:Š­Ú’¢“=¶ØÉVí±£ÁW>üɯ|ã< )>yÙy6$@ @ @ @ è{” JñiEþK¼˜ø4Rt²ß\þ ®M¼œü‰¿+ònšDüŸÚÒž>׌›œÑ 9¥-AÑ!tTy§Ì²m§ e|P:±H=B^B\½‰ú®˜Øbƒ_6|tºx@ @ @ @ ôYÄyù/ôpc¼Xÿ&BLüõµø7ql´ÇþNí©ƒcËù7å©S|xA„xÔ£ÿ‡®®ä¨eˆ ƒ'Åa"þ<•Èzì´©žA!tŒÎÊ^ƒ¡: {uêä“öÅøÔ!y|K¼KØÄg @ @ @ @ßE@|#O&N Müü¼˜ê6‡£-~ÔÖ³>/ç߃ú<>zõ7÷]U©yUkKJ9b°Ú¨‰HÇÈ“*ïÙŠ­:„’NÊ)þév¤ÚRG½ìÕÆUÉ?õèðÙ‰fkõ]@ @ @ @ ô)Ĺ‘jv")<˜x3ê\/þ 1çÓD.»‘'-¶“oqm¤Š-«R|Å¡Œ elܶ¤)|bÜHòÎk ¤"ŒüäP§±I„a¹å¼y m$è‰Mm4(–{+¯6*Wú䤣@ @ @ @ Ð'(¿Å›¾‹?Ëù/òEþ ^ ÞN\šÚ‘b¨ yô´Éù7é«ñoÔÑž6Åøø@—Çoçêê‚£F¢@²Å©“§"Õ Ní`jEjÒNö ýÈ£¬X4:üОvòûÃ/ú?žAéH„@ @ @ @ ôI2‚þ‹ €âÔ(‹C‡äüeÙæüzÚ‰‡Ëù·Üø7R„TþHóøâèàþäϳ{ò5…A5‘Š" Õ)Ú1•©GH¥WR:.â6ªvj`¥£L^‚:ù“ž”±#$@ @ @ @ è“”Wçü|—f(¢&þq§Á™‰“ :ò¼´Z“áڔǟðjØ!jKJ½Ò¼Oò›ótÔËŸgk  T@lÕ9ô$õƒ Ç¯6ìÉ 8Êò!òÓU RùT@ŸTm‹ñ5`Å'íK¼1$@ @ @ @ /"”â×|™80ôðeâÊÄ)Yé&Æ’mD<yø9KñoèhˆoquÍðت­â«½WU‚49–œSfà ª×™«žÁçX¤Ê&yìH¤g“_•}î³Z|l¬}H @ @ @ @ Ðg(?¾PÜX»w<ŸÀWä¿Ä¹‘*/þ "²È¿Ãæðo=áÿˆYWxk2p‘´cÊÀʇl ûÜ¿lrjƒþŠg“`C`Ò–z|ñô$¾›‡@ @ @ @ }¶¶ ½&n­•ù7ú(n<§¿u¥2ºV›ÈGÌäTD!>ŒÏËèTG^ñ E<Ê^v¤øF"{ò²û‹­tÝâÇo @ @ @ @ èÈ?cEþKüu9¯&>-·‡–Û©½ìño‡þ´^|¯®psø-ú£m]ihà­!eGòtR!¯NÓ¼Œ B;u2~™)Rê„v¤¬a‰®Z|éóøØ)¶gC@ @ @ @ ¾…À† Ðc.îKü—ø0Rq`Õø·ä'.Õø7¸<ˆÈFüÛæò´«+o$ù s[9×À,yA™ Õ»* Zeù—|¨o¹^y|ÅxÄćâ{6$@ @ @ @ è{ôïŸxCñ_LöCÄQ¦Nü|BM§&;läŽ-דß§:ÊâÛ<›ÚÒ^Üœüã«L›š‚£F’‘t„2)¢‚‹LtUª'-Šü3Ïc`èÕêÙð¯SmIëÅÇ6$@ @ àšOS@IDAT@ @ x© &ÎKÜZqlâêÄ¡Q/¾­§üÛæðÕâ«ÏžVÊê`EQ%ƒ Jy‚ ò Nsl¥“žvz]9„#åÜ—+Œ.~8yüU‹/ߊåfIð+lÊU‘@ @ @ @ }q]êx5þ þ,çß(ù7ñrpnš…™óoª§-1©Còøø¬]ÎéÉ_u¥¡A¹µ:CQ O „ÎË—Ru‚PO;ò ^öL3Eφ=zê±CÈ#ªÇ†¼ôÍħ}H @/"0oÞ<[¼xqÅ#ùuꮨ«fV¯^m´_³fMÕz”=ñWÓIT¼ìX¿^«_^öPls8†9ÎkÉ¢E‹ì…^¨U½ÅúFñ·8@‹9àü ÞK–,i±žm›îtvvÛÖ’øMÚZȆß@ Í@@|Mño…²ï)ÿ†ÿžòoEþþე~Ô:ÛH ‹wÈ;Š<(mD4’Ò‘‰¹/:)@±Ã¢A‰u•ŽT±É«ßè>¾Èoäb%{»«¶¬Y×á7ãë #†µ¶~‚cûô'¢­‚À~úÁ?WïÍ£'›äo?›)~V¸×ïÕŽå'„—ù!÷Ç?þÑn»í6?~¼}àØLDKÍx@ó÷¿ÿ}Ûk¯½ìïx‡Ýÿývà 7Ø‘Gi¯{Ýë’ÄðaÃløðáÝb=ñÄÉþŒ3ΰ}öÙ§[}5݌ʊzqjµéËú Øw¿ûÝ4„³Ï>ÛFŽÙôp–/_n«V­²qãÆ5ݦ/.]ºÔn¾ùf{î¹çŒ14ÈÆŒcÇsŒí¿ÿþ}qH[­ÏàsÁ¤ãôœsαò³R<Žñ+VXQÑEÙ‹/¾hçž{n:Ækuî7¿ù=óÌ3ö©O}ªª çŒY³fÙùçŸ_µ~K•âo©ÿ­Ñ^çéZ¾_õªWÙñÇ_µzݺué¼¼ï¾ûÚé§Ÿ^Õæ¥¨ä7€}iîܹÖϯ­wÚi'{õ«_~Ÿ—ß2λí¶[—{…âïJO~“ÅŒú@ @`s(Oá®­Ã7òúk<yñjÔ±‰cólj'>TÿFõ”Ùd«TÜWUìÅõ)®úA?ùV¹[ ‰×Hp¨ÎãPÔyB'°£sä±A¯A©¬¶Ä<Ê$¶Úð䃥Œ½êŸ6ò=¾¯“‹•Þ–‹–ÚÊÕk­s£“ŸýÚlçFÚöA]Â,X¼Ì²-Ynë;­ôÆ% éWº€=Òöž<ÞÚö¥KÄ(}Cw4{ÍØ®c¬#»«ºéÒm ÍÞw¯Ù–MlaÃGy${¸‰[¸pa"lz«»£G¶ÁƒÛÎ;ï\qyÙe—ÙÔ©Sí¯xEE×l¦š¿Zm·$N-Ÿ­¬×÷ÈÊ£>Z“¬¨6†[o½Õ ïÞóž÷T«~IèæÌ™cW\q…uttØÄ‰í€°µk×ÚSO=eW_}µ;6/‰Áö vØa‡„ „ãóÏ?Ÿ0Ã-D¸f?Μ9ÓöØcYdØBróˆÞE`×]wµ£Ž:*9ÿÙ³g§ïd—]vI:êC6!Àq~å•W¦cü•¯|eú{ðÁíñÇoŠ œ1c†ýô§?µüã6dÈŠãâïJO~“*N"@ ô2e‚RüD—85"Á…•§M|˜ø582Ÿ-Wáç<[áÞÈë®[üeÚVã߈A=©ì+ü[¹Ê^L~àð°m(Í”§ƒ¤”ÅÔXÅöDÔY8vôG ª½Ú*?õ‡:âÓVñU7 · Ê{~Ê–­Xíiíg|vfÇšµöÈ6vÌH;d¿=m¾“—3ŸÑ/’|Ðþ1锤 4À6øR3:Öæå…‹—Øò•+íûïaC·»6¤7˜¹Êl™ï Ø´Óô†ßð±õ8Ì ÊÜ»wã<µ¢wýõUoÌX8ôÐC›7ˆ-fâô–ì¾ûîö‰O|¢âŒÙ<›+EµüliœZ~[YÏwÇ,XnÌ{JPB: øÒý£mÌšfò2£W2uêÔDÀ1»*¤+{î¹g"!"!u‘iÓ¦¥”k(ò"(±AhÒû€³°¾çž{AÉ,ó£>º÷ƒ½™•ðe:òpwØRfCH¹1ÀQ|Á±QOŠP§˜”É£Ã'"¿prÄ’/ú&_è(³Õ”fJ9'%TUÇ)Kè,e‘ŽtšÎÈŸg Û¼ÃØaÐÿØ W|ù%¥­ô¤”SüÞ^â½óèQN,®IϹé\¿Á/‚¼S>“ò…ù ìŽUkÓ‘Ú¬sÃF[ïzzL§´ ôöóÔéɶþN\®·õ>³òѧgÛ‘ïå½/»ßà{Á òåƒÌÞ;± |‰?÷¸Ùoæ›=sŠY{¶§m­eı„xÛì8ß~ÖìòÙfóó:a´Ù—4Û¹Ìë_æúÌôê•fûújâ¯bvÀfßœnv‰ë‘·ßm6ÔÏ×¼ÒìÔ;͆yþŠòýÞ ¾Äœýæ_|åç›|åëÇàxõ6Ì>õ¨Ù>îóâÃÍ–ø©û³™Ýê÷3íÞþ/w3;wO?Ñøg©×ý³ÛþÞëFøém>Éåïlm•eåYȱÇkÌ0+”·ß~{".ßýîw3EøÃÊw¾ó›Ý$²äøw¿û]ª¾ãŽ;ì°“O>¹B|äíXFw÷Ýw'r„À·¼å-ÖÞÞn¹¿C9$-Áûío›fu±T—Q–íÖŠÃ3.©cy) 7ü¯}íkÓ,OÆÎ2Êw¾óvã7¦YbõWe?ûÙÏlÒ¤Iöæ7¿¹ÒEtøzßûÞWÑmÏ Ëa—-[–f¥òlE¾7fÄBXJè/X13ˆåºûí·_ú^®¹æš4ÖT–çƒc=¬xFèÅ_l¯yÍkÒwôç?ÿ9á¸÷Þ{'ìÀ—ïëÀLßIvѤîlÓ”}›}j„ ]ÈI:A?§L™’úSk\|pÚ×~ÿûß'?#FŒ°Ã?ÜŽ8âˆÔ®Ñq¢}ë´ÓN3|hé(efg—}®Õpƒl¼ë®»Ò>у@JjÖÞôé~2- û"‚RûÛ³Ï>kì[ü„%Èù‡™½ªs3ªY~› Ëg90Ó•eøœ§$tÔsÞaÖæÔ©S»ìïê凔?ÎpN‚À:ì°ÃìOú“Ý{ï½).Ä,¤öŽ;ú_ÊZXê¯ÅnCÒ1k˜¯œï87ƒ38¬ô?sÎ}ýë_Ÿ–÷³ïÞyçéXÿÃþ`<”ãEçäVÝw5ff›2^ö»âaj™ã™sòãÿ8µ;餓ªþ®p/¡ß8~“áÅ*Žuά.KÎÇœ+YiЊçai @Ë#˱—&ñ;Ô ÿ¶Áóði¤ˆ¸8Ù Ãl‘#Rì±!O ùG½Rµ¥ìwÃIä[í³\Ýͧô]R7#\nŽNÓ) LÁ'•„uÐØà‡ ²óll4PôØN­øØ ôG’ÚçɪؒtÒ„±¶Ïä]}6俉ï°uë×$Rr¿¨ì7ÌÙXqéÿü:)m̤ìç3.é úÎÎõ^†Äì´åþ<°¥Ë}ÚßV’!ŽäÇ÷êºì³ _®Â2â"[²Œ˜%Ä_+tè¾\í…q/w ™«meù¬ñ?Ó;ø„Ùd_Mø·{”ž)ùÑûKï^löÉGÌvlv†“‚O;Iy–×uú™g'0‡•ϯðÇôáÏ¡Dæ®1›·¶”ç“8³W›­*‡ ¿s’?Ëý´?ÖÏZœÈÞý§ù ù‘ùµiN€Î*ùùϧ̮ž["-Os®è'*×è¬W2ÙnŸÜX=öØcé¦ò‘ç“ñÈ 7ŸÌ)=Š¢¤¥ÌM,Â,ntñÅòCnxsaÆšf@ié'7ÄEùÒ¹¼7Äô‰YmHî˜?ÿùÏS_ > (!LêÅa‰ïÃ?œ–®bÿÐC¥e€øff'7ß,ëˆNâCLÐ"áóé§Ÿ¶É“'§r+|°¼tdC¤Kÿ¸üòËÓ÷ÍóA™yŘø¾Á,ÁïdÔ¨ÒQ+nøù^!—¸™ç›ïƒ} 2 ²­^F£ÙPš&LŠi­qñ³´Ò|!̘‘ †4:N´oýèG?2–NCAÜáiUÜxþûÇ9Ç$Ç$$ûû°å.ìCûÇ Ä çÈÿûî»/ÕéÂòèСéç´¾ Îg\#Aìji9ú›nº)í[ìË=?üá+/iT¯¤óçÏO¤Ç9û-ãà–<›•ï¿Õ¥ÞñZì;çS°†`ç\L™g³rì3fð¼êª«R3ö]Îë¿øÅ/ ‰–Ÿ“[uß…Dç÷B?ªA´³ÿJê™c”ó?Âyاký®ä¿IØ7 \9fX©€_ÎÃìƒ;­Š%ã @ hm¸–wáC<›ø/¸/¸2éu'Ê É-åßð—xøV|“G°“^ñ±·GÿÒ<­)PM¯ HîLTHéIñI`§žö¤Ò <ÊØSF¤ÇöªøxÑVññC;Ú )¾þ¢ZRõÎç„]v²å¾Ìûé3­}ÐàtÁÑ>bÒ—poìç7qmNZö÷›A–wû@úH$¦µ•HK¦]nð:ç(½²Í—Œ¯´wÚ;+x ìíe³…}ªØÛˈc qï}ý¿xÞŒMò Ÿ ø­gœñ‘?(Mb²qNFžû “‘Î#ãË]'™MRjÅAÿ“9%Âñ]»9!àDá³Î±}zßž½lg±“Ÿqþçì)%¿79aùð²Ò±Á@¤þx¶Ù‡v7{h©ÙN~ŸÃ±6عO•LZâ26=× â…›6n˜ &šf!¼Ô†Wnnÿû¿ÿ»jSf9AòªÞ3(O9å”dËÍÞ׿þõ´œ±è¢”Y+ÄÍ¥ZnT!8ˆû†7¼!™_{íµ‰Èƒ”ðòž~ðƒ•Ù6̨¢Â\Ì0!F+¿/ ò½%74àÆùƒ›!œÁ;'fÁ•s^ñœÜªû.„"¿K»<Æ‚›ý‚—1+¶Þ˜ùMáì—Ì×Òªý®°_W“jx‰¼ä<ÌÆ68¶&û1Æ~ݪXV_è@ Z ®a\ø_ÆX%RñeÔ£ãÖ¶ˆòÔaC½ìd+NMz7)QZåŠ ¾e[ŒïUþ6Ôˆºª‚³F"GØ’Wç í!'¥t€ Á^JŠòþ°ÇÍ;LÅBmão­%gûî9Ñ·¶v~ä!í~áÞæ›;~¡ÏŽ³Ñ—w3srÀÀA6À K¿:LKAû;!yÿƒØW¿ñu¿˜Zêâßö–;úV³ýo2;ÓW¹Ì÷Ye.ÇÞRZ«^1;ì¸?”êÑ-Xgö*o÷õi%‹{›ýÅ]¾¼ö·f§Üî73Ù$–Ëž÷3 Oð6ÕÞÖÌÝ×ýÑlÊfoðö-/ù]æ$}ùés¥e·{ù$”©·™ý9»Nœå(gãž^wº÷!ŸWòÒüg­~àá»3œ »Å—;VŒaŽÇe ñwC„>¼õÎRžÏjآט®ñÙv_yÚì@÷w¥/¤´,û#SÌ´±´z–ï̬\äûK³ÙþÏqGØWÈÉžgi?K­ì·DX¶}–÷EòhyŸä{S?p.òá-áìã|¯ ·0~Ékï|j†/W€ áÆÒ$Ÿ1Y/3k¸aÕ[çUÝ2‹…>å3_ÔÈ&È fA2{ bUd“lò”Y*ˆž¥GžÙ[ˆêÈC äK™ýE„KÚ!.ˆÝ 9ÁŒ>fåñ=²‘‡0â&Ñø¦LÙ´ó–/dªAö°‚¨ÊŸÝÈŒ â~ûÛßN³+É·‚äÄcÞ–x2” WRx°o‹¸gÆ(Ç û%x7+ù>i9DûVÅqiÉöŒ3ñÈy‚YeläÙÿ¨C4CUXB’_zé¥ic&DQ.Ârœc½Hö@""Þ!t°a3¾Dß ç£Fõ©Aùã /L$ÜßøÆÊìnü"ÌΆ¤cÖl«K³Ç+ß3-ÁUè^«ú® òüûªuNnå}—ó×Yge<¢„ý˜ýçW¿úUei4æ-ùÞ«áÅ>Î>IÌqÏŽý~¶2–[‚E´ @ Øúd÷@¥‹£äß8<¿›îFsµ¹Ž­®À6œÀ¡É¢§ É(_N'$8µÏ}P‡m¨§Œ`C^mˆ)¿äÑëËÀ.?Ðˈü’ïÏAoÝLãP2oÁbè̓ü…ë×ûlH'yq‹¸‘®G‡´û rÖv”fS:{é³VÛk_uœ·ógVz‹™sØ33禖}6ÊÞ¾„|œ¿¼7„¥®"rðÇŒJžÝDzÙ/?evòÎfÌ4ûî 3–Í^å~Úàm bÎÛ³Ô´y¾,–e«ïôkúÛlœîÄÌ!;š1ƒð 'çvê$â%r’·&ÿáUN 8qÄrÙÇ}æ3ÐxËes¹ÛïcX¢;u¬˯¯ò¸gy?n9±´S±ôöïv¢Ò' ½q\©_ó ÷áÕ¾Cz?YvûœÛ|`÷Ò2kfÏÕ-#–ÍNŽÏ#¬×»YzFá«v2;qŒÙ¯ýþ–Ù{•%ÄN첄xxyO­‹­fLX[|̼ )‘|ÿº_W$ž)€{úòîÓwÝTÇØ_x.ä9N‚3S’}})'¥Âf~r‚‘(’ï§{y_$OÈ?ï[ZNþ£ÙfߘVšÅù{߇wÐÙ«d¶Í?¹Á‡X@¸a- us„!Ì”ÛÁÆ[§o¹å–´””¥§Ì°ÊŸ™÷‹ÙR„ªDyf!jEâ„gò\:žÑ¦Y5ò±½S§ÌfÕŒVõ‰:n̵ÌZ„Žêk¥°R»"VÌ6â&œ™r~Ç̰Îj·­Sf‡!̄ճ)ŸwÞy‰g–U.ÅqûGŽŸÆ$lóöÍäƒ ÊVÅqˆtäøbFä­úξŌbú"3óz¥ Ñ1¦ržÒ&»¸Î«ºä…·ð§’ø|7|Oêsgœã˜=Ìx~*ÙÉÌ;ö_ŽêÞþö·Wfæí[%ßìñÊuØøÃ›f«2¾§ƒ:¨Ë ¡ð­¼ï2ö«)S¦¤çæBž ¯ZcîB/˜1Ís-/¸à‚ä™úÌóòÑi3m§Ñ#¬¿ÏäØRÀaö£äµ~ÿvñõ—ÍBB´Ðö'ß 'Çû‡ÛÊåNØñšãœ4<ÿQﻟ7ÜÝß ý;Ÿ•à¤æ'ö*E-.—U_Hë-ÑQ¦œ!|T©Ïzd¦}bY-ä+/*ùä>¥zfÞüb)_í³Ú2âwL¨ß–ö"™ì–ŽÍ9{¤bú(.!‹zK’!Dúÿ»K/u)iⳓœt„Øæ¹‘ï™ØÝâŸ)æ7_zsûÿ=cöïO–N Ý­K–`/îá GRdï÷ÕúžåÝìó_xÜì{3K3}_½S©Ýöúd–$7ò,µž2eJ¥,™†ðãÆœ›w Õf0Òˆ›]ˆ Oƒfg^Vna†¥Ìo{ÛÛÒ¬=žïÇr¹Z¥ˆfri‰*y„ZõfûéÅ<§j¿…Ýßâæ|/̇ãŽ;®‹?–/ó¬8HdÍ@ãæ\³F» °*˜WŠüα¬çøA˜‚q¾UŒ·aÒ ²‰ç""[ªü ¦yvœÈNÆÅu³3'Õü3Ûǫ̈jUÜè7Ç8cfÖb¾ïCH2s²‹Gè³Úß_3B€ãÒ̾ɾÎE¸Ž]úÈ~͹‡¸ê±—ð˜âB¤³Oˆ¤d¹?„6dÇQ>nµo•´Ùã;ÎÌåù§“'ONÏ ä{â;hæ»*޹U÷]Îü6é;¥ßèøbÞ’11èI™?r1û¢’ãE³„ñѪXöd|a@ °}(ÿ‘úÈï>/&þ ³7IyH=›ßI§”:‰ÚË=œB yl‰Až*{6åáæÐã¡ |œâS'ÿž­/8k$`Ã1)B§:!=) x±CjO;6ꡟäOm\•tØP/ߨÑ&÷CñÑcOlòøR¾/±Ì4»ºwä™Y~ÃâÑ¡&ýJ>õ¢Ñ¯¥S™™”lpà³'¡.;œÈå)mY×±ÞF j/,ZêRkÓ é-—Öo£_L¯·¡CßP5dÌû'm²ª¶l–Ú•åÝ Â‚ò²ÙfòÙ”™å÷îÝJ:¾fç¤!³ÇuòémŠ$,¹•—ËJ¯TKt™™/ÑA9²LTb?¥LAž>áöÈ1N”JèK=9ÀMvBF‡¤V?/OfýПÍÞ²‹“°“Íx–e5).IÆ&ÇVåkwr²~Eß'³c/šQzTÀ[Ç—ö9Hê¹é±ÃÖ¿œÛÕƒö÷ÉNv¾Ù¿?öëç™}õi_Öèß?i#a¦13‚/UÚïyÑÔÕÎy±þÛfÿïY'¿<ÏL_„ÝÛ[4뎗ËäKté³ß ½X§¥g<›’²Ú©ÿÜ´Ïž=;½d‚zÞª[OD\àŸó/3vDîÔkW­ŽY0¼(,5ÅäR-ýƒ\äí¬Rüp³Á$¢¥ZtÜØCrqà ÈÍm+ËÍùðÜ´\˜íÆsÖ £™)F|`æÔñvb–ƒ„ß/7ï°Ò ¤<y|''šx G+Ï„ãEA¼µœ—·°œò| ÏêcV13-y6!/ÿá­éèÙ'òa˾Ç~ÄóæøÎø}oeÜè;LJÕ)S¦h8‰xfü샚=I%ç¾wÎÀìc,÷†â{ðÇÉ“'§ÙÉèš!Èðn»|´g©>›ÕÕ'ÃìcêÔ©iàíÉì·Œ“ýƒúƒ‹Î+Y³–Ê6:^5:ÍwÀqÏËZøƒ K½Ù™IÊ’vfó±d xæj#iÕ}—eêìì|¯üq"?fëÒPß9ûû2çÐj¿+ð)Ö³¤r—±q®ä÷‡ýžãªU±,Ž!Ê@ ­‡@ùÞÄï|ÿEÊÍŠ3#éöS7.p|"áÉÊLOâפgF=~(;»“þ¨C(#ò¥zlÐÑVñ=Û¥>úùF¿ädzե‚gÎÈkCGžŽh ØÐ9Ù’RŸ·ÑÀ’øªÃ‡ú# >ä[öŠmöŠ»¡·ÉÉ•«ÖØü‹œhdFäÆtÓAøtóí³ûù²n:ÖÑÙÏFÚцï0Üv: ²É«*Â,If`®\µÚo~X.Þß—ƒ´Û~Ú®øÃ=öÁ7_±ÝÜÌPG§§Ëf½»6пۉÈ'–—f BÐðÌIž/Éòf‘žŒ•Ùc¹Û¦’ç¥&¹`WM¶d‰®ˆ¿ž¼…›gñ _õúA›ï^zæäÏ”bú%>c”Ù¨µ¤Ö’dÙ³C‡4‡ÀùûøYÏðŸÎ1ûÙs%Rð¯'™±Ÿž9¹D¤¿ùŽÒ³(?äå'}ŸÕ 7{ßàKò™Uy “Нóýì×¼…›åõböß HJf ÿÀ Ñó)š,%gÆ0/ÒAž÷eû_s«ü,iù%Ÿ¸!º=…›4nÖ ÜŠä$ýb¶ Ää—–Ÿ‘g6/a`ö‡„›ýØa6 ³Úx»2‚o=W­Vn¸o¸á†4>Ú@Rñü¹fHR–ºB,5C¢à{[„"„Œfæ1Y–!ÆŒAÈ^€qýõ×§—‚p‰¤g3òÂ^"áà ‡x™K=¬j}ÇÌÂâÔs“ÏËŽt£Ÿ÷m{äÙo?üá§™3fÌHû7ýö[öËœÈÉûÈwϾŬTNHFÈ0–d"´¯uœÈפJ¼ÄieÜèä#ä Ïôƒà’ðs q,ç%ÇûÇš^DÉ/¼hÙ‰‘„Oö¹|¶›bTKõ‚'Î;Ìàdê©§žZ!ÆÕç>yV ¤)ß+ç½)S¦¤ï‘ñrÈ÷Ú“!å¾·Užý±ÞñZܯÙ_™!ùƹì!™™IÊ9…ï•s=~I«î»œÓøàß+íƒÆÌ}øMã¼Èì`öÍj¿+ðÉë9/BJr>á¼È¬}~WéãÇ>ö±–?äc‰| @K"ÿ½Â-¯8²õå2œšn…eƒ=zlÐiCGÁ_âÑ +È#²Q;RÅP|Ù’íð…=ñÉ׫g$‡Ø*0ºZmeÇ lÙhKÇœVH‘"òOJR$Oñ™—s[òl²‘W\übìšÞ$)›6Ë^˜¿ÐŸ1éÏ©r2’—áz Ï”äf°Ÿÿê7BÇ w³ºð}jö<›6÷E{ÑgLv0«ÒíÚ½ýÐ!ƒm¨’¼D‡Í¹J›þü|ûÕ]Úz/|üo²×Qzfé±ì~C‰,yøµ]›Böìy£?ƒÉg„ñ,Çj³![u"ç|ÂÏ<ôæÒì3HÊ;¼3ÄÎòçÿ]ë„–Ø}½Ãý@tÎzC±¦Tf‰.ϼÞW-ä’–è^÷J³ÉNt“Ùi>kî[‡•ì¿à“`x1ÍM'˜±Ôü³91tpé9šX¼÷^³ß¿höÌ)¥eè¥VÞ?'©x¹ ÄÖ¿;yT”zýàù’’;|쌉e¾_9¨ôüCfí=zré-Ѱe¹{qLòÝŠé“sWؾã³i¦Û±“¾Û¦¥Ų̀e³²‚y´ñ™ZÕ)åÙ£ÅçAÒ3|{"ký_ç[Ñß=/A¢}U¸±â|Y‹È£žó³Ášf®A¬õÆ9_>9q¢>ÔŠÃòvb7Û_ü³,2‹.ôeaI+ã.~—lE}O±Â1ô’ˆVÅ òš!aò10¶jû6µŽfBÖñlCˆáj8÷Ür,šÉ³ÿ@Õ <ù6÷\Pïû êkAËË3j™µœ¾§ÇkqŒ‚²'ßG+ﻜ»™ý_ï|ToÌÕöŸZ¿+E,‹eþXþ‘|$Íʧ‚þ–[nI%D{+cYO”@ Z~ïüšå4ï‘3(‰ðó»ÒijAþqW éÈm3âw¦© ?¶Î7Rêìs‘^)~•K¥Ò'þµ¡!=þÉÓÅBÏ#~TMMñÛó†‚S‚çBt›O*¿Ô3x:E½ÊòéªJ[lðK6Ê´#åÖ_1éyì”z6•‰‡¾G_Ø×“N' _xq‘µ9™¹¡“ÅÛ>{ÒßÔÝß.ιØÖæÏ—ôšŸšc¿øåomá’eÖßg\rAÒÏo”˜-éÕÖٱΟIé–~ÃÏÎ6tôH¹×®6éä£}Dí3çØ¾þœÝÇÔXO\¯³uê-›eK?ïÏÒƒ¼áå0B¯ñ”g8îáÄ¡–¯òÌIJ^dsædðöÙ…3½¼·ÙInßHxf$¹Sm‰n©¶ú'3ÿÕãýï3¥Ô<àK|oqrrs¤^?îYlöÛùNLNð› ö:¿ÚâzK’K­ãssð¯º*ùÇ>:¦)X$‰Ÿ?: 'ýa6%[Q8®ú29Éx ëI£úbÛž’BÅöy¹ž¯ZuE.÷W-ÏŒOfkžtÒIÕªû”®QSŒé)Vø¨G´ X›ÛÇZø1®fŽƒZ8÷Üzúý±ÿÔÛ‡êáÙL¬FíÕ׊±¹ûG-ÛJ_ëfú°9ãnå}—kïFcªW_mÿ©õ»Ò_fë‡?üaš• ÑÉ£xf/ä$ÒÊX6_Ô@ l?ÊX„ûb“p› _Vf*߆Ž2¼D%w¯èhGªS þzòâä¥ÆÇ‚ÅKÓŒIŸãÞqï4¥GìܰÞ:ÛJ³„ ò/]nÿ8k µO{ȯXÈÇ å™<éÅ9ë}–Ry‰8¡úõ`#wßÙö|ÓqÉ?.}yrS¿ûPÊõîG½e³Dâ9”Ÿs‚’·‹Üyã.þò'(ó¥ÍGûòçÿ=Ôì?ž,=#’Bso'1›‘3'×_¢[Ï$á|‚)Ëwy9}æ…9ßœ^¯UõºzýXào`–$³;!ÃNõÞ½ä§ÖbןÎé¾$™™|!@ Кð¢f¿5»µ5G½Ú^Lž<ÙN;í´ô¢žíÕ‡ˆÛžeyöÙg§¥ãJ¥Ÿ2®Q]QËŽŽ “NfÊcƒ cµø1éÙ$”ñ«c‹¨­ò”Ùä‹/2í¤'è³ á×2cÎ<›1{núËgÉ'Ï ,-íö鑉õUßi ÷íþBœ›¯üµ­^¸,˜mnÇrpÊDš–ßê½ÑgPô¥Þ‡}øTï9/Úñ™˜¾ŒrË"½ß^þàλØdf]n0Þh\\6»9¡XÞÚîݬ6»¬ž¿f–èÖk¸Æ÷¼áì[ ú±Èqbv)$lQª-!îMl‹ñ¶U¹•–xo«1Gœ@ @ @ @ •`E®?ºÈ‰#óic‰ðcv$´äy6–t#äø8quØ‘'ŸF½Ú¢G(cû‘3 ØJG½{鱑oRõ R+oãÅ®‚q³¢`¤(B"(6 vb×Ð DuXv”:DíÈã›Åï-r’,_±Ê—uwZ‡Ï†dç…Õ+WÚjîM'ä£OŸ„`„ŒÓ¿ÓÖ¯aý²?'ÀË í (;78 0àn?j¯‰6ÌIJÊøvÒrÍ"ø£Û2ØüÙM[KðϲTf~n©@Þõ”œ$¦–ènn ·”œl¦àTœ¤­f™’—ô&¶òi @ @ @ ¼¼È–x‹ðƒrh)þÍûSäÿàøà÷è«x>ÏV—fÊÜöÚ¤‡}#O`R:D °U'HÑÉ^md«öê¼›&_ŠCY±I¥—?µ/Ƨ]¯ÈšuþÂzçDbš^ë¬ÞZŸí¸Û„]l¸Q¶ºÃ_>°Á Hÿ‡ÝÀ¡þOK³'¨ôg4i¹9ù6_æžIéoïf¦dß 0Ûü9• ž˜mCxœÇzrͪÄ÷Ê ÂI @ @ @ @ŸA Ì%5â¿`¬ÒíÉ¿)¾¸;ø;ˆÕº‚q³‚3H@9¥-AÑ! ^y§Ì²mäaÎöâƒzìÔqüÔ¼Ø0û²GñÓLGoÔÒáºv1‘“ÖoH/űAClìˆ!öôº!öÍc¹Øék…;œ¼:fdZ¦ÍRíþÛièsòÒ—rCp®ïð™‘NH®xnëm£ç‘ k:l錹¥—çølÊU>ƒr¾¿'$@ @ @ @àe‰€ˆ¡–äßü@EàÿèkÎÿ¡«+9XË/1'%i#bH±e“@J"tŒÎÊ^ÀÒˆGìÕql¨“OÒÅïÍ%Þýü“ëýMÞýüyÌ|tŽÑúïyÓßÍ úÙ—²é«œEõ—æÌõuËÃ'Œ©Ì˜Üè£/àöî3¸Är–éŒçš¥+퉟ßbóŸ˜i ŸœmO^u› Þnk}‰xÿò³(:Q@ @ @ @ ð²D@|ZKòoþäüüúÛ¡%v³Þ7+G†Õ6€AOʆH‡^BC§²|á_Ä%í4å©§ô´W[ϦXè°ÇN}Ág¿ôB¦=ö‚´·4 ¥ñFnèÆ~>K²ÿà!i6ä =üÆ~ö›%ƒí­ãWتm6fßÝmîÝû³(×ùìÈ iI7£àù“‹íUžøläÙR–{ûkKR?y?Y6Ž´£\yi e¦ä‚…‹Ý µ]|6åÚ'} ÷Ü5ÇÓë¶Û‹C͹ÆgW¶Ûøcö·Y·Üï3!•[ùüIf`:)¹±m€­¾‹ÍÚõH[Ó>Ò{ÌÜÊ6vÕÛ¯É_‡“h†ø³,K‰Ú´|òsÿf·Þ~WMû¿þËwÚY|¿]|Ùvó­´wŸñvÞY¶?Ýw¿wþgÓÇ/ºàk¶ÿ>{×ôѪŒûg×\gÏΘe -J³\wŸ¸›øÊ£íïy—“¯mÞ óíÜO~& á¦_þÔ† õ/nÉóóæUbß|íϺBG˜@ @ @ @ èC¤÷ lâ¿Ôsqa¤9¿ÿFBR\Z^ïê$ÒQÀ^ -'=ÑãG>æe„ö9É)^N¶òƒmÞŽr7iØ­"S¨s" Õôl©HI•±£ ùœ©õb²§”uX:ÅRJ;üˆ€ÌÛŸXè¤oK/qâ°7dذ!‰Œ,шýÞ>²ü†íþþ ÉuþÖm_ú=cY»>Ø_˜ãÏ•½ß$[9±½øÐ´rÏÚ|&¥”Þ§ÕCGÛ‹û¿Þ|‚¥·`×wX¿AƒmaÛ$»öñN{Ûá6ÐgZvzŒöa;lö P;YZ”Aƒ€±»,[¾");}ÖæŠ+»´°†™‰_úê7íw¸­ÒKfª®óç‡>=ý™´ÝzÇ]vÉ·¾Y©L @ @ @ ­Š@™ ¤{ð]ð_¤ü›ˆ/ñoâÕ°©f¯vÔ#âÝ s/çÚ°#®b‘g£-º¼Sqðä„eI“}6CPâ H^’˜!Ò© ¡ŽXê¬ê(cG°—t9S‹ ̳;òŠOÁ¶¨K›û1fÔˆôbÈÅôBf<®_k W®µIÃÛláÆ 8ù›»`­ÝÁÃ;É7á¸mÐCí…?O³ þ¢ÿ7À‰ÌÕã´kœØäÍß“>³R²bÃP»eZ‡½~¯uéÍÞcœÀÜ\9ô ì[_ýϦ›O}õ öÂüùNj¶£?¬év­`xéWVÈÉ#;ÔÎþÐ_Û>{íak üýmwØÿ~ç{vÜÑGYÇ?$@ @ @ VG ¼:X|\|85ÊâÜDxh¢ uØÉV\D“lh£<öÄ¢^mHÑ‘Rˆ/$_-¾ü(Å®¦4ÃzÑI:w ‡8d£cØ uœvتãòC½ÈGϦ%ÛLéË;NÍ–T;¥ÄÁ–Tñ‰C™4m½¹Ä»ÝgŽØa˜-YºÜÝ—»á‘-Yn9yyÿ2'y3·ËÝ3Ú©‡vø3$ý9“mëmüûÚØ÷°yÆ–>ûœ­[ºÊ:ÜÅ:''™ui>sÒn™ˆÊN_:Þ¿ßF{nñ{aE»í6¢ÃÆmCB­Í‰×¿zçiõ>zò|ÏžØÖ‹Y¯îÅ íòŸ_•LŽ?öhûòç>]!"™Eù–SN¶×œxÜv]N½-p¨‡QÔ@ @ @ @ßB ã¶Ä‘Šƒ ‚S צr™¼Jü›t<2‘<œšþ Ž0×ÑV>¥'.þ‰Í–‹lñ¥XÔÓFí)W•fÊ¢â¡3Fx$yÕ‹y¥Sˆ:GJ{ìˆOYÇŸJ=eâ«­gSY bǦçQ’GðSì{ªØ’I»íâ„ä2™‚¶ù‹qf>¿ÀNÞc¬ýpÎúôlIg%í…ÕfÌ_gï¼Úm}ÆåÆN8l°ívÜÁ¶Ëáûø¬È 6jÙ@›û”w{`{z‰Ë»Ç[c/,nóŽ3d³'´ÛIã% ¶¤ß=i;çù¹ö÷Ÿù|jrÉ·¾‘½{ïоò¿ÿÏÞú†×äß^l÷?üˆí4ztz¦ã_7µ‚g_Î7ßþçËÿf¿üÕ vãïn±y>#“Ù˜ÿüwçÙŽ#6-Wvæ,»ð’ËìÑ'žL³S>`;ןƒ¹ë.¥—ó(îio:Ŧžx¼}íÿ¾c÷=øýÍ™´·½å•˜d®ùõ¶víºDJò,Íj³$›yÎ$KÛòó_¦™˜ÏΚeãÆŽ5^¢Ãó:Õþg¿¼Î®ôÙ©ÿüwçVúAÿîögx‚SNòÞvçÝvÉâKÌŸµ‰vµ7ž¼ ¯JãÈ@ @ @ @ P@€Çºäü$c‘ÿW'­D,•87ñþÁD8Á§‘Gð#þM1ðEPù¤ž ©Æÿ¡Ç)ƯW—£SK.ªÒ9œ³É)„TTGÕqÕ{U¥cäÙÈ—ÊÔI‡O¢á“<›@£­€¡­|QOžTä§g{O&ŒÛɦ=;ÇV¬ô×y;KÉNÒoíJ›Ð±Èvs¢qŽO‹ìç³õèÆ=3Ú¬}ãÛk§ >K²ÍÍéšwÎÉJfÒíê3#OÚk¹=þ‚›>Šw]a?7ȆûLÍ¥káôQ,XÞߎÚýù‘ÉÑVúèèè°Y³ç$ïåƒÀV­^tW^}­]rùOØæo1çmß_üÊ×m„“ŽÇsTj9ÉóÿöŸþÙ¦=3Ãv>Üx6$/®a–ãÅ|=ÙÝÿÐ#ö÷Ÿþœñ6í‘;îhcF²?Ü~§Ý÷ÀCö㋾ecwS‰û‡?Þ™ˆÎ§¦MOm&ŒS¶ò1ýÙ™)¿ÿ¾{'°RÑÃÌ'ÿå‹öÇ»þ”Zí4f´Ít, Ro¿ëû¾¶¬K–.KxŒ·sïŒì/YZÑß|ëíöÙûO§ÒFç[¶l¹ýßE߯ÔG&@ @ @ Zðn‘²@,ÁÁ•Á}±‰?#ÈFyçzño%²jç©¥z|jÆ¥g“-±Ù$âëTÆŸê‰Í¦²lº¥•v«éªÈƒË) (>‚«ŒåIO`æö¹ÀH²ÇŸâ—XÀñE Ò°·dÿ½''WX’í¤c'§MŸi=@œ ôæ˜ÏìôåÞ·>=Øþ4g¨u¬ßhëœä[·r¥µû›¿wñý‡¾íµóF;õÕvÊ>Ëmþжz]»uôó¡ñÄþa¿-Ÿ=Él?f&æÛOMÛ,PFiùÀ{­½} 4ÈþòŒ¿H~zôñ´D;w ñ¸ç”ÉIuÌ‘‡Û~{ï•ò÷?øˆ=é3!!ñó¾w½ÝxF$rÆ[ßœÒy4¥úà¯_úìùöž·¿--f¶eQ֔ߨ^ëíäEûjå_ßôû¤>Ä—n3a̯Ÿúꔿáw¥úThâã±'Ÿ²ç|Éþ¢Í~îùbUÓe-m?äÀº´9è€Rì+Vúòí%]êêž{~^ªf ûèQ#ë™F] @ @ @ Ý(¿c^Œ þ ® Î ~ÉäÅ­QP†#£^œ:D)í¨ƒcS{ÏVêÉçüŸâ“Ò–MÜœ|ã«ì¦µ¥‚’Ž˜ ¡L$AЋpôl¥ž|.jG[ Ž<"R1A^¶ø§Ì&?è4xòò©zWm=a"Ü‘‡îgwÞ÷ˆ-[¾"=Sr½³£û-±¿´Ì~`SlÚ¹““|&äŸMyÚÀ6hc‡¿¬»¿ÍXÑÏ—}û€ü¹•ý|‰t¿A>SÒÓëÖ¤NŸ²K›ýã~À²åÂsO?õM[ͤªÓŸ­YOxf%³û—ŸŸi÷žwœÞ­ÙÇuÓ5R°´ûž??`Ì…H¾>4 ³„?GS"•ë¥+Wù³J]·oùLØzq¢.@ @ @ ^òù·j/§&r0çßÔ;6Èø5q|”ÅÇ‘Ïc’‡ðD§TÚô‰¯Ü5Š±ÉªÃq#‘S:J唎 QHYõÒSfs¶. ~ê8Ô›€¨§ 1D^Òj“ƒX-¾›véå^•Á¾<ùÄcµ»î{Ôæù³™EèÜ›Ðß>6èI»gÃNö«Îq¶´³Ý†ô[oµ-ögVôNõ³ik89é³%ýY•}]Õl$Pe" eK6…èó ´¡Žv¤9k›û¢“Tö®Jíº¨:µSlìhC=zÆC;ôÉß¶˜µÆ P=`/›4aœ=øØÓþ¶ç‰¼ó>x¯ÖÛ>Ø€ýe:ým€/s^ÙÙÏvt>{™ˆA î\gïš2ÄÎ;`°œZµì3?pö¹v´¿ôfƬÙö°¿‡Y~ß{ºõùR'§MÖFŽÜÑn½ã®„ oß>øÀý“íyg}Ø>ýÅÿ´ïþà2»Ú‰Î#;´òòœWw¬}ùóŸé泑âç|$= ó‘ÇŸ°«®ûuÚ†’f:–*›–˜×òóî3N³koøÍñÛ¼ï#k‡r`'oèßá¹þqäè#^‘–mC°¾ãýgÚ!íoö—ÿ,[¾¼‹ëñãÆÙ‰>ÈìE[X@IDATÉÏù«vƒ¿„ßóæÏïb×Ê…'ç®ØªÝ»¹Ä oÕÛËù“s·Wäˆ@ @ @ ðRA ‰9ÿïE® ÿ"Oª:Ï&žŒ÷&®Mm¡¥Äçá“2›x6Ϧz|£Cd¯XÔ¡S?d‡‰|«Ü-m† Ä!!yu;:§N£W[ÙÑq½@B¯M:ùÃ'uØ“G}noÅë„<ÜV2jÇì¤Wn‹—®°³Ÿ·g}Û°š!x™)éÿ7lØhcû¯²s‡Ï´§Gîc'8#ùúqýlx3ßĶH8<×ñpŸEø«ßü.½µ›—íüÃßž]!ó¦S_u‚=úÄ“éÅ5„o9åd·=§bBý7þã ö¿^lÏú½™á8dÈàô–îs>üŠ]O2ÌÊüÎ7þË.ûÉÏí–?ÞaÓÝ/¤êŽ#v°½÷ÜÃÞ~Ú[ìÕÇ¿²¦K–pÙÿ|û{‰TýõooN˺™9ùw;Ë&Oš˜Úòñ/~æ“ö_û[°h‘ÝvÇÝöÆ×½Ö˜Iú…ÿúZÿŸù‡'¬î¼ç¾ä“—ý\ø¯Ø¿òucfi«Ë¾ã7=³Õûý @`[#Àqvßiȶñ@ @ ú K-ðI\# ®ª_%=«9%ËK“¡Pˆ §† ÄSÎÙ‘G°‡mbÉ6:qgèå\:|ˆ££NüyÅT_²¯ðoå6*{1Å€Ãö¡Ð°ÁNÄ^¤§e:NgÙ´î]ƒÉã tÒçöò‡%)í¨ËcÒž:ôlºÉþ…^JÛécõšu6ßßn½vm‡=3c¶­Z³ÖWt·Y»aûì1ÉöÜ}ÂvêYÏÂ2ò“ÿúo6ʤ_]y™¿àg½­õ±T{ÍûÏ:מžþŒýã¹ç¤—ó°ôyÄû-Ï{ÀhfòÒœòÛ©òêÍÎ3kré²’ßÍq»yƒx½gGBP6¬á‹pV¯YcŽÅ›Ó•íÒ†ï (· ô4úAPö‘/*º@ @ °¹åÊ•+mÔ¨Q¼(c¶oðaÚ *ÙàÊ "x4êIE’J‡{Ú¡ƒ?C'ÒS䣫Rôب­x<ÊØÃÉ‘ŠëÃ/"ÿ¤Ò¥Šâ‰œ“ª“ J !¨ŽQ– §L :A§Ù’‡d Øâ_ÎíiOl4ùÅ'>ÔôØâ+ÅßK¼=VM2xí>a—T?Ñ—€ßóÀc¶pñæ¢Ù´gç8±5ÔÆí4ªfûV­è/ÅØä´Ï‘;Žh8ŒváÐØö1 ízjÙ é¹¹!ÛHv=º‘Iª2x°±…@ @ @ @ Ð,åISp]pdðhlø/qkðbpel9·†Þò[äß°£6ä~ =öJÕ–²ø½Ä¿yYíÛUIrŸÒuK ÚŒàQGÈ£S§é¢ÎÐI6uJL¤clø¢;Êvø¤‘;6ÚKT–oôŠO>ùÕÔXÛ[†8 ÷ªcó7~/òeÁKlÁâ¥öØSÏöI‚r{cñ@ @ @ @ èë¬ñÕ–³fͲ%¾‚ñ™’6iÒ$koo¯ m½¯buÿ§ÿ%’²È«a &{Êâß”R' „<[5þOo÷¦´Å±½tp{”/áêî‚“F¢`ê ìÕ½:GžNªƒ€¢²HIRÕ焤byuª¡ÉÀñAYä%:åi‡„ò®¾Ä÷ºzËs“e|4D€gèùðìLÏÛô7”×y¦BÀaÐÄï–ø¢@ ÐÂÄïþr¢k@ @ ´Å%Þ«W¯¶Gy$½·B]…Ka5èèïé(=ë|ÅŠ6zôè|‰7ä$\)œ))ü¢º¼^¤!6äEbÒ–<"_äå‹<õ”±ÍS/¦²ø@Ê6ô!V]ÁWÏ'lØ’€T¬¨gsš§9ѨÑ&ü‰qÍA“ >‹¶Øö8~#2MÁ"­X6ƒg[Ù¶•f¯Ö]Ô@ @ @ @ °m˜={vrRÑyŸuMó2Ü¢´eø7ïS=þ¯!ÿØÐÀÈFd!eaH9'+E6º:¦ö"ÑÓ;„ú܆:MLÕmV|žA@ @ @ @ ´ZÖ]­Oy]yüXËòoÞ7ñwEþ>75nhèزå¤!d¢HJϦ:¦tB0bǦ·Ñ=õ´‘¨½RôbóþmVüffü©#‘@ @ @ @ Ûz“êòºò Ê"oÖRü›ã•OD¬ÆÿÕ…4'k¢T¢fKRP0£Êã?'$sÂRÏŠt“Ä«/´#,Tñz?›Kœ@ @ @ @ @ Ï 0`ÔZâÚZ–óþAJæ¼ËÔyPOD Ö³$dI¨2)íéyÊ ¢1oçÅTOª™•ò—Où”•ÄÉý¨MÃøñD  @ @ @ @ è‹”gS¶4ÿæ¸æ¼0‹sQYz×5*Wb ªM$!eùA—×£gDôtRéÔ;òJ©'/!/ß¤Øæm±SYõm1ƒXB@ @ @ @ ú"U–xwã¿|\ðfâÚ¶9ÿVˆ/^ŽT|žgkKNÖ²*R;ÚQf†#ÁØ$€ÄôMR¦T3)Š2í¦g+¶ø‘­|RFHi4?fP–‹Ï@ @ @ @ ¾‡@ÿþ‰kiþÍQ…·§oGî/çÿ¼X]hÐH°Ñ†sò"-EŠLXØPY‰-eÅ”lEf¢Ë;N™v´!íqü˜A騅@ @ @ @ ôI6l€KÒ²ü›÷ÞNOü_Îõ¥Î×ûYXÏ’M ˆDT`‘޹/åIéHª½êD>B`RÇFžz6ùÍã£ëQü (±@ @ @ @ >‰@a‰·x5Æ¢<évåßêć˃C¬+H]£r%ÕIÈÌG‚ˆ4$¥>'.Ña§ŽPæå8¤Ä&ÕF"ßèµ#oÞtü¶¶ž ‘0!@ @ @ @ @k P~|!\™8³–ãß¼oEþO“é«x¾š€6ÃÞ厰×&½–p"¹ˆ­:AŠNöØb'[µÇŽ:_ŠCY±I¥—?µ/Ƨ]H @ @ @ @ Ðç(”ø¯VáßÄÿ‰»ƒ¿ƒX­+7+8ƒ”SÚù¨¼‚SH¥ :ÈÜíÅõØ©ãø¨yÿ°aöeâ—_ÅîÍB@ @ @ @ ¾…@™ çÖ’ü›# ŠÀÿÑלÿCWWr°–!6 ^bNJÒF>ÄbË&”D蕽€¥:š5éª q)Ÿ¤=ŠK¼1$@ @ @ @ # >­%ù7Ç5çÿàïÔ_ñu¡»YÏHŽ`;«mƒž” ‘½:„Reù¿ˆKÚiÊSO;éi¯¶žM±Ðaú‚Ï~ÊØˆ¿£¬öä«JÞ¸ª+±a#¨“j`ä€g»tBÆ&†ž2`Êy;òµ¡ŒžØMv¤,÷ÖkÆrÒQ @ @ @ @ è“deMþËWƶ]ø·&ã»Yu¡ÓRQ$%`@¢gCHÑI(c'r“¼Mtˆ|žüG¤S,¥Ôá‡çVÒ†M¶øÃw®oã”±ÌÛQ @ @ @ @`;"°jÝ[±f½­^ÍÓ2dP› <À†Êi­Öè›zQ&()Ây‰O£ 'Æ&®Mü›¸2lªÙ«õˆx7ñò“smØ_±Ä¿a‹ŽMí<[áì,¾kJ3%ª€ä%ù€©G¤S:B±ÔYÕQÆ!`/è $i+˜«*väŸ<¢6¥R|@ @ @ @ l–­^o‹WAñˆÚ.ÝètuG§­îè°QC7Úˆ!ÍÐdÝ\luEyu°ø8ø/6ø:IYœ›x3M¤;ÙŠ‹c°²¡òØ‹zµ!EGJ="¾|µøò£»š"R°¦WÐI€aÓÀÑQ/&{ê´ÉVÀ©½›TOž%ÛˆJž6²Ùñc‰70†@ @ @ @ °ý€ 4OH…òÚ~]é9±T-õ¯kMË”²•Á-Ë¿9Xõø¿œë«Šk3ÔpÑ EBFŠ˜TΫ^Ì«HPRu–ö䉤blI©'%¾Úz6•Ñ#²Ã–ø¤lñ‹}Oñ­ƒÀãO©'RÿC/®®_¼^]ŽNÅQ-¡stHd m(3À„P¹L 6 ûÜ¿'Ô© y6üÑ.ï4yÚÂNЖzÊÓ“øn­ŒÀ!ƒmذaݶ­ÑçG}Ì8äp»äÒn ÷á3@ @ @ èú÷‡ K"n­•ù7ú(n¼ø¿Òj|6;%I$!nȃ ›ÈF¥"s{êRêˆE:‰uT>H…<6ˆˆHÙROß5PÅP|•-–x_H Ð7øÞ…ß¶c9z›töñÇŸØ&q"H ­ŒÀ£>jsçÍK]œ8q¢í»Ï>­ÜÝè[ PvØn½µRüqÇø @ ^ ”_’ç%¾KyÍjd˜ª#¥n>MeÏVø5òèá×rO|õÒk œtøGT&ŸÇ¯ÅçaWUš!(é,A´i£04(ϦÎÑA ˆDòê°Ú25QçÉqHEDz¶GP—çñA!¯XI@ iÓ§+i /[.ùÁíúë¯OãÿûÞkçŸÿÉ—-1ð¾ÀÊ•+íÜs?^Ä7þÊ&î6±RŽL @ З(/ñ†ïçgF9çí¼X!'É#Ô¯õM`ΗáÁ86òðiâå<›|°¬\vè_íÐá[¼¡89â+6U…F‡ršÛÒ)\ÀhtŽ:üS‡P–/uŽ:é”bK;lHs=yI³ñei ôq:;;íÛ^dqÆ;í ÃŽLÏ®¼ô²ùs–óSƒÙ5×^gïyßì°#±Wu¬÷wÿh‹—,I£'ý§O}Ú®úå5©|Ù®°wÿÕûíoÎûD*ãïäSÞlçú³]Ðúü¿”ô^tqÒßqç]©|ÑÅß·çž{Þ>röÇìàWe?ºü'•vOO›ngÿÍyv܉¯±£;ÑÎþع6{ΜJ=™Ÿ_uµ½å´Óí C°SÞüVûòW¾j<3$—#œç¯þå/íÇ—_n‹.z9Bð²s|ß/«¯;@ °”—x·:ÿVÿk8zˆÄF(°¥,Q¥ɼž|.jG[òyDd¤£˜²Å¿ú ?èÄJ—OÕ»*$^J|ä쿱›KztÃìo³f϶Ï}áK‰ üÔ'ÿöÎÞª*ûãK¤»»)]" ]*bw`aÎèè¨SŽcŒ:m;6"& ŠŠˆ¨(ÒÝÝtþÿõÝï­Ë¹—[ïñ€÷`/>‡]k¯½÷ïÄ;çw×Þû÷n¨ÏüýŸÒ·ßû.^¼X1Ù±s§|=äY¿~½ èÿ¡0ì»ï‡ žÈ:Íß¼e³T¬PÑ¥·oß!Ë–/—Ê•+¹´ý·aã&—¿uëV—µwoªK÷ýòÕà!2wÞ|—Ÿ7ONšü³Ü~ç=’ºoŸ”,YRÊ”.-ßý0\ÆOœ$ß ,åË•“/¾,¿ÿãŸÝxj×®%;´í7ß~GŠ/.wßy»5íC€GÀ#pÚ 0}ú ùóŸÿâÆÛ¸Qc)Yªäi3öÓq þ|ŸŽgÝÙ#àðdÿSG•õÛvH‘‚ù¥hràÐa9tø°>—ø,_É¿EÔx185x;$È¿¥å¤ñhè·Á¯ÇGÚø8âÁ6‰Û´rkG³Â[AZaŠÁ†‰¥£tЌ҄†($må–Oš#í«=mÀÖ.ö¬ã Ðv¢œ:´aä%ícÃêAŒÖ¾ª†õ‹´€G #ðü‹/K)%ó‚ò·§Ÿtß7C‡9r²D‰òÅÀO¤jÕ*²U½k.¾ìJGHÞÔë©\©’téÜI Ç­rï=wI:µeÚ´érÕu7Ê”©Ódæ¬ÙÒ¤q#™9u’Ë£ì¿{Pîè}[°É ŧN›&åË—“?ÿé÷Ú÷RÒ®í¹‚ûýO=ãÈIˆÆ‡|ÀíþêëoÊs/¼$/½üªüãoOÉÀϹ¶{ô¹õæ^‚'Éè±ã¤õ9-3Ô¯ìðxNæ/ðëŸ*ç2™qøó J^Ç#àðœ^¼÷ãxY¼nãQƒ¾§G'©\ºDXþu>y}èHéР¶œ×¼¡ôùn´lÞµGþtUÉŸ7lܾKrå:CÊ+V/'Œg³!oɉÁÁ™Á³¡c‡‘•äõçFý çÇTnãÿÈ7ûÑÚ·<Ó¡-8=ls`!´ö]FäFFæG¦1jBÜ£a:A# Ž8¢cgõÉC}3°4êôƒvL—2³gqë7:õ,Ïoí;ÿŸGÀ#ý€DŒ”¿üùO.kЗ_¹ðš«®pä$‰RêYsÁùÝu'îeÆÌYŽ <·Mká0iÞ¼™ÔªYC–,]¦ÇRGPZYV„¸Ú¿úò‹Ò¬i“¹ÙsæÈ¢E‹%þür÷]w8r’Â^7\ïJçÁƒ]ú¾gÏ)\¸°têÐ>dÇG<Àé†ÀÂ… O·!ŸÖãõçû´>ý~ð€G .žÝHrç‚>J“â… Z4fX£|)?¯äÍF}ýóL©V¦¤toÖ fœRXÖ þËȾ ÿÆPŒŸ³8B6òñI9|™ño„:po‘üùèPÇìÓ.ñ`û”[ýÈr³¡*±ÅH½ØG:Ô±FiÄ„<:a§ŒiÒ,'ès ¦O<8hê ä!Ö6qêPn@0ÙÙÃ#)W.kBK½x<ÙÞ·Þ,)Õ«‡õ¯D‰â.½tÙ22š#RÖ¬^–A8ÁBY³v­ìIŸÎmÓºÃ1Q¼x±0rsK—.wVYK²yË6Gµ`ëP^Øã™¦ÓY3søð¥{÷®r×·K½³êUÇgx<`™ŠÅ‹—Èâ%‹õY³LJ«×ùYõêIË–g»å"b¡tàÀ™={ŽÌ˜1Cøq„:íÛµ¥–¿ió&Y¸`±¶»HvîÚ­;}×’³µ½²e栎ԯ²y‹z¬OÕfΔV­ZI‘"Eä°Nµš;wžz´Osm7mÚTÎ>»…¤¯¥älðÒ½ný:™5kVÈæâÅ‹G:EŠ–)5BeÙ ;Ï›¿@)±ÉòutòÆê)Ÿ;ýÃÄô'ê;tâÊ•+KÆi*¤‡ÿø£[b£s—ÎR³ÆÑmmDÆ3c“sÁߨyóçë:›[¥V­šÚŸ†R¡B…Hóaé5k×ÈŒé3õoÛ)V´¨têÜIêÔ®í–0™©?t!ݺuucÇ“ÿ]VĤ}ûvR¨P!Kºp–Î*X·n‹W׿½gÅøÛ“Œ­ú·m‘^£›õz¨VµªÔ©[GjתºF3{¾Íþ±„àÏ=´hÑ"·ƒ}õjÚ¿:u¥F”°k2² î½±ãÆÉŠå+dë¶mRV—n9G¯ñX¸YýDX˜ž=€G –µS$¾OdDz(©”-êM Ay*H:A ÷Åa¼\PÄ ­L£Ž?#DŒ£œ¸Õ…k3>Û¤9ˆ“b›<Äô­-ÊL»¦‡³mé£ÂdJ Òyà5DqëztÎ:M¾Õ5=êG»Öiòòí°<³‡MʰC±öÉêcÛÚû_`ž>u¼x<Ùn]»HëVçDíá™é¿ž]~Ù%rVÝ£ ¼N;¸zlLóècO¸)ÝdàŘ¾ãYT»Ç#óÌ3ÓI¥K—’;oï}TE‹¦M1€Í—7¯¼ñv·Ž&kY2•ýÝ>oJÛs&62ä3<Ó #GÊ3ÏüÝR‘?WŸÿû3QIÃ%ú\¼÷¾ßÈj]·7([ùã¬Ñ´S×ð}ö¹çeР/ŽÚŒ ²ëÏüI.¿ü² I÷ÃÐCÿÎåݮϿK/½Dî»ï~YµjU˜^ëÖ­ä¥^Pâ1íyØòœÖ’šš¦óè_ ¥Û·o+o¾ñF(½{÷nyJ7/³ÈCð{æé§QÌñ¥—”-yt­à ÆÉСCå¯}:ô7bÕªÕòÔSO«$ŒgÔæÈ‘£ä/?~Ô@µ·ér#÷þêÉ—/_X»üØþÒËÿ•·ß~;,ÿ¥—_‘›o¾Ù‘ºÓ§Owe“&Œw˜îÓõíœL ÚZ¿~½#¢ Ü&Œ+Ç“Þê%hòóÏSdjúÚÃ? ÿÁÇÊ![©ÃñB ÿç¹9I_úü‘|ÿý·2bÄpyî¹ÿ¸¥}xö?ñÄnó± M‹ÿëßϺgsƒ ïÁž={J[ÝäìX$žMÖ@~õµ×yFÎÛ˜Q#å‹/>—‘#~ ‘¼ßÿƒàei9$'ï¹çnùyòD?v´|÷ÝPiÖ¬™©fi˜ŒGŽ"'«W«.cÇŒrž›ã´¯x¿ÞÔëFyø¡]?3r¾³b`,cøcs>vô(™:ågwíà¡‹°ÁSO§mrç2ô¿>}Þ ‘“wè2,Œë£ß—1£G†àê{e%tÙ…Œ`aíøÐ#àðxÂX®äⲛݑºÿ€+œµrÖ¿ñ=Ïn"õªT®MÏ ¯”žâûª~ÕŠ.U®xQiZ£ªÛé;ªrÉL'(³5ÿ¦PÂß!qv¿geN!Ú~Ɉ±|F"R‡<êÞZC„4Î×5:¤PÄ£’4¶(G4‚6)C,=ê›XÚl“oíwví{2¼x<9®»V>ùt ›>Ø¡Kw];í\Ïíz·ã¯9R÷.]#í±'þ*5kÖ”¯‡|ã6ljyçNdü„‰nƒé3fJ%ýggíömÛŠyTtîv¾´hÑL½§8ïH±Ò|ðÿE§>Þ÷ÀƒòÂK¯HÿOH›6­dî¼ùnݱîݺÈëÿ}YnPÏÎJV^yÅån=1ôó»w‹eÚç{<§)x)BH2µ:øns>¢äá ‡Ì¬Y3ÅȦ3{„°ü÷¿þZ/ï½k¯¹Z ™êMÞzó GnâfÒ¨QC¹óŽ;2™§Ï5È@H·HaªòÇ} ª&¿{ø!§}Z¨ëÿ!?ûLŸ³ÍQI–.\HŠ+jU]H{6-™ŒGÿüg7¥Û”z\püôÓH§Ãš›Ë–/×ÒjZqXøÒ‹ÏëÚ¿ÝÃòŽ5Ëæ‹/¼èÈ/ì³N$çÍÖ§þú¤ô¸ð"—õÕàÁb|¯½þš©¹ózÿoî ¥Áõoúwë /åeE$³³ ×+ãBø»Ì5i×%yœëdÎ7ºY!/<ÿBˆd¤úÓCfñ¾ýï+¯H×îç¹5T™þýáÊ]wÝétæÌëBî;HV»ÖßåW\á¦~£°ÿÀÁИ2‚…3îÿóx<£øõ…ZƒrÇžT§WªhøºÊGU>3øMþË85^¾\¦†‘¼šf9þÌôIáOGH™qh”™çˆÆÿ1UÜò©‹ lYûôÍòàöHÛÕdl1"0¶FšÁ ž5n²aƒŽX¾ ròÉC±ŽSÆÀ«‹º6 òM2Ô¾M%²Ê>ôxr&||úñrÃu×èn×…dÔè±ò½n,S¥Jeyþ?ÿ’úõÎ’reËÊ£üÑ­;ù±’}ÿÑé{M›4–—ž6ôAa£¿ä¢‹¤[×ÎêÁ¸Ï•;wíreÊ”–—_xNJëú‘æÙ½kyö_ÿ8ʆي^Øã|é÷Î[º™Dgç‹/Ëš5k…54ŸÒém|è\©^I¬o¼ù¶üK=‚ØÌ‡]Ë/»4k?6£õÏçy<9ˆÂ 9i#h­u˜¬ ¬õ8cæLËv$›ÇDJ4{¦±$'-?¸Á /Ëku3²hÂÀArÒtº~„‰\›Òtb…3cbs’ƒº´SsƒG)}~›,]²Ô¢aáUW^™åäd<›ÓÓ7±¡­Î9'¬¿ô}ïž½R°@×Ç`Ÿgê]qùac q†{½>*û˜22‹qåJ•Bíúâ Á£4£ç7d ‹#3gÏYŒ†#„ýE=ÓbgôyÏ@ø¦à_Pþ/0S«rå#ãÏÎXûïã€G §!P@àBRõG¡ÓMÒßÙ²5ÿ¦ç$ÿgaÌS˜Hì/±‘‡¤l´úF0Zh$"ºt‚´ÙѨ#Œ ÖEÁÚ N]ʃö¨gyV×Bkßïà­ yñdgرzÙ¢yIu‘ågžzÒ›7oQ"2ŸÛ!6Xù¶[n’^º®ã–-[¥T©’!oà@D¾õú«Â&xq7%èÚ¥³L?Æ‹E q•Ô½ô’#/xA&êwÝ5•ϺþV™Ò¥ÃHÎ[nºQ8¶nݦ¤ƒHIæÅ#àð$BÏë…º¤Å:B½~ýÝðe|¨Ê¡Cü°&«V®²¨T '¡Ì$#xHÎVoLvÙf‰ Ò–©°êmZ^¼°lÙ2¡âuk,ÙÊŒY¼dI¨”õ5¯ºæˆ'b¨ Y lÙnWé`:+âìTM¶é3>¸–'SˆãɪôÍŒXcr·Î0 `–w<ÂÌbܽ[7y忯º™\3¬É‘|ùå—ËÕW]é~@<}Žg“õ/·ê; ØD“*{„ ÷LÚéÌ ÖÏDþò—ÇÕk÷)W®œn4Cúôíëòy‡€x6É®XXÿ|èðxrS—®Üé¥Ò_¦t§”/­kq‰ü4k´ª“"S–¬Œ9”|ys;ÌõÛwɵ¤v…rÊAåL 8ßÁ{á9Ih|› ÊÒpgÁÁ¿Y½ ŽÕC—|ãíÌ6iãá‚üyèÛ1Ûj±ãÉ)Ö@L-ˆ4DڄΆ,4[E¤ÃVߥY® =ê˜.ù68«CçÍ.qò±‰`?Ø~—›v¢¬Î™¬A™>W?½ØÀ©€c,Á¢B…ò±ŠÃò‹)–&ÊêΜY!x•×#–@¤zñx<‰=zŒ|üñÇ2vÜø˜k+^^eó–-!“lì‘QTûà£uÝÊ/ô›M±«ó6–ÉŸïÈópßþ}¨©/y‡x}LÖɬV­ª% ñ4hܤÉQù':ãÐ/GHcÚfÝÌx歷 Ù ÕHGNw'ïxHf1fGö~ï¾#?ñ¤[:Àú‘ÌØéµônß>ú÷9m­g+?ÞáÖmGÈIÚ*R¤pÔ&ƒï¬=mr뭷ȼùóu­ÊÜ’ 6ßÊ¹ÎØøÇÎùÙ ë³=@N@`Ède"R^7ÉI)WZÚÖ¯-,•y«ÖIûµ¥TÑèÏuÈͶõjÉØyKdå¦-rk·vR¥t‰€ÅœM÷ „ƒ3>ìÈ‹ÑÂrÞÎì ]ô©X¹qlÔ1Îʉ[{¤±e¼qò('Ïô̾f¹<ì£WŒÈ‹«”^hÆèõ Ò‘etÊÊ ÑGÇ:L:N>y6HâØ0»uqÒ”az6xÓ³2³÷¿x/€Zß‹GÀ#àðx<l@¿~ý„K–¼`¿æÍ›»¥-¦MŸ.Ct½ÝH)_®l(k»î¤œagâ[néÚô¤l™²Ò¾C;©^­š”Õå4þòØã¡5ý2b]<@Mlú¬¥…µjÖ©4jÔP7ÏIÃ$”™ #åÊ–sÞþxì#>ªëfÆX3Ø}–- Êöm;¢N™êdEüX0®¦×;³CJ6L¾ýæ[Y».m ¦{?ýÌßäµWÿ›ÝLÚË pÏàÕ‰lQâ>šå–-Gv‚ ß̲è}Ûm2jÔh· ß,mÀ}иqcùÕ¯î Û,Ç:–±°¾ùÐ#àðdgnéznÜîõhÑPº7­çØ HÈ®M4ž.7u¯Û­i}騍®zb抺LŽÕË aúÐAÒÐø/òø5”´†pdÆ©)µú¦§j®¡ñjG›äbÏÒuq¸9ò±‡P>ÎÚ§,í°F Æ p`˜¡S°|B:A㑲úÔã G³gu4Ëå¡C¹ÙFÛA;”Ѿ‘‘”Ç–ÅsqÓ™fÍöâðx<€G g!°|Åò9Y¼D yïݾR»v­Ð öìÙŠßy*V<â©¶L§IeäˆõW¬\áì^ýunZkðG_¼äBÂYdöœ´MG¨R­jµ Ô© öfègE¹s'ó:›¡f²\¹FaGndŠnL” A‰Ç$›"íÕõ‰‘¥Ë–*!ÖÈÅýW@׳ ’r7l’”ê)‰ª¹ò¬À˜iÝ>p¿¼õÖÛòÒ˯8Û¶k{RÉ"%fUp-[¾ÌYdó¤h»ŸÏ™sd¹™Z5Ü_«½{ß.t½Ó‡~û€Ü®;ysÝa7ÉNX$Ó_¯ãðxr¹õ‡§d%Ot“µy2ôÒßwàÄàËŒ#ƒ|äM  á¥ÈAôŒ8ä–Å33;A>Ž<ãß4ÆÿYšrãä¬}Êéböéý"W‚Fb)bŒÃŒYšú\ÄtŒ0¤S 4XO“®œÐX\³g šõȳv‚v¬NÂöƒ/êöâðx<€G '!0}ZÚÝô¹QÃúaä$y›6EŸ~w— ^l#G²¤ Ùù{ìØ±ay–˜6mšEuýÝK¦$oÑ5€Í-¤%rðàÁ3(‹/Ñ]¶ eõˆXRÍdë¶#Ól-¯yófê5ZÎ%ñÄ|óÍ·¬(Ò·aþ R¡‚“éqþ‘u_}ýõ°5­[Â_=Ä’. ’Ÿ~: wH²×ßx3Lßàœryî×­[ç6ê1ý`x,§êÆsAá=ü†ë¯eÙÎÞ–‘è|›Þ±†Ýºw ™øè£þak{R°tÙ2ùþ‡B:]u§u¦vÛ¸†}ÿ½ôÿä=fŒLžü³ó þ@`u­Žå%ÂÂô|èðx<h°|¡ /Jða¤ ³ÿ–Þ B}4ÎÑÈQÊb ,f²‚a‡ˆ† Þ@!n’GÚòHSfuÉ7F—Ðô,4}- Ù$Ž`=;H[{ÄK[Ü{P:Xü€GÀ#àðäTÊèôj“‰'Ë8]ƒ²™uëu“œ÷ú½/Ÿ}þ¹;/IKà%Ö´YSˆD~ø÷Ò«×R¾|9çÍ÷ÃÃÝ&^¦ ÙÈfÆ .ë½÷Þ׺ºiÊcÇ“gŸ}Þy…ôy3‹"ûöí—+¯¾Fn¿íV]+²º,Ñ nÞ~çÉV¿A}馫¥råʲråJ—õâ‹/ÊŠåËeÂĉòðCI­Z5¥`Á‚:EúOrÿ:W_{]æÎ›':u”¢EŠ:¢é«/Ëšµktœå峟JÉl°ÎïŠû—ƒ;B‹ [.¾ô2¹ñ†ëuLµd箺ÑÑD>üG‡Í† ëåÎ;ït㻽woWFb–nTtëm½å‚ ÎwÁ9RèfI±¤mÛså“O>uÅýûâˆìöºqÛÂE‹å Ýe;¸OÐFf1fÔ_ß{Ÿ°Ë{oíwƒzõdoê^yã#$rp#ÚLt¾ƒý:–ø=wß%Cuº9×Åê5«åšë®—뮽ڑ¸KtCœ~¦;vì l–gÂ92™7o¾pDJÝ:uä6½Î/¹äbW”,"mú´GÀ#àðx‚D™â ÿeä$qx1{+Ã!2ò!2Íòꑦ<(”ÑC¬.¡Õµ<êX?¨kùÖÒq’0‘`ŒF‚ƒ²ÎZƒØ°š]ÊÍå“8ù–6›šª‹ŽF9iêæÕÃÚ¤?ÄѱP£.b•½%xñx<€G §"ТE3©\©²#WØ¥ûλîJ×.]äÇ€Wb°ð?,wÞ}›&¼ÿ~éÓçP1Sˆ/¹øb0p`(Ï"-bÈ·¬%¨‡I‰’%ÈOË yÃÛ’éâ‘‚wßßÿöLd¶\tQOyõÕ×\>ëþsÁ¿]œõ?xÿ=·l¤æ:ͶoßwÝfA#GŽŽHa×ëì2ýOÁýóŸò€«xJîØ±C W#…iÕ*U eŸ{nGâ>Üå1E:8MúœsZ:O¾P…@äW÷Ü-ß|3TÀŽë¯R„þ´iÓ:D~ª¹hf0~ÿƒÜùÀ³#R Œ!ºƒ’Ìùêg6ÎfuÏ>ûoyèáßéNôë íºÄcõÉ'kò›&Mš–L,\´HþôÈŸ×\Ò³gOÉ A{>îðx<HÒgÀ}q˜À‡Á—Á!ðmäQiÄ¡ñfÆ¡‘Fƒ<Äø8B#5©]«­}ò8¨GˆMã ±m}Òhl¡B"AÇ#Ncˆ±¬ÖAëG‡4`ÑtI[›fÃÔ¢0fêQ'ŽÕMØ~:ˬճü¢£›¢³–ÞY)ò .Gt÷ôèeè Kì*»ôÅwáâ¥Bx<äÀƒ²qSø±ãÑŽ·éðx<ìˆm¯¾úŠ#ƒý«‘RCþóïÊ+¯¼ä<¾¢ý(‹åG}àÖ.´é´vç×]}6PîºëG~ígZîoî»W êZ†&ü@ è >²{'ëdÖ¯_OžzêIéûNŸ¶óu°o±âµuÝÐ/¾øLî¸ó©ZµjØéL}¾ìÒK]ù…^fâÅžÓótg˜'(„õJ¤±{t,)S¦Œ ð‰n¦ÔÌ’¦×¤Icõ¼}Wž~ê¯QϽéeã—_~IüíýnlfƒÝ±o¼á½ÞHänòÉžï ½ÌÆ!õ…Ü|S/×G[S•ûsó»‡r×T¹riKÐëNÞqç]Žœ<«n4èsùlàÝüyñ…çÝxƒî ö½ë^f°Èì¸|=€GÀ#à8=,¯cì!G¶áß´/ptAþ8b^Z*Æÿ¦£8”mz„5Š‚‘–ˆ·Šäq˜Õ!m@F®.Mš¶('D±º'Ía¶ AISÏò «èIüÊ^@4}Òeî.b¥ ³5̈4**òœ®‹Þ@Ãd¥ÇU7ÊŽ;z}ùª SºÚœs¶ÜvãuR¬h‘dÍ$Ôöãùë?Ÿ“ÇÿðôèÞ%¡~<…CºžÒì¹ó¥‰®±eR¿ùã2eúLyýùIÓF âU÷eYŒÀÂõ{¤n…#~YlÞ›óx<9ž“ÕJ!ñŽ÷€vêßõ ºöb‰Å…µ3"¬‰·qã)W®|ñÏ/ÃkÖ¬‘_þ÷‹T¬PQ )ãɘ1ãäî{îq*QÆuq¼øvíÞ%•*ñŒg‡éᬛYNÇX$‰w¼â:(eJ—qËij]Ê8ëÖ®S¯°Û:ÁÙW¦(çÉGÏa‰Ætø^R›4a|T¼À“óX¦t)a£¥ÌHF0fûVÝ9¾tÉRaÄj¬v3z¾cÙÉH>mnÜ´Á‘´”ÑÏa–F@þûßW¤KçN.üoúôérc¯›]ÓÃ_íÕ`±[o4#X„Uö €GÀ#pŠ!°r˾l?¢ãý^·sÛá‡IþîsLÔ¥l"ÅÞ p¸kݺµ+æ]ªT©R¬%²N?¼#áÀˆsÏvPãi>θ:ôˆŸF¹Õ%!¼œñu¶,rô-{f›ÐúG¬£Ép‰þ×8\ÇRÁN #¤­34JÚtŒœÔ,7H@DÇ:L>b%ÁžåYÚÚ0ÛšÖ>õ¬úäÿ_v"''n¹^g¼äÒ^Þ[C¤›~W4T± £Œ"©zêæ(‘9|Sš'åEDú·i]2ŠrŒ,H¾{n»Y_ÚÉTÝíòÓA_É"]kçÕgÿöË}Œê'<{ŠnðУOÊwƒúëZRi$jš5dŪÕú!Vú„÷Ç7èðx<설_¤Z²ýÃ2¥zJ²êN÷ˆ d†*”ñ¢ãHV ÈŸÔN×f/¸!Œåe&Œ77h¯¾®¯˜ q¬ç|°®fFÏÉÌxâ%x,’ŒK)Ê‘¬D;ß±k×ð _ðÔ­Wï¬øJQJi³zµêQJŽd­Z¹*”€ˆŒ$(!ð‡=²üA›6mBúÉ(Vχ€GàTDàL%C~ùTMöú—]%´4þ‹Îƒ·Ãø7ˆ@òH#‘z¤,D™‚kCÌ6qò8(·v¨o:V®YÎfPÏÚ@×ÚA/ª$CPš!kÜ Ñ Àq¦#`ùÔ1@, ”Ygó¦Ç±…žéP—8yq+#m6‰c Â’:ØÎ§GPW“'Ovk¯î©Ò}Ö*˜\‘*2Bg.—ÑžžåÇlˆKò9.© rÕ¤4#Ûët™dΜ—‹^×^é~Û×Êm÷þV¦Ïš#«Ö¬•jU*Ÿ<@b´¼,ð"h*¿¹ûváðâðx<€Gàx"p[ïäÞ7Þ¯ŸÛ æxöåt·ÍfGÑÖéŒÄ…õ!™r}<„ݼß|ëm·™Ô;ïô•ï¿ûAêÕ?ËyéîÚ¹SfΞíÖY¥mú¹Áñè“·éðxr2E ä–í©P6JåË“ƒ¤4ýË®’NPÒAãÙ,„ ƒ‹3ŽŽ|Ð%¡xtÈÇq=Ò¦Oy0/ȧaƒö±gm¦ŸÌP[ØC¬}«gvÓJcüŸô­! ÓQòèy ’8ùÁÆÍË‘<0qt#ë™]+W'ئ=ì¡ÃÁ` ³CH]×7¶bÏ^”}VˆlØ/òŸ†iää?м²L{™.íõGæ·›Ç&ñ´|\þýl=XËj&rA×Ñ_êY/r“.Z¿`ÑéûayîoOʳ/¿&ó5ýIß×¥¸zg øb°|ùÍ0Ù¬StêÔªáÂzuj»ÆøµøÅ×Þ–ïGŒ~}nñ«õ÷ÿÎå¿ü¯gœþèqåÅ×ß–ûï¹]:µ;×í0úÑ€A2D_ò¶nßîêÿî¾{œ½Aƒ‡º:÷þî7ìí—ž•—ßì##ÇLwþûœ”PWhÖºÄÞä©ÓõDŸ!­[6—î¹S× *${öì•›ï¹_n¿ùz™4eºŒ?Q§¤•“Gºÿ¨~&œ×ôx<€Gàt@ Úº˜ÑÆ\›3Z¹Ï;vt[ÔuJ#-³øñ’Z5kÊ«:µûñÇŸtK°8GPòæÍ+×\}•üæ7÷e›M™‚ýóq€GÀ#€Ì}æ²gÿaÙwÊ&{H<¹¤pþÜR0/tRö”t‚þ ¡£ŒHˆÿÒ8|:äyofšÕ#Xš8yvRŒ3@¬ÌÊÉ·¾Xû´ÇHHž•kÔÙ&²˜’ AiÇ4ì¨å“gúÖq@!Ÿ´‘•èc]â6XôL_£NÇIÒ6 «Kžµ‡êû-ÈI:9x½HYõ”¼Vß¡¾ÙFN¶×Ë×W™©Ó¸ß\.òØ<‘£]¨ûŸÅi¶2CPþO×/˜·`‘3^Q×£\³n½ó¤|øÑ'YLwódÚØŸ~&¯¿óž”/WV:¶k#?Œ-wÿöÒÿ×¥R…òòzß÷å³ÁC¤ÕÙÍå,.ôÕд!­×›¶l [Û*uÿ>Y¯ëe±Öò^ÿò滺`ƒúް5n‚°Že¹²e±‰Nýºuõ×é´5½vìÜåêÿ¢d3k0<ðÇÇdÁâ%Ò²yS·[äï†ËJõ}ëÅÿ¨»øÿœîߟ}Iš5iäúÿ㨱ò¤®‘9°ß[ÙrZ;ãõâðx<S³Îªã6îa,yò0A&gÉ—_ÊQ.]ºto:^ à‰[õxuÝu× ÇÉv;ÿ2vì8™7ož®ãºQvíÚ-•t&RuÝìˆÖÙ¡Ü‹GÀ#àð$‡$`v&“ÅIÓŠä¿àÁàÉàÑBÒðoð}pgÄ ô±ž…Až >´èSßÚ%$M>º&f»ä£Chõ)Çf\I† ´†¬ƒ‘! ’GÈXù6òƒ3;اèQAã”ÛàÈç°ºuuL=ë 6Ï€ÐJgšÑ=i²l¯HG%$õÇùA×”D^R2²œ’–—V™§$%Äe<‚’ºl–3jKZýdþÇÛÂñàÁƒêq8C–._!­Z4ÓEêuθ®÷ˆ”*YBÞ{ãeÉŸ/Ÿ ÿ~ÿRRß „›ëtißV~÷ØSòÑ€ÏåÁ_ß%ƒ¾þFªêKÙ ÿ«Ã¶JåŠòÌ^L¦;Î>Þ“´ÿÚsÿpSÐïºí&]—“Ó&ÚÇi²ZŒ¿÷Î[CkP Ÿ<Å‘“—]ÔCþøÀ½®è©=/ßÿIרœ%µj¤¸<ÈËÿù´‹§¦î“1&9Ö¯cDÓÇ=€GÀ#µ°stÏž=³Ö¨·B… y¼c¢“uÌÆêС½;²Îª·äðx<ä€ÛR1®þ‹0ÈÙ9¨Ù®¢%l!çiš‘ˆØ nü›Õ³ºÄ­}k›|òʱk‚ŽñwäY}+?* V>ª0=µ† ™%NÃ&ÁNX§Ñq„¡† ˜t°q«Cš|Ú6ÐLwV`Ìö³9©ý]'âL] ü€¤îÛç²ó©½ÌȃðÊz·ä^.É[¢&ÖWë»åûÐ#àðx<€GÀ#àðx<@NG  dp^Ƨ‘†ã0®Íø7òÐE¢é[½44=ãÿÌyƵ¡‹MkËø7tÉã°zuº´ƒ Û1ÅŒÆTÐ Ð(;¢É9Hœrô­QÒ!2BÔì¡/äu¨cÄ&6‚åšLº}tOª° ÷&åÖ>]#ÒS—©ùM ‘1:Uû׺³7ëO²IÎÓõãw‘ºØÀV²r†NI©™R]ð"ŒFNFÚ©\©‚›n=wþÂPÑâ%ËäÐáÃRµr%)«k!k×ë|ôt9|ˆStD /wêÚ<ѤrÅòÎþâtOÈh:ñòªVIcqç-8Ò?ë+Sͽx<€GÀ#àðx<€GÀ#à8•HŸÁ '†ÀqdGþÍø?#%ƒü})FÆTÐŒ‰Dâ„¢‘†40è;aõÐ¥=«OfËú€kž‘fS£®ŽyKZ= iÇníÓù„î°iÈš>©rGu‘W‹<µ@¤q1‘?Õ¹®ŠÈ\õˆ,£ëPžS"~÷æ¨uËçÁÖñÖ¡ìyAw2ìyú?/¸h>Ö5#‘+/ééÖ¦¬W·¶ÛA›¾ó«w%Þ…u GŒ禕WSB±Ï{…Šñä<¿[gùö‡ŸämÍo®Ù‡ü¼ùú«‘Š2ïTªPAºthªK¤SÛsåµRýäóÁßHüÜ&9ƒ‡}ï6ôiÛª¥ìK÷° «ä€GÀ#àðx<€GÀ#àðxr(™¯Æ/Of!\›¥C æÁ¿Q§fg?Ì£®Ù´|ãòh›#(¦‹-kŸrêX}ÒQ… ‰#6 tƒq:CšÃæüZ9ƒvÀ:GhqOÜ:oàj–³iikƒü ÍŒ´OÝ“&Et¤¯69 ½¿t¢ÈßÕoÈÎeb““©ŠÊäíiºÔ¡.6°u<å·÷Ü)—ô8Ï‘ŒÕݯwíÞ-ÿñ!iÖ¸¡kö‘‡îw„à ¯½å6ÔùUï[ÜrëÓ=½ov›è¼óÁÇòÂkoËe={„•³ÑN—íäýOÊ}¿ÿ³Ì_´X5¨çªŸßµ³óødÝÌ~*‡qß‘òëæ²ndáÂGÖ“46ê‰Õ®é°39ç6O./Ç…ë÷HÝ …‡ioÓ#àðœðœ¬VºÀ)1?€GÀ#àðx<ã‹ÀÎm[¤xñâŽË€Ï˜8Q½Ñ"Ä8,8™Ö­[»ÒTåH´ÞÅšX§ÜÞ\FX’ q4?³4qÄBì·g^–”ç‡Mó¸$nº”#–¶2 ­>\yÖu¢J²¾x²F‰ h [HÃA}ÊB#­£Ø1}³AH>bmÚÀM—rúN9yÖñ`:&y¦z'E ¿9Wdú‘™:m{ÎN<%£IAeCÞDë4+® Ñ”C7E<’0±ìFÑ"E‚ɣ⋱ÈÅxíš!¦‹{ñx<€GÀ#àðx<€GÀ#p*#¾INﲸ ߸1BÊáæ`œ,­Q—&Dȇ_ rxÆ×QnùðoèÂJYöKG1]âÆxYÈ‹*É”tãv˜Q:F`PF¡s6HòŒH$Ž.bue Òo €vˆÔh¨€²`ÜÀB—¸µE:ÛDãÙºæ$‡€GàØؼk¯ìÞw@ª•)!gæ:CVlÚ&ùt·ú %O »qœ!Ð7úX®Xa)”ßwG*¤8$vì–ÒE JÑ‚ºÈle£ÖÝ«6RÊ•t½ V÷ê€GÀ#àðx<€GÀ#ÍHŸâm||œ™ñoÆÙ1ãíˆ#ðoºÈ ã×Hù2#±cqì/§QgOJÓ#ÏÚ·zäaÛÚ7NŽö­t¢ •IpA]:EA`ltŽ2ìSŽ6[Ö9Åò ê¡Che #“mŸz^<lŠÀÏ‹WËÕÿþ ê±'‰M>3CþøÞ7²+u¿á£“׆MÈ6£e|ôoæ <ñ–ùk6ºò±óW]˜DN¿Ÿ¦¸ú‡'|Þ'aÍ«x<¬E@—Ú‘={vKú¯þÎød¿.!“•²{·NK9…eÝÚµ’ǸsçÙ¸aCè ¤îÝ+éR¡¼œa¢Ë—Ÿð1¬\±"Ë{vêkv¾ß²§ŒÞ£;vl»Ç³¢Çjãxß§Ùé~=îÇõ·ô„ž<ßX¦°u)µrð#Ù¸µH›–oåÄ‘Á¿Wl/®KшAˆBŒ“¦10qk\£¡rËGŸÃv2Á޵‹=ô(gª8ó­œ<Ú0ðÈ·Ír¨C:ÖŽFX ʼx<9ZJ˵ít›z/€GÀ#B H‘¢rKïÛCéÏ|*uϪ'7vyyrÛëUH%ÛD #ÛœÛVš4mæúI9{ÖL7Æ•UR¡BEi~vK)[¶lV™<ávÖ¬Y-•*W>ámguƒ¹õÇÅ–ç´Ò±Tq¦ñžúõ`ùÍoÊꦲ¥½šµj˘Q#u#È’/_¾PçÏë¼`ׯ['*V å/^´PªV­s]ö¢œ0NõgJä=z€= ÷ã‰ú[zNo2sDã¿àƂܜ/ˆäó‘¶^ !®Í87êSfœŸmŠC=òÍ~´ö-Ït¨ç‡ml"„־ˈüψÂÈüÈ4FMˆ[c4L'h„Á§Qt ò¬>yè¡`–F~ÐŽéRfö,nýF¡žåÙà­}§àÿóxr&~ø­ä× •»¶›Àd2Í´æ[:·VuªÆÔö=ûä/ “UËɯ{èÎTéò¯A#dÏþƒòô ç[– ™NÞwøÏ2kÅzÉ“ûLéÚ¸–\Ѧ‘äÒÍ¢^úzŒäË“[ªê:—_Lœã¦*^Ô²¾\Öª«»ïà!ù`ä4™¾L§àéš“çÔ®"w×ZòçµÇ’È–]©ò·?Ê¢u›¥NÅ2ò›žmc®9¹pífùPí­Ü¼]Ê—("7uj!ª•wmmÚ¹G^:^–¬ß"xðS»ÃΣOxN5råÊ¥;6Y´:×¹¤@þüayÙqÌLGÃû³bÅJ¡îmß¾M ."YINb¼PáÂÒ¾CÇP;91²{ש5E½e«´G9{÷ÓEjÖª% «V®ÚuêºaoX¿^öïß'•«TI#(ñdÃó·s×´wœÓ£ì>ÎÓᙼG³ûù8–þåôûñDþ-=œ}݃@`¹ø/#û‚ü1~Îâðo„õ œ¾Ìø7Bþ`GòoÆáQÇìÓ.ñ`û”[ýÈr³¡*±åÈ×slëDPÃ¥ò胣et˜ÐˆÈH[ès ¦O<8hê ä!Ö6qêPÎA>ã1=nj^î½x<ÙÔeãŽ=¡NÓ c ø¶îNÕðˆ‡Ð~%7+I·ÿ•ØR¢p)[¼°Œš³L®oßLH¯ÝºS¦,Y㦑kòpyúÓá²~ûné¢Ä$2`Ü,)˜/¯\Øâ,Ù±w¿Ì]½QJ-äÈGHÒFM“ÆJÖP’°ÿè2fÞ2iW/E ÇÌ[îtoèæ5„½þc¦Ë¹gUW´¼°.åÛ?L–‡/í@Q˜¬Ñ>>Ñÿ{)§}¿¬uC™½r½ü㳟äÅ;.q6ŸÒ~nÑMwÎoVGö<,#f/ «ïÀé…À®];eà'ŸÈ ½n’ ºÁϘ6UÖêš…=/¾DXC ¯Å 9¯.Ö¼íŽ;eäO?JÉR¥dËæÍÂôÛ3s)ç¶k'gÕ«ïl@šŒù“’*ÔniÛ®ƒT«^= \ÈÑ#FÈú ë]ý5kªöò‹’.6…{øß9ϰ””2wÎGV}6àÉŸ¿€\tÉ¥aöhsÂø±²uËWÞ¡S'ÁûyÿݾÒí¼óeÒÄñÙS¬xqé~~)S¦Œ@|8PzÝ|‹äSò6(ýÞéãÚaú1ë8^qÕ5ŽZºd±Lœ0^öéZ˜… ’;‡¼·hû#~üÁaS¼D ©¥}`Ú-xΘ>ÍJ_zY¨™AŸ p¸ÕoÐÐåų=IÛ„¾wéÖ]='çÉÌ3äÐáC6”Ôm«8ÆÎ!}_´`8xÀyŽÒÿÒjŸ†ÿ Û¶mÓÙr¹q±nUËV­Âˆíxx9è3G®qÝ ¬?÷ù€rãM7Kþ$ˆç¢h„G,Ó*©[¬XñÐÔïŸö¦§]~åÕ’¾Ž–³ÍµXF½_éïÜ9³]=g®/ˆ>°Ï}fniب‘œ­ž™0ezñÂ…·òåËKÇÎ]\{±®K¼cݬeÇ”ôź\@Þ¶­½ÿæéSÇ‹GÀ#M€Ôã0¹÷Âs¥SÚ–ÌTØ­qm™³rƒ#/9§° ϼ#ƒ2méY¶q››bÎ:˜»cÿ8s±#(M÷?·ôt;q×­TF^ùfœÌ[³É”7vl¦¤g#çù?}»íåOeæòu$(‰_¬^—Èïû Q’r•ˆB²~©š¿è‹Ý.ï$•J“ÍëJïWÈè¹ËÜNåôë %.¯O'?·ïÝç<7­>ôxN/ødB“ƒ*ñ–ê’‡Q­Õ«´üéÔ¥«~$pùûuÍß'Mr¤_ûŽd¦’¬s˜’RÑ|ß"U‚…hH” àù)ȇl‹.v¤âèQ#äûaß äÝ…š×çÍ7äœÖmÆÇé™:õwÊÏ“åüFýñxÕª•Ò®}G)§³³fLw¶zßYÕM“å£rB¯P¡ÂJ —aß ‘^·Üªcû¿£6â±¾²FÙwÆJ‹³[êw;Gâ­Y½Z† ýF.½üJçÑÆæ._éÚ½nºEòäÍ+sÕ«§¸1@Üùú+3'‡[<á‚ÂÇ=ÎH<Ûp3t\7ßr›#l L!Z5nâȹÔÔ½n|‰¦í3}xƒ’ÂW_w# §*‰4P‰Í[o»ÝÙ…h(¯ÓÞ!c¹6>|¯Ÿ;÷AO\úo>ŒmLèÚuÆÇùñ0B?uoªp–-WNš·8[¾Y7X.¸°'Eaä$i®ÅI&¸ké2='… rm@ˆÖkÐ@ë]äHÚ„¬W¿#Û×­[+W]{­# ØÐò,Ñuë~€œÜ°~\réåŠ]™ª×© ç4Úy³òhaÍšµdæÌ®ß¼w@¤vêÒE*ë´wÈcÎ}ÅJ•”Ä\êBH_“•êy Ïu0ìÛoÜX/¹ìŠ„˜$º§Í>krý_¬c¥{#”$Σ³U ÀB¬œž»«¯»AvîØáˆs–ûhÂ5ë|ñ#Ã0}ž0í?¯Þ_ÈÔ)?Km%=ãÕ£­hׯömÛÝŸ-[ž#•ÜBx®Ô­{–‹ÿ‹wg—gŠÝ[±žáÇë™b÷(xAVçÖóû]w»{h“•ñ$Ñ}6SŸskuÙŠ[ní-¹ôÇ‘o‡|íÎu ½/x&ówrOb~@7f´Ütëm ¯‡x÷i¼þæäû13KãaáËr6é¥ñ_p^Æ©10^Ôx$ßø0†ø7^TŒ“³|l!A>º¤)#n\\$ÿ†-Ú ¶«}l„¿HjF4±ŽD+³<:FC’o&„†ƒe…èÇaƒ%/¨«I§‡>íp ºÁC“amŽÙ¾'(Ç‹G û#P½l aÚ´UJ?æNŸS§Š)ÏyQblÜüåÒ,¥¢óD _±i»KŽ_°R˜RαdýVY¯d  ¯BùÓ^¬+–L[;mNçF˜þ]XÛY¬Ó®±Q@=>w¦†/¬_ªHšgú5Ê•Ròÿœw(é Ð—3õ#ëµoÇ»~<3`¸~àˆlPïÎU[v8ÕzUÊ…ªœ™‹žyñx<±À£±ûy8ïd(Ïc„{—¿=\#ñ®‡D÷iÚH¢ÿŸ“ïÇÌü-Ž‚Ï=àïSºÄä¿´ü¤òoqÚ7>ÐÆ5„L$†¡1¤„–†D´/ä +J>i# 1]â¼-n¬4‚Òô©Ot(CÌ.:ذ>.}síó0 œHÍöâðdGêV*ëÖ•ÌʾåÑ£Žê…9äçyòÓ¬%n w/]Ï1Rì¡ÔTÉK¼MòéZ”É^𝠛 ;tÝËJ¥ŠÊ^®^H_Êb‰¾£9ÁÛ2R(Ë}f.j~䃥‹:uBØâu‰äM²_‘¶}Ú#à8=ÀsѦÇB‚ŒHÞ›KÕc‹[ûöyË}\â}iÓL)gjhõf ¾c•/—¶Vî.]O1ÉFýh‚§&ÓŸñd3M #õ ¤ ûR÷9Ò'²<˜6Ëòð*„8[ªdªIÕjÕÝôçÕêÅÉ8áeõ"Ãx¶!zðVeŠ/^tL o§S™ƒFÚ‹L³ž#˜+~äoõ!žwê9AZ´lé¦+3%z‡¼rŒ/R2‚w°.çþX0 Ú²8$xPöìÞíÈJTšp}BvE‹Mv&º.±y?˜rÅJ•­©°r%£ç­téÒŽØ_¦’xݦԨéˆ 㕤(Ë1$ª‡íÈkƒ<6Å3z”óÊdª8dgpC"tŒ\ã§ë3œÎmÛNŸ÷cä3]š¡ …mÚ¶u$6eÑ$Ñ}V¯~}ùòóÏeü¸±ºdB=ç3CÔ,mQQ='¿Ñ³lMÖfêeÍs,Þõè>ÖOË;ÕîG—O?ÒßàºàÈàÑ8Løp%MÈ^è8øà$D(cJ;”ê¡CžµÁ—+ùè[huIÛ§´µkõ­mUq´iyG…4šŒØ€¬#Ô!Ï:M'ë ä°NÙ@  lQŽyzؤ ±|ô8¨obi³M¾µOÜÙåeÆ‹GÀ#s€Œ3OÅÌŒ¢›N熠|äT·e‹šG€˜G$Ä`äôïDmâyóÜW£¥bÉ"òì­麕yäï}s”eÐÎjõ„dó2º¦%S¶ƒB_ð¢¬Q®¤¤è”¥¶¸$kuÖO÷¢<ü B/À銀‘[¬_˜•™ÆZnm2úÍzb—]qU¨‰"E‹Êõ®ähïZ¬ˆ)Z$¤—l„iŸ|ü‘®ÙIÎVOL^ôa÷®Ý!˜s€çÓÈWSèK¬gøñ~¦Ð6žŽlÔÄ:®x'³@µjÕcþø“è>ã-\¤°@ "×Ýpch:>é\ºt¼è:wu?°ïz°%'âݧ؈%§Óý ŸŸóÀÛXþË85ø/—©a$¯¦YŽ?3}Ò|8¢g!eÆ¡?*‰sDãÿx9°|êb›Ö>}³<¸=ÒÖ^° Í>ZŒ<ºäHƒzÖ¸uŠ4@-ß§œ£­ÕG»Ó§MqSÆéSFeËæ-®=¦žBÒ~¤¬\¹Âé°vÜŒéS÷M¤wddhiÖ|dº)SØG¦½²ùÍ„LùE¶lÙâ°u ý¯¤blâÃÔ]ðkó\D'žm<€lm76´É¯çt„µA)Ç&Óêc D#kóMž4A(¡Fß'OœàêÕ®[×UÃŽþ1]ËæMnÁÈúDxCü±ÁX8|¦Ou)Fóç¥Mß )"ù ¤m\„7[¼±ª¸)ê¬Áˆ–½ËsMpÎÀ‚Êa ëW"lì¶™¹.¹Þ˜nϺˆx H_ŸÕúï¼±vmGc¦3C<1-Ù„ûòïʵj[vÂéî±0IX9 Àf8„v-‚ër%Rm“§€jÔh´1'êÞ’œ3ÖŽåÚ·¥&Õ‹ÚÍähÚ¬™#=9çx‘’èG?;(ñ|aÊ0ËðñÏZlxeV ¾ð¢á£‡ÂÜúc õbí²+¯RO°ïÜ;|´v xŠõÅ™êǺrïõ}G§‡çwSbñ Ên:þÎÛoºi%Ût%¨“Lœºx´á%ä¼–ô™WÄ_% »v?ÏM‘fCt™V¼Y‰>„8D˧ý?rä-ñê)5BÍÆ³½G?Ƈ?L?ÄÓHQÖ^cwdr’ùµW^rDç4–°4}{W§ßó±_T½YÙDňa¼¡ ĘZ™ª¤ÃâÅ‹d¸qrKïÛC&áÍŸ_ù¥ôyë g·mûö2Þ\WR8#¦•Ûƒ¡FÒ#%Õ‹ràƒ~ïºu"¯»±Wˆ˜Ôµ4ëÖ]zÅÎÓðW_qGÙѺs—4\ØÁy¸z!r=0Þ’ZÆÆ2™½.ÏPrúú_vdždvíÙ½'êyƒ85b;r‡{ÆÁµÀRŒÂ'(l.AY+e"L‚öãÅé+×¼xÉž¡Ï:ÅÖvñŽW—²…óç;Ò)8ædúÖD×¹e—ösÛµ 5‘L½rDB•iÉÍš½tª‰®qt²Ã3%Ñ3üD_©‡dùË5]V=tøÁ+’©Úv䆨dƒœX‚m†É®!ÉZ–yõ£5(l°sP]üãµÔ÷q€Gàø!Às²Zé#;ð¿–â[æYÇ8óÈ‹¯\)äÓ‰ÙD‡w­¬xß‹‡ :" W^|Þíº÷d(ÄFVd,$ldßÁÓÚÁC‘Ý€ù¸6kúYÏÊ cÙÆ#‘þG«KY"¼­ úöôß„ C¾ûvh‰ç"Äï¯î»ÿ(ÜbámöC´µý(bdúñÂŒŒ-h‡1ÒVpœVÎ9B"¯ò2s]r^£µƒ½hç }Îeä4tô§ÄÃ$#ífæœ@¬â­ki‚Ìö-£õø…õ,oèusÜ!ǺƳÛ3…k<Ñ3üx?SðÆÛ5#í>ëûö[ºKüe!OYìõÿèiqvKÙ¨žÎ<û‚Klà‰¿^Ÿ]]ri¨éx×C¬ûôt¼Œ%#X#³k'‡@÷‘†ÀÎm[tÝëâîo?&NœxTÛöw…gCkýÑayýAŽe¥ËH㾂ëÎÀ…ñ¹ÉG[§ÅAMx5ÒA=ãÞ¨c\^PÇ86òL×ÈQÍríoœ¡õrÄlX˜–åÿŒ¼Ýa ¡cÔ£ó6ÀÈ2ò­œÐˆD¾²­ŽFÐy#,í+œ6Ð3»(ç°ö©‡¡éY™Ùû_´—?Õ?©ÂEÆîf©úÇ€hBß™òSP§WÚEM7^žÙ?žXðG„ñð+¾Àñ@ °þª›™¤S¡×ëØ7$á ‰}ÈÀHB0™v£M׎¬—Q»…”&xUzr22>Ï#pú"À;B²ÄV²(ñތͬ" éW¢ÍugV¶‹|KÔN,+ˆm,Ûñ0W´Mœ>FöƒH»¾æ<ø  ˜®Þªu›¨¸%Â;Ö¬ýŒœ‹ŒŒ-8ÖxmD#&­n¼z¦Fâ,ÖÿxúÁºYÏÌØ¢õ!Ú˜¢éó˜ÎõK2Û·ŒÔã[jÚ”)‚§o"It'º×Ù,uÏ$j‡òDç#–íxõâ•Õ÷ ’“ÔvÞð^ýqø÷Î+{”e'SjèfQúƒÊðï¿Ó !ÏÔuO‹º%2æ«÷xœŒe×ú뾋•oõŽW ƒÌ´•‘seöYk•M®<9iˆœÚ!Ï>ã¿ ‘Œÿ"B’4ä¡qgÆ©Y©E¡ú¦GžRÆ«‘G»”'4.Ïôȃë#{uàãÐ!ŸN0)A9‘ c FÆ­œN§“F4’¶r‹[91{”Y}âèðs° \£¡S¬ÕÃqlR—xe%ç¾âŸ]Br®ÇA˜ ¬¬»&+üB5lØ0÷‹°PqÇŽu:I-g‚òB… éÔc÷ëß¿¿¬ÐµDzõê%Uô’—SìäA™YDYïqÇÞTiY«J¦Iþ̶íëy<§>ÙŃòTGo)¦DÛ4æ9^ÖÜÍnËúq/+¦'³¾ë\V­VÝMýÏ }÷}ô$ƒž~K—.‘³êÕꉜŒ ÿLI¥Ìë°!Ò¦ikõ²æ(ä¤}“³å ]û”éßEŠ‘ÚºÔE""9ó=ñ5=ÙÌzPÂ!éÒ ¬¹³ZãÒ CƒGã€$DLjC84âVfuÐÁ†qiV}³¡Ñ£âÆÉaÓꢇ`±>Ò:Øãˆ)z‰ÄØ= ·ƒzÄé˜1¢èXÔ‘¶Ž“F(GöÍ6¬?fƒ™ŽÙ6}Bl£k ƒ¾µû‹=5ï¤Ë^]ô|™>ŒéSÚ®}Eá˳ÏG.Â]ºæ /Y²D_Žk8R1Ñ`pq8p ›^Ô¦M×æÌ™3e¾®cå‡~(]ºt‘fºÈô±J9Ýí“…äù#ãÅ#¨S1|‡ÏìÔ7߀GÀ#àHd×ÇKÎZÆ´ø¸¶M=2Vóähãe•ì†''§‡¾UÀ±!À4äc½Æý3åØÎA¢Úü ÄMJ”()%Z”ŒVäó<8¤ÏŒ…ÿ‚ ƒ3Ž ’4œšqc¦ƒ>ùFd’ÏA!bü›ñhðlÄÓ±z„Ö†µoº„‘zØBŸ>+FÆUJ/ v’¬£Ä­!ë¬å‘¦Ìê’O›FFšž…¦¯*!›Äl gik8bi‹ç 6;”L·Yµj•û•’OH¦ÝìÞ½Û¹ÆãÉ)—äs0eruêÔ‰ºÖN°þºuëœívºu{]بLw vîô´ŸUÒ¥KGvf•=oÇ#àðx<€GÀ#àðx<€G@‰°£§xÃA ‘—F¤Ì¸8#*3‹äÐ( åðv&ÔC°O™µIhyä[?¨kùÖÒq%‚ÒXPB”uÖ¤ë0¡Ù¥<èòI¾¥ÑÃ&buÑg ¤í m¤& ²Y›Ô%Žž…uiúélfr’Ná]ˆW£MÓÞ°aƒlÒ)C&L³®¦;.ÆZKB³‚®/Áôpl᱘ŒÐŽ‘´¶.$ç?þèª?^f̘!ݺusS³‰O:ÕylâåÙ³gO)«žȼyódôèюܤŒþ ]»v•1cÆÈÂ… åÖ[ou$lß¾}rt¹º÷/^¼Øíâvá…J%ÝÑ ‰g+3ëo8£þ?€GÀ#àðx<€GÀ#àðx<§é\܇ ¼|™qkðmäQiÄ¡ñfÆ¡‘FÃñgGäÿ°kõ£µOõ±i¼!¶­O-TH$èØAcÄi ¡ÃˆuÐ:EçÑ! Xt]ÒÖ¦Ù0µ(ŒÙC‡zÔ ‚cu¶oƒZÿ¤ÊÎ;ÝBæ%J”pä$¤dUݲ™ðzŒ'Ô…dÄV")_¾¼óÒdZø›o¾év†²u/™úc›xfBB€®ÖŒ¿ýö[7M»aÆŽýòË/Á ):xð`×ç.]º¸!3Á—…±M¿p;&øÐ¡C…iíx|nÛ¶M¾þúk×íD¶Í—{<€GÀ#àðx<€GÀ#à8]`Vnº_FÈ‘mø7í ]ÿ#އ—–Šñ?äa"ÁE7HZ:H`’g$$!uÌ:J b‡Íl˜4ú4ºÖ¾õÁÚ7{è7»=Ò¾y’y2…ËYŸNÖ”D '!÷ à‹·Á u!™žH !¯¼òJ2dˆlß¾]FŒ!&L+®¸Âyj6iÒD—^*õë×[ƒò׿þµóvÄ>ØÍš5Ë‘k×®uiÈÉŠ+:¢2óì³Ï޹8uõêÕåºë®s]e:9ž”ô=3¶×—{<€GÀ#àðx<€GÀ#à8€ŸQá?¸/ÄKãÍŒ³|t¬Ìx4x5ʱhö,D!Ía|œbzf‡87'JÀïða>ϼä2r-1žSå>Ï칉öÌ–—YûÇ»^¢çÛ:uÚ½;ÍÙéx÷ÅÛÏ~¤/_WfœY¶ãß´o‘üŸ‘¡ôÕx¾˜à&ãAi†ƒ„¦‚ â4 ¡hÄ%ùÔ±¿€t†<³A™±§¬-IœrôL‡ºÄm Ä­L£.Ÿ:õñÀ¤¶óéÔÕäÉ<!ç`½‹-êH@Ö‚„¸#Ÿ)ÞLáŽ'ÔE[É qJJŠ;XrãÆnsžhõ±ýÕW_¹u#éKú.Q®ÏLoÞ¼¹[k’õ&óëîy]t‘#)£ÙŠ—‡fVي׎/óx<€G g!ðñ‡ïËæÀÝ‘½ÿÕ}÷KNY«zÔˆŸdÞÜ9RV× ¿òêk#‡rÂÓƒ¿$—^~¥TÕ5ϳR–.]"5kÖÊJ“1mA¬õëÛGêÕo »v‹©«€û¯¾ø\îºç×R `Á0µ-›7K¾üùtÆSѰüÌ$âµieŸ87¿ùíC‘EYš†øøbÐg²/uŸ4mÖLÎ>§U–Ú?‘ƨKFUª\ùD6{RÚŠw-E»f×}9øhmGêœèt´gn´<ëWvC2Ï·?þ 7‘&M›Ù0|x!NPÂÏf¡qqÆÑ‘‡fÆ»Á•¡C>Žƒè‘6}ãÒ,/ȧaƒö±gmR'Øu˳zÖVZiŒÿ1ž¬XC6¢<:Fƒ$NY°qHC:Ež ˜8º‘õÈ‹¶ÉÇžµ]ÆìZû¿@²e/J<!™n )ÉÆ3¼@L"¬G qO¨Ë¯hæO²‘ uj×®R3r3Ö‹ýÈ‘#gdïÞ½Ý&<'NtSùøåiþüùnGðzõê¹ié±6ô 5#’•¶b4á³=€GÀ#àÈ\rÙå!ï®)“'ˆõëå¢K/ $Ö;LH!›D YgÏš)·ô¾=K¯l2¬£ºÁçC¿|ÜÉ5k˜¥‘Z*±V©rËʲðçÉ“¤f­ZR¤î±”YÖ©,44}ú4ç`pC¯›Ý’SYhú„›Z³fµ,Ðï’Ó ŒîɼfOfÛÑ0‰öÌ–¬›ÝÆp<ŸoÁqûxÎE  „ÿBའ‘ÿ¥qø2tÈ3òÞÌ84«G±4qãâˆcþrÄÊÈCÈ·¾Xû´ÇhüŸ•k–«e1%‚Ò:Ž!;h(ØQË'Ïô­ã€B¾‘‹¦‹ tIÛ`Ñ3}º!IÚduɳö°Cý`²9I'K—.í6ŠY¯/ÚL}fóžäad›Ö M ©Ë&9ØJ$¬79iÒ$aHÖºÄ[“µ/[´háªÉÉ:”\èlŠÃÔmR—Ý¿ÙiÛR‘>°ë7D%c`'ñV­Z9PÓK&ÌJ[É´çu<€GÀ#àÈ.\$ÔÑ|:[ãŒ\gè¢ñg˜„*d£ÈöíÛôÇç"§49 Ü{÷î ͸9Qð·lÕú¸4uªO›Ü®V–¯P1Ç““œüÝéëù— !=™×ìÉl;Ú)ŠöÌ–¬›ÝÆ@ߎ×ó-8nÏñDò_ð`ðdðh!iø7óv$nd¡ñoèaËôÉ7žúvO}k—4ùpo&f{ä£Chõ)Çf\I† ´†¬ƒ‘! ’GÈXù6òƒ3;اèQÏF‹SnƒC‡ÃêjÔÕ±ºèY_°y¿ì¦3Íèž4ÁÛ¢u!K•*åˆ=6Ήåá Q)±¸uëV7ÕÂ1ÏEˆCê.X°@Vèº6x@NvîÜÙaÀ:xB.\¸ÐíÞ éÙ²eKïׯŸÛ(‡ p6ët0d=J¼@kÔ¨áÒlv3uêTçÚ«W¯ áš•¶2Ô°Wöx<@D€5ÅæÌž%ÕSRdúÔ)nöE­Úu¤K·î¡wˆe:Ívâ„ñ²kçNGŒ5ÕeY6jìF;ò§¥¤¾w0é¸gæ:SÎm×NΪWß•³ß„ñãd±¾ð7¿|… Ò¹K×£¦ÀBF5Jgh¬–Ã:£ƒú;wqïY´QFg‡lSdîœÙÒ a#iß¡£n´·C˜bǬ‘ÜgæÖ>5ÊôÔÒ]»vÊÀO>‘zÝêÛŒiSÝæ{=/¾Ä¥ß;}ä¢K.•1£G ku]qÕ5ni›DøÙeAGëÆ‚ë7¬w8ÕÐ5¼Ïm×Þ-¯“ÌyÀ΢… d¢þ  ^Ÿ øD½Ö ¸>%²Ç%¸5Ò­‘xÛwZ·\¸Zlž0~¬lÕ©­ØìЩ“züÕÓ!1I¯ƒ¹sæ¸|Þ÷¸N éLdë–Íòóä‰n} ÑsÞ=t‰¡´uÂã]CŒ=²\ØÓáŒÝŸöw×ÁåW^öžúþ»}¥Ûyçˤ‰ã7l1ݲûù=Bë‘Ç»>b]SŸüÔáÄõǵËu¿Hßw<àf(uèØYJ§¯wÎÌ£Ñ#GèF‹$ož¼’¢ï±‘Â5ÿÍà¯&“ôþY3g¨—fk©¦ïÜñÎYÐN¢v’¹ÇÀŸûò­ZõwÞXÖéK–]»N]7fÚܱc»|>`€ÜxÓÍ’_ a~þƒ»÷r‘Ëyò­ÐR¿ŒÜ§œûv“ÎæZ¿n­ðühÚ¬¹dôœgöš\ºd±;O|?Ñk±U›s]-Úµ¼`þ<™9c†:|ÈÝ_*V’¶z&º¸Û´m+“'MÔµX÷KÛöÜ×ùÝõ‘šºW¯šîZÅq±>ó=…# וylF{ÎTÌ„Kô¬Œ÷àþNÔoÐЭù ázJô÷Élƺ^£=sëÔ­õ9ŒxøÅjƒzA‰uŽ—/[ê®õënèúûÌ’Ÿ|ü‘\|Ùeîùëo öƒÏ7Òü=e6Á¾ýût£Ûêú÷Ìæ(õrº!Àµ«b\!üa³3rP³]9ü‡‰’Æ¡‰ˆ âÆ¿Y= ©OÜÚ·¶É'¡»&è&±úi©(ÿ§=£²¬Œ#fÔ`³FI30„¸å[[Ô³<³iƒŒ¬C>íÿ?{ç/eqîÿ‘*HU) °*¨ØbÃÞ{Œ&ñoobÊMîÿ¦Ý$7j¼–˜X¢±DEÁŠÁJ‘&EzüÏwÎþÖ9{ÞÝ}÷œàyø¼ÌÌ3Ï<ÏÌoßÝóîo§p‘×ÀIyw²w%yüÄýdÖ%16ÔrÒ÷#ÜöðKGXz ñÇ•V˜±ØÁï³ùd׿ÿ@.&µà?$9]›Y™Âé†nÄfã̾<tP3pàÀðpÞ¯_¿lØûî»/û«9ûQrInºé&eCÚ·o_Ç…óŒì?CÀ0 CÀ|µþ«ðe™• 眡[òå—á‹9_–Ù‹ož®>l¨ÿbx†kç÷ŒžæÉ˜—=aÈß÷®Ýö ÏoûUEGu´ç—z¾øüs~êÎ~Ͻò/è |áÎ:÷\ÿãéŽ$yþ¹áî´gVx üðŸ/}ôáPîÒµ[ˆ¹Ó¥[77Àïs¸C“¶4){í³ë×ÿ”ð¥B²Œ~—*üp»|ù²ì³íyŽYµjeÖ3bF æzõ>Ðrèᨚ5kfAüÔ˜ý¿ž|üñ0†“N95¯¾ò²{nø³áKw±×A~ “8`ð·ßr'žÔ?|IMã{ΜÙ~lëAôÜ6{ö,wøG¹ý*œƾúuåÕ»UØ›|îœ9n¬¯»ô²+‰ËkÖÀ“\°àõ«_¯¾þìÐ@†ò:»‡{nÿ YèÕÛ ?Ø¿¾'‡¹?¢CðAN’t‡&îå_pÇ>ã.¾ìò°”¿ÐýÁÚ¹÷AV®X^wò¹Ÿz2ùœóÏwsï¾ûŽ{ܓ—_qU?ää§ŸÌw§ù÷Äû»þ5Éž9î8÷Ѓ¸ž~oFHLàb¯Yì§Xê ½Ç¸·Y)uö9çâmî†x=êècüxW„÷’âé}ù¢ê^xnD˜ ÏûãŸÿ¸?ÜC"'iw¤÷ÃkÁûû >}\à «ôš«JÓÜ“º·N>õô@úòCÃÒ%å‡|[¾{¹»ßB ¹¯¸—‘b÷ãŸ>mZÀ˜ï`Cž~*ì{Þºþý ÉÄÞ~Äæ3•=[;øCG?ó+Ëžöû„^|Ée®±ÿŽ•ô9#LH‹}Vû<®ê=«>ä{Ÿ½ÿÞ;‰ŸòïýòùgŸú¿[åq…pE’>CÐó>/„6ˆî£¤¿sü ÊýÌmÜx‡JºrOž$Èóž/ƒ¿¥±ä{;ûýxŸ÷ïIˆ`þ ìE¼}£íÃûïÁûïËû7Ûøó†øÌä>'>?0<=s f&Û(A) $Ä…‘JGÿFB’²x3ŸÍòjäÕFzx5¸4x;„³ð9ô°Ð"°×…Y;+2{†§ñó‡ nçÿÞ˜Kã{½MŽ?¡_8Ć٨¹Â,1b^cI€œ…¬Ž…ØÌn…LäõÞ×\r@¯ý3gó@²€Ñ矕¿~iî¡ÜþÑH„qæ;\¦WïƒBL^ ȱE‹†Y€iîÜ{Jã Õ½Ûçìaˆ#0òL¸_!ôQ¨‡èîïYp¨S§ŽÛ=Ú›=öÅk¤×q€Yš×Lý(Gý,ô£oô Ø {ÏžnFÊ÷ ï[ÞcôŸ¾Cǯ9}{f0sÿbi]•×<ÆŒ|š{’YsÌØd8}äþ㳪˜`›t/Ów¾¿ÔóÄ$ca©0Îw/(3ùÀ˜ÏOî]f¤Ñž•m`È,s„Y´Ø€Ÿ‰¬z›;wŽ\…¨¹Ÿ3ª,öYYèó¸Ð½$ÿ¤I÷¬êó½ÏÓŒkÿz'~FÈw±Ø±]n>÷3DõùðP=i¡û•{%÷37IûK¯PŒ¸-ù|¯1÷ 3á¹ï%Œˆï´Ÿ)jÇßffærßòyKžûÐdÛE@?Lyòò_¾.æÍ69ÿ–2Nd“ø?¯Ê/°šÅDƒÂyL Ò2Q©G¤CŽŽ‹öäUG{D@b/èb¦ÔKdGYñU'ÆVåZ“ò ˜DÖšNæt¤§PbçLÿ……¥ûøÙÀÃR¥&}•Ûì CÀ0 ­ú~¹ªDf$uî¼{…aAÎ1[&Iôã$_ê™…DºÄ/ _åg²Hð3£á †ÔdyåL¿]Íê5«ÁÏÒbvV,ËýrNÈ„™ž¨”ð Ä—Ñ)Å$ÆO¶,gù)d‘d—w Y¶ÛI’$?Ivi|×õ_lsO–Ž}ñ:³t›ÙUzí•ÊÒ˜Y²,1gY/_l÷Kíã1ɲ{½Ÿ±‰¤¹‡ŠõO~ ¥üÌD„“¤Óܹ÷Tì›%–ŒÇæßÜwŒr—ûY³mÛµï7K/öšiÙ|±8¥¼ÇÔ9ž¯i—Fzù-š˜¡Éö Ìl†Ú­c§¢Mkâ5OsObXª”r/»’b×oP?ªãs‘Ï8„×Bhúôiª˜ÆŸw…>gŠ}VfúLüy\ì^ŠÛ¥ÍÇïó4ãŠýÆmc}Uói>Cb<â8iîר¾*ùRbòßG_@IDATz{øïÍ÷ßóvØ~ÙÂl?¡Éê…Bcô™¢¾ÓŸNe÷ï¥bYºe À3”—˜ƒã¹85Ê<ÄŠ7ãƒMüv²ÅŽ<¼™lh£<>hG½Ú¢#¥_H>)¾ü(Å.¯¬˜ÐI:wŠ68ä¢cØ uœvتãòC=ƒW˜ÿÍO¼qÇi£åÚj§”8Ø’*>q(“†+ó"ú¢Iuhå]äª ©I_5Ñóa†€!`l©ðEyyf†ŸÆÀR°¦ÍŠÏ´` 3>˜áTlVÓÐ!O‡Y;G{l˜å5ì™! —˜6óû B~zØáVk$§PŠdcï¹!M›yýrZˆY=?j†(X®Œ–’—¿º¾YRÊfGu´ëígu2CîÖ?ÿ)±Ìråâì—±¶ò³]™ÑSHªsò›[7Ï/9GÀ’»:÷÷.÷IJ¥Ë²ûhòÚ1 ŽÙŠ5±Öúƒ«"Å^3ù,§”÷˜|~2~XJ¹Ž'ʘI˜O Ò˜í ¹Ý¢e«°/¬Þ+ùÚ ¯îkžöž$N>‚¿ØØÒÞËÅî…B8$Õ1Ë“÷X˜‘›dPDWêg¥Ü»—dWÕ´ºã*—{ŽYÝKª{¿¦éW)1 ½Æü°RVÖÙÏ¢ü Ìrçó—÷iÚÏõ•-Ûªúù%–n]DŸíâ¿HÅ‹‰wS ×'FYZ¬ã õpjø7ø¹X§ö±­¸ì'˜$³ü¾—‡ìo7öýwÃÒYˆ€b÷G’¯XÇ5ö2}ëÍÑn'!!GÞ3:ÄÚ³k×€K»ß}çí0Ó’ƒ*Øë.Ÿ4öK}9 _ì›—ö5ãõ('Í{ ¢Vï™6uªÛÞ¡«­ZµvÓü~v«ý &Æ8îý÷+ ™Òì‘È– ¾ø<ìA›†Ô¨êk®àiïI–Asˆ K¥ˆMfb#…ÆVè^fû*깯¸ïŠÝ !X ÿ±Ï%}ÔgUÀÝo¥ÁkFJý¬”Ïb÷’ì”æÞ³ÒçK«;®Øonì–þ>å/îE^>›™É\SRÝû5©¹c(%F±×˜­T8¼k¶ÿücF%’ö3E}e&4ÛUðÙÉgËÿ/^¬jK·A2«&Äé{måßÄùÅüù‚¢Ù‹…Œ°œB’‡ý„T$EĆªäa,²‘/•±‘Ÿ"%ÕŸä¹è )m!@õ;ê©#ùé³&†€!`†€!`l@ q³å˜iµ'nzùÃKø²•F8¥úõW_qÿ¸ïž0[‹½ê88W8xfØÐ!aÿlf,•ù®¿ö_šò 33O?óÌp Ï]·ßf%rrô1} _`Ùï‹/üô?@Bsìqaé2‡Ä@Àííg¦pAMDÞ€³Îöý¸á‹_(õTWªë›ýðØ›ü÷Þ[(ë¼{˜5—Û¯åË–ûCS†{|Ëbß?fÇ“ªÞC-ýŒ=f+rð¤ãù]\‰…ô»ço ¯5DžÔ)të¯ê¹ïYÞ|ßßï_à›ùY°ú#BùØãOpÆ vwþß_¾zœ:Ÿï~Pà}ð¶?H§ßI';°K{?‹Sì=ÆIêœÌO¼‡yïîïßÃûv4Èýýî»Â¸8}zÒÄ ‚ l£°[ÇŽþ”òú~–ïªpbùèQ£ÜeW^•µIÊTõ5—¯´÷$[:0þAO D"ô~ä‘ÁM¡±º—! ù¡âŽÛn ?Äð¹Pì^P¿Ó¤Ü§LÂA9aƶCqøIÚýJý¬ŒûTì^Šm“îÙ¸>7_ÝqÅþrcó:³ÿñ¿y(Ìæ%_“Ë“«{¿Æ}W>w ¼çÓþ--öÓ_gf7óÙˆ”úwBbŸÓÕ™] Ñ™æó\ã³tëC€™¸ó‚;&žŒjòˆl”‡7‹94ôâߘM‰=‚OláÓTO͸ôÙ`‹—D|ÊøS½ø>•eS)U'*U$(d›;xô誔À”µ#U^zìÉc£¶…²l©ÏÍK§Ô›„¥áíý/8ƒ)˜†@éLþdy鬅!`Û[—ï«W†ÍLŸ43æ’úʬf^qˆD>af39˜ÉXа_þùÂ&Á$3ÌJüжªãL‹þBˆrÕ´TÇ7„.D_R»Ç}¶UéUî¡|mn»åÏá$d¶€ð¥OI’t$ÙåÓqOà#¾¿b[î—|u±]¾×&Ÿ>nK¾Xœbï10â5KzO0³3÷}y1âÙaÈHf³BdçºëóâÛï|¯_®]R9í=IÛ|q’ƦX´Éw/'ù+v/ÈoÚ”¾qï”ú^â^¨Êg¥úUì^’]Ú{SöJ«:.µ'MŠM¿ù|*¯Øo±|Òë^¬M¾ú¤1`›&Fu^ã|q“ú‰m¾ÏÎ${ÓÕ~–,Zàšûmpø¬ç3fL¥NëïŸi}üUÈ ?cÜÿXyºÏÎñ"œˆG‘–ÔqQÛùbÈF„z]Øñ@F:òª‹ó^|J—TF‡ÐQÊCqòJòSBEsÈ@uŒTNéu€žÒĨÎXóÅMê)¶IíM·õ Àç½ø.q`ð_"%á×à¿Ä‰·óª Ô³)³8@ÚÊ68öeüŠc#O½x9Ÿ ¦ìÐ)¾Ú¡Ã·â+ñ›DQç+3JÊilG§‚#SG4:!¡=¾ˆ+{ùW ù -ùX¯¼WPä;ò´áR|RCÀ0 CÀ0 CÀØÄ°ô¶e«–›8ê¶rð¢K/ ËÖ9Á›=GOì×?l°í `#5 C`Û@ ó#Ü—ø/ø18/ø5&ŠoC?†‡#£^üšx5¥´£N¤¤ôJ}U¨#ã“§-—¸9µÉ¯²7Í/iJ:©Nà‰2`¡N„£ÏfëÉÇ¢v´%GD*â QLÙâ_}t¨›£o[KLfÂ¥9kk¯Ã0 mfˆg$—“>NÅ‹Á©‰Œù7ÙbÇ…sø5q|”ÅÇ‘c’ײrÅñª ‚¯Ø•ŠQÁ0.¨ƒ±.7;Už`t!Ï%QÐXŽM‡Hs¬¶btñ a‰­+PÑ‘§M)ñ½¹‰!`†€!`†€!`†€!`†€!°E"ól ŽL:qkè6'ÿF|HLRx;R®¢ücQï¨<"BÒQ¾”ª€D=>Èkæ%v’èÕiì¨°Š£z_l¥OŸ6&†€!`†€!`†€!`†€!`†À‡æd¤6óo¹ü¼|)ý.(" ‰ŒmbRQD¤HDÑ“‰ØfQõyâS^v>läGñÕN±¯`|NO31 CÀ0 CÀ0 CÀ0 CÀ0¶D2eAþËK¼šìÄ£1äο‰/n¾$J‚’a§Ê©tq=¤¢ìD$RŽ2y.­YÙCÕ‘â› q?03„äB°˜R1´¤*C"B<"è%è)‹t¤£ˆlɳq'àÆd¥JÙÓž6ØP‡È/6øPЋ ñy Ž^H_½ù…‡óU«V¹•+W†4ß2túݨQ#׸qãÙúÍk÷ÕW_üš5köÒr†€!`†€!Pã¬Y½ÚAH¶ïÐ!ëû·Þt 4p};>«Ë—™ññÇnÄðan‡Æ;¸OêïvÚyç|¦¦7 CÀ0 CÀH@ ÃkÁÃÁ‘Á£‰¨ÄþKܼ\W<±›µþBð#îM)í°¡¬ðkèÅÿ‘ª­ø7¯ íâöŠMû,×$üOÐ4‚sD!Nfˆà ¸Ô) 2QƒÇõØ¡C°Ã'uˆôØqÑ^¢²|£W|òÁom#õ &çΈI:YL/^L *;ø/¤iäpŸ~ú©;ï¼ó\ÇŽ³Mðwß}÷…/×^{­«W/í-u‘:3xð`7a„Jöôç /¬¤/Eñøã»™3gº‹/¾Øíºë®¥4­µ¶ëׯwóæÍ ¯sm#Õk-hÖ1CÀ0 ŽÀܹsÜG“&U (K úÊË/¹C=ÌõÜoÿRš™­!`†€!`†@&iyÿ§ÿ%’2—WÃþLö”Å¿)¥N: y®$þo]¤§->ˆ­øôM:È&ÊŠÇðêÊ’†Â¡ÈD<QH50R|j@>ìhô(eì)#Òk@"4¸£ø²U|üÐNm‚=³k I¹bÅ ÷±ŸE@Ú´iã˜ýᘌbf%„æÒ¥KÃ2ªiÓ¦¹Î;»vØÁ³°ôíÛ×=øàƒnäÈ‘î²Ë.Ë5Ê­Y³Æ{챕œÌô™îÝ»‡±J×¼yse«œîìg_0›£iÓ¦UöQÛB¸>öØcî†nHMD×¶1X CÀ0¶>–ùçª Ï2K–|éÚµk_UÖÎ0 CÀ0 m ¯%þ <à¿àÅ )áíêщ{á^öØP/N =õâÔÔÞ«‚?ì¹°!•¥øÊïUÙ>¨­l¨K:PLäDW§è„ÚC "Jã`§£ÌøC6q[ô´A“zRù£tj«Tñó’¾í&fÇÍž=;ô’bríÚµnÙ²e(L"!.ÑsAêAnâ£K—.®n] 5y̶ÄnÊ”)núôén÷ÝwwÌž?~¼kݺµëÑ£Grà%Ön»íV£ž!`¹¶&p51 CÀ0r€äcâ´iSÃŽ»ì²‹Žhìvó+ºvÛËÝÏßÝ)§î^{u¤›ïgâŸyö¹®m»vîãéÓܘÑo¸¥K–¸&Mšºý8ÀíÛ½‡›4q‚›8a¼;ëœóB(ü?üà®÷A¹n{ítS§Lvã?üÀÿýîèÆëÖ}µÎ=ñØ£Þo{wØáGžc^záyÿœ1ÍÕ­S×zøáÙö¬ô?Ì}fp°}áù®~ýúîè¾Ç…ç|}ÃxäK/º6;íä-Zä&ŒÿÐí³owwÄ‘G¢>ÿüsW¯n=?–î¾Ï»žáùípÔ/|L›:Å}ÿ=wö¹ççmW(u&†€!`†€!`Ô¢}¼!ƒ´œ[ü˜º©²x;éáßÔ.¶Q½ø7Ú!ØbGY<\Ìÿ¡Ã†”¶ùV*½øDôyEòøŠ\G”%t†2d¡|::I‡Õ^ƒòªP‡md‹^ƒS:/¿äÑãÁ_˿固}u_|ñ…»ä’K\Æ C“±þ Òo¼áÎ>ûl÷ᇺɓ'»Ë/¿<ÄÄìöÝwßПV­Z»Bc\í÷Ôº÷Þ{Ýáþ ÕŒ3ÜÔ©SÝŽ;îèú÷ïïÚ·oïT衇†ñ~òÉ'®W¯^®gÏžnÈ!Ž2_O=õÔ@ ÓI–ï¿üòËáµmÑ¢E Q;uê”õ•/¯Í{ï½ÆùðÃ\.½ôÒP¶ÿ CÀ0¶mÆ}ßÍ›;Ç]vù•®Žÿ[÷ì3CÂ3EçÝ÷À,[¶4ìñØ«÷~)õ᮵ÿ»:Ïÿ=>l¨;õô3\;ÿ7 ÂîeOüAîê4|nø³þ‡ËE®E‹–nŽÿñsùŠå´AùÑĉŒìÞ£g W®\ö›¬_O[üÍ›ãŽ?¡Ÿ;⨣Ý8O¾øüs®¬¬³kèŸ#ÆþÖþ§œêþþ×»ÜA}q;yÒ±±ß‡²Pß \ùûû¦.èÒ­›pÆYn‡&;„ñô« öÚgׯÿ)á™aГOøº&n÷=öôc}ÆèÉJöÅDÞ}çm·çž] ¶Ûkï}cöŸ!`†€!`µÌ J81ø7ñað_†ÔÕq!ØbGJ{DõâØh#ΔK|yÅÃ_âåÈ££ìäß«‚ÿŠ.QŠD­°åR§©Rpù¡B‘Ž‘çb:‚ zê”Ïf­Á££"¿Ê—¿6“t~‰Ÿ½À—H+ò“’Ì,„ [¾|¹›?>¦y…¶ø }¤ƒLƒH7n\˜=É~{ìQþ…æÑG Dñ±ýàƒ{;"ìo@–ƒK8Ô$'³&˜ÙÀ’dH9¾äÛ“¥Ëý.ˆ@ KÞ‰ùöÛo‡Ùž¼á ÷ oÁ‚‚U‡ _p é 4‚/l ¾l€-RhŒŒ¯´gÆ)c‚|DTÿì³ÏÒ–¸‰ødïN]ÆþüóÏ{ˆã‡z(ôíC mÀçË/¿ÌúÊ‹¥êúR Ajb†€!`€óviÛÎ5ð?Øñ÷‡Ãjø{È3d¿ý³wñC°ÔìÒµ[ #ù⯬¬³û`ÜØ0›’–3ü dÊäüƒ‡¸Ïý“«üßz~P={–Û³Kד¿Oõ<1Ù´i3·½ÿÁTÒ¹óî®c§N¡=üþ’<;äžì½ýöåöC{úR¨oòݲUKwøGúq· ífÍœáÿ¾¯ 3:ùÓÃû¿×í;ìêg‰NweaeJc¿OæÄÐü ÿŒµÀ?C0ó²P»|±¤·Ô0 CÀ0 Ú‚ü„—\Þ¬Vño¾–H>þ¯¼6Ïÿ ¦˜—ÈHìE \zR:3 I]\O;.êµW¤ÏfÛ'6Ô«­^õƒzêˆ/2Tˆ™ }àEÌ0ÍÞtó D$}aOIŽ/,ßáVèÀÚB²,<­qĘ|î¹çIÖ·oßÐ2"pÿý÷wýúõ :ˆ9–€Ïš5+ô+M ˆÀ+¯¼²Â—¤Üvz’vþ QYY™Š!¥=³6Y–NXÆÎ²pˆ?fIîãgK°Ú ÇueDíU89ÅÇ.nGžþÑ/ùñÙdICPâ Áy]èÈÓ‘†Ô©ãqê€$¾êð¡þȃ‘ ¾ÐËž”Øèb°WÜõ<¼nËÂþ•x`XÍ ABDËËcB‚’:ÙÃ.žÁ‘dá¦%éZ®ÛAN"-[¶ )K»Ð1Ó“}7™ÉQ‰°„;ŸðZ‹œÄ¦ØEPÆd úÙ©x4>¾|5ö{\1ÍÌeV+ä)D8*ú¡C‡f»ˆž”’b±dg©!`†€! ˜uؤi“°w#ºó/¼(,ŸV}R aÉêŒX˜!Ù´Yùár{øg‚×^}ÅMþhRX™ÀÌÈ.]»º=ÉÇ2ì=}~cI±¾%ÅmæÌ…ü<ô°Ãxf¶äè7F¹™þÇ>ÆtÎy7ÅÚ%Å2!`†€!`µ ÌÀâÄàÀÄ‘AR†S7F{ô"2ŧ¡Y† ¾Ä£Á³‘Gd£v¤Š¡ø²%͵Ãöô‘|A)jµ–cœs)0yùQgUžK$¢:¬ÈN>h=)mä×gC;]©âg¦ÁÒ~³ ¤¤ýáôn„™‚W,ÁæKûÚâ#‰ä+ÔŽåÛ‡ãH ýØ—ò™ãëeZ0MCkÖdYYYjâ“ ‘ô’’=/ñ#±`§2•55Æ4±d×H–Õëbv*³TM CÀ0 ª"°hᢰd™½¿ô?Šq( ûG胔Dfø¿¥Ì”Ü{Ÿòú84gg¿òh¿Ê¥àH§²ÎáǶY³f†åÝAéÿãǵåË—…g¾©®ë[’ëaÿKf‘êÙŽ1 ÂlIfM¾üâ aNÙAе F9ÿq°Ð,¿=‰!`†€!`µÌó¸3ºUëø7ß§˜ÿ£¯ê#zÊE3 å’Š0­8;+‘À©üÒMù$^eìð¨-6brÑqQ¦);ŸcCLÚ’ÇF©Ï†2äfð™†@£ÑÆÈGfØ1Û’Ù{ÌðÓì–J›µH[H·RHº|ãiÉr.íI©=0ñ¯Yƒì;¹¹„“ÇùRÄ8¸Ç|I])6Æ’œ¥0&¯)çhƨšA,›†€!`UE€ƒnx–XåW|á·bá4kN¨fÆ$aoÈ#>Æ ~ú)÷•vØÎÏðïÕ«w8Å[ö]ºt ³÷ØcÏ béP™')!5õ7” fSNœ0ÁÝqÛ­à<æØãä¢Jiš¾å:¦o§Ÿyf8ˆç®Ûo ³([øç•cú~Ó—ž~LNýæ4qIšv²%åáŸ=2Wû¿ÛôÓÄ0 CÀ0 Ú€“¡¼À}qIà½àËÄ­Á·¡£Lf¦ÁŸ¡o¦yU~a Å$Œ:G0„«#ñ e‡bà[RD>(ëátò©2íÕ¿ŸxØ"øEÔ½(þ9³vìAÉìEö„hƒtƒÄ‚„¸âᙥ؅;ÚBÆ3! µ)TÇroHQžaérx ÷{ZñåbéÃÄš2eJØ’Ãd¦g6Ó/ä7©ŽzâÃqXÍž‹Å„7`7‚'§w³tzo¿W)RlŒšaYŠÏB¶,wŸ4iRXâ­“ÏÁ—“»9ø¦˜€ ^ìKÊØM CÀ0 —^xÞŸÆ=ÀiV ˆ<òЃa/Gʻበ±ì™‹ý•ù[Ÿ+ûÐËqÅrâIýãbÈsÒ÷åW}«‚ŸÜ=01¼þßX©-Ë¿ÿƒUÒê[’o´lÙ*,Ýf¥Ï-ú1UÎÁ')V¡v¹±øaûÊo};õ^ÜŠm©!`†€!`õë¡Ã‚$ñ_T _W&’4æßÄÍ¡'Q›¯Ê’“j+ŸÄQ;Ù©¬ö²EÈGy)Ïÿ"óTµ:-ÊiH™<—S)‚0¤ yt ˆv"&0e ¶Š _ò‡ú#?Ùøµ… ä=Y® ÑÇÌ–z³Ï¡ö3ô㨠„“ªÃ©˜Œ¥S§NáÌ †U(ðEå¼óÎsÇw#GŽ 89ú¤“NÊ>”Ÿp ŽÃuN•æ NÜ.UØ×2–Ž;¦"(iÃ2ï÷ß?¦ìÿXŠcM”òsÚi§<9ì‡×µ¬¬,5¡ÌX!„9Éœ˜ÎÃ}c²éà=6eÚÇ®É]ûvÅIåM×3‹dÛ2vÝÕ½øÂsn¯½÷ 8膿ewOK9™ªaŽQMù‰ÝVÅ'?ìVEÒ¶Ë%>«ËÚ†€!`†€!P“ð]Õ‹x2ò1ÿ&^Žzé±§F½84ê±ãJþ”b‡Pæ'bBvðsŠ#Ž=ü_–“óy|Ðj㳕E¤_嚊ì¸p*‰PG0tblé„:ªÁxUèeêÔiô²‘ŽÂ’Èž8ª'O½úA\é|6äwõ/â` µE çúý’HKf]B€‘Ö´0Ido­Ùqkz…üc¡vU­3¾¥ý2ÇáÄwíë7G~ò'Ë]×¶M6GèÍs/yÒwXŸƒÜŸþû?7K,¨!`lYð9Ù±uÍÿmÎEÃ_>ÿìÓðc%3!'kË66¹}µ²!`†€!`†@2K-+&yŽã3fL%C=ãÁÇè `¶ô«pOñÆóüW!È…Župr”%Ô£S;RD)\[ùa%AlÉц ^Nþ°ÅiìSvŠå«ƒÐ'.êóJšŸ q‚sR‘ƒ8Ä1z’ J–ž6€tòA€`Ýy|a'Ú’G‡Weù$O{KÚ໡¿b[_ÜüÑÇž”ìïHš9©R瘇=3Iu“V2¬¦¢‰FÌ-œžBc¬&|‰Íu2ybeem!'‹tÓª CÀ06!ÊÊ—‰!`†€!`†À¶‡@†ÒÄ<ñ^¤ââÄÑÁ‡Á•i x7lE êToʲ—Oéb> âß𧘴‰cÑ‘N툡º`ô_‚Ríä Ç" ÑÑ1t ž°¥¬Áb'{Ÿ !IYR[tЇÚÇ}¨uä$61 ÚƒÀ #_swßÿ [¸x±;ᘣ*ulü¤Üí»ÏM™þ±Û¹Mwæ©ýÝÙ§3³¾\ÆŸèîøûýnš_~ÙÞ~uùEç¹¾Gîh÷çÛÿêfΚãvÛµ½»úÒ‹ÂÒqµ³Ô0 CÀ0 CÀ0 CÀ(\þ  ž  !¥ ÿßwFžÿ†¾dø4ÚëBO{Å%¥Œ[‰üã=6¤jO=> Š‚2ÂŽqH›¸ÓÒ+p›eJ•ÇQ,ÚI'Ÿ¹íUBRä%e„”é¬ì]I?"5É3ë2Ó`}ÖÄ0 Š<öÔpàÄïÿëçnÏÎeîâóÎv}O=;kô࿞p°tÛ~ãzïßÓqò{ÿs.v÷?ü/wž'4~üÉPÿûÿú™Û§[×ÐŽm%¾X°Ð-Zü¥;åÄãÜ¥œ“õgCÀ0 CÀ0 CÀ0 C T"‚R<.Ä…‘JGÿFY“ýÄ›yUàÐHµ! ¼\š&’Gù¤,¡=¼ :Úˆ—“­üøª q)W5®T)Ñç˲¥ètaJžN颬Á©ŠÉIÙR=B]”‹4öƒ}Ü.oü|ÐàÜÄ0¶m>ž9Óíà÷ƒ…œDrSš:}F8ݾûÞ{…úúõ뻽»uq_.YHÈ©ÏíENbÄž·mZ·r;ïÔÆ=3â÷íntÞ{Ñmð'°™†€!`†€!`†€!`¥"­ÎËyŸ1o¶Éù·œøâóªø?òyEÏkà+4(9Ûðpê¹°çB(#¤tH³(iƒ.&#E8Æ6´Ø”=©ê}6´O[CÀ0* °jÕj×°!±“eÍš5®Aƒú®¾¿$MvØ!d׬]ëÖú«~ýø#©Ü ’òö?þ6ìEù‘_þß¼Ùýîo• K CÀ0 CÀ0 CÀ0 ÔdV§á¿67ÿ¦øðyˆø¼¢3vb²±¼iåÿq*¨©H[#Ô‹”Äž:]Ø“§^mÕ1‘¾*{‘|¢£ ßþ«ß–x£‰!°å 0kùd÷·)¿rßsŒ;gd—p‘GG]M 3™ ¹|ÅŠàö«u|$}#»vhï 1gÌœ•U²o%3-ÛÛÍ×Óþó/dë•iß®­ûÍ/~âž~ä~×¹Ónnäë£Ue©!`†€!`†€!`†€!&Ád¤Öòo¾…ø¿˜ëÓX*¤ÙVÐV,àD„"5q2’2—¦!©žoúqˆE©òä!"Iñ§6تL^~UïU¡^uÅâcobµuÖº»&ÿÌýۛǹÁsþîæ¬˜êV¯_.òè¨ÃÛšÃûäØâ¹Ó½ôêëî?~ùßÜž“9­›#^éþë÷ÿë>ûü 7 ¿°ôû´“Nö¿þÓ-îí÷ƆÃs¾ÿ“_¸¥Ë—»ÿ¹åÿÜØÇ»¥Ë–{›í\ûv»TðmCÀ0 CÀ0 CÀ0 C ìê%-ÿ%ÎTùMÉ¿)fŸ|A©¼6±²9È)ä y1¶šÙHJê}6‡¤ÙÈ—ÊÔKâ"%I|’ç¢/¤´ÕT'õ;ê©#ø>kbµÇ_½Ä}°xTÁn~íßÚCçþÓ—Óܯö{ÐÕ¯£ß% 6Ë[y–' ÇOšì^xù÷ê¨Ñî² Ïu«ý²nÉÁ½p?ûá÷ÝÝÿø§ûÕïÿä5Ú>œÖýÝ«/&‡y‚óû×~ËÝóÏGÜõ?þ¹kÚ¤‰»øÜ³Üª•«Ü¬9sÝwxS8„§s§Žî§?¸^n-5 CÀ0 CÀ0 CÀ0R#P·®(²,'L<¾È#ðbÑÌäáÍbÍC=N9€{Dœ|šø9|bƒ[ì¸$âëT– eñ}±½ì*¤êDež‚lsA•jP¸R;Rå¥g´“=ylHBY¶Ôçæ¥SJ[6kï7LÁÄ0JG`ò'Ë]×¶MJoXb fEB<–"'w¸Ì]Ûõ7¥4Ék»jõê°l»~½ü¿Ù¬X¹Ò5nÔÈåÛ6bÉÒe®iÓ&®Îv| • ËÃ7|½!¤#¥†€!°u!ÀçdÇÖ¶®AÙh CÀ0 CÀ06 K-pÍ›7ß+ùn9f̘Jqô“ƒqúôéêWømÉZ´hqº/Ìñ"œˆGÊèuQÛùbÈF„z]ØñE–:täUç½:ø”.©Œ¡=¢TÜ_¹6á UY•È@8–SòÔiðt+.cw†z m$º¸N@“Â`‹T%~yKûß0j%ì+9lî%÷65µ'e£í·w…ÈI:ÇißúC‘ÔÙ›5­@NbÃŒKÚ™†€!`†€!`†€!`UE ³Ä;æÛj#ÿVˆÿ£ï% A“Œ±3œC  —HE|Æ3E<ªŽj+ÿJ}U A±ÁW¬'/I_ö–†@-Dà¹ùø7yüÖN×IÚÐÖÄ0 CÀ0 CÀ0 CÀØšÈ,ñ®íü[!þ¯èË“† Ë) (“‡„«¯‹”}npé‰ç±©þPÏ…ˆL„˜j[•øÁ‰ýgµ ÷½RåU§m•ƒZCCÀ0 CÀ0 CÀ0 C`"­æÛø7q…âð@ ¯ ˆ,d”K Ò†`"É+8~°—Nztì Iª™“ò+–{£Ã/„¥òÄãR»ªÄ÷ÍM C 6"ðÅêyUîVuÚV9¨54 CÀ0 CÀ0 CÀ06=âÚÞL:qkè6'ÿF|HTqz¤\py¥¨A¦µÈHŠ"É<”/¥ê QO;òZÞ¤$z.ì±£^À’GT¯¼ôiâöŸ!`†€!`†€!`†€!`†€!°¥!À9©Íü[.ÿïŸGJ¿ ŠÈÄBF"c›˜T)QAE4Æd"¶YT}žøÔ£—ÏùQ|µSlÅ+ÃÌL C ¶"ÐfûöUîZuÚV9¨54 CÀ0 CÀ0 CÀ06!‚² ÿå»#^MvâÑèéFçߊÄ·G_%^»žhà• ˆÄ„ ¶è€°ƒTáˆ^meGtÄÕ’ntz]ÒÉŸüãG€+¾bàûx*é†h>õ&†€!PËØ¿åQnΊ©UêmM CÀØÚàók¿rËV}åÖ~Åc‰!`†€!`†@M!РÞv®i£zn‡õÜvEi´šŠZšŸ A)þ‹^ŠSÃ\XÌÙ‘G°‡#[ç/tâÚÐë¡R:|à—2uâßÈçòoø’=v”¹È«ì³Á¶E%-AI :EŠÐ9@`ÊE:ǺwòLÜY8v²GG½Fâã‹KelˆÏX_uõŒ ô¨˜µÚ]à†Ì¹Ç¿¡yÛ¦—íüÇm7§Ì;×5kÖ,\k×®u”;uêäêÔá#jóÊ‚ 3ÈwÚi§ÄŽ,^¼Ø­^½ÚµmÛ6±¾òË/¿t«V­ªRÛB~­Î0’øÒ“Ôuem&˜Ö0 CÀ0 C Z,ñÏ[¡~ùòåîå—_v³gÏ˹ÑwÜq~cg>ý_®õëÝÈ‘#ݤI“?Öxàá •9ÿM™2%ôyåÊ•®I“&®oß¾n×]w V_|ñ…{î¹çB?Z´höÜÌinECÀ0 CÀ0 CÀ0 „@4ùÞK˹ŷ)ªÊâí¤‡S»ØFõâßh‡`‹eñpâ©C§´ü˧Wx‘o¥åÚoøDôyEòø “r$b‘6tŠ2d¡|::I‡Õ^ƒòªP‡md‹Ú© 1å—uîÜÙ~úé$}ê©§ÔÔRCÀ0 CÀ0 CÀ0 Œ@f œü›ø0ø/‰Cêáʸl±׆NõâØh#ΔK|yÅÃ_âåÈ££ìäß«‚ÿØ”¢Qkl¹Ôiª\~¨ƒP¤cä¹X£Ž`ƒžzå³ÙAkðèh‡È¯ò%Ç·rŽöŸ!`@àÃ?t¯½öZöbÆbu„™Žp€ã „_»Þÿ}×»wï n?þøãp6'sêöÂ… ìÅiÓ¦eí #ËÊÊ\ýúõƒOˆTlfwr@³4!>™ñÈLL m!Ké> /cß²£o“ø¬¥¿œNq9}útÇLÍ£Ž:*§DM CÀ0 CÀ0 CÀ06 ™”¹¼Y­âß<–H>þ¯¼6Ïÿš˜§:¨Å|ŠŒD)‘àÒ“Ò ˜QˆHòèâz‘–Ô3ãQD¥ÚxUÐaG½ÚêE EG=)ñE† 1³¡¼ˆZîèmM CÀ¨„À‰'ž˜]Î\©²Š ˆÃ1cƸ3f„Ù™|uíÚµ‚7fcrD¥„Ï+ˆÅ$a6#õÌäD +™¥ ‰ÈlKˆK–`çÈEf…æ ý€Äd¹_³fÍr-[¶ ³?Ug©!`†€!`†€!`†À¦C ³uœ_Å‘1!n > ãƒC°a¿¦¼83ù‰ù8t\ø#BQ{Õc#NNñ±‹Û‘§ôK~|6YÒ”8CpF^:òt$@„!uêx܆z ‰¯:|¨?òÁ`dƒ/ô²'%6ºìw½‘“ CÀ¨v,îŠ@²L›Y”ìÙ«W¯J?–4oÞ<ì+Y•Ï*–Z³üøãü<ýôÓ»:oÞ<·ß~ûU²¡ÌÐd–d®°ÿäš5krÕV6 CÀ0 CÀ0 CÀØDd&©ˆƒGIHNMÜ|yìыȟ†Ž<‚ ¾Ä£Á³‘Gd£v¤Š¡ø²%͵Ãöô‘|A)jµ–cœs)0yùQgUžK$¢:¬ÈN>h=)mä×gC;]©âg¦ÁÒÞÄ0 ’à€Yµj•[»v­cÈR„Ci˜ÈžŽÚ‹2nÏ,EˆÁW_}5»¯%³!çÌ™›åÍcÇ2oN gdîÒt–{³<›™–o½õV‡؉BZ2Sqë{ï½ì;vìR–Á#–ãÇ›[Þ0 CÀ0 CÀ0 C`#"°ÄN,æÇ6;ÿæûóôM}DO¹ hÆb!£\R¦çbgÉ#FHå—hÊ'yô*c‡oDm±“‹Ž‹2íHø bÒ–<6J}6”!7ƒÏªÌJ‰‰!`‡z¨8p »ýöÛ È ÃRÈ9–H·mÛÖí´ÓNa)w.¢LÓç`¾¹å–[Â,ÊV­Z…‘¹¶Iåƒ>Ø1k’%ØÌ¤d¿I-ÿÆžq˜eÉÒo–’0 q 8{S²Ìƒr˜1Ê¿=÷Ü3Ìe&h¿~ýÜ‹/¾.l‰ ¡ib†€!`†€!`†€!°ñ`uŸ¸/. ¼|™¸5ø6t”iÀÌIø³˜7S?Øq¡CÄǑҞ”öøï–ö¤ø¤ü©O^•_Ô‰ü£ Ø”€êˆò¤±]@ÑëâXäU¦½NàöÙìlK@¦ŽT>|6”IiO|aC^¶øl↓1’Ò#abTÉŸ,w]Û6©BË­« Kœ!øJö{¼ë®»ÂiØ…ö†Ä/³!™QYŠ@(BJê6iý2Vls£o,UÏì}RJÍÖتàs²cëFuŒ³¬²Ï⊰97 CÀ0 CÀ¹Mñ\·dÑÇ[ðT\œ[+â°øÖ§OŸPÍÊ8?1å4_˜ã/87‘‡e„ºÜ nz.qväáÐhN©ÏfýËVŸüÒŸ:•±— Eö±®B‚¯˜(˜ì(C*0eò\Du¤°DttŒvú&®ŽRÖÀÐa«øèÅ—?ìÔùÉÆçÕ‹ZÛ†€!`”ˆ@!ò¯+–„³Œ»9‰ªiˆÇ46G¾±ò9ZÕ>Ê·¥†€!`†€!`†€!`¥#·åE<ù˜/G½ô؈S¯F=vš(ŽN¶¾*Ôc#>Î Q|ùA·GòI;|Ä}óÅŠ’† T Iè¤ÄƆ ÔˆÎŃaŠ)e Âgƒ¨³èùÒ(ë’oMÅ§Úøl9 ¹³€¨01 C`S ÀÒî=zlŠPÃ0 CÀ0 CÀ0 C`+E 3ñNü)ü—x5Ê\âßDBRFrí(‹k£".¼|“¿G½âˆ#U=¶øŒíŠƒ]¢¤!(å(î ÎD⃼:FO"@¨SgÙ[’0‡&†€!`†€!`†€!`†€!`†À–‚@† çV+ù7%&·G_áïÄÿ¡+(iJl¼Ę”Ĺ| ÇNu¤$BÇè¬ì,íЉxkëUYâR>IKŠoK¼ÑÄ0 CÀ0 CÀ0 CÀ0 C` F@|Z­äß<®1ÿ§þŠÿ+½ØÍBFrÛ™t zR.D:ôêzHH•å ÿ".i§A(O=í¤§½Úúlˆ…{ìÔ|ng‡äxL CÀ0 CÀ0 CÀ0 CÀ0¶H¢CrÄyÁÁƒÅœ¼zDüöM‡†½|ÿ&ÎM)í—T±ÑÓA/¾26âï(«=ùD‰'x%6\U`R Œ¼ðÙ P§± „¡O0å¸y‰ÚPFOl&;R­™'Ÿ7~f¬711 CÀ0 CÀ0 CÀ0 CÀ0¶,"‚2/ÿåG?ƵYø·”ñ½Y²ÐébË ©(’0 Ñs!¤è$”±¹I^„&:D>O~È#Ò)–Rêðþ•´á’-þðëë°¥-óö¨˜†€!`†€!`†€!`†€!`lqdJú ç%>2œ—¸6ñoâʰI²W;êñnâÿä'æÚ°#¾b‰Ã—Úùl–³Ã‚ï¼"§y |J¸#¾˜%ÉSÏ…½‚RFHéQù‹¡/ècÚˆØÄG\ï‹©ãckb†€!`†€!`†€!`†€!`lQdVÉ!ðc\µ‘ÿ'R2æÿè{^ICPâT àH¤¢˜QtÔ‹”Äž:]Ø“§^mI‘äY²0 m45U:ùHß–x 6K CÀ0 CÀ¨=¬_¿Þ-_¾|³vè£É“ݨ7F»™3geû±jÕj÷Á‡ãݺuz4ÍV%fJµOtRÊ•«V…±0°E’t5j“¸Xüå—aû F› 0oÞ|wÉåW…H¼÷–kÒ¤‰KÒm‚®ÔHˆ>ï®øÖ·]Ó¦Mݸwß,èó¶ÛïtÞᮼâ2÷ó›~\Ð6MeÒ}‘¦Ù†€!` Ìâ¼à¿ø%5—ÿW'Ñä?òký%þ<‚ñoŠ/85ù¤ž IâÿÐcƒäƯW£KCPÒ9œsÉ)d€ê¨:®z_•íyD6ò¥2uÒáS h`ø$Ï%Ðh+`h+_Ô“'ùé³&†€!`†€!`I¬|‡ûjÞðJU_¯œç¾âòuõÚ÷s º~·’M)Š—^é~üÓŸ»… e›Õ«WÏ}úÙgázmÔîÏü½;õ”“³õ›#ó¥'Q‘Å‹—Tÿë®woŒ~ÓûN}>û FV0 CÀ0 C Ô­+Š,p^pgpep_âÉðNÃ!oshèÅ¿éjtâôàÓTOlðˆÃV"¾NeÙPßÛË®Bš† ¤Ž€ Côt˜4,{Dª1PêñƒŸòA*äåCÀÊ–zú®˜Š¡ø*;[âíQ21 CÀ0 C 5cåÖ/z/¡¦¢ ’òëUŸ»†ûýªbEÊÒâŋݿÿèÇnÙ²e®aÆa–ÛñÇëZ·jå¦M›fUŽ~óM×çàƒSzÜxf¹åÝÀ§¹K.¼ B 'U(«Ï^õ–†€!`†€!P]2‡äÀy‰ïR^Ä#!TG*þ >Melį‘G¿s~â먗þ [88éð¨LD¶äOýA—(iJ:‹s]r*‚0¨©Hç¸4HŸÍ‰ä±E¨§-SC‘‘äqHEDúl6Ž .Î ,lÉ+eCÀ0 CÀ0 ÌœŒÉɺ­z‡™’ušî,6,›fO®_X>c[ÚTe&%Kp!'Ù?éŸÿ¸Çõ:à€ìëХ˞îÎÛÿâ–,YêvܱYV¿¹2{uëZi™ðÊ•+Ý'Ÿ|ê7n\©[Iö•ŒLQ¾tÙ¤‚‚Y¥!`†À6Ž@f‰·ø6ø..83ñoâì@J¼yþm¿ÄÆ|™Düˆc#?ñr>|0“Rvè_íÐá[ñÅÉ_q°I“x±-"@ Œ@ç¨Ã?õeùRçŠô¤í°!UÏV ÓÆ§‰!`†€!`†@°çd´¬»þî—º†=ÿÓÕm}Û®aËp‘GG„™”´-E8læ¡Gþš 8íÔ ädì'—œÜ°aƒ»ç¾ûÝ™çœïºï ;±ÿ©îWÿï7Öycôw܉'ÿ¯û%â§yŽÛ»Ç®ÿ©g8–”DzbÅ ÷7ýÌ|Ø‘nÿû„åæI‡ôü÷o~|Þu÷ßCó§ vßþÎu!¿fÍwþE—†k䫯]®=Ê4}ÇîÚïþ›;÷‚‹ÝüùŸ„±õ:ø0Çuãêèo,ƒ‡<ã.¼är·_ïƒCÿ¿ÿƒ{z–"ï½?6Œ œ>ÿâ‹ M_}íuwÂI§ø±~¯‚^Æ&Î:×í»_owòig¸þùËÌèf¥ŒgÁ‚…î'?û…;¾ß)®ë>=݉'Ÿö‘T¼85k¶»æ»×l°{rÐÓquÞüT?;÷;×}ßzÄ1î CpßùÞõnÎܹìÓÞYÁ0 CÀØÄd–x×vþ­ÿW1ˆÄb(°¥,Q¥ɸž|,jG[òyDd¤£˜²Å¿ú ?èDh’—OÕ{•‰!`†€!`†@.ˆ#©ÛòW¿ãÙ*VJ©Û°x|v¶%m”phÎŒ3³'bŸ~Ú)•üçS\ãÉ»_z9Tï¼ÓNþP™ âñé§w-š7÷$ÞÊpØÎŸo¾Õ-õ344¨bq 3¤â³Ï<íöÜc÷pªõ_æÆO˜èxÀïС½ƒxüê«xUTyO>ûüóàsÁÂ…A1aâD÷λåËà™½ðáøñA¿xÑâæÚ£LÓwìæÎ›ç&NúÈ 8û\a·Ã;b’%æ tëÍÂÌýú·¿w÷Þÿ@È7ßqG÷å’%nÈ3Cý¬ÎOÜcü3èÓü·_Ïþ Ÿ5a|O |Ê}÷Úog›áÚôÝyç&ß à öÌtÜgï½Üì9s©ÊA<:`(íxfÏžãÎ8û¼@°6hÐÀ5¨_ßM:Í]÷ýwîx;ìÐC²ýZåO'Ÿ•+W¹zõê»ýÇMá Ž;6k—›yó­·ÝUW_N2oÙ²¥kÓºµñü î1oºÃ‡Ã™x=ÓÞ¹þ­l†€!`lJ¢•ð_pqˆx°òÒ7ÿ‹ƒS“mÌ¿É;.ø6ø5q|”ÅÇ‘c’×”âxUÁWìƒJŨ`ÔÁX—›*O0:‚ç’(h¬GWß_¤¹V[H=~!,•§\jGž6¥Ä÷æµ_æÏŸïAŸ¿Ù;úé§Ÿúá˺“:S¬>©é CÀ0 C ö ÀiÝ’ú’ )Õ“Æ6qÛØ&_~Ê´iÙªÎeeÙ|¡ÌÐaÃ9ɉك>æF¿>Òyý•@6B‚Ýú—oN×ÆÏ’¥KÝoüûΛs»vèf1>öøÀfÐà!œädëgžè^~~¸{õ¥çÝn»íZ¨¡îç?ý‰ûû_ïy–xO÷^¸ÎpZbÛRûŽȳF õýãnüá ÁïÐg‡‡eñúst8•µ„õÆè°?è™g Èê•aL“-Z´Ø ~ê ÷âˆgɱÉkK±ñ@w\_÷‹ŸÝä8qü­1¯»?.¸¸çÞûcWD¦Ï¼¶\G~X¨¿ë¯«` ùÿ~ÈÉk®¾Êß;#=Y=Èýðß¿0ÕýSû"ŽgyCÀ0 C`!ól„„7“NܺÍÉ¿“ÞŽ” .¯ 5È´HQ"y‚ ‡ò¥T$êiGòQö’è¹°GO½€%¨^yéÓĶ„ÿØ›‰«¦eÁ‚îw¿û]¸tÒd¾<ÐÝwß}î…^H4)VŸØ(G9xðàlÔ/Ò‡~8Dzô"ø}æO51 CÀ0 ü|½êÓle¦{fóù2±MÜ6Ÿ}¬_½ju¶È9iä©§³Þ½{¹Ý÷ ùV­ZºÓN-Ÿ9È?KÄÒ¢Es÷­+¯põýL¼víÚºcŽ>*TÏš=+¤CžRfpvíÒ%äwÙeg·W·n!_“ÿ•Úwb_xþyò–ÙçŸwnèKªgÏ™ò‡ÒÇÝúç?:öëD8`·ÇîC~Úôé!Mûß¹gŸåg"Ös3gÎro¿ónh6dè°0Ë´ÿI'†™©¹¾´¤úܳÏÌ’º¼ýN<>´;îƒ MŠ¢ô~ûkwÅe—øY¯ Dô)'÷>˜ÅKÓ¦M=ùüCרÑöáõ½úª+B51YrŸ$'MrS¦LuÛo¿½»æÛß ãÅîâÌÁGš»)~šÎ0 CÀH‹@´¥Šø4šÖ6þ-—ÿ£ðy¤ô» ÀjH@†²%—ˆBôqPÚˆh$ÕìÈ\_tR€ÊÞ«B½üþY½zµ;àÀo–‹«^ûPnÊûB±-5 CÀ0ª‚@† ÿïE® ÿ"Oª:Ÿ <)"bŒzòjûµÏ‹ÏÃ'e.ñl>êñ‘½bQ‡Ný~$ò­r¥4 A‰C:O0*:òêvtNF¯¶²£ :⊴D‡ ×%üá“:üG}loÅÛ­Ó§Í6)’mÛ¶ {2Õ&‚²GþWøÝjô5a¶(³'LjÉŸ,¯9gæÉ0 C V °]£]Ü×+ç…¾lX6ÕÕi^>K1_簑ж‰—u³Œ¸w¯Š6×¬Ëø‡LÅåU9ä]!§: g{? ocKM÷þ²÷æÏ~ñËì^˜Ì dUKUå¢ Î%$å¥]öÕdf)33“¤nòÇo–µwëÚµ’ÉÑGYIWLÁÉî‚ÙÌó:ûo¦‘xâÁ†¯ùºQYêÖ-ÿj~õUWV2hÖ¬iÐmÊû¢R'La†€!`”€@† ÿç%N /<ðG½ø0ŸÍòoë|žzqgâÞ°‘´¥óoqLåñ%{ÅËŸ‡ˆT-i JÑR„ Êbj Lv\0E¤è”:/¢QíÑQ/[ê_\*cC|Æ¢øª«·­”³ýŒ¥~_¦ý÷ß?ìá3jÔ¨°¡:„¥d’_ÃÌCÒöÞ{o©³i±z²Ìæå—_vÓýR#64ïâtûõë–îÈ&MJÛçŸÞÍð³'X‚Ô³gÏ0ë“×2_ VŸzê)Çþ˜´a™ú{ìú0pà@×»wowÐA…ðÆ óËšfºk®¹&¶÷Þ{¯;æ˜cÜþ4Ë÷Þ{Ï{챎Ùsý¯ùŒçs¿Y>{.õíÛ×ÿJß)Ͷ*›®m›lUã±Á†€!P“l©?âÔmÑÝ}•!(×Í|Â5ܯ0A‰„¶¥C?ìPÇ)Û¿÷~¿„ùÇ¡7±ðнlÙrO@–Geeܧ~ËN¾ì’‹²¦ïû2ÂRñvþYfÒ¤ÉÙºB™¶»ìf\ò7}cKYYº¾§íÏ>—]ñ­€Ç÷¾s»À/gûE—^áFû_ª"‡ô98,)g¶çO=ñ‰\tAòìIê:w.s3ü³Óî;{Âï TÕNÿ¾ùÖÛ‚¿ÛÿrK˜ÅÉ’mNH/&s2³5yöëо}¢yYYYÐ3óÊË/ Kç“ 7å}‘ßt†€!`†@Z¢èà¿ø/¸/Êpfpb‘ðeuäáîbþͳö´•zÊplø#Eð¡”É£“OlŸXq|ùBG®¼¢†y |68Õà±UP:A½f-Ƥ%ö1ت^À†<w˜NËâCƒIŸå1Û²°¼éæ÷XâB¤#Ï¡<ƒ ˈ<ðÀ@Ò¡—«—)$'34;û‡×~szòo¼ñFlR!Ï~‘쉩K•>ú¨?¡qªÛgŸ}\Wÿ+ý믿îÞ}·|¤|1س‰y½ùÒÒÞ?°ò…ˆÓ9YòïQ´råÊ ãËõø…¼Å¿à3ó¡‡ Dë!‡ö0zì±ÇB_ÕOK CÀ0 -z»ôÍv}ý¢÷ܺYßÙŠL†:l$q[銥œò̃5„ÑgçF¾òª_¾\¾7%Ä×µßû7w\¿þŽ¡‘Ã+_–Ëò_Írãoö˾rÈÁ•´…O·nå³þ˜1¨“»Yæ;{'ç ÿ1cádíø™"Á´ÆûþÑä)œ„¼ýÁ ×r’Ù“‹2§ˆ'õ!îÂóË÷ºädsö€pú©y›yÄa¡î¡GõÏŽŸdí>÷?î¾ðâËÙrÚÌÈ‘å¯ã¾œØÍ>”øJ’uëÖUXÎÿð£ÿ fÌÄ¥]’tÙsÇ£,ñ¿ã®»+Ì6}yä+OÚU÷¾HŠm:CÀ0 C`c !(áÍj-ÿæû‡ˆó#¿§º`ôK#bùC$!:ÚÞ*)õŠbJ)‹PdF%e.|á;Êvø¤‘;®xí®Êò½â“~kã JH?ȹ\á¡™<¹òl6o×®]n“‚eÀ?úè#צM›@Þa i7qâÄ0KüwÞ >Î:ë,·“ŸÍ÷§?ý)ë·X}ÖÐgŽ>úè0Ó‘.ùqóÍ7‡Yè“„ÃrbùÑ~äfù½©˜yä‘Gf÷Ê„À;vl˜Y(3ß~ûí0ÖN8!¸ž—sªd/Îóz\}õÕ~_¬ÖAýÌ3ϲ\ZµjbßrË-Ü=üðÃ㦖7 CÀ0¶8êìØÍÕkßÏ}5oxèûºé¸ _N º:M÷º ˦…úõ ËŸPÒ†¶¥Ê^ž üå/~ê~ó»?rèÊ«¯ ? ò£"?"»íº«[¾¢|[‘+/¿Ì=öÄ“þ¹`¶ëêî {;ƒùàÃñ”úéM?.© —_z‰øä 7aâ$wÊ€³ÂìK“ÆÑ~={¸æ;îè¾dŸëK.¤éÿwk˜˜Û¾¦ûÑÆ3K¡ñËÿr»ï¾»òÌЀGnìRÊgy†ûãŸo „+‡qÂy>áЛGÿõxØÿòȾÇû“´u 4t¯¾>*ÍÏ<ý¤Û{¯ô÷3@‘>üˆÚÿÚÍ÷Ï~øOˆìSýkv„'Iu°xÜpýuIæAqùó›~â®ûþ¿‡™š<ú˜Æ=8¼þìãy¼?Aü¯wüŸ«î}‘·Va†€!`Ô0™Xá¿Ä©ÁÁÇ!¹¼:ø3ÙS†COGJ84ê$ä¹àåbN®N34ÑÓøR|ôÒaO9Žë‹ùED`~‹r‡±‚«³>èˆô õèHÑaƒ¨ãÔ10Dm±ÁVB/))>DÙ¶*Ó¦M ;v „($y¾°|aY3_ '‘ +òüW¬>kè3ìýØÈÿ˲põ¥#¶Sž,»ÖÅìEÀ ‰úÀ„ ‹/ÍJ¡XÅR–ƒ‹œÄ–~€ÅСCC˜ÕɽTìôbq¬Þ0 CÀ¨-4èú]W·å7ûABD®ùà×nÕ¨ËÃE>&'±¥MUå¿×áà'䤳™¸§ŸíÆl¾ÁO=‘=a›g“§žø—;Û“hË–/sœ"=ÙK,Mâít‚uÚ¾@žÝvËŸýŽ-ÃéΜâü­+/wúŸßæ]þûfû˜ÿ¸ñaIñûcDzPKc;ò5Ýw–Ãÿ̲Ìâ|Ømüß›„é­þSª¾çöOåwl–=‘ûÂË»±çí_?^§&Mvp¯¼úº{î…Ý®»vpþãÿ”DNâïß®û®ë¾ï¾püõïþǯbíîýÛ]nÿýzR]A 9]ü©AƒÃ©ãŠsÏÝw¹>TÁ.·@›ûï¹;ÜSÌΤýܹóûhþ¿_ýg0¯î}‘Óʆ€!`†ÀÆB 3ù>LœZ­ãß|ßè_>þOa^ˆhœF°ã‚íãRQ ølÐ«Ž”@>JG]LJR¦^v"#I•§ŽøÔ)ÞÌ „èdÖãGÜ —o¼1Œ™‘ÂCñî¹çž@ŠüñR…“åãÛŠ°·šíA¹­¼Ú6NCÀ¨ |Nvlݨ*MS·™µ`ÕFý,^;ùŽìLÊ|bæduÈÉ$¿‹ýsK‹!ôŠ 'v7o¾cµ9ÅX°`aðųE©Â•,­Îww’¿šê;Ké;±ó-mNŠŸOÇþ•ìcÙ£û¾nÐÀÇò™%ê¿øbÆkèXáSY´h(Sáɉ߬6b&k©Â,Lf¿¶ñÏq­IRû"ÉŸé CÀ0¶<6ÅsÝ’E üsHóp@¤ã˜1c*¥•À›åÑÇ1(ÃÓÉÏ&Kš'³\G”% Ì€ä‹££"Õi@v´‘­Ï†çv^~i‹­ž*°‹ãkù·üújW—= sgR±µ ÍpX Kº=ôÐ Ã}íµ×üì)á!Kƒa_rO„,V/Ǽq8¤†} ¯ºêªà‹ƒj Í TÛ8¥=ÂkÆá8±T%†Nô.µ¬l¤¿‹ßTçwŽ»ayCÀ0 C`«Bâ‘}%¿úô%·~ñ‡îëUŸ†ñqZ7âPW•eÝÅ@jáÎÓJ)„`1Ÿ­[·*f’·ž/ ¥ö¥Tû|Áy¦iÛ¶´ÔóùBÿ׿ݪ9Ñ»TiÓ¦|KœRÛåÚë¹/WŸT†Ì檊ðƒù.þ*$Õ¹/ ùµ:CÀ0 C &È–âßćÁIàÊàÈàçÄÁQ'þM\:Õ‹c£87R.ñmä{bŠ—#Žzt²“¯ :ücWPDþ4ÊTÊ£N9·½êIEDªÃ´‘Ðqôè4Hòø_Ÿ yÊÔáv¼ìT'¶ErÒã–XCÎîë—ÏôèÑUV.\Xzö§dióœ9s܈#‰×ï¿ÿ~ÖŽL±zClBŠò¦áWjÈQša?ÊR„xüš@?x‡ d¹7äiß¾}‹ÆàA—eéÄÞ›´øåàÄ¡oÅ„S¿Y¦ÎŒPþÍ ßÌÄä CÀ0 C`kB²Aö–Üš0Ø–ÆÂ^œ¯¾öz Nñ©ƒL%qò5 ¢T è(“âàÊS™è³ÁNàhÆ#zÚ¨/´¥Ì@¸äßgCž4uüÌ‹H›mJ è æX+Ý»ûÙ~Yˬ{õêNʆä`ö¨Œ—0«—oüqÀ³1ï¸ã÷á‡Vš)ÛB)~XZ¹ÈLONÎþä“O\YYYès±, ç5g,°,âÀfˆ²´œ}9S1Ì<í´Ó!:dÈ@Tòë{¼Oe1Vo†€!`†@mD€½§NúÐzå%¿xá™…µ±ÿÖ'CÀ0 C`[C ³5ø1qnµŠó¯ ä$}CàíD\ ÀbqH.ÙKG[ò"±ÑìE‘‹Ô“'Eä[uR¶b_eC*mÅÄÆ>©W\Ùc‹¾'«¶Ù=(!­°¯³MóíËS¬^q°cæfšý¤Ô&_Ê)UÌÌÌõU,ñ¹2oààÒ’™âSªÐ_±¿R}l©ö¶å–úÊY¿ C`S!°)ö*ÚØ{Pn*¬,Ž!`†€!`µMñ\WÕ=(W¬XÁ Ñ¿Yþ‚üƒ‡C4qP)ü—&bÇ_†Nm)ËNz¸6å}6+èÚGmå“:l(ëRYý‘ol/­ÄÎ@P„R°X ˆÖÜ¥ýÜU!'é3'M CÀ0 CÀ0 CÀ0 CÀØÀmyÿ%ò¸´`äËZò-.ŽÉ~")åë²õ±Ð{ þµULRéh£~ÐVzú(>ÏgóK‚2¸¥Î* ÔaRù¥v•väÑ«Œ¾µÅF ¡ã¢L;RÚMüø… |/”·rÒ÷×µk׎ÄÄ0 CÀ0 CÀ0 CÀ0 CÀdV»Â}qIà½àËÄ­iv#ex1ˆJ‡âÍh#?Øqþ̧âãHcþ¿jŸ_þhGŸâ ñÍ…® Ð ˜`£‹`äå˜#ê :E籡 Xt[ÊŠ)ÐW…6ØÊ6´£M ŽÚŸa™}sCÀ0 CÀ0 CÀ0 CÀ0 C`ËB€íï2"¾Œ”«Öðo¾/pt1ÿGùÿì¸U•þB!$@:=tÐÐ¥ˆR†®(££Ãè(GÇaÆÑGÁ‚T)Ò‘^H „ÐbH i„„^ÿû·ï}_NN»©'ð®çùîÞ{íµ×Zû½É½ßyï.âðZZU¾Š,¬ÒÕ8âÔPd"~оT§ÄF,jª–ìðCò˜–º|¡£^Œ®MñMP&Ä,FÀ#`Œ€0FÀ#`Œ€0k$e[¼Å¹1Õ)W+ÿV#>\ž¦jeÑD*÷.©e¢z µÄS¤!%ýbLió`§Dh³Ä”’زQ"ß´£ŽoÞpüò³qd1FÀ#`Œ€0FÀ#`Œ€0k­Ç•‰3k:þ-åVÎÿi±"¹Šç« w#eÑöz¤×n£#!ê‹Ø* Jt²Ç;Ùj×â ÑV¦95-FÀ#`Œ€0FÀ#`Œ€0F`ÍA pIŽ8/ø/x°"go†?†½D Å¡a/ÔÅ¿‰sSÉxÅ¥TlôŒGÐ‹ï£ø;ÚO½¢W4HJlxªÀ”šuªK$¡¤±É„a*™0íâ8ꡞØMv”l÷Ö«Æ79™P²#`Œ€0FÀ#`Œ€0FÀ¬‘ʪüWš\ÏjáߌŸÌ*K#¥ØQÙ:=x¦‰¨‡6 0Fã 4!'éCdK¿t“Q,Ê¢ì‹ãhË%íßgP&$,FÀ¬PÞO+³GŽz-&N~s…ú­äláÂE1å­©•ºÚ¤›0irøça› ³±0FÀ#`Œ€0F )h%(É¥*ÿ•úмÙ*çßÊâ‹Ï#gñuÔ«Š’¯j:4)9/Ž„~ìyÚ% Áâ"ŒA'BÇ¢ c´R{Æ©?UóøFâck1FÀ¬æÏŸg~ùã¢ß^¶BüÕròÏÿúïqìßÏ¿ør-³Rßâwß熿ァ÷Üñç8áŒsâ—ü®dçŠ0FÀ#`Œ€0FÀ¬´în„ÿZÝü›âÃç!Eþ¯ESåk‘l¬b’‰E€HEÆ¡_Ÿ†I‚>=ØS§_c)‘ÔÙ²È'uÆhi*mD>Šï-Þ- ù«0k&Ûl¹Eôب{lܳGCúÜóñ•þvÌ7¯d߯oŸX¿K—Øró%+FÀ#`Œ€0FÀ#°f P¸ºiù·„$|]5þ¯ÈõU½¸±¢AR–;! KÈH“ë¶ÖÕ/ò‘äJ%ËxêÄWò”Z1II?%ñ56Us=";l‰OɃ¿<÷Üá/FÀ5¯~éóÁÓ¨Œ÷ÆR¦»ïºsÜ{óuKé­0FÀ#`Œ€0FÀæG u‡œ8/ø/ù•ó_âêÄ¡Q"ZüG}QzÐçQGðCM1ðE[>é‡gC*ñè±AÊã‹×«ÉÑ5BP’Îyä”™ UâêO]¥Ä¨#²‘/µé“ŸEÃ'uÆXÃXù¢Ÿ:¥ÈÏTµ#`–F`öœ9ñã /Ž'Ÿy6º¬·^:øÀøÂé§Ä˜±ãâÿ]ðÃøÜgŽŽ“Ž;6ü¯ÿýe<“V'þñ²KJŽø%ñi›÷=÷?;wŠo~í˱Ǡ]rÿu7ݼù¶˜;w^ ÚyÇø§/16Ýdã˜9kvüïE¿Ž¡ÃžöíÛÇà>öñàÇã÷W_?ýÁñ“ #^×ýþâ¸ê7ÆÃ>—þßOcÃnÝâ{?üq¬Ý®]ì¿ß>ñÛ+®ŽiÓ§ÇñÇ_:ë´¸ì×ÅŸn»+ÇgeÇŽã·¿øI }~xüð'ÆçO?9Ž<ôààü’ëSn·ÜywLövl³Õ™Ýn›­óXbtJc·H+.¯ºî†tvåßâäãÓN<>÷¿4â¯ycÇþýúÄ9§ÿ]|lŸ½rŸ¿#`Œ€0FÀ#`ŒÀŠE`íµE‘eÎ î ® îK<©#ðbØ ÔáÍŠzñoº€8=ø4õã| øÃŽG"¾NmÙÐßW´—Ý¥,¡¬Ð(—SÐD‚«+Õ)O`í‹vBÿˆì©+>€Õï-Þ@f1F —üþʸï¡Gâ˜#C>y@<5ô¹X´hq~&O™³çÌ- {gÆŒ@W8œ8{â©xáåWâûïS¦Nó/øÏ¼µúÙç_Èç=öO[«O?鄘þÎŒûÆø<ökç7îO1÷Û{Ï8ðcûÆ÷<ãÆOŒyé\Ë7&LŒo|ç‚äsD&<7Ø`ƒ˜1sVŽûÞû-?ú¦¿óNÜÿÈ£ñËß\šÉÐ¼üš?Æ#?={ôˆN:朷80vÚ~Û\_¸`aöYŠ@zþüâ߯‚… ãÀï/_:ï[¥KˆqÇÝ÷Æoº5HDh»vků~wyüuÔè|¶åùÿöŸ1}ú;qæ)'Æ€~ýâ‰DðZŒ€0FÀ#`Œ€0F`å Ðú9MÜY‘OSPõQ®rþ­ñËù<å£\—*YA)&”š$Ÿ” F,,z‘ŠôñÐnùDý‘˜T%05–¥¡ˆ’§!Iò”""SµGñé+ÖñQŒI^#`Œ@E ÜX•xΧF‡ëÆßþŒŠvÕ”Ûo70.ùÙÿD»µÖÊçDBx>šHKIää´úrŸ=wSO<.·Ÿxzh¼:úµ8åøÏ”¶mÿã¹_H`»xñ•Ùf£îÆ—\;tÈíJ_X¹ù›Ÿÿ8’ÅëiK÷)_ørÜóÀÃñÃï};ž~ö¹?qR|åœ3£ëúë/5œ±W^{Ctß°[\ùo¢ý:ëÄàý?ÿü¯ÿ¸þ¦øÖ?~¥4æŠK~‘Ï®ÜyÇíãßÿû§ñü /ņ‰4e~Gvpœ~ò %[WŒ€0FÀ#`Œ€0F`å кžKü—óÁ¯Á‰ƒO7–ª™Ÿ[˜Jq€Œ•->ìűQ§_¼\ªf¬¤”:Å×8tøV|Å ?ÅÁ¦¢0¨žàPN‹¶$E‚ M€äèÃ?}mùRrôI§[ÆaCYÔS—4_ö.€0K °ÓöÛ+#?wÖóvé3g.Ñ_¯Ñs£29‰ÝÀ­¶Ìæ“&O‰q‰œÁ2±7bä¨Ü5æõ\îµÇn¹äKá°ã¬;áØ£k’“±2rÙ|@ÿ¼U|Ò›oæv½/“Þœ’Wyî˜VWBN"»ì¸Cö9úõ±¹¿¤\¬ƒôïÓ'—³fÏÉq7Ù¸gÜqÏýñÅó¾wÝû@¼Ÿ¶Œ[Œ€0FÀ#`Œ€0F`å кŻÙù·Zü_]`Z>Ö6ƒXÒ(‚R„d±ŸzQ4ޱÔêˆÈHMF1e‹å ?èô©˜º|ª?©,FÀê|å gÆzéìHÎbüýÕ×eÒí¿½¨ú€=k¥UÈßþö~ì¼Ãöñ£ ¾›Ï„¼û‡âÞŽÿùþ÷òÖqlD R/—r²¼¿R›3)9'²Y¸°ådÎÜ”tèØ!ÖIgš,H[Áë ù]ôãƯ.½"ýË“ñý´}Ø /Æwþù¼zCÝoŒÀr À#`Œ€0FÀ•‰ÀŸ‘Vf”eñ]8¾°œ«äN¼Ráí"ÿÖ¢iáѰ…oã¥8>Úâã¨cR×y–Š“TK¾Š>èTŒ% ‹ ×9%Q”SA\üd¬~éió´O‚ÅÅŸg‚º ˆ~ÆCä%ãð¡1E+ÅO¦KäEÛbŒ@“#0nÎȸwÒµ1lú#1uÁÄœmÏŽ}bP÷ãÐÞ'Ç€.-«WÄ4¸¤†íÝgŸzr\øëKóÅ1/ÿõÕ¼ýÿ3gÍ*…)ž=YR*c^—[}{÷ÎåÛ'x8ò¾ù/1äñ'c·]vÊ}£^Sª\,Suâä7óY’ýúôjh|ßdÇ–ô—Ó¹“’Q£ÇÄâwß ÎÌlDúôî?ø×ÿ—WŸ2·‡{Âe#ÀÙÆ,½6Ùd9F{¨0FÀ#`Œ@=fMžSÏdu÷‹gSâÍÊ91¸383x6lôÀ«!èáÚĹ1¾Èù±•[üzù¯_:Ù Nß<øA(?+Ê¿ˆ(,×—·q*¡®`& ‚09êÅF ÓxtØa`+U³}Ñlé“?Õ•76ã¤Óä?ø‹0ÍÀâ÷Å¥£þ=îšpeúA¢Ÿm-9Ÿ;*xni|ªïéñù­ÿ-Ú·ÓñµË>/Î\ä"nÙFø«TŸ^›ÆFn˜o±~$olgåÒ“érynø yud‡t^än¸)ŸùÈ¥2\œóØOǧ=(/æg{DŸÞ›Æþûîϼ¼üšë󙕤×Üð§tÃ÷—Ê]×l¿Ÿ.ÌùÍåWg’“ o£;$—ýú´¤·ÞuOšK¯|KxîhýÂÙ–G~Hºç¾øþ{î¶k\sýŸrïqÇY4­XŸ¿`A\xÉïâ°ƒ>‘çÂnæf1FÀ#`Œ€0FÀ•ƒ@aÁ ü—Ⱦ"ÿF`ñsªÃ¿ñ”’­tôóÀ—‰£D°{+çßÄá1Fþ‰K½Ÿ~/ï—dR]DêU·hIœÀEQP‚HБ“#1úG)"RJª,Øó ²§^œ4cå ØèC?zæ#³?>È/Ë–ÉäÇbŒÀ*Brò‚çO‹Þy¼fDˆË;'\‘ÈÊÑqÁ®W-7I9eê´¸ôªkÒ­K—õâÿpnôM«‘o|õÜøé//‰ þû'ÁyÇý©øãÍ·-‘߃v__vUp®#ã~ø½Ê·h¿3cfÜ÷ð|[6?:pÿ8ñ3ŸÎñüøûÿ?üé/â»ÿù£à‘ï³Wp¦ã+#_]Âw­+ ¹Tç²?\lÕþÊΊ}÷Ú#9ì O¦½ŸÌ·no½å‰Ýk)Wç{N^EyoºMœ3$7éÙ#¾wþ×KDíR ŠY³f§[Ç'Ä—¿ñí|+ù› ˆùú× ®#`Œ€0FÀ#`ŒÀŠD • ÿ%Þ ® ïF©>ôðdqoôS×X¸6ñyø§Í#ž-Us¿ø7µñ¡XôáCy0G]¾©W ªØÙªÄ†ÀJ˜Rã”IhRJZ“‚4ÔìÑ‹HÄHRõ1žº„66‡^9IÏjL :ùèÊ; {õeãÒ™–¸ìÕrQJæËlrÉÈïdâ±-Žì{Fœ;ðmRÑ–ÛÐfÏ™Ý6èºT?à˜3wnÅÛ°eÌ1óæÍËD¡t*gÌœ•ôcÖËh¤§œ›Æpåºë¶m%([ª‡¥Û´¿ç¶˜?AtLçGVú‡ÿõ:w.†\ªÎ/¹yóç×µ[j`RûýtÞf½•ÆZg>Lðsr@N+uJã¦Í_%?‹Wê$ìÜ#`Œ€0MŽÀªx¯›9}ZtëÖ-†ãsÜ“O>¹*ú|Ççµ}öÙ'÷Ïž=;6Úh££ScrzXY$! µ0ˆ½8=tˆìÅåɾMuƈۃ§c %zlyÔ†‹£.a,:Å”¾bY$+$¥’U‰ãŠm’DD ª²ä°ç±ˆ®h›šÙN„#ã±×ä4YM´áøúÆÀbŒ@ó!À™“lën«0†±Ë+¬b¬DNâ—Õ]×_¿fV3/œ)ã·9‰ Ä^[ÉÉ¢oê:u¬HNÒ×qÈÏÇFìðW.Ä^Ö±å¾Ü6FÀ#`Œ€0FÀ|¨µã·ØW¨7-ÿ–¾Õø?ñ5¿ÅÕ9‚,„8D`?Ñ+8Ä¢XV‘ˆØcG‰ §M©GdReø×#{lð-ò2UÏ (‹0Í‹┟9ÙH¶ŒaìGMÎú»“â?þ囵i{¾FÀ#`Œ€0FÀ¬¦¬&žV‚®iù·”[‘ÿïÇG]}©ZY˜X#"–0DR¢c<ì­Qҡȃ mšlŦÍ#‚;Úvø¤‘?<Å­ÜjË7öŠO=ûõ J °æE€Ûº—U–gì²Æ\ÝãvßuçÕ‚ã#`Œ€0FÀ#`ŒÀ @ _¿~1sæÌàè±¢°Ó>É»ïæ…“‰SƒÿÒjÊr^aðg²§ ‡†JúÄ¡¡“Pç©Äÿ±U\zÆâŸŠOnÒÁíÑV¼bŒ¤^ZD.Ýó‡E;WR´I¡”^ÁéGG‰N¶Jœ>&†h,6ØjBè%mŠ_¸éHã]#ÐDL]0q™³Yž±ËÔ€0FÀ#`Œ€0FÀ¬:uê;î¸ctïÞ=1ÆJIêèè“´.¾kjþ-åZ‹ÿG¨)-UBÖˆDDä!m‘/‚Q¥HDlI‚¶ü¤j‘Ž"#‹c1P ꌥ¿èqÒi¬JÅ÷ Þ $‹0FÀ#`Œ€0FÀ#`Œ@ó!¹Í6Û”ÓNàâ‚»BÞK;™Å·i¬Úpgðbø7+Ú¨_ü›x;l±£-®Èÿ¡Ã†²G¾U¦î,âÑW‘„U R‡l”m€$C²PIz„„5’ÁVKQé/ÚÐÇ8{õ-S|ŸA™qô#дôìØg™s[ž±ËÔ€0FÀ#`Œ€0FÀ¬BZIKø±¦åßRnâïÊù?r®+\×0`ËS$ !ER¦jîƒUÛŠ-{ÔÆ¢§_fª–Æ—û¡¯˜ß2Å/Üt„?‹0M†À î.sFË3v™ƒz 0FÀ#`Œ€0FÀUˆ@ë ÊrÞ¬©ø·Gq!b%þ¯&bE°š!ð *!­–,®n„hø/’E²xá c” cÅ‹U¼6Ç/,ƒMn-FÀ4‡ö>9-¿æ¿zÛ„1Œ]2a„˜5kVNaÑ¢E1f̘h–UÛÓ¦M‹·Þz«*<ï¼óNLž<¹j­Ž3f,óØZ~ÝgŒ€0FÀ#`Œ€0K#°Î:Pk™kkZþ-å)YäíDXæä™@-iÄHÌ P׃_ê‹Z®‰VOÒG›þâúVV_}øP>òÁÄd#ß²§Äw9Ø+î{Ú»Ÿt#`š]ƧúžwN¸¢MÙ1†±Ë#‹_|qEçw^tèСbŸ”wß}wì¾ûî1hРLTÞpà ñµ¯}m‰ÃŒe»ªË¡C‡ÆâÅ‹ã裮úå—_Ö“N:©b-å+¯¼cÇŽSN9¥–™ûŒ€0FÀ#`Œ€0F` кþ . ®L™©‰” öèáßÐéAGÿ& ž:"£T Å—-e¹¾°'Gê5E„`M£ÖÎb’P¢ÔHÉJG›>EOL‘‘²S)ûdRòIÁvzh+uDmÕÛ±‚Ò$eÆÆ_Œ@Ó"ðù­ÿ-ÆÏ/¼óxC9î¼áÇ‚1+JŽ=öØèÝ»÷îê‘“K»aŒ€0FÀ#`Œ€0F`%!P¶Å[d %™¸4ê„$}ââDTŠ3+çÐè/ ýðvÆ!ø§O1)¥C¯<+½ò ]S”T-#œ!”Jšq´´ˆ„´ê1U3SŠÆ‹90Ø'+[ù¤P¶9¾ÉÉŒ¿¦F }»uã‚]¯Š#ûž‘~èÇÁÒ)Ó‡ ¶ŒYQÒ¹sçXýõ—xV”oû1FÀ#`Œ€0FÀ#°<¬½v¦ÃàÉĕᮩø·”¼8=xCêäÈSÌ;5—–FVPŠÅÎENâ „í°Q ÆÑG‰ˆø„°Ôy”"1±Ch3^cˆO=ñDXÊž’Ÿ9¯ LHXŒÀ€„ã¹Gô9=îtm ›þHL]01gÎmÝ\ˆÃ™“Ë»­»-P\ýõ±í¶ÛÆÎ;qnãµ×^guVÍmÜ#GŽŒÇ¸´jü½÷Þ‹‡~8FŒü,ÜsÏ=óS ^¨¼úê«9çyóæE—.]bðàÁѯ_¿l1uêÔ¸÷Þ{sn¸atíÚµ0ÒU#`Œ€0FÀ#`Œ€X™ðÙ®U*ñ_t¡_Wo&þŽ:¢ƒØŠW£F[[ªVäÿ°‘=%Bbâ5^ñÑ#ŠßÒªòUäa•î¬VÒ²QÒ" iSçbê£$!@ Aê膈IMŒ¶&†[Å/N _ò‡uùMÕ\Ï¥ J`°5Ès¶¹`•&ñÄqÄGdÂðÙgŸ νÜrË-£cÇŽù ɾ}ûæ~ÈÓ«¯¾:ho·Ýv9&çDuÔQ±ÿþûg“32ÙÊÞ§OŸÜ¯/œ·ß~{œp ѿÿ|Î7ÞgŸ}v¬»îºAN[l±E|úÓŸÎ$éÍ7߬¡.€0FÀ#`Œ€0F`%#P¶Å›hEþM¼ü˜ô؈S¯F?vðâÝTb‡Ðæç†ÈN~Љ‹£D/T}ø`>Š¹Ñ¿„à Q!´˜4uú‹Ä!:‘“©šíÙ ^“Ú$«äS5û¢Dð­ 7¿]»¶L1Çñ#`>b¼øâ‹ñ裖žZ·_7 +wÛm·à¢„_&Æ ‹=öØc‰áÜú=þüØl³Í‚[·ß~ûí¼jqôèÑ%;ÈÈÍ7ß<Ú·oŸ}B¤b‹°º“ zX¥ ñÉŠGVbJ YJ>ø€¼,ú–¹ALâ²–|Y% qùÚk¯+5<ðÀLœvêÔ)竱.€0FÀ#`Œ€0F`å"Ðz|!ü™8³¦ãßRnåüŸÈPr%÷šÒÈ J9¢,²}EKH 9)â=ch#$ƒN>è{ÊarÔéÇN6Œ¥®‰PW_ª–|Rg<+0ƒo®ß-Ú¦¦Å#°4‡vXi;óҽ˦8|òÉ'ãõ×_Ï«3!ý¸„3VcrD¥„_<‹•„ÕŒô·Þà–ý²J‘Õ–—lÁ®&‹¬ -ò€Äd¹Ò_ãÆ‹îÝ»çÕŸêsiŒ€0FÀ#`Œ€0«V‚²È³‰o'Ž=ü"Þ ® ô,ÄŽ¶ìÅ¥IWäÓðÇC|ü)&cб‹H§qŠÕÒ[å+αˆBt$†ŽIR§¯Ò¤ÐiÂÔy°-‡® øF?Åǯ€‘JÅò^E™±#Ðf íØ†½,È6mVQr^Èî»ï^:Rþºuë–ÉFΕlý…£®†J¶Z³üCÉüÜzë­5ÇMœ81vÝu×¥lȃš¬’,Ο\¸pa¹Úm#`Œ€0FÀ#`Œ€XE´~^„ÿBà½ôAµÄ%|6èD›‰CÓ8ÚˆÚÔÅÅQÿF?¢>ñ‚è•‹âG‹Ñ©?UóxtØT•FJ%Ž#=*&*=:Ù+q@A/rQ¶øÀ–¶&‹ìS5OH„$mMHcÑ)~_ÌÁä$YŒ€X&¸ †Ëc8G²’3 Û"\Js饗æ±\ŽS.¬R„2dH¾ü†_:¬†d¥¤.¨)Sl?>_ºÃ 䬂dk:„¥„íÞlÏæ¼Êçž{.o'×;²¡„´äœÉí·ß>Ç]´hQ¼ôÒKù,Ëäqlƒßi§Â’¾bœ¢/×€0FÀ#`ŒÀšŽ@Úü³,Žsß‹‹<Ëš>³5?ÿŽí׎në­];¶O‹<Öüù´qåü<(À£!”´áß´Ú‘ºþ‹Ã_²G |ãõ g¼âRÒF­Dþñ‡J§Ÿ5¥‚R”`yI@t”<ˆtè5ôʼnÉþÉ;Æ1‰b~M=Ʀj#{ì” >×b[岬L±Å6ûí·_ÜtÓMqÑEåЬ0„œkTØ"Ý«W¯ØxãóVîòqœùÈÅ4\|óóŸÿ<ÿ¬Úh£òŠÈrÛJí½÷Þ;X5ÉlCΛÔöo칇U–lýf+ù±Ç[q 8gS²Í‹rX1ÊÏÍ­·Þ:¯e%èá‡<ð@~°%.[Ë-FÀ#`Œ€0FàÈÀ´9‹Ò.¨¿EßîcívP –fAà½÷ÿSg-ŠEï.ŠžësZàGCøŒ–D\%üe‘³9˜Ô¹¿È¡!)M$">¨‹Ó8•Œ¥®øŠB?~%؈¿C§ñê_ªÄ ž(€È?¡]œ¼ôøCȆzQ‡­õÑV,ÙÒÖäѫ¶þ5ʆ?¬ºÄ®Wú&Þ‘J‹0Ë€ÀÈÉsb`¯Vä-ƒ‹Å¶8_!ÿ@IDATCðµU8ïñ’K.É·a×:¿\~Ã/VT¶E !%kå‡M£~™+¶åGc[Õ!U-FÀ|€?'ôèôb%ÔÆM›ïŸÅ+W»4FÀ#P ÑSæÆæ=;›œ¬ÐjÖCR¾>u^lµÉz+4“Uñ^7sú´àˆ-ÒU[L'}+)™çÈθ´0åèÔ˜‰”p`poÚòM¡„¤„?£TméRµdC=~ùÆÑÓ/ÁÜ›ÆÀÉ!”ÅxYYïK#Ÿ4Iˆ$  z„RÄ¡Ú$Ã%Vdj±‘|Ê&*&«’qøá¼âËÄ*êÛù Ê„ˆÅåB ùWË1[Â7۬岙Zvô-+ñ×ñ؈ò«6W~I.kŽòíÒ#`Œ€0FÀ¬ @€yådó~§øÞð=ú(I¬„óŸÁ†ÀŸÁ›‰+CWÉ^ãèGÄ»‰ÿ“Ÿ"׆ñKü¶èx4.U³-qðˆøli•}m„ ÄAº¤8aúé4†Dè#–’UmìêöòNL­|hbØÊŽºâSGˆW®ËÍúE[3ËW.5k¾ÎËÚ°µ{çw®mä^#`Œ€0FÀ#`Œ€05h]U)>®‹¾NœmqnâÍ´P>ìd+.ÞL6ŒQ{bѯ1”è(éGÄR¯_~TbWUVOD“bŒ&N‚<$†­€PâŒÃV‰Ëý"S5/Geed1qÆhµ¤Æ©$¶”ŠOÚ”ùÑÒØÔnZaÛ$O¼ýöÛ9GΠëÙ³g¾X£i“vbFÀÔE`Ûm·­kc#`Œ€0FÀ#`Œ€0µ(,dÿE)^L¼›J¸681ÚâЊ:¶ƒÓ§&+_è§ñE[qyÄæ)ŠâãKñégL1º¥¤‚²Ü qŽ ΃¤®~1¯$…(9JÆcG|ÚJŒ£¤Ÿ’ø›ª%R’ºì°%>%‚ŸòÜsG³|)“Ô¹¹á&^ÈJ•ÍòrFÀ#`Œ€0FÀ#`Œ€X=À%çyÉXΉ«‡F‰0XüߢTGŸFÁø7Å©(Ÿôó!•ø?ôØ åñÅëÕäè”`‹‹Ê_Iç„’~„‰Òtø—J!/Ø#²¥ŸÜS1_íª·"álUK=bRùp£Ïò•?úÑòí¾ò«òðÃAƒ©Ùærò¢‹.ŠN:ÅW¿úÕ6oÖ³gÏŽyóæÅ&›lÒ¬):/#`Œ€0FÀ#`Œ€0Fà#†@ë%9p^â»Tñ"꣟¦66âר£‡_+r~âëè—þ [88éð¨MD¶ÔOù «(”$‹s=r*‚0è©Hr<šdª–ˆDêØ"ô3¶e_s‹ z„ ‡RDdª–âúŠu…-uÅ¢½Z…L¯¾új,^ ñÑ¿ÿÒŠÉj‰‰Ê7Þx#¯¨|çw‚sí=[“Û{÷Úk¯%Blºé¦K´ÛÚ`+ú†nX7ÿ¶ú]ÝöC† ‰™3gÆ)§œ²ºSq|#`Œ€0FÀ#`Œ€0FÀdZ·x‹oƒïâ3ÿ&Î{ñvÔø·…éXäËD âGuü‰—KÕìBKvè_ãÐá[ñÅÉ_q°©(J®bg«²8É¢I!G¦D4 ’ÐÄ•½ü+qù`,õ¢^õ¤Î È'vÔãø”M#ë­·^^IBalífb-™?~¶• >Ú"묳NpÀmR×ÒóœsΩk·¦L›6-˜›Å#`Œ€0Ë‚ïm³fÍŠ 6Ø â18 .Ìï‚믿~tîܹÍ!Ø]ƒïöh3t`Œ€hj¦Ì˜s.ŽÍ7é^"XŠ ×ë/ÚºþáD u‹7Ü—ø/ø18/ø5ÿ‰oC?†P‡#£_üšx5•Œ£O¤¤ô*SWî£,ƧÎXqsS_ídZ]!(IRIà‰6 0I‚Ð'Â1UKýÔ‹¢qŒ¥ŽPGD*â QLÙâ_9È:Mžº|ª?©šGXñȪInçæJ^`7Þx㥈J^peÇ,ªÙ-ë ,X¿ÿýïããÿx¼þúë1jÔ¨ãSŸúTtïÞ=.¿üòØ|ó̓íà’o¼1Èë´ÓN‹ßüæ7y.ŸùÌgâå—_ŽÇ{,>÷¹ÏÅ=÷Üo¾ùf|ñ‹_Ì/Þ'NŒ‡z(Ÿ©Ùµkרm·Ýb÷ÝwÏ.o½õÖL‚ÉO<¬2Ý{ï½cŸ}öY¢¿K—.1tèÐèÕ«W{ì±ñøãÇðáÃ3fŸüä'c»í¶SŠñÔSOå>¶¡0 =ôÐ`<9ûGy$¦OŸžçwÔQGå³4o¾ùæœ7„îe—][mµUì¿ÿþñôÓOçØ|èׯ_rÈ!§R@WŒ€0FÀ¬ð{ÿOúStÐA±Ç{,1ƒgŸ}6î¿ÿþ8õÔSóÙKt®¤«xgèÛ·on©\IÑVž[pýóŸÿÇsÌï'Š8a„à}êàƒ.½©¯‘òˆÑ£GÇ7¾ñàÅbŒ€0͉À„·gÆ?]z[&1þïKŸ‰7èR3ÑËCGOˆk¾~J´_zdI©×¿¤õªmMŸ3/fÍ[›mÜ}ÕþˆE+ì¢å@„Ÿx°r4Ä‹Á©É¶È¿É;ø6ø5q|´ÅÇQ/Ƥá‰(NK냯ø*ú G1>°*«)Á2õÍ¢SÕ F"u‰‚õèXšFY>a£‹_þGb«É TtÔÓ–øÉ¼9„“g[o½u&º )y™7n\&ÿ ©£‰-cê­¶¬4C^ø_yå•ÒÃíàd þïºë®€ÌÛf›m2iwûí·ç8l/á…bÑ¢–KØZNN|p@;gΜ\ÇÂïúë¯^¼ÙN®Äºúê«còäÉy[:$ß½÷ÞÏ<óLG\bÐ&>ÿá 3!8ú!"‰Kÿøñã㪫®Ê[åwØa‡€d½óÎ;3nØÿå/‰|0o?ßwß}óˆG„§Nš?Œ±êR˜-÷Œá/²ï¿ÿ~^ÕÊ…ElaçûÀúðÇù”Â/;õ#`Œ€0k8?üpþƒèêž¿s¯½öÚÒ{ÇêÎÇñ€0FÀ,C^“?ßB\<úÊëËãªéÇ^÷èóqÅCÏ6}ž’ù'Å##“Nzt«“#>$&%¼%O]þ‘AˆÈ@l©3NÒR‘:AÕGè4ž6vؘHÉTÍ ýÈ–>ùS]ycƒ0N:Mš>õc³Zò ‚mݬQY\) é')_1© v´’¯ÑÕs¬T” <86Úh#5c³Í6‹“N:)·±… 䲘wÜ1ÆŽ›ÿJ¿ýöÛÇ_ÿú×lƒ¾š°Rñì³Ï.m“fÅ"ó>ñÄsæð‹_ü"¯€ÜsÏ=KnÃmàŸ¤œ·©s2!-Ï8㌼J¬X5€¿-¶Ø"!„&î“O>™±=ᄲoòÁ_‘TdEå.»ì’?ýìg?Ëßb°‚¢2„Õ˜gxO+;³Ò_Œ€0FÀ|àwóm·Ýgžyæj]‘Ç1+#`Œ€0 @ %·Üt£X´øÝ€¬ S«8‡ñÓfFÇuEÇT4±r ÐzIžà¿à½Tù2ñsØQ‡ãÜHJVŸ¡ã+ÿF‰`ƒ¯¢?êè±aŒüŸz9ÿ§ñåýò‘†T—Fþ)‰¢%ˆ@b }$L)"²Üö<ˆì©'Í¢ØÔC?zæ#³?²Fɼ4v¥ $ä@BB˜A–•¬ðC¤§.b’1Ô9¯_ „(Û°%pE)®ÊTDàÀóví‘#G†JV’[5a‹Xñ G¶¨ƒ½V]²R±wïÞyK9$¨rQ|VFE[˜XÕˆpn¢6ùêL&V9^y啹2aõ§Dóe•'&c« +)‘›nº)¯e~l3·#`Œ€ø° ÀïuÞ?Ø ?ÐUšG­<ÿüóy÷ïƒ.ý1±h?eÊ”`U&»'x'Øo¿ýbРA%þø÷â‹/f?üžåèvz 6,Û\sÍ5y'ÛË‘Zqù#$ïìª`‡F=ⳟýlLš4)oQ‡ôäÝ‚#[¶ÜrËìO_x׸ï¾ûòcyÏØyçs®¼cá—ÜñÇ?ù@ÀVõ‡JÞ/˜ã˜1cò{;];ì°œ·üóžsà 7äݼûp¤Lµ3'ÙÚŽ?Þ›x·á8þðŠðÞÈnðä·ï¾Ë+µÅ#`šã§Ä´Ysã]·ÉåMO¼£'¿[õú`¡Ð[3çÄEwý%é§e"sá»âˆZfV¯¿8ÿû‡Š{†Œ‰oÏŠ~=6ˆ¿?b¿´ÝzÃt¦å¢øæåwÆç>¶s }R<“¶³ÕüÜÃ÷mz÷(ºÈõj~èœ=aüþgâÅqé÷{ú\¿ÏÀqü~;Å/ïx<^Ÿ2=ÖMÛÒÏ¿âÎØm˾qâÇw‰9 Æå ác''þ"b×Í{ǃ÷ˆõ:¬[ÊëÔäæÝÏL}»Ç'v\òwõR ~Ä­¥ø/†z1€ÿ¢N©>ƒ'“ˆ{£ŸºÆÂµ‰ÏÃ?mêO‰otˆì‹>ÙàWvø‘È·ÚK•Jp©Ž‚‡ØáLÝÒû•v$ÇSœuˆD’¦_¢>J|óà¯ØÆ/GŠß2¯ÅíÊùJœkÈ9”¼óʪDVÿA¹ÔZ=‰Ïò¼ dÉ»õ0×ÂYY/Ø¿¬äaå#gj.+©±zÜqÇå(/½ôR\qÅygž€¿#`Œ€ø ÀÙÏüž„ d—B%¤ãÈ~¯ónÈ.üq°(¼8óG=.‰Ùi§òïˆCHK„]q¼çp&5ägDó‡Gý2©—£fxOa§¤! ò‡iÎÖä˜ÈQÞ °)—ë®»®´K„cd8ßú¹çžËføåÈQAÃùÕ¼ !>úh&Uy¿‚d…`enE2dHéÝŽ³¾Á¡’ðŽ)ËX!@ù£-Ä&Ø€'yB`îºë®™lU•|YgŒ€0Í+&‘}·éŸ‰<ê¾Ò¢£þ~úùþ¼?FNœï²uôÚ°k¼þfËQlôc#1á­øõ=OƆ]:Çþ;lœ}ù³Û†äß!ï¿ÿ·˜šˆÐ_ýù‰˜1wAìµu¿˜4}V\xÇc™d‘ÊZ~ d¾ýýñøˆ±±û}³Ÿ‡_|-Þš1'å¾~þÝÛ¹CûئOÏÜn± I8ôïÑ-útß Jöÿuãƒ9¤òºî±áqÓ_^Hgn¶‹nëÕ¾<8üˆi%(ÅÁ5ÿ–r"?ñÔyDšR¯)¬ Ä D¢JŠôøeÅääŠ>˜ðp6 8·’ØbŒ€0FàÀ¿;>úè|9—»pìJQ ü !ÿ¸?:n»í¶ù¢.’+®ºä]‹ô°ÕH!Û èx`õ$¿¯O>ùäÜÏÊF½§aÃŽ‡O|♤k4.;!ÑŠ@ŽBNBjÅcq>Ô!,y#>$&Â{ç^ë"?tgžyfÎ…÷°;î¸# Ã*Æ<0cE$8¾†üÑK8à€ü‡RÚ\¾Ç:¼å&Ì••Ÿ©Ä¿ð ƒ?Œ²R•¼8ä)+7-FÀ#М,~÷½xbä¸èß³[ôêÞ5'¹i"ñ ÷Nÿä±v»µâÙ´’‘[¹?»ÏŽqò-» Þ™;?†™˜íëõg¾]ßãWéžž­—ðð{ 2ð­™sÒÙy@¯øîçÊõù‹çËx¦Ïž­ß9ëøRËϤé3ó*É£÷Ü>Í¡åÂ[VC¶K¿÷¹uüÎgÿšçúùƒ÷Êþž{mbŒI„++H¿xhËïç_ÞùxÞêþÒéx¶ž-;#Éá§gV}®z®£4ñ5¨Â»U«À!ð_p_´áÌàÄxÙ!}Ô!2±EÐ!²g¬ìÐÓG‰àC1hSG'Ÿø’?bãË:â(~ª.-”rN)†VA5Q%N[B²´‰A$ŒÈ–:ÿc˜¶Å„‹öŒg 6šŒüâôaƒž_9>/}…odR¯á/Ãüeœ—s=E¢R[¼±­DLbË–$.yÑ }½Ù°R‚±(nį'œ»È‡ ^ªÙjÄ–è¶/Ù¬Æà¥žÕŒ¼PóÁýŠþžà¿œ#ÉÖ,nö†ü%wάlD Tùð¡€R|`<Û¾XÑŠh[y#þlcŒ€0F`M@€ßmÜ*ÍÅyl'æ÷¿„•|¬Nd‡vDèè~Ï– «!yWÑaégu ¿ÿñ¹©w€Zï2ÆÅ‡ÈIb¿k×®y9$eå!`-Æ"7dĈ¥Õ•Š—;Z¿T;‚†Õžìᮼ›±ë¤üÈGƒ+HM°*o£8ä`/á=¿Ê¹_¿~ê*aWR¸bŒ€0M…À³¯Mˆy ÇŽý7·‡Pgûôð±“b·-úÄÓfdývý6É%_ .%õúe§ròÍwfçÕ“spÌ`úƒÝü%‚²K§uešWkÒ` v‘ DWÍϸ·ZŽLÛeóŽ;ƒœ¬&c§¶ØoŸÈSÉöi®¬,Å—ÊÝ·ìcrR5P¶¾?ÁuÁ‘Á£ñHà¿Ä­A^ˆ4,rkØ´üYšÃŽqØPW ¾Ñ艫’ºìõ!óoI¯ñÄÇFRô)ÝR%A‘‹J„1è”4I J†$y””&™Hb<ø¢;%Ž>éC¤ÇާåOô|ЖotŠO=ûÕËŠÕ-¬ €ôâe’Q$%%/¢zÑç/æE‘>ÆðbÏK1câqSuQxq/ú/öë`Çù“~õ¶wǩζX³ŠøygKg+­ aå„4Û² ù`Á¹WÄmDø€Ã-⪬V`Ë^X9žÅË}ñi#`Œ€0k¬8de!gO³JO¢#YtD zßRiEàc=¬¬Ä¦HÒÉÈIù¯VʾѸòÃïkVhòÇYþÈáÊå{¼HônÈ{ŠÎ¿¦OÛÌeW­dW«MÁ‰U¼Ÿ‰Ì¬4FñxG)ú ~ùc¨„:(%,-FÀ#°f À¶fä®´ª§(t” µ¬¨çÜÆJR¯¿8¢ñç·=–ÉÏMºµl·¦?ý­«MRËÏ¢ÖÅ:ë´«œoy …éb ¤s‡>‡k5碴ÂT¢ßj»¬@ë»òÝåá"’²œWK]™?“=m^D°SIŸ8´âK užJüŸVh*>>ð©øè¥ã†¶âc$õÒÒÈE&âÀˆ ÔÄ(ñ© ¥j¶cçŸ~ÉW¥Ê·¿ýí%Ôƒ·Þ•¬¨à)—o~ó›%$`ñüRGªèûéO:¯Ü¼öÚkóñ,E‚Ryó{½­ÇÂð^yË-·ä¹ŸuÖYùýàòË/_je1'ðCŽ·“¢ ˜°Ê²üК;ïƒZEY‰ä,úsÝ#`V\$3l̤€(䙢\÷èðxfÔø`‹u÷õ[¶VOIg8²²y÷=ÑéòØ:ýE¿xdX°múÎ82o·¾å©—ã<—~ï­ê×kùáŒLdì[Óc‡þõKõiÝÚ>*]´[Z%‰Œš4-—ÚöžþÒ&Z¹ ñ_Œ…ÿ‚ƒ¤„·CèGÇ?(l‡ôaCŸ85l触ñI•m±çÁ†RqUâ«<~R•rÐXÙÐWQH žÈ‰’WR$¡ñ¢ÒU'€*ÆÂ¤°)ŽEÏô1é§”?ÆI§±*¿©H%HF^@ù«x5¢2Í)¯¦ÔŠÉ"1I}M•rrreÏC·u·5VʉÈeõÕÖØ¶7FÀ#°:à{Ü6ÍÅ,qüãwìN`5âf›m–WHÒÇ®ˆ¢pö£„-ݬ¤”ð‡?¶Z³ÃU–l§AÇY‘:B…›Â©8°á¸ŠAɪNn$ggDò2aYnô†,$–v¦°c‚wn¯%³laç]’U§œ^~C7—ñþÉÈVpò©´B“ãi¸‘™Ú¥Áe=¼äÉåD\ÀÃn|ùüÉZß÷#`V/ùëØx/-v9p‡-–º‘šËknMäáS¯¾‘VQöËÖ7?ùbtJçDr“7·lKêõËŽrF:»šä½t!ç>>>âõbwÃõZ~öتolйcüéÉ—òe<í×n·=óJœuО±E:ƒ’‹rÆO‘·psžä^[÷OvÏå›Å;¶_'_ ôà ££g×õÒ%;iië ˆ“³aF@ïf©1¤íÜâÇ„’Ú¼ÈÁ‹Iàß4®h£~ño-/€-¶ØÑæ¡^äÿÐá²G¾U¦î,âÑW‘„U R‡lpDpÚ$À£6d¡’*²·$¬ñØ*lµ•þ¢ }ŒC°Wß2ÅoÆ¿4‹¨ä&VE²T—•¼ òPGG6šk29™¿“þbŒ€0FÀ4=‡º8FÉtÐAy¥!Åq ÄD¦VõÉŽKäxg€¼ôÒK3AÇe%øá½•ÜHÍ4ä%¶lÍæhˆÀFãÊ?%» ¹Øîw¿û]î:âˆ#Š&y—ÄñÇ=zôȹÞxã9—Í7ß| »J ˆVÕ &Ä%—\’IÖâölÆ@Š2n4 ˆÆjÄ'çyrIyƒ-gQB³ub•Ý#l%gÕ&—–o*åh0FÀ¬†¼üztH„ÜþÛ/ýûÒ²}ÚÒÍ6ïMºu‰³¹7}Îüøß[‰ñéLÊÏîûÁŠËzýÅÙ¹ûvÑqÝöñí«îŠŸÜ:$ÜqËtže»6oñ®å§SòþqŸÌ$%7„ÿôÖG¢k§Ž™p$—Oï½C&!ÿ﮿牀í¸î:ñÊç]^3dX\÷èóÑ/]ôíãg Šù»Þ8­+(áÇš–K¹‰¿+çÿȹ®@Öà\¶…Zú ‘(Ò;êè°hŒ-2­ô‰lD¯~Õ„°£ÎS¬§föÇ8¶s#؈Ôħê}Ë|;Í,¼„kE%yòRÊ‹­IÉfþ®}4r9ùƒsÈ>3ö,€0mG`@NmÔ†ã¦Í½ÚvQ]Ü·É´Ò‘-å ÛXñWí=†÷þ[iwE5ÿÕôå±Õ^´hQ^Z)†l(Ƀ|êÙÇPg¥&¯5߈ŽÉ_˜#˜•Û_ž©]#„»Œ€0F Aø´²÷²êqQúùXIêõkÌûi×Ü´½|ý´ÊâdY¥?lQ_' ­Eaåèûi>åzn5'©ö+a7èÊøáse¿×Íœ>­t„N+éX„2×¥/¬šÌ—í%®è˜d0!=ðgZô7ÆÙÒÁù8úÔV©±©+÷áƒÁB›:þ°ç›ÎxôüS£’GþèÃN6ôi ~ä;U—‡K÷| Á±œS"rJT¿€ !êJVýŒã¡Ÿÿ…òGÂètØÐ/ßèðWôCŸ@ž~|àKõv|CõÍMú¦^FYqÀŠI¤|‹qS&í¤>2¬ì_Ì =Q#`>”|ÔþS‹Ó7¸ÞÑ(¼÷T#/«ù¯¦WÌò²Q22°œ,÷U©]i»v¹][ýV›#ï…Χ<·€0F yàæîjä$Y×ë×̸Q»k"'—WñS-_Vm¦ßKI9a¹” #Ðú^!¾Lä#ܘЇãƒCàÉD¯©.ÎŒ~üù8tâ×èCh#¯~bŠ“S|ìŠã¨“yÉOªV–¢“ÊŒr†s=Œ‡¤`C›R-ŽKê ˆ| DtòŽ~âýhLÝøÍNN¦y•„P““%8\1FÀ#`Œ€0FÀ#`ŒÀGÖã ›šKߤ"oÇ÷Lœ£ÈÍšßG×4jíÄÒQHBÚòƒ®ØžÑ“,6”Òi vÔUÒO]B]¾)±-ŽÅNmõç”tXŒ€0FÀ#`Œ€0FÀ#`ŒÀš†@ëvo¸.ñdKñ_­}âÚV9ÿV_¼œr¦¬)šX-#1 "±emV8¤X¾I‰À”b§ñ´/0Sµd‹ÙÊ'm„ ‘†ã¯I+([¦æ¯FÀ#`Œ€0FÀ#`Œ€0F ÖãqššK™ÂÛ‰Óƒ·£÷WäÿR³²0 ž`£çÔEZŠ0™(° !±¡-²R,®bʶ"3ѧÍ8ÆP¶9~ñPÑ4ÞbŒ€0FÀ#`Œ€0FÀ#`Ö¸Ô¯Uš–KùÁÛÁé‰ÿ+r}­éW/DV·h!E6b'Q?E_ªS’H Dõ‰|„À¤‡:ý<ò‹â£kS|” 1‹0FÀ#`Œ€0FÀ#`ŒÀ‰@ÙoñjÌEuÊÕʿՈ—‡XS4‘šF­LT$!+ "Ò’þ"q‰;%B›Ëq(‰M©‡6D¦ÈÈT-£ŽoÞp|_:t#`Œ€0FÀ#`Œ€0FÀ5Öã áÏÄ™5ÿ–r+çÿ´X‘\ɽ¦4BPa¯Gzmá&0:¢)‰­’ D'{l±“­ÆcG‚/Å¡­Ø”ÒËŸÆ—ÇgœÅ#`Œ€0FÀ#`Œ€0uX»ÝZñÞû¢2êšÛ`#À÷†ïÑGIZ Êzü—þÑ®nþMñÅÝñÍ‚X­)7*8ƒ”SÆù¨º‚ÓnOgt‡E¶ôc§Äñ#P‹ùaÃêË6Åo½Š= ³#`Œ€0FÀ#`Œ€0F 6tnSg-2IY¦ÕÒ 9É÷†ïÑGIZ JqnMÉ¿¥ï*ÿG®Eþ]MÑàZF" ! õ@ú¥G'{‰’èió/H¶øÐXJú±“}ªæ 1F>™ã56UK}è_Ì!š}‹÷¼yóbñâÅѾ}ûèܹ3óÉÂù‹-Š:HåÒ#`Œ€0FÀ#`Œ€XÉôè²nÌZ°8&L_ C?XšŽí׎në­];~´ÊVüʼnÿ¢Ôb@LàÓhïÁ÷ñ—ºþcìTy6ø4Úz°g¼âRÒVüTÍ"ÿâó°Á—ÆÓÏšÒA©@J°¼$ :JD:ôšúâÄäÿäãD0ªN¿&‡ Ʀj£±Ø)|®Ñ×Ê4cÛ42þü˜8qb@PJ (ûôé:uŠéÓ§çþ®]»FïÞ½cÝuו™K#`Œ€0FÀ#`Œ€0F`%!°Vb6èÔ>?+)„Ý6!P¸$Gœü¼Y‘³ƒOCˆÃ^"SšHD|PÿƘâÃxÅ¥Tlô䀠ǯñwè4^ýK•ÅÁKu¶*°á!¨SjbÔ@ªæº’PÒØdÂ0•L˜vqu‰ÆÐF/&;J¶{k‚Uã7#9É?¬qãÆer²gÏž1`À€ „¬DO·nÝò3kÖ¬xíµ×bElU÷]¾em“™3gÆ›o¾YŠ?eÊ”LžÖóÂ7cÆŒz¦î7FÀ#`Œ€0FÀ#`Œ€¨‚@ ¬Ê¥¡pe<«…k0~2«,$]O !ER„!z„„6vŒá¡.B“6"€'?ÔéK%}øa-/cxd‹?|õí öšm›÷´iÓòömVKn´ÑF)åô×™ 6È«$YUI?„eÿþý£cÇŽ™è{çwJ¶y@ƒ_ 8|ðÁ˜0aBÌž=;ûëÕ«WtÐA9F=7>úh¼øâ‹ñÕ¯~5ºtéW^yelºé¦qÚi§ÕÊöôË.»,ŸýìgkÚ®ÈÎ÷Þ{/¯<íÛ·oÓ}ßWä<íË#`Œ€0FÀ#`Œ€øh ÐJP2Y8/ñi´áÄxĵ‰W†M%{£ï&þO~Š\vÄW,ñoØ¢ãѸT-qvø@ð]Uä´ªAêÀA RL$5Kä uúy°WPÚ% ‰•¿" 䂾hÛø(ö§fÃñ±ma{7Ò½{÷%rR[ýtn¼ñÆÙfîܹKØ6Ò˜4iRüö·¿#Fdtï½÷Ž-¶Ø"ÆwÜqG#.Ö8›±cÇÆþð‡X¸pá—»6FÀ#`Œ€0FÀ#`Œ@9­»ƒáÄø1žfäßÄÿ‰”,òä^UDV5H8-‰Ô1£ãl‹Ih¶ôc§ñÔ!•[¶Y)"2Uó­–Ô8•ÄÑÄŸ8è)óÓŒ[¼µ¢“-×\Ž#Ñlõ£—níµ«¬ë—÷Þ{o^©yðÁÇž{îYÀjÌ«°úÔbŒ€0FÀ#`Œ€0FÀ žHü¥x1x280•pmj‹C+êàßè‡S“ÀÙÁÏuŒ•OéÅ廜¨’-¾?U3¨ñ´+ŠÈÁŠ­Êr'$!„dŒp‹ uõkå#I!JŽ’ñØŸ¶’Çã(é§$¾Æ¦j‰”¤.;l‰OɃà§<÷ܱº¿¬¿þúùGÎsd+²„6B?Âvå7Þx#×¹,§-–îÉ“'ç vŠä$>6ÜpÃ%\=óÌ31lذ˜3gNl²É&1xðà`x#òüóÏdzÏ>o¿ývÞ2~ä‘G–V}2ž-ö=ôP¼ôÒKy ûa‡›m¶YvÍJÑx ÆŒ“/2bu'[ÏÙÖ¾`Á‚øýïŸüä'cêÔ©ñÜsÏå¾vÚ)ªÅ|üñdzί¹æšLþž~úé9V­9Þzë­¼ãŽ;Æ=÷Ü“·ÒüññôÓOÇСCójÌ~ýúÅ!‡’W¢f‡þbŒ€0FÀ#`Œ€0FÀU€üPq^ð_Œåü—¸:qh”ƒÅÿ-JuôðiÔüˆS |Á©É'ýðlH%þ=6Hy|ñz59:%Ûâ¢òW’#!Qg m&˜jmˆG“Á¾è_““?ú4†:þWLš:c“±ôÓ˜¶ÄOæÍ!œ7ÉÃMÝ#GŽÌãPÒVÝ«¯¾šICÈI‘–ÎRô«%O<ñDÜÿýy¥æ6Ûl“Ïo¼êª«ºÜ†­âþóŸsn{¬^¼å–[ò%?Š9zôè|þ%gQræM7ÝTÚ~}Ýu×åó-9o“³8_xá…¸á†òPÎWà‚ž!C†Ä#âzë­—·§W‹ Fºí‚•3>‘zsdû„’~b‘$ñ¡Dåƒ=‚ ""R¶ô“»&ªНv^™‡ƒfnîfÅ$Û­!â ÖX½Èƒ°òíß´ËW<62|"µˆMb@ÞAü}éK_Ê$à¶Ûn›IÂ'Ÿ|2?üðš¡XUøå/¹´ªR’‘Ø:uÊc{÷î§žzjþ>@ü=òÈ#1jÔ¨Òå?ƒ *ŹýöÛóJKÈA½ÉÅ>çœsNôèÑ£”Kµ˜;ï¼s¼þúë™èeå%94:GVs2fŸ}öÉqžzê©\îµ×^™ä•¾”„+FÀ#`Œ€0FÀ#`Œ€X´^’ç%¾Ku­j$ õQŠ?ƒOSñkÔÑï9<ñuôK¯ƒppÒáQ›z1~5>»ŠÒAI²Ñ£IŒ>ÀФR5'G‚š$:‰Ô•°Æ²4QòÔ„8”""SµGÐW¬ãƒ6B]±²¢Ù¾ ÉòÜ:wî[mµU¹ºá¶ˆI¾j2cÆŒ¼š²Tl¼¶œkfµ±Ò³â’•Õ“¬>DæÍ›W"(!%u(·#˜¬¦D 9%ÜZÎVð·Þz«DP‚A‘œÄ¶ZÌnݺÉU©ltŽäX$!µú’Ÿ¶{ì±GÃÛÞKÁ]1FÀ#`Œ€0FÀ#`ŒÀr"кžKœœí"o—š%r’:B?7 ‹,òeø@ð#Ž:|šx¹TÍ>ØV.;tНqèð-ÞPœñ›Š¢ä*v¶*q(§E;’BŽL‰h$!¡=¾ˆ+{ùW ù`,õ¢^õ¤Î È'vÔãø”M%°ÞÜ0]ë–é:ˆ=&°hÑ¢¼¢²¨«51¶L#Üj½ÿþûW4Õ<Ä’°j²rñbþÝÕVr~#«YåÉjE¤•Ù_j°r§¿Rlå¡>hŒœµ5¦|É7~™#DíqÇ—·—Cš¾üòËÁ¹”ËCk.?œL˜49æÎ·Þ²âÇOœüûÝf«ÊýµAY/~\ÙÔ#`Œ€0FÀ#`šÖEep_â¿àÇà¼à×X8(¾ =üBŽŒ~ñkâÕT2Ž>‘’Ò«L]¹²Ÿ:cyÄÍiLy|µ“iui„ $I%'Ú$À$ BŸÇT-õS/ŠÆ1–:BÁu|!Š)[ü+ùA§ÉS—Oõ'Us «'MšT“œTÆjlf%$$Û_ÿú×¼ œ•}Z)ÛJ%«9?‘Ër†»ì²KÉlâĉy‹5¤" 9IØv3ÏØzòðÃç³Ï>ûì¼má\ˆSN*ÊVeW$&±Eú)Z±ÛsyæÈ™œþ•ò‚¸Ã–­ÉØsÉ ¶“ÜÊÍjEtl®%Ä®¾áOé2©ùqòñÇÆi'Ÿûùw}ýÍ·Å-wÞS§½ÎªÜ"¾ú¥ÏÇvÛlÝP¿âk‹ùm¾'®¸æú8ýäÏŧ?uX\wÓ-ñÇäŸs2í¼cüÓ—¿›n²qöí/FÀ,;üœУӲ;h`ä¸ió?´?‹˜¾MŒ€0FÀ#°JXïu3§OË»=á@x*‰ô|F”À ¥ãðŽIí écC(e$D¤84ÈGôâåR5“ô£W Ï&:ãhcCIôO\z|ðȇâÉŽ²865+K#Ì+(%ÂXêÅþò䔼’U‚‰$M¿D}ò'Šmü01¤¡øúƶ Y}_ù‡ÕV’ ûâ?He_I§¾Ze5‚PcêõË®X‚o-r[ljùfÅd£ä¤üÕ‹Y-^5}qNÅ:[ÒÛú}+Žw½yèÓ»W:žà½¸?’’?ß÷`Lœüf&Ñ öB¼1~Bºøf«üñkç7}â©84–Ÿøø~qé•×Ä·Þ¡á¹üß‹.‰ÝvÙ)6H«‘/¿æñàÇJý‡påu7Æ^»íšþ]­¿úÝåñ×Q£sÿU¼1~~ñocAúCÂß7^12¾tÞ·r>Ôë/I•ÑcÆÆO~yIôÚt“DÀÏ>ÿBüâ’ßEÿ¾}âô“NHäi:BâñÅ!®#`Œ€0FÀ#`Œ@“"Ðʉƒk:þ-åD~åü¹ÒWS%(E4ŠPdÎÕ†lD\새ĉR¢+Ú¦fÖa_ì°Á¶ø¤fîk(~³”\m%º*©¤‹0mG`ß½öˆ×ǽ‘WFŽûF^¹¸qÏñÌsÏggÃ^x1I°û®;Ç_žz&FŽz-þî„ã⼿?'?Ûg¯¸õ®{–üëŸÿ8¾ñçÆ¿ÿ½¬¿ûþ‡JýíYÅ%¿ˆoýãWâ¾xvÖ?Ÿ.ÎáœÓ+¯½!ºoØ-®¿ü7ñ¯ßü§ø¯ï};uð‡ëoªÛ_ *œÿúƒÅú]Ö‹ÿö7ƒ˜#G¿–MN>îØ8õÄãâ·þ$öÙs÷â0×€0FÀ#`Œ€0F I(ðIMË¿%èàîàòxŠüŸÚI]] ë ŽJ1´”jC"A/AO[¤#‰"²¥Î6lÀ-’•"(eÏxÆ`C"¿ØàC9 9>+– ßÈÔ½z„³Y)Øáï7^r f%][|ÚÖ€ dKõð—^ ¶|oÔ=“zØ!I÷ç¼b’[·Y Ù1móõµ1yà ‰'žšë“Ó­÷³ç´\%¯œH¿>½ÓêâN1éÍ)êJ?ÖJÄaË–ùþéÌUdÖì9Ùfn:÷t÷A;GûÖŸ»ì¸C^}<úõ±uû?qòÙçÆ”©Ó2AÊ|·˜Ëó/øA Þÿcñ¹ÏÛ lÙ:ž;üÅ#`Œ€0FÀ#`šV^ b MD%9É[ƒƒ+ã).ìÆíß~Ľ©d6´~ ½ø?Jÿ–Ty\q¼bÓ‡}¶h*|Åq#‚s%%{t$^Òd°a"ÒcƒˆLÔ¤ð!;ú5!ü —`ØÅ|Šß,+(ÙZÜ–mÌLûò-É•tØZŒ€h;{Ú5‚Ã_|9žú\ì›V~lï=óè¡Ã†çmÐû%ÑÏ’}öØ=Ÿ[ÉÙ•_<óÔøÎ7¾V5p»µ8¦Uµeá–ß]Ö[¯dØ¡c‡X'ý X°`aÔë/ J•O$)®ÜÜy‡íƒ³1·Ð?î~à¡øÂW¿§¡#`Œ€0FÀ#`Œ€h~¸»#ISóo)?8@ñð…âöàòê~0f@=!@Ñ™ˆC‹”")ñI`§Ÿñ”ÒÓÆ/mìi#Òã{Mì˜TÑ_j–l?ŒÓ˜lÏ>} ²#ÐÜŒ›32îtm ›þHL]01'Û³cŸÔýÀ8´÷É1 KËjÀ1‹N:Æ®é˜á/¿£Ò Éï}ë±mZYØmƒ®é¬Èr”ýûöÍíuÚ¯“.Ç9¬nø·Ò*Æ9sçæ iê÷íÓ+oÅ~9;)5zL,N¿„87²^¿ÆPþã¹_H—kÍ;ï½?;è±ÿ¾{çî>¶Oðp%ö yüÉLÆǺnŒ€0FÀ#`Œ€0͇@+¯%þ‹á¿àÐ`.áÍúщ{á_öØÐ_äß触ñI•ýaσ ¥ü¨ÄWyü¤*å ±²¡¯¢@=‘%¯¤HBã!•Å `§„²Qëü!L ›âXôŒA“~Jùcœt«Rñ›b{wÊÓbŒ@¿¿(.õïqׄ+ÓtýØi4~î¨à¹}ü¥ñ©¾§Çç·þ·hßnÝ:놀¼ð×—æË{ï1(…¬’¼çÁ‡39Èe:ÈÇ÷Ù3z§Kgn¾ýÏyË7çÜ÷à#Ñ%õÈ™“’K.»2X™yÍ7g—ÔÔ¶yø!qÇÝ÷Å÷ü³Ø3]¢sÍõÊÃŽ;æÈ¯V¹nÿ~ü©§ÓE9ç-ꯥmâ=ñt|êЃò¹–Ø÷é½iù0·€0FÀ#`Œ€0F  (\– ï)I)~L«-ÞNzø7+Ú¨_ü›>ˆË7mÆù?tØP2V"ß*¥Ÿˆ¾ª(@UƒÔQîHÄ"cH†6d¡|:’$aפ’*÷aÇÙ¢×ä4¦uôøDð_ŒÏÊID~©¯Ý,gP’ŒÅÊ@N^ðüiñÂ;W6hÕB\Þ9áŠDVŽŽ v½j…””Üp½÷»Åz;çHŸØ¿¼š3*%ë®»nüä?ÿ-þçÅïÒíÝülЯoé²ìXíÈíØ\xÃvísÏ>½´‚Q~ª•ç{N&Gï}葸ëÞb“t–å÷Îÿzif½þ¢ß º®_ÿÊ—â‚ÿþi¾)|¯ÝÅ}É7svÉAî'~æÓÅ!®#Ð䌜<§É3tzFÀ#`Œ€0+ Ö”pbðoâÃà¿$" é‡+ãA°ÅŽ’ñˆúű1Fœ%ø6ꊇ=¾ÄËQGG?:ÙÉReþ]Eap=Á ÎeKr‹ ÚvÔ•ýJNä¥ú°Ã'z•«¤K§XOÍÒ8‘’ØàÁ§ê}Ë|{Öú‹0mF€Ä{µ\êÒæÁmpÉÈïdâ± CâȾgĹЖ!+ÌvÑ¢E±hñâLBVrÊÖîΉìäí¶ ›7~‰,-_¯¿Ü¾Øž1sVʹs›/ì*úpÝ%àç䀖T®àÖ¸ióWÉÏ✶Ý#`Œ€0F`B`U¼×Íœ>-ºuë–"¬v¡ô…U“1{öìØh£ŽI€NHü™ÈI8µÅüX‘£Om•›ºr>x|#´©ãO¤$ãÑóA—>Jù£>N6ôi ~ä;U— ê Žy•rJèhS Õi‹„LÕl8ˆÈEê$¬\+ИŒü§jÛã¿¡8°#Ð\pæ$ÛºÛ*ŒaìêVS/´)Ͼe!'ñÃ/#­ä,÷ÛH¥1Òq¶æ:­·„KçÒ#`Œ€0FÀ#`šÖÏqâÇĹÉ¿Õο%EN&<¡ˆKxÁº"R°–¡Q‘’jS2ž¨#ØÐa XÅq©™û)ETʶùQIœ¢©_̳»4F ¹àBœò3'É1Œµ#`Œ€0FÀ#`Œ€ø0#ÀcIššKùy;òç(¢]U‘qUƒB¶°´zDйÅ]±Ÿ1<€ˆžd±¡”Nc°£®’~êêòM‰mq,vj«¿WP‹Å4/ÜÖ½¬²)V5¥pJù¥¦T¡W;|#‹½VM¢ã¡Í8J®íULÆRÇFeªæ6ñ²Ï5eåŒ3bÚ´iä=zôÈg䆿9SL\æ.ÏØeêFÀ#`Œ€0FÀ#`V!k¯ Í•¹/ø/ ¼|™¸5ø6t´ÀÎeø»"o¦:~°ãA‡ˆ£d<%ãñ+Þ­R|t<ØSâS¼!¾yÐÕÔlô(99Î¥~%¨¤HÚ€E2ØÒVLù€©«ŒüaÃ8ÆÁÑØºñׄ”óæÍ‹7Þx#§K7x¨£³#`Œ€0FÀ#`Œ€0FÀ|´xï=è¸,âË(yš†K¹ÀÑù?êˆ8¼–V•¯" «tg5Žxu¤D/3U³È/%6bQéT~HSÀR—/ù-ÆGצøÍHPΙ3'Æo¿ývpŽÀ¬Y³Ò´" ê3gÎÌdå”)Sbܸ öy@IDATq%ú,FàÄ@ÏŽ}–y:Ë3v™ƒz 0FÀ#`Œ€0FÀUˆ@Ùoñjd :åjåßjćËÓÃT­,‚ •@*@‘4Ć ER6W‰`ËSÚ€G[‚N+4ÑÉ—hbÚzŸ›À_ߌb¬¿µk§ï.W¿LŸ>=&L˜oê}çwâÍ7ß ˜ðöíÛGÇŽ³ž‚ÙîÍÃ?BæaÙ¯_¿Øpà Û<‰ÖÃT³Ÿ6n`óèÖ­[νs›%ÔýÀ?wÔºFŒµ#`Œ€0FÀ#`Œ€ø0#Ðz|¡ø/Jø1ñj´yÄ¿i‘ m¤Ü޶¸6Æ!pipmˆ|S¿G¿â0^6êÇŸE;ÅÀVq°«(”r¤àr$’Ô•=cˆtL¡OÉr¶$uÆ`'ÆR×D¨«/UKq¨3Â’1øîž¢mj®~ „pÜn»íò6nVQ.\¸0¯œ™ºÅ[dâ’6çQB^¾òÊ+Á9•m!(Y}y÷ÝwÇäÉ“3yˆ¯<0¶Új«º@Ìž=;ç·É&›,a‹ÏõÖ[/ºté’õÆ Ë1öØc8äC–°uÃ4‚À¡½OŽÛÇ_šþ3ë¿y#£øO¾V0vu lèÚµk~-Z”ÿø°Ùf›­´?´e®üƒ?Nl¼ñƇñ‡… D¯^½*ö×Rò³hþüùË4¶–_÷#`Œ€0FÀ#`ŒÀÒ´”EžM|›¸8qtèùp­Å{âÝàʰAÏÂAìhË^\štE> <ÄÇŸb2¦‹±ˆt§X-½U¾â¼QQ ‹(DGbè˜$uúŠÁµÊ&LÛòqè*‚oôøS|&+`ä‡Rñß㺈¿¤_íÒ¡C‡LüA ¬¿þúù)OŠ”ýû÷/©Y=ÉJJVX6*œeyà 7dòsß}÷ÍåðáÃcĈ ”C† É«6O9å”%B^}õÕ1xðà4hPÖwïÞ=çUYb°F º ŒOõ==îœpE…Þê*Æ0vy„ã.¾øâŠ.Î;ï¼àÿk-á»ï¾{þÿ€/þÏ}ík_‹N:Õ¶Jú†š‰8úè£+Æ{ùå—3¡zÒI'U쯥ä&cÇŽòŸµÆ¸Ï#`Œ€0FÀ#`– V‚þ ÷‚dDJüWªÃ—aƒNä!¼™84£¨M]\uñoô#ꃓCÐ+Å'#%:õ§j޾ªÒA©Äq¤‡@ÅD¥ÿÿì½ ˜ÕÅ™ÿ[²É&Ȧ ( Џ¡‚û‚ˆ+.‰{&c2æï’dî,÷>óŸùß¹÷™ùÏ_˜üàŸy`Ç8‘×é×âÐóhl¬¦1²ÇNsÁçr9ÙÇ€%lå†äCr¢Ì+¶ŠnÚÄkÁhÓ&ôë×/m¥ )H&(ÛÁÙNÚ˜ì,襄ã+¯¼Ȫ\½zuÚb~Î9ç¤lȇz(m1g·Ýv[ʸ„ˆ|öÙg™Nx饗Âo¼Î:ë¬4¯1cƈСC‡†Gy$›©“'ON±O8á„pâ‰'¦±¬aìØ±aÁ‚)ÖÀCëÖ­ÃᇞÈ[úæÌ™“2×ÐA®6§ Ø´Ùæ´mÕ.üýÑw†ßÌúßá‰ïˆojý(©ŠmÝdNBN2f[IÇŽËf3o+ÿöcŒ€0FÀ#`Œ€0F ©8$qm”ð_”9g'r0âÇàÈ$"$Å¡‰DÄuñoâÜT2^\¥b£×ïèñ+ÁFü:WÿVe-¥à‘S-@CÐfaué‹qÒɧ@.ƒžø<ÔµpJÒYa(¨ã'Ÿ'©ÄØÒ\ÈIˆ9H9‚Qç¾ñM9 Ù·oßÔÏ™‘è8§’ù80µ9ÿ‘‡± eSöîÝ;ÅùàƒÂ-·ÜŽ>úèpÔQGÉMHÆ &$òbqÊ”)bòª«® lÛfd̉s/©+ã“ qˆÏ:ÈÍ3+!;?üðÃD¤<8¼ÿþûáù矜ËÇœˆ±fÍš0bĈ„Ç›o¾™2ר&Ë|Ø: ¡ ) QIÖ'[Þ-»>Ž7òOatß«ÃÓ ï Ó–MKÖÕ½g¸­› q8sò«nën ’÷Ýw_2dH"ßǹ÷ÜsOø“?ù“â{©œ¿÷Þ{/Lš4)\}õÕéØp¬ÃwÜ.¹ä’zï_ÞCÏ<óLzoóOÞ§"ôÇ—Ρ]¼xq˜5kV"óyï@Þ#«V­Jï/Þs뀞èçp?>íÀÏšãŽ;.=ipÉÞ«Ì™÷'gÌŽ5*]Ì…Ù’%KÂÓO?˜?øG‰Å#`Œ€0FÀ#`v A)ÀâÂ(¥‡#ƒ£ !I[¼Y¬y5ê#=¼\¼B±ø‘OÚÆç$§x9ÙÊöù8¯WŠ4¬§,ihr" 5ô<¥HIµ±c õœ©ÍdÏ8&)?š°tŠ¥’qø™'>±ÐIߪ¹œAÉød rw¾…›mÝdNBöèÑ#N½N 3éƒàìI.¶Œ˜?~ºÝ»!‚Bñ²Ë. ?þx"U Éh¼ôÒKÓ^~ùåD’\qÅ) „Äc=¸ÄçÌ3ÏL„%D%Y•ˆ“Ù³g‡Ã;¬x%äJ9¹æškÒ¼ÉÅ/ þÀ’Â"ò’­ïd{’ÉQ3räÈr®­ÛÅ€€¼vðßïÐUr$‚2˜ Ìû‹÷„;ÄŸ„Ÿ'¼/ ¿¤Þªäý ¹È9¼o²•ùþÎß»ø†ð<âˆ#Â×¾öµäR”Øèxð¾=zt" _ýõt)Õ AƒÒû‹dð£ò”3bióÏ d^<'ò /L™È¼9#“­ìúgH2Š_èã}ÊÏοåŸ$÷ßà}Ì?˜—w}ýë_O?ÇøGƒÅ#`Œ€0FÀ#`v Ùß ð]ð_”ü|"þM¼ºröG?"Þ R_ò“smØW±¨ó`‹.›EnHNXÖi²¯rš©¶ªâ@ó‰`˜/˜ ñ`¯ ´J&$BTþr@˜ ú܆1"6ñ‘÷ÇfÍñ±Ý©±AVÄ›²›¶÷„ Co¸á†À%O<ñDÊzd»9™Rdtñ@‚ ÛBt¡¤$BlˆW‘¯ˆ.Ø4aN¿øÅ/—ôP·™¼'N,>d ~áŸÆ \TƒðË„ï¹í>²„¹ ’žs_É´æ}K³„÷îÀÓÑ ø„HÅ!»“ «ø¹ñÉX²¬%Œ…,e>ø€¼Ì}ËŽ¹ALâ²–ùò^…¸ä}Ê?GN?ýôDœ’9Í|-FÀ#`Œ€0FÀ#°c(ðHpbüOsäßÄÿ‰”dÎèàÿªŠÈÀjF8͉D93J0€Á6Ÿ„ÆaK?vOòQs M‰ÌH&NÂeKjœJâháŠOô”éÙQd`ŒWUÈŽ„8€à²˜ ìȦ„ cIB}gP’)I€ˆÐK¾°~ˆ ž[o½5ÍAả,­\´õ<×mË:Ù\wß}wøÏÿüÏä–­çÊö:ãŒ3&œoÉVÓ3f„k¯½¶Á›”·åüìk÷DàÜsÏMäÞ¶\=Ä!™ÊsçÎM¤"¤ß!‡R/Ù˜d;CTJxÏB,–~fÐÏ?<ÈJ²4!ù'Ä%[°+ ä"Y¡¥Â< 1ÙF.„ÄYÛü£ìO‹0FÀ#`Œ€0FÀìx²»9ÄQŠƒ'ƒS צ¶8´\ÿFÝ–±þ ~.×1V>¥'.¾ˆÍ“‹lñ¥øô3Fãi—‘ƒe; ÊR'Ä9ÂdŒpP uõ3a옢ÉQ¢ÇŽø´5yüi¡ôÓ&¾ÆÆjj Jìxˆ¯z¬&?¥sG¿Sdß}÷MÙGœ×H–„#[&!Èj¢­3*!'ÑчÑ©1Ùµkתgßiqdeáóàƒ–*ejA†iÅ77¤„¶Ÿ¶s…m®³•œi©LKÂB°½› w¸‡ì3Ö>pàÀí<+»7åà{R„~y‹ÊZÈ@þÀ÷1gArΪÞÓÅy®|ß—öɦZÉVk¶ƒŸ}öÙé\RUMøÃ?J…yp‰Y’¥Â± d\[Œ€0FÀ#`Œ€0F`ç Àß”QÄyÁA2ÒFàÑà¿êH¤?phpiƒÅÿmˆuôðiÔüˆS |å>é'RŽÿC "OñÅëUåèd\ç¢üW&ÇÄD1´Y`B¨Ð&ƒ}î_‹“?ú4†:þ—Oš:c“±ôÓ˜ÆÄæ;W ä !"²óRF%Y„ Hu²,%".Èf‚ô¨EزÍrœoG6"%·…C˜@NB–°›sçÈêäÒœßþö·) ÿ:/ò­·Þ*nÕvm²µÈrÌÏì«eNØpf%¤#gÚÝ{ï½áá‡Nfô‘mFÖ$ó”@œXŒÀÎB gÏžé¢'2™9²1Â¥4d ’]ç!9Ò@?x}ôÑG5…ÁŽmÞd[ó‡Ò­édm“yÍ{õÕW_Mÿ¤Ð;yHK21—µN:5Ù÷ïß?•lƒG ,ù¹`1FÀ#`Œ€0FÀƒÉ3—Ö\ù7樹QÿWXBùB jùÞ?hE¢¡2<"UŠhÌíéC(éG ™$>4Qù òØ ""eK?s×BCñÕnRV·—ðÇ=S¹@Bæ¤dÞ‡-™—œ1W‹p ¤Ê»ï¾›.Ê`k(¤$Û¨2¥˜„ d™ŒlIÅa<ëpÉ7i“Jæ%ä)Û@d”®! ¬ò…L4¶‰’9 ÙÊ“7Ìñ‡?üa"X^|ñÅ”±Æ|Øv[m»j•Pî2Û“O>9<ðÀᦛnJ$ ï›Æs|ÿrlÿ˜ {¹TÈ–æbš§žz*ÜxãéçG/Y‹ð>%k’IÉy“ÚþÍx²•ɲdë7ñ/¾øâ²ï)2¶y¿ñ Þ§¥d_ó þ)rÞy祬f2›±%.„¦Å#`Œ€0FÀ#`¶?…„8/ñ]ª+«‘I¨Rü|šÚ؈_£Ž~-çðè'½ÑK‡Dmêy|ùP<Í»²¢e; J&K=±ô}œ)&H[•mT%JÈX1ìð…~ˆÃ#"2VSŸ€Á>ÆJÚÇJŸø">&Es(9ŽŒ&n†€8€P€AöY†d¤ÞÌ™3‰Ñᛘ³éªe^BdB*SSþ™$J.̬¯Rûܦ\BãÑG ×]w]ñÆr2=ÇŸJZâ±´Ú|Ëù¶nû ðÞ¢Uá>·óä•ïI½?3mŽfàÒ'nÃnˆl'Ë‘÷+ï­Æ?GxßT›6µúe­Øfgœ¤é07¶”þù$9©ìQâUšOcãÙÞl+ª‘Õb°%|À€ºËfªÙÑ×Tâ¯â±ͯÒZùYÒÔ9Ê·K#`Œ€0FÀ#ÐR€+úbíÆ°|ÍÆ°n£è–ºš–;ïöm[…½;¶ ]:4Õr×ÑØ™¶x³bñ_php^ðkŒâÛÐÃ!ÔáÈègmD%ãèË Å¼Ÿº¾áóøÔË#nN>Kã«M+K-%Ñ$ðD› °H‚Ð'Â1V‹ýÔsÑ8ÆjqÔ‘ŠøBS¶ø×äO]>ÕUÍOÈ¢â‹óÉZ„t¤QI 9@™“l ¥­ÐŒc gKÈÙïÿûéL?ˆWÖþùç§­ã-y]ž»¨„[»‡Z©Ûz#`Œ€0FÀ#`ZKWm›·|úuïZ·uÑB&¿ M“×`ÉÊ aÃæ ¡×^l¤Ý=„„‘‚”òoÒç¥x185‘ƒ9ÿ&[ìxp¿&޶ø8êyLêʺTœ¨ª'øÊ}Щõ óŽ9e¢LPN™B`…´Õ/=mí[ÄââOg|w©Ÿ1Äy‰“ƒX.~4­7/ÚÍBÈTÆ ¢AɹŽÊXDG¢’gWØ#+bæäÀ^MNîä—rbrî’5»AY€]<›^ñf¥œ˜ŽCTæ¤x9x5=\›87ÆÓ'Îmણ—ÿrñ¥“ ±àüð̓„Rñ“¢ô‹ Kõ¥mœJ´ÚF´êò)˜$ýŒ£®ÌKì=öØÑ¯…QGÔ¯ºôµÄOšó2 ¹)òR’K(¸•×bŒ€0FÀ#`Œ€0FÀì|ÈÜsæäΘ¯¯Çî$Ùy”Í™+åÿàýàó(™wUQ&c5#HÀÒW^¤¢ˆBÆçA#¢‘2gms_L’‘=u-Š>‘§ØØ1†~ô¬G$gòÇ9ÊJŒ}ÍVºvíx,FÀ#`Œ€0FÀ#`Œ€0F@Jñ_ð^ÔáÚñn”êCO&÷F?u…kŸ‡OÚ<âÙb5õ‹SŠE>4Æ"âñ¨Ë7õ²¢ –í,(qˆÎôÐ%]Þ¯ÉaÇäxò…QçHTöd¬&Q%¾yð—·ñË‚‘šâgûôëFù«0FÀ#`Œ€0FÀ#`Œ€h!JñcpcÍŽ‹sb~â©óÀá‰Û‹ÕÊÉ×àPD#%Â8éi‹y%pÞ§}çèx˜(%~rÛØL:ìµì°Á6b³^ Úã› ‹0FÀ#`Œ€0FÀ#`Œ@KD Û\‘ÿŠë‚;Ûiü[•øâ«BQC"G…‡ì'z-bQ .:{ì(ô´)õˆ€Œª"é(‚RöØà[ä%¶5Çg‹·Å#`Œ€0FÀ#`Œ€0FÀ´D %<\³åßâÜrþO¼Ÿ’ÕWþZJ‹,ÌíÑ d¡HEŠÄ„T”„›„°Á>d«EBôì`‰ß”‚Ð¥0FÀ#`Œ€0FÀ#`Œ@KC`Ó¦”8Ù¬ù·ˆ) ø?øBq{âÿªÂž~• ÛAò £º&@)=¤""[Jt²ÕıgÒˆÆbƒ­²&ÑKð#Ÿ ÆÏn:Òx—FÀ#`Œ€0FÀ#`Œ€0F E PH¾ƒ §Öìø·87qu”¥üŸ8ŠxçÄ_%#H@‘‡8¹HPD£JôêÞ:crÁö€ ¬8±š„1ŠE¶”Ò1N1W%úÔ—íÓ*‹0FÀ#`Œ€Ø1|þùçáÓO?­ì‹/¾HýM=’ßË—/¯èßFÀ#Ð2Xµn}˜óé²@YNê/7ƺ–@–|'ÞKe‘ÿŠ+Ü©ü[ñä4ˆd#b¶€0¤“…"£:‘Š/r=ãS~j,éÏmèÐÄT_“â7õ_Œk1FÀ#`ŒÀ.‰À¬Y³Â~ô£ðÚk¯mµ¾×_=õ-X°`«>+‡À¸qãÂí·ß^qÐĉSÿš5k*ÚTêØ¼ysûÌ3ÏT2±Þ#`šÿç­…+~|gxôÕ·œÕÔÙ Â_ÿvLx=–夡þrc¬kÙ2(áÇš-ÿç&þ®”ÿcÎ Š7h °åÉICÈD‘”±šú´Ï;žtDa,Ä£¶m£C4^%:Æ!ùüšß”u@ú«0FÀ#`J?~|X¼xq©Úm#`Œ€0F`"07fC~´tyhµÇá…·çnCÏvµ» PÈ ,åÍšÿ_‹<±ÿWõåÊ ÀJ†Àƒ¨¨lIÚ fTuüç„dNXraŽ„1š cÅ‹U¼FÇÏÒ`Ë¥0FÀ#`Œ@D€,¼G}4^7&FÀ#`ŒÀv@à…wæ&¯#æ/ù<=Û!Œ]î´iµ–¸¶fË¿ÅùAJæ¼Ë4yPMj1‚$DºtÔ!•®‰ ’-%ýùú2+‰¯>|h>òÁÂd#ß²WìR°WÜ;Å;¢a1FÀ#`Œ@zõê–,Yž{î¹pÎ9ç”±¨S±ü7Þ«V­ ûì³O5jTèÝ»÷Vöï¼óNxñÅÊ+¾ûôé“lÎ8ãŒ0vìØÀΖÃ?<<ýôÓ¡gÏžáÒK/ ëÖ­ Ï>ûl˜;wnàsÛÀÙgžöÜsÏt>ãC=†Ž;î¸äë©§ž óæÍ ×^{m"Vo»í¶pÚi§…O>ù$¼ýöÛiÜyçú÷ïŸì§L™’¶²oذ!ì¿ÿþᬳΠ]ºt©7w¶³+›´[·nù2~ýúõÿ§œrJŠÉÖø®]»†Ñ£G‡ýöÛ/ùàHÆ.Z´(´mÛ6œ|òÉá˜cŽ©çŸqlç¿£>:Œ1¢^ÞxõÕWÃŒ3’-s8ûì³CçΓÉÌ™3“ŸÕ«W‡Ã;,æº0FÀ4S¶|ùe˜4sn°O·pÁðCÃs3>HY”ßÙ­8ãÍ[¾ ·?7%¼øÎ¼Ð¾]›0x¿žÅ>* õçÆdkÞ5~j˜µhiòuùICÃ9Ç ÎMRýÅ™ó½/¾–¬XèµwÔ»GÒçŒcÃ-c'‡Öñwöˆ# ¿~ú•ЯçÞá^22‰yû³¯…éóÅßÙ!=p¿ðQdžN{¶ Äýׇ'„ó‡ {hòõ‹§&‡ó? ÿþ?¾6lÚþêö1áN=*¯ùÂÛsBÇ=Û†ëÏ=)ÙëÏ[Mx7WŽ/„ÿ‚ ƒ+G¦ÄA85‘ƒ²Á=ü:=è¨#âßÄ£ÁáQGd£q”Š¡ø²¥,µÃöÌ‘zUiÐ -Ç8çQ`êò£Éª=HDMX ‘|0{JÆÈo¬¦:vzjŠï J ³#`Œ€0F`kN8á„D"N›6-|ðÁ[DÍË/¿œÄ7†ƒ>8,\¸0Üu×][]βtéÒðøã‡N:%‚{HM>Pó_ȹٳg‡Gy$Ø!÷Þ{oxë­·¡Ù½{÷ðæ›o†ßÿþ÷©ÌNÈNˆB ä:>ãá›:¤%塇šâ@jÿÃ?Lä+~O<ñÄÀXæ™ í»ï¾;p$6íÛ·Oññ+ÿO>ùdòËú—-[{ì±ä‚9<ðÀËnŽ<òÈкuëD¾–^ŒÃ9‘£;v “'Oï¾ûn>…b¾çŸ>ì½÷ÞᤓNJXƒîd»2§c=6ËÅ®#`Œ@³EàÍù‹Âç«Ö†“韈À>ÝöŠDäÜô{L“¾û…iá©©ï…ƒúô#Þú°þk õËdè¿<ð|X²ru8#fk¶¿—~=î•Dʆ’íæÿñø‹¡[§áê3†‡õ7…g¦Ï ŒoÛ¦UX¾z]˜:gAøÙ#ÚõÃÞÑ"æï{6LˆÄâ‘°ìÛ½kxþÍÙáŸï.¹Þwe,Y±*¬^·!µù²bͺ¤cð–HÂÒÿ‹±/‡ÙŸ|N9t@XqùÉÃãSüâ WÊ"Pà¶xÄ“5;þ­07ñÌUsdδ«Š2«•’Š0­8;›ƒƒ& ¿L¦TD¯6vøF¨#Ø‹ÉEÇC›q”í⃠1K•±šÚÄK>A $#`Œ€0FÀl]tQºlîšk®©gA ™xÝu×%nÈ!áþûïdúåY—h|x5jT"=É&„8$ûQg‚¯]»6Œ92@Œ"–‹dž{î¹IÉI&$ä"„_-B¦æ7¿ù͔١C‡0iÒ¤D¸®\¹2 'ûòÀ,ÆÍ}¾òÊ+‰ô#›³Gi¾?ÿùÏi:lذd:`À€på•W¦:™˜¹øÞk¯½Â·¿ýí„k8p`"7ÉÝwß}‹a¾õ­o¥¬Mnõþå/™Ö޹€5s!óôòË/O]dN‚ÇgŸ}¸¼ažd±B¦þô§?M:1FÀ拀Μ=C»6­cÖäËaô°!i†+×®ß9,|íøÃÓ8.晉ų®;§î÷øŒ™³Açĵ|’|T©ãýý~gïÕaÏðûI3âe@‡“‡ ¨2Ê]…ÏDp_<x/ø2qkðmèhó!ŠÌIø3tâÍT§:D|%ã)_/ö”ødœüiNQUYD$V¶¨¿MŽ`ÖDòEÊÅ`rØR"òÁBu%:ù” ã5¿ŸxØ"øE4½(ñsò—é¿®Û_€0FÀ#`rØÒ̶ç'žx"Œ3&yêWöâP$ ûõë—ºK/×XCæÌ™“È8FHJüKøÇ±ÈItòAv¡„:%}Ú"®¾J%Džþ)­­çÌ]s%£òCIäc©O²!Y¿„ÏË—/W3eUªA6&ÂÖtJêøÑzJoåÖ–r°h×®]Êú”?•Ä#S”±wÞygRC†"ô±ŸmHßÔð#`Œ€h–™øÊû†}÷î·[· Ÿ­\·o×ý¾„؃ \·X¯Û°) é·O‘%‚h”4Ô/;•Ý;wLYŒsâ–ëy‹?Oj2s!™6gaèÙ¥Sx;Œ{¶mÈî”0‡‹ ä$ºyñìLä°8OÉaûï›Êù1ÎÁ%ÛÒeSZvëÜ¡ø;ûÀ}ë¶•/ŽXª#À¹á)ÇÑ…^|\™HBÊœ7‡N<%mql±Z$'5V>‰£q²S[ãe‹‘ºV…¯"+t'µ&-MZ¤!mê7úëI-¥äß›LRòIcCPúe#r2ªÒÞ}ØhèMV ‘/-€¶ùæÝ øŒÓ˜XMú/ýße °¦#ðÞ¢UMì‘FÀ#Ðbàb¶i“©'á,D2.€‘%Èñ•I(=Ùœ­xê©§¦³ ¹L¦¡-Úl©Fˆ+ÒP±È6$)ÍHLÊ _4enrn$÷ÜsOàÂÅÂë`M€ù¶lúØ’^MæÅËzØNΚ¹‡¬Í[n¹¥â¶…ƒSž1*c°æs+guuÔQRK²D¹Ì‡ñ¸Y&EÑÆ#`Œ€h^L,ÜÞý¸•ºsÜÎ,yíƒSfå”Xy@Ý1˜’MñØI½:¥j¥~ÙQ²•ûþ—f„oÄ‹h.;éÈ@fâŸþò¡Ü$Õ!='¿;?Ù±µzŸ®C›Ö¢b¶2OоqÛ9ÂöðaƒúÖÕÖëÌ–ôvu·L§­á©³†/Ƴ0‘Þ{7ü¾ÜíÒ&|‹"þ‹…x5Ú<âßDBÖ±Ê[Û1N\ãqyÔ固ø=úGü¥ú±Ågn§øSìÊJ-¥åÄ™HB|P×D€Àè#@¤c}š,“0}úôd‰È¹ŒÌR‘lH@¶—Êüùó7\Cðqk7—Ñ0þã?N™¡GqDºñ›q¹pF&Ä*gpê¦ð©S§&ÒQÛÅsû¼Î¥;.Ê!“²œL˜0¡våHæ¦Ü˜Î¹“\øqÊy—œyÐA¥õp:¸q‘Å#`š/ËW¯M„áþq;õ…ÇVo¢èÈ œ·yŸ/‹árœó†'^7e1Þ3qZѾk§öUû‹†±BLÉÒ/V§LJµórÍú aU<žuÉMãluև᾽҅8l!/•7ã­Þ“f΋g]î‘æ×%ž—9¼@x–Úºý eγ‰o'Ž=D!"Þ ® ô$bG[öâÒ¤Ëù4üñŠÉ˜<cé4N±êz+|Åy­¢@8QˆŽ‰¡c‘Ôé˃+ËLÛÒqèÊ‚oôøS|+`ä‡Rñ7óÁÖY”‹0FÀ#`@€³&É$+Præ™ñÀþ=öH ·msž"Dfi %}"É„èãlF.‰)'’ocÇŽ xHß¾}Ó…9ÚÎ|öÙg‡qãÆ¥›³é‰—ûƒäökb’yÁ$b“ÌK¶usÑkàbnÀÎâ“‹‚ˆ1ÈçÆ¤ sr»ruHCHL¶ysÁ géÖpæÃ%7ÌÌÇ#F$²±œ?úøìÊMæœÃ‰=Ûæ!iY7™¦±¶ÜòÝÔmðåb[gŒ€0Û# Ç?ÞÎ:êà­êÝ#]Z3=’’œyý¹'ÅÛ¬'„Ûž’ΫüãÇÄÛ·ÿðO¯†úฃ÷Fßló~ø•·Ã…ÇšˆGõ«\oÚæìI.ÑhälLˆÇy‹—…øãseV¯lß®MøÛx‰Ï/Ǿ¸U‰H.Ìi·§CÔ°eyÿ<ÞÎY›çÆm䮹t‰dèÏ¿oð^ȼüÓóOÙjkynïz‚þ ÷‚dDŠüW¬ó2`ƒNä!¼™84£¨M]\uñoô#ê/ˆ^sQ|â(éúc5G‡MEÁ !Ñ„p¤‡1è™E/-Tª¢-`ɇl±§.ò‘ßèÆÈ'º\O¿dlÇ#Ÿ}ã„ÇbÛbŒ@ 3È”MÎCŒ€Ømàçdÿž¶ëzç/]Ûl~k{q¹?øàƒé2—ü&ðÛo¿=móæ¦ë†²ßå¶…ó±ÉòÌ…ŒMnÜ&Ûðk_ûZàRHÏRÁ²¯¡Zƒø"GKýTjãŸ¸åæ®1øÆ¦ð†ÔK|²ÞR{΢dÕbUtê#`Œ€h2;âo£Õ1³‘3Ez”N¶¡~Ùsw‡x)O¥mÛÿúÐøt.æOþäÂ4’æo~;&ÙÿÓU£å¦b¹‘3'ã$ÛÆß™¥áI†fçöØÒŽ sºæ?î §ÄŒÑ¿¸è´tö&slªl×cG|®[±liÚÍÁï÷ÒßñÂBz>ÿHø§kÜòõØþ(>âÖàÐ #ÑñðíyÉ‹C;'*c3qoèàÍ(|aËêøäQ[Úø¦DòoSÆj>Š›û’=6¥– J!/÷½&«©.["a!j«ÿÌ=>XD^§ŸqÒÓ§±±šÆÈ^€á'—¼ zq1¶#`Œ€0FÀ4 H¾JB6!Û¤ýë_§Ëb¸y’íòË/¯4¤ž¾)Èg¹Rr²ÞàB£9IÛÍk‘jë«6¾ÿõ]ɧÎå¬6÷#`Œ@ËD€ËjªICýË–íj2|P¿ð_OMñ›Gã–¬dË÷ß\~FµaÅ>2&+ ß”’“ål¿ 9YÎß®®+•9ÿÿE;çìxaÐ#âÇàÏ$’´Å¡a/ÔÅ¿‰sS»Š¼:ÅFÏxD~ëZu6âïÔ/[ÙÔ+k!(qˆh‘Í ‰¡Gh뻕ºôŠÅ‚¥“Oüå:ÕÑ_ài1”0¼{©ã'Ÿ'4<>¶˜œŒ(XŒ€0FÀ#°àæk¶`s$Ä$7QsVbc‰¹ÆLB’ÌI¶–[Œ€0FÀÚ5ô tN$çA®Z·>Œ8üÀpìAýB­hmQê[AH’9Ù«kÝ¥?õ{ÝjŒ Æqa”ÒÑÁ¿Ñγ'óþØ•D:ŒƒWƒKËIOôø‘OÚÆç$§x9ÙÊöù8¯WŠ4¬§,ihr" 5ô<¥HIµ±c õœ©ÍdÏ8&)?š°tŠ¥’qø™'>±ÐIßÊgPF4,FÀ#`Œ€Øp+·næÞáÒ6g¶w[Œ€0FÀÆ!зG×À³£„íælï¶4 l»7|ü%Ɔˆ¯†®œ½ÆÑˆwƒTÌy¼œkÃŽ¸ŠE‡±èòq±Yäæðä„e&û*§™j«*0Ÿ†ù‚™ö J¡dB"Då/„¹ Ïm#bylÖ[‹0FÀ#`Œ€0FÀ#`Œ€hQvÉ!ðc<Í‘ÿ'R2çÿ˜{E©… Ä©@À‘HE1£èè)‰=}z°§N¿ÆR""©ë"#a „e“ã{‹· tiŒ€0FÀ#`Œ€0FÀ#ÐÒÈ.úk¶ü[Ä´ÿ—s}eá‡8lHp"BÛ¼I›G§¹ªò1Ÿ±èˉKÈG‘—”øÓlÕ¦.¿êªÔ¯¾†âco1FÀ#`Œ€0FÀ#`Œ€0-Í›SN`­ü×ÎæßÊÅGWU4ˆ£†Vd$cÄØ*k’6Ä!Ã>÷/ù£Oc¨óàO¤d¬&Á†±™Œ¥Ÿ6hcâGs‹0FÀ#`Œ€0FÀ#`Œ€h9´n –DÜZsæß˜£¸AêâÿêVPá+FµˆˆDl©#" ñA`„zÞF§>JŃXÄOnŸÛ y‚Èžºâkëw/÷‡mð#`Œ€0FÀ#`Œ€0MD u«=Âæ-¢-šèÄö ¼¼»“.É©ÊE<ô ºSø·’øzÄç1÷ªÒ A Y(;-’:}ÔyΓ·±áAð¡ Òf,Y’¼0Ž"R„%:ü4&~4·#`Œ€0FÀ#`Œ€0F itíØ6,Y¹Á$eÓàÛf£ 'yx=v')lñÎù¶æÈ¿Uãÿ˜{UükH  ¹­œ‹X„L¤ÈC[Äb¬¦6z|Wöò¯òÁXê¹^õ¨N$%>ð‰â1Nñ)-FÀ#`Œ€0FÀ#`Œ€h2=;· _¬Ý>^¶6¬Û(:¤Éî<°‰´oÛ*ìÉÉ.v/‚²°ÅîKüüœüÉÔÑñÀ!ÔáÈ迆QÉ8úø¦F'½Ê¨J}”y|êŒå7§1¥ñÕŽ¦•¥‚’Ijx¢ÍX$Aèá«Å~ê¹hc©#Ô‘ŠøBS¶ø×äO]>ÕU#`Œ€0FÀ#`Œ€0FÀ|5öˆŒY{»[æÞWCÍ£·Ùñ…¥ü[¹âÅàÔDæü›Æ`Çß¿&޶ø8êyLêÚ ­8QUOð•û S1êæ M0וÖs§ªŒ‰ Ôy$ šëÑAoS–.XcÅèâÂ[-V ¢£Î˜ÆÄæ#`Œ€0FÀ#`Œ€0FÀ#Ð"Èy6G&¸5t;“#>$&%¼%OƒücƒÑ "2PuD„ ¤£|©Ô$‰~|PWæ%v’è5iìè°Š£þØ•l¥¯%>c,FÀ#`Œ€0FÀ#`Œ€0F Å!P¸$‡y7gþ­”ÿƒ÷ƒÏ£dÞUEdb5#‘€¹MN*Šˆ‰¨ "s2[‘ø#>ýèe«ÉF~_ã[ñªÆß²3‹0FÀ#`Œ€0FÀ#`Œ€hyʪüW\•x5Ù‰GcÁÛk ¾¸=æRVj!(YvZ œJ—÷C*ÊND"ýèhSçÑžõœ=T%¾yð—·ñ#ÀkŠŸíÓC-FÀ#`Œ€0FÀ#`Œ€0F å P (ÅÁ5;þ-Ήù•òpxâöbµ²@ò5$¹(B‘qÒ3^d8d :&J‰.·Í¤Ã¿<ØaƒmþÄfê«)¾ Jà²#`Œ€0FÀ#`Œ€0FÀ´DZµ‚"KÒlù·8»JüŸøÀÂÊÅ–ïNZ9‚,„D`?Ñ+¸ÀDÁž6%‚^ýÔyD@Æj‘tA){l 6E^b[s|oñ.‹0FÀ#`Œ€0FÀ#`Œ@KD @PÂÃ5[þ-Î-çÿÄû)¹P}ᯅ d°ÈÂÜÀ@ŠT$ HLHEé±A¸IüðàCv±Z$4±A/Á–¸ÑñA)]#`Œ€0FÀ#`Œ€0FÀ´46mJ‰“Íš‹˜ÂŠÿƒ/·'þ¯*ì9áWɹÄ!:ú¨k”ÒC*"²¥D'[M{&h,6Ø*k½?òÙ`üì¦#wiŒ€0FÀ#`Œ€0FÀ#`Z…ä;¸0qjÍŽ‹sWGYÊÿ‰#¬ˆwNüU2‚äyˆS‘‹ED0ªD¯>ì©3&üa¨PÁŠ«I£XôaK)ãCqU¢O}Ù>ý¨²#`Œ€0FÀ#`Œ€0FÀ–ƒ@–|'ÞKe‘ÿŠ«Ù©ü[ñä4ˆd#b¶€0¤“…"£:‘Š/r=ãS~j,éÏmèÐÄT_“âû ʈ Å#`Œ€0FÀ#`Œ€0F E"PÈ „k¶ü[œ›ø»Rþ97(Ü a4À–'' !ERÆjêÓ>sìx6Ò…±Ú¶Ñx•è‡äókR|gPÖé¯FÀ#`Œ€0FÀ#`Œ€0-Be)oÖ¬ø·ˆjžˆXŽÿ« |NV2D¥DeKÒP0£ªã?'$sÂ’ s$ŒÑ\+FX¨â5:~–«X.€hælÜTÓ?X¾Ò*>]¾2ÌùtYñ‡ÚòÕkÃêõªúÜçµô‹ÕUm¶Uç’‡ùmÞR÷coÞâeaѲ/¶•û¯ì‡Ÿ­ÌïÓå«*úú*s.}}*q‡0FÀ#`Œ€0F`G M¨µôçk³åßâü %sÞN„eš< ¨&"«Ù@òˆ$T›’ñL€:‚ mË‹ÍÔO©ÌJùË ùQIœÜÆ4¿%ÝâýÙgŸ…Ï?ÿlš½¬]»6|òÉ'aݺueçÚPÙAÛAÉ[ ¦Ûaù-Êå´9 ÂßÞõT¸úÆß…oýôîðƒ_<~óÌ«aíý¨Ø¶Ë¹ý¹×Â_ÿvLØIGÈÉïÇxù›G‹A6mÞÞþðÓ"AHÇ¿<ð\²›ùñâ¢ÝöªÜ;ñ4¿/ÖÔ½ÇÀææ§&o¯pö»nã¦4¿»Æ¿^qìW™sþúT à#`Œ€0FÀ#`ŒÀn€@áøÂfͿŗ!çíxUÄ9Ѝ¬úJÕÄb<ä$!Œ¨HIê ŠŽ¶t´éÓXôÄ„x¤”JÙÇ®¢Oê>°ÓC[ñ¨#j«ÞŠ,Ÿ–@R~úé§gï½÷ݺuK‹ùª_~ô£…ü νöÚ+ì³Ï>aøðáaРA_Éý»ï¾žzê©pÙe—…Áƒoåëý÷ßO<ñD¸øâ‹Ã¡‡ºUÿŽPlÞ¼9ÜvÛmá ƒ W\qÅŽéMDà÷“f„û&MíÚ´Ãõ ½ºv ,ú,<5õ½Ð£sÇpñ‰G4ÑsmÃ:´kútëöéÚ¹8àÍù‹Âÿwÿsá¶?»2tn¿gÒا{øø³¡Ç^‹v®#`Œ€0FÀ#`Œ€Øž”lñ†„ÿ¢„#—F!ˇ>qq›JĦ”C£?úó$Bü#«˜”Ò1Fó`¬ôšíªR A™/\‹Òd š0¥üÒSÊ8êèÕÆ߈Æb#ÐÐñÐfe»ø(&c©c£2VS›xÉgK"';vìúöí˶™´mÛ6üñÉߊ+œ9sÂ}÷ÝŽ;î¸pÖYgm³8vdšŠÀ‚Hø=0ùͰW‡=ÃßýÑÙ¡¯?ôo}øIÒwŸ¦º®yÜžmÛ„Ÿ^sQ=û–®¨×¦qõÃÓ³U‡FÀ#`Œ€0FÀ#`¶­['î‹Gï_&n ¾ m@TŠ8o&6v<‰?‹¥ø8JÆS2¿_.>:ì)ñ)ÞßšS¬VÒä‹Ñä†0aM$_¤ì°Q ü`K‰È Õy”èäS6Œ×ü |âa‹àÑ\ô¢D’¹ygP*srràÀ¡ðMW·šmð•s FŒQô´jÕªp÷Ýw‡)S¦„!C†„~ýúû\1;±ÓÞÛ¨·„Kb–dNN2—#è]œoî'^›ÆMŸ–­\îÛ=‘…ƒz÷HgGþÕíc•§ Óç. S>ø8eCÞpÞIað~=“Å+V…›žx)ff. ŒY_rÎå_ÄíÝû÷Ü;ü__‘Ó±ÓÞKãþþžqóŸ®:/üöù×Ã+ïþùÛ£C׎íêuëÃíϾ¦Ï[3µC8zà~á;£Ž ölWÓœæÆ3ï?5ÌŠsjß®M¸ü¤¡áœc¶ÎH.‚P¨üëCÂÂe+Â?üñ¹‰ØE=î÷ÃC¯¼þç%#À}þ@ò2Ç[Ÿ™fÄ9¶ªg=(\zÒ‘¡UœðÄw憇_~+\5rX¸'n)_ϸ¹×€0FÀ#`Œ€0»ì-H9þ‹.ôâËàÊà×àÒ(y¸2qsèÄãQÒÇ«ErRcå“8';µ5^¶èù¨kUø*â¯BwRãˆG P'5 üä¾T§Ä$5^}øÁÒÇ#2Ú”y|tŠ_HƒÃšŸlor²ÜŠ;wîÎ8ãŒÔõꫯ¦rýúõi»öM7Ý~ò“Ÿ„G}4lØPwYgL>òÈ#ág?ûY¸ùæ›Ã„ êmg wÞygøéOxà€¯\¾øâ‹pï½÷¦~Ê5kÖ»,Xîºë®ðoÿöoáW¿úUxýõúgÙ½ñÆá7¿ùMøñœ¶j/^\wîsb.3gÎ /¼ðB¸ñÆÛo¾™ü¢»å–[’ϧŸ~ºË•æ‹ÀÇŸ-O“ƒÜ«&D"s 7ijO|@˜µpiøÿ{lº¤eK¼HfI$ o~rr«ŽñœE‰tÛÊÛGR9¨O0¸o¯T_σĎxü0úÇûž ÞžˆÄfßî]ÃóoÎÿ·…#µÌé_x>,Y¹:œqä Ð6þGì×ã^IÏ$U¾5°OÚj>iæ¼¢Õ“q;<[äsr2Í1®ûµHØžzØÀ„Û}q;=[çÎ÷üpéòð“‡'¤qý{í^õQxèåÂû)žµyËØ—C·ˆÇi‡L1öè !ÿ¹Šï÷,N¾?‹ûüëCãËžÊÖø¿»çéðÅÚuiË~§öíV“µ¼>Å…ºbŒ€0FÀ#`Œ€ØÍ(ü &žL¼(¨N¹Sù·*ñ™·ÈËX-/u—ï+Õ²P $¡PŠ4ÄFL©l`h5lI1¥ x´%èòÌKùML[|“â£øz1òX_¶j¥×JašG¹3ÈI­¼ÿþ©ºtéÒTBò½ýöÛá°Ã l§Þ¥K—0räÈDH¾óÎ;á„N`Éñ“N:I®ÂäÉ“Ãᇞ Î|饗Š(FšœAyÀ‘Pš3±ÆŽ —\rIàB ÈI|wîܹB‘33Ù~þÑG…'Ÿ|2•Ù§OŸ4§‡~8\{íµ)ódÞË–- ®:u . ØpŽ'>æÍ›Wœ§+ÍÅ+ênÅî±W§Š“ä&ë#AÙµSûðók/mZ· 'r@¼´æçTèô@IDATùðè«o‡?:íè4vhÿ>áÿ¹òÌT‡|ƒ<#ÛrN$#¹úÒ˜¥ùÍǤþÏ#ÉÅ<åÂpú¼…aÑç+Sv¡Î Ìm§Í^üž}ôàpÝ9'¤®ÿ3)¼ K¶¦+´Òœ8ÇòŸ¯®ËÄlßÇØ7üÓïŸM™ŽÆìÐjrZ$ïˆÙœÞžÎvH"?ŠDãwcöf.SgœÏoœzT¸üä¡© žY‘çR4½æ¬ãSfåº ›Âw~oxç£OSß¡ýö 7_I<´îlN~)BÂòšuéXw.çÁ}z†Œ™œ§‘yÏ Ó”HrŽ8üÀ¢*dj’)K†gß]Ãè8ïkþ㾄X5æõ©çØ #`Œ€0FÀ#`ŒÀ.Ž@áøBñ_”ðc^ÔõˆS’ m¤Ô޶¸6Æ"pipmˆ|SGÇC¿â0^6êªä3·S l»²R A)G .GL‚ø N`&Ò3F€HÇBú4YΖ¤Ž/ìdÃXêèêê£-ŸÔaÉ|ó×sn›ÍC ùØjÍ7ئM›Â|Ðàĸàf¿ýªg˜5è¤`Ю]»ÀɇŒ92œrJÜÒ·™C@- aˆž[°Ñ³Mœíâèr9÷ÜsÃQG•2.±˜Ëé§ŸžÈMt·Þzk€Äܸqc"6!#¿ño„Ò•ÿýßÿ=Lš4)‘‹ûï¿øÁ~ºvíšÜ1¯3f¤9ï¹g1²råÊDXöìY·…—ÌO„‹{¸ "•ŒPKóF¢rjÙª5q[qÝë]:c²ì Ù: 9‰ ‰äot¶ K:wàGIpé Âg²‘C÷ß7•|iÝŠÑM—y…¸‡ÅyH‹þ!(ç/þ¼HPVšë&Ssõº ‰DœÇ + ·vËg¹’K}N2 <÷æqëõŠ0)nÕ&³””Ï—Þ_$c!W¯¯Ë–ï½ ¸±Í¼[çaeœ“rò“HÔ’É\² EPvö:ëWÄ*¯W©0ˆØ›Ÿ|©ØßÖÉ·ÆoË×§Ä#`Œ€0FÀ#`Œ@ G ð7œÈŠ÷¢'ŽŽ~¸2ø9D¼¶Ø 'q;Ú²—OéêþðŽQð'þ ŠÉ˜<cé4N±êz+|­… ÔPÂ1åAÇÄбHêèóàÊrD§SçÁ¶tœüª?š$Á7ñð‡ ‹0òCÉØ47H°æšEç¸S„íÛ<Ý»×eiq‘ä#ˆË—/O䥶bsiú_üâaèСáØcM„¥&Þ¡C‡T…ð$“qíÚº-²ê'SÒ»wïtS9Û¾Ù®Íë¢309{bâBròóÏ?dz²­a^"(¹[ä$}K–,I}“ˆ_÷C³ÿ²_÷.)c3ûÅ̺r²!ùHÇx¶£dÏø=Û:’•ëã–ï†d]$7¶@o+QÜ|N:·qCÉù–•bÞûâôðØ”wÒ¼{3ø™q«:%[Ì_zw^8áŠçQ*?¶Ï“µ(Ù³F wo|ôÅ”MºïÞ{w€X,'ñ.IÊõÓ¹‡fIRxà‰¨c›7ÄàÀÛþr€©$óçÏO]½zÕ«G{̘1)«³GxyH]¾*åˆ0ö«HßB\æ4lPßäŠs1‘>5Ì Böþ—f¶__/­!³ñOùP_Ë.ÿáì˧ãå8d6þ ^^S*ÂbËq+ÿ=aZÚ®þãï\.%z8^ÂóߦÆ×¢¼§–Ôeªæ¯¡,™ Y”dYrÁQ.³?©Ãm[¾>¹×À®€À!}:ï ËðŒ€0FÀ#Ðlhÿ.å¿ HøëLY;”´ùcrîŒ:%"þ ;|É=6ðiŒ×ƒžñŠKI=¶ùÇzl(5ž~|V•ZØÒKK¢£äA¤C¯… Ï&?øgØ1ŽEäuúµ8ô<«iŒì±Ó\ð¹dX)‘Å -ûî[G’@RB ¸cHJ¶–?ÿüóiùÇ|ÚÒýÐC¥lÊï}ï{)ñ¶Ûn+^fÈöîSO=5<ûì³áµ×^K•MÁLH^ 2#ÉÞäüHt"JÉÔ$ë‘~Ϊ„8½æškX½üòËiÞÕ^K².?þøãD°Bvf·\5eº³ƒàFíQG”²ÿúŽ1ñ‚›Ò6ã·?ü4¼¿pI¸~ã‰ébÎ…|~Æé&n¶z“yˆœ{Ì! ÎtØýÂm{¼–.éo§æ&onû®&Ú"þÌô"AÚ9žyÙ¿žyÝ<§¦®¹P‡‹^ž‹óëÕ¥S‰Öu dvêBœ.—Ëh=õ‚4Ð8ëèƒÓ Ý¢"^ó!ÃõK·™Cbr9Ä ëéù½xîdCR7Ç/ãÙ‘_¦ó6'Íœ»Õ·ãy•ÉíÚ¶¼úNà¼ÎãÚ+»ÑÆÄLÏù῞š.<öÐÔÏíàWœrT$¦ÿúlÀ #`Œ€0FÀ#`ŒÀ.ŠÜV¾ˆó‚ÿ¢sv"£:ñh9w†N„¤84‘ˆø .þMãT2Vq)}šX¡¿lÄß¡ÓxõoUÖBP*Î9Õ41ôm†P—^±'| äÒ1è‰ÏC] §$•ýžÔñ“Ï“¬Kbl©FhÅþ*;‚¤$‘ËdΜäüK¶Is‰ Û«•­Nl£ž={v" 9w¤Î%7.¡a+x-2uêÔ”¹hÑ¢À­Ý\¨Ã–òáǧ³7üñQʼØú’rõ꺋SØ¢Ï9˜\ÔÓ°å›30!7¦M›ÖÐ÷7¾?ú¤QùT$¬8Ñ =nņD#Ãá˜V±ãÅH’kvàRÿã‚S1·rmýÛãK—µo$¯9ó¸pWÌþûi¼±ú˜˜ñxiÌZ|`rÝmÕ¥ö´GÄ‹h^[Éäfìáõ«gÆy{Å™á—c_ wÇ‹anûæÂœ¶q uCåqñ¦ñãšÙæMf"¤.Ö©¨Jã¨û¥Þ³²mÓ¹9óø_—Šs|9Üã@¢²ÕûªÓ‡åfë ?4ÌŒ7yÿ¯;ŸHåp±ÎGã§Ælüadñ=߈ «RöëÏ?9€M©pfèŸ]xjÄsZà2!Îç϶þž‘ÔmìëSêßm#`Œ€0FÀ#`ŒÀ®Š@FPŠGc©âÂ(¥ç¯5þ £ !I[¼Y¬y5ê#=¼\¼B±ø‘OÚÆç$§x9ÙÊöù8¯W¬!‹J ò ÓDD8Š”Œ]uä`¡”-㈗OX‹•NóaŒêŠ){tHÞøÁFãûÅ,ºG›ûY„ÛëFïýèGÅ­Ñ“t®#ç36,@æIÆŸ.­¡Íös¶TsãöøÃ0nܸ0}úô´%›mß\zÃxÈ¿§žz*]HˆÜtÓMi‹õõ×_ŸÆp 7¶Œ‡hÄ÷…^tn%ú‰'¦3'ÙF~ÄG„Q£F%“ìÉ|0‘¦dTrö%™ŸW]uUʾ¼ñÆÓíß_ÿú×Sl¾°mü±ÇKñ°Õûä“ON$(ñ®¸âŠ¢+µ#@ŠûŽÞV¸±p~#äZ9á‡gJrQLc…L@γlÌX.çiÈ>Í9þäá¢šÆ ä*þuùOcÆÿw$F™¤·|ÿ²xiMݱ •Æ3Çq[»ÎɬdWª‡Ô\ç¸Wô¯Ê¥6ü²\o¯Õ7—ô´‹X•¾ÆMy}Jçâ¶ØÑðs²Ϻó˜·WìùK×îðŸÅÛk-ökŒ€0FÀæŠÀŽø\·bÙÒ@ÒIb•ê¤/’ .îêˆGò] âßÅprðqÔ!E6R1«É®,·§®|à‹?hËŸ~²G—÷Åf‘×Ã[úÅAЇoý¡,;ú¶’Jo–ÊiN$6Áù¢M]c`X D-€~g²òM´y°g,"ŠE[~d>èÛ/”7w‚’ ‹¤ä5ÏV¤oGä$¢ÎÌc¢ç\J‹y_-u²4‘JçCâ»\ÜDzij(•ÍYK,lX ¯y~¾e­cmWAPÖŸ[•€8½á¿Hàüå×FT2³ÞíŒÀŽø k‚r;¿ˆvoŒ€0FÀˆÀŽø\×T‚’ãúâQy_‹Óü8>}pe""áШËmˆ83tØÑÏ“„Ô±«#lêú©£cŒ|hœø9Úù Íx8|ˆD'‘_µ·*܈0Ô$TjráabØj"è°åÁV×xú™¸æÀ–mÈÌÆ “¯XMþ(‰ƒ-¥â‡6ezÄ<Çv³¶{Càí,2•mו„95•œÄg%bRñÊ‘“ôñÚ5–œd\µµÐo1»”ן{b8´_Ýy¶»Âš¼#`Œ€0FÀ#`Œ€ØŒ+ÿE)^L¼›J¸681Úâàrüý9ÿ?—ë4>·%®HHâç¢øøR|ú“ûE·•ˆܪ#S”:! Î&C`„ó ©«Ÿ cǤMŽ=vħ­ÉãO ¥_`klTIIêôcÇ£ó(©#ø){êh®_¸AÛbŒ€¨î;†“‡ ¨ÅÔ6FÀ#`Œ€0FÀ#Ђ(\œó_Œ¥ü—¸:qh”œ›ø?2,ÑçQGð#þM1ð§&Ÿôó!åø?ôØ ¥ññƒ®*G§Éâ ’09&D ꌡÍyMœ`Z ö¹ÙÈŸ&§‰ÒÆãòISg,`2–~ÚÓ˜øÑÜbŒ€0FÀ#`Œ€0FÀ#`ZÙvâÖš3ÿÆÅ RÿWðœ@¬fÈÂEªQˆ#Ôó6:õQ*Ä"~rûÜß AdO]ñ!,µPÍýa[ñÐÑÔé/FÀ#`Œ€0FÀ#`Œ€0F #P¸0'çÛ¶â¿âôÅíþ­$~)ŸÇ|«Jƒq4d¡ì´HHBêôQç!8OÞÆ†Á‡&H›±J¥÷AH2Ž"R„%:ü4&~4·#`Œ€0FÀ#`Œ€0FÀ–‡@a‹wη5Gþ­ÿÇÜ«Šö W3"€‚ävr.b2‘:"m‹±šÚèñE\ÙË¿bÈc©çzÕ£:‘”øÀ'vŠÇ8ŧ´#`Œ€0FÀ#`Œ€0FÀ‰@a‹7Ü—ø/ø18/ø5’ÿ¨£ãC¨Ã‘Ñ/~ ¢’qô)Pz•ØæüŸâS2–GܜƔÆW;šV5$¥¡ÍDAÁs¢Sö¥~¥'f^ÇN¤¢æC?þµ-}SâÇa#`Œ€0FÀ#`Œ€0FÀ#ÐrØch°$-W˜óÅh!¥¥ÁR}Þ.%C0‘ŒÔœqØK'=º¶…>GÚò+1ºøUö$uâñh\SâÇá#`Œ€0FÀ#`Œ€0FÀ#Ð"צÉ7Gþ9A¢R*ÁPüŸæ]¶„ì«EDFb+‘:AÈCùRIÒÏ8겇”Dσ-zúEZRGÔ¯ºôµÄOüÅ#`Œ€0FÀ#`Œ€0FÀ´4 —ä0mñiÔ›ÿVÊÿ1?øâøô£—]¬IMêô!§ØŠG?ÚõâoÙ‚ÚbŒ€0FÀ#`Œ€0FÀ#`Z‚²*ÿW%^MvâÑXðvç߈ϜªJ-% ÂN ”Séò~ÈAÙ‰H¤mê<¤{*{2V“¨ß<øËÛøà5ÅÏöéÇ¡#`Œ€0FÀ#`Œ€0FÀ#Ðr(”âÇàÆšÿçÄüJù?8;µÕŸ2(é°#`Œ€0FÀ#`Œ€0FÀ–†@Éo¦¿ÿuðfâÚv8ÿV_¼¥ø¼X­,9XÉJ ¨HEìG› G‚ñH‰ôMJ¦T™”E›ñ3V‹¶ø‘­|ÒF(Ôß”u€ù«0FÀ#`Œ€0FÀ#`Œ@ËC uëD‡5kþ-¢ o'NÞŽ:Ü_ÎÿÅfya@C‚œSi)ÂPd¢À‚„ƶÈJli+¦|`+2]>qÚŒc e£ãgW±Çá#`Œ€0FÀ#`Œ€0FÀ#Ðrؼ:,I³åßâìàíàôÄÿå\_š|µ/" «Ù@ò‘ˆ ,Ò1÷¥:%IWŸÈGLúx¨ÓÏ#¿y|tŠo‚2"f1FÀ#`Œ€0FÀ#`Œ€h‘”lñ¯ÆZT§Ü©ü[•øpypˆUE ©jTèd¡z É|$ˆHCJúsâvšm.Ç¡$6¥Ú™øF‡hu| ðšã·jÕ˜%ÆbŒ€0FÀ#`Œ€0FÀ#`š…ã áÊÄ™5;þ-έ”ÿS²"sÏWÐZØ»Üöz¤×n£cBÔ!±Õ$(ÑÉ[ìd«ñØÑ‡àKqh+6¥ôò§ñ¥ñg1FÀ#`Œ€ØðþO?ý4,_¾¼b´Å‹‡eË–Uì¯Ö±nݺ䟲’¬]»6¬_¿¾Rw“õ›6ñ1skaÍÙÖ«­ ¾‚†˜«W¯þ <Ô#`ŒÀŽERgΧ˧ËWòùʰ%þ¾´4 AÙÿ%€)w&ÿ¦øâîàï V« ‹«Uä Ç" щdñÔ5Ù·:&Ç8ŽÔå§tã«þXM‚oôøS|ü¢Ç‡üP26Í«ØEѰ#`Œ€0F €À¬Y³Âƒ>X?ÿó?íÛ·/ÛW‹rÆ áöÛo‡rH¸øâ‹Ë¹óÎ;Cï޽÷¾õ­²ýÕ”³gÏ?þx¸à‚ ÂGQÏ"ïþûïŸ|òIØwß}Ãw¿ûÝzýMm¼òÊ+áÕW_ kÖ¬ {ï½w8묳 Aƒ’»iÓ¦…çž{.”ÇsL8û쳋afΜ™æzÅW„õµVž}öÙðÚk¯%ó믿>Å®ul%;ˆÔ „~ýúù3r%¬7FÀzL™õQøñCãëéÔ¸íÏ® Ûï©f*7oÞþú·cÂðAýÂß\vF÷Æûá—O¿Î>$üÉ™Ç%›¹‘ÀÜ»S‡Ð­s‡zcÝ(@ „ÿBà½ØŒù¯X‡/Ãüœ84£¨M]\uñoô#êχ^sQ|âÀ1R¢S¬¦ñèè«(µ”š8Žô(Ÿ¨ôèd¯‰ z‘‹²Å¶´µXìd«iA"$ikA‹NñðÃø|þàB#`Œ€0FÀ”A’ðÀ¬×Ó¦M-ë i6É“''rò¢‹.Új]Mäo¼ÆŸÉÁƒ‡)S¦„x |ç;ß ]»v Ï<óL8ï¼óÂ>û쓈Ù!C†„ý÷ß?|ñÅaìØ±aذaM"'?þøãDNyä‘á”SNI±šº†|ÜüùóÃïÿûðU‰èܧëFÀ#°{ 0¨wpÌ}ë-¶] Ÿútï:µoèÕ-…¼ù»{žWÎ9zp=nTE ”ÿJx2x4„’6üèàΨS"â߰×ìÑcŸÆx=诸”´Ñc+‘ü¡Ç†RãéÇgU©å¨i‚¥%ÑQò Ò¡×BÐç “ü3ìÇ"ò:ýZzÕ4FöØi.øÜƒ-7¦[‹0FÀ#`Œ@>}ú„ÓN;m—ÁcéÒ¥)ûó°ÃÛfk‚ìСC¸ä’KBëÖ­˜Ýzë­‰<>|x`·ÎD2OÙºNvâ˜1cÂ^{íN?ýô&ÍeÉ’%iÜᇾÍÈI‚‘Å#`Œ@S8¨OÏðSjôÐ#ènÿ³oÇ-ýbuX»A €Eµ+UÈ.Éçÿ‡–sv"ñ$~ {‰Iqh"ñA]ü›87•ŒW\JÅFÏôø•`#þÆ««²‚RpŽÈ© ‰¡Gh³0„ºôŠÅ8éä“å:ÕÑŸ‡ºNÉws»‚?ù<ɺÄÇ““‹0FÀ#`ç7ÞvÛm)soÞ¼y-ád Ž=:ì·ß~É™…loþì³ÏBÏž=Ãù矟²âŽÌ÷ß~;´k×.œsÎ9¡ÿþê®Wržä¸qã±Èà:th8ùä“Ó?™ñÃVçwÞy'ùQüzbãᇠ.L[­ÙbNF#sÂ7ãçΛü 80œyæ™aÏ=÷LçT²NHDˆ»©S§¦>m_µjU"!ÈRæÃë}饗¦lRƱ­Ÿ¬Q¶¾wéÒ%ùó#`Œ€ؽxéÝyáwßËW¯ §: ÞâßšÿI¸ù©ÉáÊS††^]:‡;Æ¿žúœüVxvú¬ðݸíûÐ~ûÔãF}2‚R<âÂ(¥‡/ƒ£ !I›'ïÍ$ÒÑÀ^ . Þ¡Ž?òI[Âøœä/'[ùÁ>§ñõJ ®§,i$¢/êdËÐéÁœ:“ÒC[‹Ó8”““²¥{„1zh+eîû|\Åø|¨µ#`Œ€0FÀlÀçŸ8'QÏÆuÙ |~Z±bExòÉ'ÁuðÁ'¢¢ aû1Û—;wî ­ )yä‘Pøðœl>øàƒdǶh¶;sæe¥‹k~÷»ßì!±Ÿ4iR" qô /¤:™‹Ä‚,'œy éˆ@ÈA$"÷Þ{oxë­·R»{÷c‹3¢u¾øâ‹á¥—^JdbN®\Yw¸?ëÌ…6¤"¶‚¬íW¿úUʤ$ÛrâĉáÔSO­GØæã©¿üòˉ8sð…\½ë®»ÒåBd`2W¤[·ni="HÑ3ÛÌÁ•-àôABZ"›—Ì…-æ¼–ÅdtBv"½"{«Í[ÖÊÙŸ¼Æ¼†:uJ¯go2ÏO<1]äãìLв#`v]}þE˜4s^ñY¿š'„Y‹–†6où2\pì¡áÃ%õ/Ê[/|[²bUX³~cÜêÝ6tíXwÖuïnÃ྽â–äžYª!}ƪÈÅñ9o¶Ãù·’øâóX–ø?êEY b‡%’Pd"c Õ(ztL„’X€E]}´±G$öò.gjñA¿Dv´_}blÕviŒ€0FÀ#!@Æ"äûßÿ~‘ÀBÇÅ.dî!dÉA"BÚA Ýpà ŭÇ|h&[" R üâv³@èA42’1ˆ/ˆ5¶š“ˆpøôéÓÃÑG2þ Á4»'žx"w‘ê‡zh"2!ÐtQ ¾¹0?çž{n²ã‚È:ˆN‘˜¬é{ßû^Ê Ìs)"âS}´!7ÉÎ$3”ØàÂ@¬bpΜ9iídGæÂXHAˆ¾ë®».Œœ]É?d_âó ƒ œ ª yäL¿ýío§ñdn80+I–(k"{’Ë|¾ùÍo¦ÌG°Õ®"l ¦ÉþdKz-s!.7£3æ„NHÓ`žÈqÇ—Îû”>)ýÅ#`vIfÌ[x$ÿuÃ¥a϶m¯¿›Hœ¿ºdd°O·pñ ‡‡«~vÌê•öéÎzP˜6gA8eÈ€p¶Ï ¬‡O¥Fá÷xοÁÁ׉S£ ‡F)Þ >Müv²ÅŽ:¼™l£:>G¿ÆP¢£¤_H½\|ùQ‰]E!XC"Â0Ÿc´p&ÈÃİš8ã°ÕÄå‡~‘±š¶ló/Ý|âŒÑvmSIl)Ÿ8´)Ó£c±m1FÀ#`Œ€È€øâEIž=ˆ.¿Í[}rdⱕ¢‹ìIt„žJHI}ãFm³T”õG'„"‚D#%Ä(ÄŸB®VY¼xq2eû±„:%}"(ÁmË¥"¾Ÿ~Æä±Ðm%µ”¥Nˆs„É!'—ºúYúh:בL=²ðª‰²l{RÑ\}Ü&.”N¶"CN"M½Y\[Öó HÕ!%šƒÚ*!¶8çB;'`é›7o^º8ç‚ .HçB’úƒü ¼ûî»i+÷ñÇ_$ [sa<•ãÿbwòIY_¼^ÕŒš * “Ã9œ2A¨‰jâê]ʼnQGd#_jÓ'>Y4~ôÝ­:mæBÉXÃXù¢Ÿ:¥ÈÏXµ#`Œ€0FÀl+8ßíÇßýîwÙ‘lWž0aBE÷:›íÙ¥"RBí¨£êß ºzõêdÙ')|@W³jÙ£GÔÏùŽÚ&Í¥2H¹¹¤Žì $$óc[4q!¹]›ùè"Ì!lÙvNÖ"zH;.‹!Ã| Y‹ˆDH]HQÍdACx «$¡œÓÉ9—l‹' ò–[nIæò­¬ÊJ>¤—}SæÂÙ™S¦LIŸ/=öØâ4èǷι„ôc«5Û½¹Í›µ0 ëär›3f„1cÆ„¤lHú9»²!þØGçAÒoÎùäb ²,ÙFNâ“ËlDÌ‚7u.jÊ\¸(‰-ù²ÊÚdÝ#`Œ€Øuà2œ{âMݹ\vÒ‘áØƒú…™/¿÷J8þàýø7ÞÏM¶ª÷éÖ%é8‡²Uü‡Ýé‡Ú¶ ´•¹Â.8/ñ]ª‹x'õQÒן¦v¬¦6%‚àsÎO|ýÒÿa 'þµ©cƒÈ–z)Ÿ‡®¬ÔBP2YœëÑ"™}€AŸ¾›˜«E"‘:¶ˆÆ’ŠàOˆC)"’>Åôåu…-uÅ¢m1FÀ#`Œ€È€\äÉ…­È äÕwÜ‘ÈEÎ$«2—þýû'ò“ì>ˆ°óÏ??‘„¹ uµË/¿<‘€j|'óqäÈ‘Éô¼óÎKäà3Ï<“.~á<ÇqãÆ¥¾†¾@Jr¹¢2<¹á"‘¸µ”\°Ã™Ž¬r©leÖ¹šlož5kVZƒÎ¤$“ò½÷Þ 7ß|s"'G½Õ?ÍÏ<ó̤ƒŒ…@„4½ð ƒÎ³¬¶6ˆÞÞ½{'’yA¢êbµ±c: ç®§ÇÇ^aƒÄúÚ(×rÔ»ÛSz‚Dzz¥¿¡I“n—\ÙÐߦK`Q/Ó7Š<ÒØ¨“Kº\ªf ”ŠÃ§ù5¹5¿ÖÃüš‡˜ªÆÀÖŒÄJªRªÎxêäÒBð u™r¯: ÖxJµ•—ãÐÍÇXrjc¸ä‘~ɽ,µm&`ËAà©—çÄž¾±ãƒÏíÝêϕ嘩c y)í4š;w~¬³öè4;ÆtüU¾’œ5kNŒ=²|°DÇ¿+ßAG&0~êü9¸éÔê·ê>˜cìæ?‹ßª¹Ú’!‘“uLµ±ÄðÉâû «Åɇ¨Æ/àÕâ «ù5¶µ’Üi…G¤ZÒ¢¿­k žÝ’­½²­yµ0ø#ÂÖº'XrßÕ¸Õš³–_sV–¬ü­Ýcå8·MÀLÀV>ã½U¿W,\¼$Óµ ’Lë¶`Qú7*½»²Ö» ëex+oõïu3§MÍpåžkÝ·üü%ãé‰ô߃S{BºøPN'Ú›vQJ0Ô‡vJô2ú©óÑ#ÝŒÃ(‰¡T>JâÑ×(éc¬úS5¡Ä”›~¬X*GSO¯üW£E³`-šqP“R—)^m•ò3g±N¿DE­‡~.òs£sjìòÌŸ“ø‹ ˜€ ,/ߟ~V|â¿ØÐŽ›Fæxö¹qñÚ´i„¾ãceqÖyççïÁŒ´#Ëf&°r ðËs=q’ÕSM«µrv5ÖŠ¯å¯•«ÒOîZB^el­v[×@|#Â][ój}œ˜^ïžè«•»­~ÍYY²†Fî±rœÛ&`&`ï<=ºuFÅIî¾g÷®5Eºw»#‰–)KGÐߤJÃãæÑñêšÁzA• c˜L"#uMNâå“;))¹”+U³¢‹¼–ªÃ¥qÓ–ùÓp› ˜€ ´ü5ì '|#n¿óîö³¨•´³XIà=­ ˜€ ˜€ ˜€ ˜€ ˜@G" ­MkF7“¯½èo¬ Uš%Z^]k5 4Zb M ˆÔ™ÓvNêÊ©E‰~ÆQ×öNâØfŠŸ‹xâèXê˜úU—¿‘ùs10öDàÕt ì¼yóÛÓ’VÚZÌb¥¡÷Ä&`&`&`&`&`€@áqïö¬¿Uêè~èy”¬»®·†Ö D”`¨‰Š ñ'eŒ„FJ„H|•¹X$¦xêº)úƒi šc”?÷#‘3çãtI?v’¨ØLÀªø×ÕÿŽË¯¸*ÆOœ£F¯~ñ ±ÎèQ9öåW^‰ŸœòëxòégbìºëÄ‚¼Wø ã±ä?žuN<ùÔ3ùtÚ|øˆ8äÀýsÀI?ùE~ÿÊN;lg§Ç’§NŸ‡¾÷À8þ£GŃ?8ó¬÷× ÿW\óïøÜ'þ'6ÝxÃxìñ'sÎqéÄÚ¡éäÚOll¹ù¦åœ]Óévûî¹GüêÔ?ƨÃã¤oãjÿ¹ñæ8÷ü‹âäï+~bŸ|æ¹8÷´ßå÷\ô÷ÆUÿþO¼6czl¹é¦ñ…Ï|"¥Ó]±o¹-Î:÷¯Á»÷Û{Ϙ>}FôÐ?¾úŸó.¸(»>~úÃïÄðtÈÄâô^³~ü3±Åæ›ä~Æ×[ÿ%ÿ¼,.¹ìŠ˜›VØ|ãâsŸúx¼<ùÕš,ÈךqBîïNÿSÜûÀƒéƒN±íV[ÄgÿçøèÓ·OzÃÍ·¤û9?^I‚ð¨‘#bƒõÖÍþOü¸x~üøøÍO_œÆ c:2vØn›Ö¦t¿ ˜€ ˜€ ˜€ ˜€ ˜€ ¼­J¥ô/t/êz÷¤t7Jõ±>t2™´7ú©k,ZqÒßhs‘_ã)¥¿¥j9^sѧéÄIÇ£N¾b_3Ó›9+$ÐBƒùŠýZœn„Ò¯6u.„D­›MÕìW?¹¹È‡OmòpãXCóžÓoå¯&`&P"ðð£Ç)¿ý} <(öÙs÷xqÂÄøþÉ?‹eéñkþ¸ñåo|'{â©8è=ûÅZC‡Æ3I”Ñÿõïþ ^}ujì¿Ï^Ñ=‚ðëߟÏ<Û3=‰7Þz[ßÎÎãªÆ_.¼8n»ãÎè—ijUV˜S ºfl¼Áѯ_¿xáÅãÿ¾vbÌœ5;Ž<ü°èß·o|í;ß„RŒœwÞso|ïÇ?‹9Iä[eÕU²¿Ú—yéЂ “&Å׿}R<šDÏÞéaýÓɳ½èoñÇ?k­5$Ž>üñø“OÅ÷NþiNñÄSOÇI?ùy> âÃGã’€wû]w‡Þ÷8kÎܘüê+éÀ~47mõ×[ÿ=§žqV OBàQéަ͘ã“0X‹…ò×+ùGúË'~'®½þÆÄÇÃ׊«¯»>Nøî÷ó°çÇOˆþì—é4àUâ“Ç“Ná]ÒKó®:Å7OúQLŸ6#Žþàá1bØð¸û¾ûëMç>00000X)J¥ô1´±v§¿¥5±¾JýO¢)}u­‘”$‘Ð(AQâ¡ÚˆLÊE~.Þ;I)1‘…j±‹7 x|ô+–Z¹¸Ô&†ù¹ͯ¾®(› ˜@UìX¼àœÓcÍÕ×Èý¯'á krǽ0>8µû¨$^}ücGçþiÓ§Ç]÷Ü—ëìÌþï~“èÇÁ Ûlµeœðíï¥|ĺëŒÉ1œžú»_ü8”ÅÇc?õù¸î†›ã{'žî·Oε箻ÄA¼'ÇŸü‹_åÓVðí¯'ÁlXzÐ{ã}::®KÜGüPŽ™•ÄËO÷±øàÞŸÛ­}YuÕqúï~=ÓA ¬ç‚¿ý#ÆŒ?úÎ7óÐAƒW%oü„ ñ÷´»‘ø¾Ÿve²‹ôCiŽÞÿÁÖ¦(÷“›Ób«­¿gÏž9îð÷Ûn½e|èð7Ö_E9iÊé{ñô³Ïe~_úܧsäÉ?ÿU\{à Ê÷!ùÓÇë]73øÅoNC~ogåÝ¡ˆËG~ðufq— ˜€ ˜€ ˜€ ˜€ ˜€ ¬\…'ƒÑ¿0ô/´/ÚhfhbœÐ^†ÑG!³¨¿¥f9ž±ŠÃO[:%FÍA›:>ådnÍÏ\Åù• ópÕ´FJ%§”B«Iu£Z8m‹¥Í,‚cŠ¥Ž ÉÍ[\p1žñŒ!F7£¼ä$}Äà§$Wžßx'60š''%!ò…'Ĭ9srÜÌ™³âùç_ÈõÍңȲ®ù±ò†!<ÎIcžN»&Ÿ{þùÜ1½pº4 !5bDtK»,_~ernWûòܸqYìüÉ)¿)wóCoÒËoŒá¦#;´ÜßZåýIŒCœÄ^š<9?^=#‰sŸýÒW³oþüyM}/MŽÆ¿}z÷.?âŽðÚ«·þƒöß/§úÖNŽÝvÚ1;ä »Þ:mIß"v\‰ùfIh–mºÉ†Y ¤o£´3»ëÞ{cõÕgÑk Y3ztïž|«eAúÅ´Ó”]²ûìµGtNß3› ˜€ ˜€ ˜€ ˜€ ˜€ ´'%­‹ièh\2>¨J[C“hXÔÖˆYTP©¿Ç8b¨kélÄÓO©±Òß’«I+•š“RVÌ)_‹²ÑOŸµáÓ¢õ©]7ÃMpÃBt#ˆ‰´¹ÈE?q´1âÈI&?q\Œ—©­Üø5?õœ×;(Aa3¨F€÷òHó=÷?˜ß÷ÈCŒ]„óKï›ìÑ£{µ¡Ùwö_.ˆ‹Óû{ôì«•ÞáX38utIââ²eú±Ö2’w(vK¢àûí]î¤>zäÈr›J[~®þÒVÎ1|­¡ù‘v9˜wýônÆùóF÷:÷«øZe½õo¸þØøÁ·¾ç¦÷X^wãMqýM7ÇÒ.ζݺVºVý Ò#ÛXïÞM޷OSǹÙ5É;AÏùë…ùâ½”_û_ˆ^¥Ýœ¿üñIqúÙçÅï¼+~œƒè‘GÓ;Hÿ—460000xGèÒ¹SzåÑëé³ ’IÇ´Ž¾þå¡Î“jÉøÀÊJ.ô/‰”•ºZêÊú™âiKSIŸ44|2ê\Õô?íÐÔüä`nÍ_>´=Úš¯8Gr·4´f$”˜H¬þ+Ö(uc”äÔ ¥jŽgê¸OÖ\»,\ÆiÃXÙ°ôX÷³içæØuÖÉë‘ÿÍ*‡®¹fÞ¡9uê´ôˆù¾-ÒN{ó¾Ê9sçf¡oÉâ7î—`íÄä1óléçkÑZ[?qñøõ—¾þ͸ýŽ»š ”ͳ3W¯ó>KìɧŸ.çyüɧ³Co8Œç¦ÛnÏߣ=vÝ9ÖL÷,ã¢ï~ã«1#½“õÜúß;-P ŽK0000w$½»Å”Ù‹bµ~Ý;¤H‰8Éú¹w“•t-é_Ü:úºÚôQŠ~|ÒÞˆÁèW<1ôKSÃO¿45O®¬ãÏE ¥ò¨$WåüÉU^ƒÆ*†¾ªöÆ'µªÝÙ©$Z¼Å"4SY¼â´ TúB>Œ›"¦8?cðcÌI?¥ò1N>U©ù}‚w‚d3Aàõ%ÑiÜY±ìå§Œú_¿iå¯Ï›KÓÕiÒ•ÑyÈ~ñú˜ãÒOýøYþ»›6mzÞ-¹té²ü.Ãëo¾µœlû´³ïÔÓÎŒó/º$=öÜ+žH'yßsßåþiéÀÙ«S^¿ýã25Ë%¯˜8+à½ù&Ç—ü=û÷ßgï\rèv×Ý÷æŸSûî¹{ð8öM·Þ?ùÕoãðCÎBé¥ÿº*Ž9êÃé—[äøùÒ¥K—x_ÚQxÉeÿ ÞÕ¸{íxúö;ôÝØqÛm‚ƒƒ~uêi±ËŽÛÅåW]ÓlºµGÌí¿^tqz´ý€ø÷õ76믷þ^½zÆïº' ¾G,^ÌÞ"íZmz÷g5ÝÓ#Ø­Ù.;짯zn\zÅUÑ«GÏü¾É+¯½.ÖX}õØ~›­ƒGÙSÿ’NJ‡k¿~ýc1£Óû;M‡óô?¤C{öÞc·t²ù€4U§RZOkóºßLÀLÀLÀLÀ:*Á}»Ç¬ù‹câ´ù±`qóÏ]ážzvë“8Ù¿×»K ,’÷Ý Q’RúXªfSí ]L†þ¦qÅõKc¦Ü´¹£àôáSYœG¹U¦°lÒñ×4MP3 uT&¢-cQ´ • PøX$ ÖxÝTrå>â£XüÄ0NcЍã''F\q~ýש¼Ätñ;(Á`3vN ‰“ñøI±tÚÃuŠp¹ô¥«£ó‚I~+ýкÃjv–DÀ‡">õÿ/”sØ!Æié]”üðg·áç>õ?qFÚQùÝý4 ^[ÅÑ:"ÎK(c;m¿]¬·ÎÚÁcÞsøûßð4!ï3|ì‰'ò=þ䱋íRŒƒjØÕwËíwÄC?#‡M6Ú NüÊ—òœˆŠ[o¹yŒ1L)W¸<þ˜¤Ãr–Å5ÿ¹>¿«‘Çžy ºg9èÀx,í@¼ñæ[ÒîÆ;ó=Ýÿàß“]Ó»#YóM·Þžwžò^I£–Õ[?‡ÙÜ`v©òØùî»ìœÞCypZ‹…òÖ*=’„ÕS~wjœñçórØFŒ/~î³À9{öÜüžÉíÒ¡<ü[0åµ×²˜ùlz×'œ_œ81þï„ó÷›SÀ¿ú…ÏךÊ~0000xGà•ëì>|·í@ìèß¼ÒJ41ô7éaè_2 †ô£•qaÄGÉxLýÒØ#Í’KzuÍG<¹¤ËQÇG?>Å)reù57¾ªÆàÖŒ$$W,‹C`@#ŽºE¿'A}Ä‘¿JÕ¢K«XOÍò8‰’Ä#§êÃ’Ðð¯ìõ06xêå9±çƒ}›{noþ×~s­Ós§gáQY» Ú&ºŽ8$:÷k¿–Í~&¿xY,{í…D—¡ûÇëk¢Ü^Þ Âï¢ìß¿Õ×Að^JÞgØ;í¢¬fìÐë•úŠÇ#Ã=òX\Å?Ó»DÏôžÊj¯› wXVÎ2wÎÜèÖ½[ÚªÍ+cWZ÷tOk;aY{¿þýZÌ=Á‚ü(8y÷=ø°Øa»mʧ~㣿s§ÎQïýœµÖÏD}úô.ç'ŸL,–%æ<_i½Ó>•œ³hѢ̗ƒˆdß<éG19=Zæ©M‡qÏŸü—Ò÷ª[œzÊOss.KÂ7‡ÙL`yŒŸ:?F®þóayòUÃc‡4ÿY\-Î>0000å'Àçß·ú÷º™Ó¦¦'¸æÏ.Õ>²zùùü"ã3ë AƒØá11]èg' â5ùÐÇŠz}j«ÔØÔ•ûÈ¡ÉȃѦN>‰’ŒÇFG%—òч§ú4†<ʪ-MÂaËž7<$VrJLIYª_ Xu-VýŒã¢ŸO‘ÊÇ‚%*â#†~åÆG¾bú‰xúÉA.Õ;ó Õ77ùm&`íŒïœä±nY·1‰n£W3—]VÙ4¸¿pq,÷—ìcLgDÊ|'%»ù àßêÆ.ÆZâ$#ú'¯5c§_-«ÕÇÎÆFì ÃlÆNÏã?zT ÑÁÏÅú]庑YÜô"æ²_õ«]­¬µþZs’C,nM‡Ö°kµÒ+w©*¦š Ë#ë?ûõïâcŸøl¬¾úàxéåWbæìYñãï~KÃÊs–®˜€ ˜€ ˜€ ˜€ ˜€ ˜@;#еé]úÒˤ‘!>¢¡ah\íty@IDAT|hb:™„Cô5Õ¥™ÑOÚÒãðI_££i¼ú™Sšœæ'®8Ž:ëc]Ê“ªÕ­’dɨëÂG…pcŠÕ‹ctã€d~õ‘CëQnF1ä¯xͯ€xÍ»Ôâd¢a3öL ˆ£wN²s²Rœ,.½Û¨#bȩ́òNÊ<&ÑÇCÚMý#ú`|ÀÌ·|=W\rA‹9ØAùfâì·Nør 4èÍH×pŽwØ>ªÝW¯^mÛ¥vÀ~ûÄÆé±ùûx(ï’ÝgÝc§í¶Zâià t  ˜€ ˜€ ˜€ ˜€ ˜€ ¼xò/™4140idˆ„´ÑÔ¤¡—Q'¿„Léiø¨cÄK:_ž,•ŠÑ8JÍ¡ùKYG.âY#õº&A°nP©³¸H&ÐB©k"-V>Úôi,~攩8•ŠO!åœÔ1r§‹¶æ£Ž©­ºwPf,þbí—§uËx¬»5ë–b–õfl§Ñ­X9ý[l¶ÉÛ2ñ[ùh2Sï¹Û.oË}'A}³îkD:Ëf&`&`&`&`&`•@éqoig)ñIK£Ž!HÒ'-NB¥4³J þ¢Ñn'c¦ù5'¥|ŒÑ:+¿ÖA»®iQõ‚H†QjÑŒ£­I‹± ízLÕ¬”§ñRN†˜âÍ*V9ic”mžß;(3;1vKàõ…SÊkÓ;'ËŽ*•bLql•P»LÀLÀLÀLÀLÀLÀL Ã`G2t2ie´Û•þ–Öƒn'MO"'k,ê©YÝ(‰ÑÅDÔ%ZJ0 ÁB„$†¶ÄJbikNå V%¾âÂi3Ž1”mž¿øRÑ4Þf&`&`&`&`&`&`&`&`†‡·–¬Ýêoi}èvhzÒÿŠZ_iùµ ‰…µ#š„F‰ÄIDÔÄ‹¹T§dADõI|DÀ¤‹:ý\ÊKœæÇצù-P&b6hÇ:õX­¼:NënÍŠ1ű­s¿ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ tDxKWãVT§\©ú[ùÑòÐëšn¤nP©“Õ…HÈÎG&‘hHIQ¸ÄGœB›gá)™›Rm„L‰‘©ZGÜÞðüœÐk3h¿:¯ºyyqK^¼¬\¯UY\ˆ)Ž­o¿ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ td¥×¢ŸI3kwú[Z[¥þ§ÍЬ•µ×µFÔ»b"âuɯG¸™ ¢Ž(I¬A‰OñħX'Ž>Œ\š‡¶æ¦”_ù4¾r~ÆÙLÀÚ+5÷‰Nø_:ý0H‡ß,~áo5Wºø…‹ó Þä1iì›i¥“Ѫ¦dKý’%üxii‹ów—7ßXÏœ9³Ó)çú1Z{Ö¶`þüÚî100000èJekú—>8®lýMóK»C¿CX­kÜ\£¦d$–PˆO â#u-@ñÝ’Å1N‚#uå©Çxƪ?U³‘?ù4?yñ“Cy(›×Æ|ï¢L4l&ÐN ¼Þkxt²_,}éê¼ÂÅã΋e3Ÿ NôÖ8<ÖÍÎÉe¥Ó» d cß,»óŽÿÆ]éúä§?={õ*§]´hQüçÚÇóãžËBá°áÃcßýöÞ}úäöUWü+fÏž‹-޽÷Ý/† Z{ù¥ÿˆ#FÆæ[nUö5Zy~ܸø÷5W¥Ó¬ûÄ~û«¯±Fyè bêÔ©±Vátêk¯¹:&Lx1¯¿èŠ ˜€ ˜€ ˜€ ˜€ ˜€ ˜@‡'P(Ñ¿0t/í’)ë_ɇ^F >ô743.ihGS›º´8êä@S£S> ¿Ö¢ù™‘ŸúS5ÇG_MkD ÔÂI¤‹‰Š •Ÿâµp à§-±’xrK]7KœâS5ÇH¤­ÒX|š<Œ/®Áâ$„l&ÐÎ ¼>æ¸è¼`R,›öp^);)¹jYçU7 ƼÆîÄ›n¸>žzêɪéþæÏŸÇ}üÑ9œvÝ¿¯‰Ë’ðøá£>ãž{6‹”:òèxù¥—âÖ[nŠ#>tdÎsÏ]wFç´3tyÄIÜ|㠱Î;Åf›oÑb]'Nˆ'Ÿx¢™@¹ÑÆ›4G[ ²ÃLÀLÀLÀLÀLÀL #¨Ô¿ÐÁÐÉÐÑ0JÚèoè}hgÔ)1éoÄ‘Kñø‰AOc¼.üŒ×¼”´ñ+S~òá'†Rãé'g]Ó$õ‚HÄEb2¦¸hù5qñæ‰/.š>ݨJÝyÉA¼ÖE~nRó´iþFL¹m&`+“@§ô³sÃoE—¡û—÷®¶ë&†Ø`Ì›`쎜”¿<¨E¶™3gdr÷=÷Ž^½{G=bϽöŽ©S¦ÄK“&ÅŒéÓcÐàÁyåÌ™3s¾Ç}4öÙï=-r6â`ç7sºVÕðÙ³fµð5*¶XŽš-Ùa&`&`&`&`&`&ЮÉAKk—ú[ZWkú_]¦|—Xˆ¸ˆ Â!“ã—x˜ªe¥Tuâ1ÍÅ8ù”³r¼úñ3?u.Œ’í¬ÝKuò×É®Kr,+mƒMU› ˜@»&Ç××N» '_—vS>¯/œ’—ÌiÝù@œôÎÉ7ó±n’o²éf±ën»Çœ¹s[à™4qbôíÛ/V[í“Æy´{5׌ /ŽÕV_=OB$†h¹Ê*«ä÷@þûê«bÿß=zöl‘Sv_òXù¬$j2Çæ[no²iÌKë¸òŠËsØ®ûwtëÖ-H—„Ðûî¹;zðÁX¼dq\rñ…i×äZ±ÓλÄý÷Þ“'OŽÞ{PŒá…xô‘‡cõ´¾‡z(úôí“×óÈÃg×®]c»íwÈ÷ÎD¼_óŽÿÞÏ<õTÞºæ!±Çž{eQVëui&`&`&`&`&`&°rJéh,DZ¥üèeèo´µÙ_±?5³ÉGƒt5´4t;Œ:~ò('mãÑñ1Fºœb•'uåÊš&Ѱf@êÐâ$jø¹0Ê¢RJ›8ÆpQç†ðkÁÄÓæF(1ݨ|šK%1ä‘YO>rã“¿³ßA™hØL Èäèã¢Óè7~0°|ýpx³o1«&PÎJ;ûöëÛbÊ~ýú}Ûí°cz7å¸8ÿ/ç¢ß{í“ß¹ù[„ò¶œŸ×\uetÈ¡1t­µâÙgžŽÓc我c×ß ‹ŒgžöÇØ6‰ˆˆŒ½Ó{(eªÓ¦M‹yóæÆž{ïݺòã0bQ:¨gÁ‚¦Cr–,]Ï=ûLŒ3&>ṟY|¼ä¢‹bìÄÿ|òÓiwç#ù±vúGo¹éÆôNË)qØGDÿþrüu×^¿ïýšÖ¥ ˜€ ˜€ ˜€ ˜€ ˜€ ˜ÀJ"Px:½Kz«á£2—´6éoÒÕˆ©¯qôcÒÝ$4*OQk#Žù5u.bñqi\ª–µ9r`EÁ²ÉSøÚˆ@I‚â„ÔeÅf˜|ÃBèc.-V}´‰Ç¨cÄ+¾¢RKÝXª–ã¨k~걕¾Üá/&`&ÐvvIï¬4Þ¢ÛkŸ}ËÝ÷Þ}W~ïäúl7^ÿŸ$bÎÌ‚#í¢=ôà±ÞØõcøˆÙ(ùÂóϧݎæøž=›éá€ÄТuO™wïÞ=8¼§²¯×-Ål¸ÑÆÙµQ*¼ÿ¾´crÓ|?¼¯Qòµ©¯eñ“Ý–ìœ7w^¾x´œ{©uÿÅy\700000xk ”ž–‡ÖÅŇRij´¥¹éC¬6 ÒGœb¥Å¡›)†1ªÏ\ôk %>Jú1é…ԫͯ<*‰«ik¤)ı.Ý8>ú›>­7ÅÓ§K±§ñŒÓÍS× DºQ|ŒÈrÏïG¼Áh3Xýû÷¹sZ>ú=wîœè?`@³”¼wòÑG‰}ß³Þ ¹ê A±Ï¾ï‰’08ùå—›Å"\’»h츜5³å»%‹1Ë[ïÖ½i—¥ÄVJ.v˜³³é—3ã¹ôØ9×ÄôNÎ-·Þ&?î½¼szœ ˜€ ˜€ ˜€ ˜€ ˜€ ˜À›C€M2%k·ú[Z_=ý¯¨õé^š•ˆ­Ye&”`ˆx)a’÷AJ„¤_â£(Rj±ôSg~üŒ£”bKI?%óklªæ6~LqÄê}”Ô1æ¯\{îð0h„À!CãÆÿÉÖ 00Y¸pa‹§s/˜??xïä{80¿wò¥—&Å^éñkÞA9jô˜tÂ÷¤f|³óqΜ9Í–ðê+¯D¿þýšùÞŽFŸ>}óãé묻^yGçÛ1¯ç000000hŒ@é >ô.é_lò«Ô¿¤ÕIC£Ä´ùú¢tI£Ž‘Gú›æ ššrÒΆUÓÿðK««œ_º^]N‹%Q-cq,Hb chsƒ\˜Îdºâ‹ù£|ô¯…Ò&㊋¦ÎXm-¥Ÿ6`Ú2 ·™€ ˜@ã§ÃqF×_wmÌŸ7/'oøÏu1`àÀ3fír¢kÿ}ul¶ùæéÀš¡Ù×·oߘ1cF®sÒwånKÃyêÉ'Qã=–Ï??®üHvvÖùÒ«W¯$pÎÎ;9TgEŒ]æ¬çöÛnÉôkÆŒéñÈíHZ500000x“ð\ɤ­¡•µWý5jmÔ¥ÿ•n¡zÑÈJFJ$T2\U2©&f}%}Â"ýäP¼rPмrHˆT,ý¬]7ª9èçR;üˆw¢a3¨I€Gš¯M»õÒá³Î<=ÿÜ8þŸÊïz|ÏïÍ%~lØðáñþÃ/çãTíÎ:çG¢åÜu·=âÚk®N;({;Ǭ½Žºr9rÔ¨Øu÷=âòËþKÒá6Òvý­¶Ú: …Ík4Ö;6ì±øýoEÍ=öÚ»FdcîÓû'o»åæøóÙŠÎé¾®]ºÆ6Ûmר`G™€ ˜€ ˜€ ˜€ ˜€ ˜€ ¼¥JŸW‹z—êÚÕÈüÒÂ(éGËCOS;UËý¨ãG_+j~Òëè—ýX48ùÈ©ML±Ô+õ<|UM`ÕÎ’“Å’\— 0–>`ÐÇKÎÃik¡ŠM®CÉ 2–­¡Ä‘‹8.ò0qø0J!¾8y—ªÑ3]CÒ7ñ_4l&`m'ðÔËsbÏ›ŸbýÜÞúùÒö|yïiä…Â_®êÞ±dÓ#jSψáЛå±[m¾¼æ´K”GÓm&`?u~ŒÜt°Uc#ÚÅ60000·žÀ[ý{ÝÌiSc`z"Ítµ6ÔÉÏç3Ù¬Y³bðàÁ¥ö„t¡}ñÁM }ŒG¾ÑÖ4ˆ:1\hk Ó…žFãËxL:~]Œ“PœCº}ÄÒV¹§üäe¬úSµº‘¤5#™n°Ëd˜DA&ÓBt,BF?~riñÄ+¿æPƪ?U›Õi3¿rG1\šŸÒf&`+L ðRâ†rñJkâ$‰–Wœ\ѱÕn"¯Ùâd54ö™ÀJ'0vHó?­ôy&`&`&`&ð#Àöj¥2h_Ò¿ÐÐмÐ×¥·áG裑Ñ/}MÚ›JÆÑWS³™XÔÿ4?%c¹¤Í)gåüj§ÐÚF¢Ö¬r!´Y4yQèT|e^ù™³X'N¢¢ÖC?ùu#ºyüË3f300000000ŽC@»*ÓŠ;‚þ&­°¨ÿ¡ãÕ5 ‚õ‚*…AÆ0™DFêšœ<ÄË'?>ǦDp¤äR®T-+ºäE°¤Ÿ:1\§1m™? ·™€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜@‡$ ­M‹G7“¯½èo¬ Uš%Z^]k5 4Zb M ˆÔ™CJéiÜB{Óß*õ?Ö‡žGɺëšn¦^DÀbLQT”)Q“Jh,ЉÄJ|$óÓ_q©šc”GókœæÖ|uçç` › ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ tD%²®þ•îKºšâ¤£qËo¹þÖÊüÒöXKUkD ä†ˆÓ *©|Å~DEÅIH¤mê\l÷ÔîÉTͦ>Jrs‘¯Ø&€74á9ý4Ôf&`&`&`&`&`&`&`&`‡@I ”>†6Öîô·´&ÖW©ÿ¡áIÛKÕÚ†Èך1ÄEmGdœüŒGlĘ¸Ø‡É|\,”_165³xòrG ±Å+5s_Có[ —ÍLÀLÀLÀLÀLÀLÀLÀL #è܉,[»ÕßÒêjéÒK·P½(ßaõîìU"ÄBDA õ¿&× 0ñãȧM‰áW?u. ©Z%P*ž„M‰—Ä6<¿ñ—ÍLÀLÀLÀLÀLÀLÀLÀL #( ”èpíVKk+êÒý´¹P}5ñ7"P2Xba1` %*2¡DLDEù‰Á8É›òp‘Cq©Z4‰Á/#•¸Íó{¥º40000000èh–,É'Ûµþ–˜¢JÿC/”¶'ý¯.ö¢àW+ Šq‡\øè£®Pʨˆ)–ŸbµpâY4¦±Ä«]“øeäQÎVç/œt¤ñ.MÀLÀLÀLÀLÀLÀLÀLÀL C(m¾C “¦Öîô·´6iu”•úŸ4š¼‹Â_­ D@.‰‡$•¸È¤˜F•øÕGbeÊ!¡’yŠy4¦Õù}Š·º40000000èhJ¯/l×ú[bZÔí@,ÍQBe]ì ®Tê$%T—DBÚʃ¯ØŸ ˆøY,1”òi qÔUÒO]F]¹)‰-Ž%NmõwöJ°ØLÀLÀLÀLÀLÀLÀLÀLÀ:"*x·Ð¿Ò}¡›Ik{Ûõ·Šù¥ËQJÏKÕÚVkEI•¨Hãh³Ã‘ɸd@bû&%†Rª”€¢ÍxÁLÕr,y«œ´1JÆc Ïï”MÀüÕLÀLÀLÀLÀLÀLÀLÀL ãèÒ%ËaíZKTÑí¤é¡ÛQGû+ê©YÝК£‹äÔ%ZJ0”˜(XˆÄЖXI,mÍ©ÄJÌÄW\8mÆ1†²Íó{e¢f30000000è–.EËÖnõ·´:t;4=éE­//¾Þ‰…õb ¹A"¢&–èXÌ¥:% ‚¤Æ«Oâ#&}\ÔéçRÞâüøÚ4¿ÊDÌf&`&`&`&`&`&`&`&Ð! T<â-]{Qr¥êouæGËCC¬k‚š”Oâ 5AQ4$†I‹¢";$µb9‡¶DÈT͆O;$q(—dbÚº4?'k~}3Šs½Þ¹³¾W¤´™€ ´•À ›Ïi6düÔfM7LÀLÀLÀLÀLÀLÀLÀÞB¥×Jÿ¢D“®F›Kú›6 ÒÆ*ãhKkc†–†Ö†)7ué{ôkÆ+FýÄ’³§9ˆÕ<ÄUµFJ%ÒäJ$‘Ôµ01~ƈ|ÜFŸÛ½Tg qŠa,uÝuõ¥jyêäB°d ¹{¤«›š60¶;¤o[‡8ÞLÀÞ5žz¹ùqÞ57î500h÷^OJʬ‹cÆÜ%±`±ä—v¿ìº ìÙ­K ìÓ5ú÷ìP~Þ%V(‹:›ô6iqÒèð£¡ióž¾ñheÄàgã q´/-M¾¢žF>.æ'ŸædLq.Æbòiœæjê­ñ•äš&"±„B|, 7I¾âäÚåˆO7L‹ØÊqøª!7~òi~nV`”‡Ró/å(vï¢LDl&`&`&`&`&`&`&`ïSç,Š¥K_a«öŒ.‘I:¾-]özL™µ(-Y«õc¯Û»ÃJ%ú†î…Ȉ•õ¯TG/#ŸÄCt3ihGS›º´8êÒßèÇÔ']¿Ö¢ù™G›ñ©?Uóx|ÄÔ4-ªf@ê †É)•°r¢ÔUVH‰ÕMà ưXÍ©‚¥•ˆÉb4¿ò¯±Ä(~Å”ç·8 "› ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜À»‰ÀÌy‹cµþÝß1â$ß;„Vî‰{{Z]ý+ñX©ú[ÅüèrZoQëùmkd¥!NV»%\RÇäïáG„T[¹È/áR"#1ªÓÏ8Ý}›ª9NñÄi-äìÄ‹DKJ3±60000000xÇ`·á;eçdñ›Å=qoï&+’#Í ý EÍÝ ?&}Œx™6JC#^9¨K“榒ñš—Rsã×7?yeÄH¿Ã§ñêoQ·è,9ˆábRML©£.©šëZ„ML SÉ Ó.Ž£.ÓÚøÉ%hŠ£Ô3óÔkÎoq2ѱ™€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ tH²¦þ•n }Œk¥èo Οª‹nÍP9%RÁ?F‰OF›8ÆpQ— ISà)uL>Í¥’>òð\=c¸K>rýýÊDÄf&`&`&`&`&`&`&`&Ð! ”JÖŽæ%=6š—´6éoÒʈ©¯qôcÒݤÿ)OQk#Žù5—ô7bñqi\ª–5;r`ä®iJZ3 u€I™¤¸Ô,‹ƒÔéç"^“ÒÆ(YQå+a-ø‹1Œ‘°IŽbj6!VmÍ‘\å~êm™Ÿx› ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ tK—æ=ÒÆÚ»þ&ͲX¯Ë›ÀÖ (´#Å6*µ%fJ\,ægŒú©Ó§1Ô¹È'Q2U³CbÃxg þb&`&`&`o (|ô±ÇãÅ jÎ÷øOÆó/¼P³¿^ÇŒ™3s~ÊZ6}ÆŒ˜={v­îåöó‚z-\¸°VWÔë«9¨Ž ÄÔ©¯EáåùuGÿb”vaÔs§ ˜€ ˜€ 4B`òôÙ±,ýûXÍæ,Xã^™”¶#Púw¾®þ•fÐ7b¥èoóWêy¬½®µF#*N7ÉofÔé£ÎÅä\Å61\9´@ÚŒe¤¬Ø‡ É8J„Hb1|äiËüŒ³™€ ˜€ ˜€ ˜€ ”\÷ŸbÌzV½ê œ7o^|èâÇ?ýEÍð|ðÈ8áë߬Ù_¯ã¦›nÉùo¸ñ¦aS¦LƒßxlµíŽqÔGmÑ¿"×=÷Ý?ÖYãiN;ãO±Í;Ç›l»ï½_Ütó-嘧ž~:ûècm¯N™RÃλljßúnÙ×–ÊI?<96Úl«ØvÇ]b„‰Í†.^¼8î¼ëîfbäù^{ìóžøáÉ?ië† ˜€ ˜€  |èçã~{qÑ•ëw<5>ÿéyqÎ ÷æöu>Ÿ?ãÒøs©]9àþç&Å ¾2îK¥mÅ”þ¸XÔÛÚ£þVOÿcíuMÂc½ &Ð$Å8’# …K¢"9iS§S?¹5VùUË8b(‹~ê²FçW¼K0000M6Þ8>ÿÙO7»zöèQˆèXÕ?œvz<úècñëS~çžó§7mñäDTe÷a¥!úýäg¿ˆM7Ù$~üÓ¢G÷îññO~&{ü‰ú“$ÔŽ]oݸÿž;Òoµ¯Çïÿpzö³â«_;1zõî'~ã„Ê´­¶ï½ïþ8ûÏçÅa‡¾/n¾áÚ1bx³1·ßqgù‘cböœ9eÿèQ#cÀ€þ1vìzeŸ+&`&`&°¼†¬Ú?úôì#V[eySx\ƒJx·wý­žþ×ê"$¶f‚@,m ˆ%Hû©MãK£ŽIŒÔÍhNÅ’_kP|,©+§ú“Ëf&`&`&`&P‹Àf›m_üÂçkuw8ÿSO?“¸ƒÞ{à›¶ö|(>rÌñÑ·OŸ3ztŒ{þùrnDƳÎþs¬2p`üñÔßD·nÝbóÍ6÷¼÷8ûœsãç?=9ž÷|ùáÆÀb³M7)?ï¯çÇíÿ½#þvá_£w¯^åœV¸Wì}‡Ç k1ì™gžmáÛaûíâ{îlá·ÃLÀLÀ–‡ÀÆ#ÖŒsþ÷ƒË3ÔcÚH ðúÂJý­Z&ébhjèvXQkò4éhÄ¢·¡¯Iã£-=ŽzqNêzZó$W3#W1š£Y`±¡}•õbRÕ™Œ…`Ô¹dš´èÇ×-]”•7¬±Ü ýäE°T5riuÆ´eþn3000h„À¬Y³c×=ö‰KþñÏøÂ—¾’#Þ2ö;ðà@¬“]pÑÅqàÁ‡Æúož¹~âɧԕ˥K—¤Ç¼Ûï´[ìµïñß´£¯–ñXù¿üÕü˜ôλï¿ùÝïË&óHÓw¾÷ƒØr›‚¾ëo¼±jšÏ|þÿ⡇Ž9sææõ°;#÷—¿úõØn§]óZ¾úõƒûÃtŸW\uuüò׿ÍsüýŸ—æ>}¶ÖZ±ÍÖ[ÆÅü%† YSî\N™:5 Ž;ï¼S'q®—vKŽ><î¸ë®3tèxöÙçò;"ŸN¢!ùž}n\~þsŸùT4s`•/ðþà‘‰M¶Ø&ö;à 8÷/ÍQ½à¢8õÌõo}çûù~_{mZ9Ãï~ÿÇøÓYçäö‘G“wÒ¸ãλÊßWÚ|o¿vâ·â”_ý&6ÝrÛ8úcÇe^<:Μ»ì±w\yÕ5„f«÷}RŒK00wGÇOŽÏœöϸéÑçòM/]özüé?wDZ¿¹8>ýǤG»'¶€qùÝÇÿýéò8æ×ÅÏ/½9¦Ï™ß"ÆŽšиdèfòÉoeêö)M’«Uý±Õ€”“¨:"Æ$â¡r©Ô"€D?9¨3Vñˆ’ø¹ˆÇO¿Àjõ§®+#ó3Æf&`&`&`&PAà…ÆÇ¿®¼ª|ÍŸ¿ G,]¶4&Nš_ûÆ·Ò,Scß}öŽçŸ!‹ˆì¼çÞûò{×X}õ¼ƒÁíó_øR³d®¿á¦¸÷þâ=ûí/OžŸüÌç“xøÆãÆZ ù>šv(þçúãƒÞïÙwŸ,Pž÷× rÈÏ~ñË`·á¦i÷áû>(‰lwkh³rÿ÷ì}ûö;-·Ø"=Ƽn?zìÇã—^ë­»n¬½ö˜¸äïÿŒã>ñ©§ûüå¯~ˆz=Òãíƒ n–wµÕÇÙgžÞâj‚&O~%Ç®¹ÆêÍÆ¬‘Ú¯¾:%óøô'ÿ'.¿âÊØrÛ²PyÌGŽ/%1výô˜5e-ƒ)âäÃ<û'†<ªýÝïÿ0ÎJ;3·Ùj˼›“±£G î·{w>‹4Ùšk¬½ûôÎ vmn±ùæ¹>þüü}EÄÅøÞ^ü·¿Çõ7Üû¥ï1ß×qdðŽÒC:0fΜ_ýú7búôéM,ë|ŸrB10xÇX²tYÜ?nR³‹oжpÉ’˜2sNÌ[¸8»Ï¿å¸æþ§b!ƒb× GÇ£/6ý;©1ÿ¸óÑ8ï¦ûbÍýâÐí7ާ_š§\~‹º]Ö!ÀïK%“žF³½éo•úëCÏ£dÝu U³5C,“(KT”Pˆ»8)c$4Rjwde.) ŠO®<Ÿ K„Ô47qŒ¡Ÿ ?÷#‘3çã¤ÅÎ5Eêµ™€ ˜€ ˜€ ˜€ d·Ýþßà’ÝvÓõÑ«×5c§wˆ?ŸuFnÏ;7‹ˆ¯¼òjÚQ¸UÜrãuy7 ü¾…ð‡¨¹ê*Mï¡âQç‹Ï?/ÿ¶úê«ÅÏOùUÏ#ÉE»ñ¦›ó©ÜmZÓµ~ýú5‹¥ÍÎÏ™³fÅ®»ì×]}E<ñ䓱õV[eqܸçãŠËþ‘ßS93íðÜnÛm’ÀؽYŽÓN?3–¤~çüéôØq‡ícÑ¢Ei‡é.içäiqÜ1½öÜ#ïJ=*=>¾Çî»5ûÃ[o»= OøêÿË—7 (4ø=ùï_={ö N?çð!ÙÝvÝ9ú§ÇÒÿxÚùûá;œÎ^íû„èj30xg˜¿hqœ|É ßÔâôïÞµ>CÓ{)¿qø^Y¤á•§^Õô{»+/MåˆÕÆ×Û#ç]¥o¯øí•·Ç¤×fÆZƒ4<×»1°$PJÿB÷¢ŽÖ†Iw£T~t2™„1im‹ÖFýä¤ÍE]ã)¥¿¥j9^sѧéÄ‘G¦Üj·((IÈB™Œ„\˜¯EÇâ´hü«c >æÕ¢ñaøuɧ|ä¤<Ô1Í¿OnÍ·¬ðœ>cl&`&`&`&`%{î±{|ì#G•y ´j¹N…÷&ÊF•«3fΈ5×\#‹“óLz|yVÚi‡M›6½,P®žvWêÄm¸aîŸ0qb.‹_âÉÜä1k~ÐÉãד^z)8|ëôˆµ~§ëÚ¥‘__›fÐcçÛn³uyJê”ôI ÜcÝÚ,N’pÕU›xÍžÝôȸ&¡ÍËìôïŸ]`ÃÅÅÓÎ83NúÞ·ƒGÉÿ˜DHÞ[ÉnJÞEYz~ƒ I{Ë-šv?"`²AyrTß,ƒ+â$6²tÐÎôýÅF™KíÖAÕ¾O9È_LÀLÀÞQúôèß ”EÍ/%Rú`¥©1í¬¨ÙQLjç'¶¸â“v†Ÿ “Ob#mú¤¿QÇG[ur)?m.êj§jŽGÃ#¶Ukä7<ÀD*IÊâ€6`˜”‹>-ŽgM¨ëfŠ‹%#¯n@ñøè—¿8?¹¸´b˜Ÿ{Ñüêëª_fSŸÍLÀLÀLÀL @€÷#î²óNOcU«/|ñËy—»±ÂãGÍuî̯ké—´ӲÒô»Ún»ì’Á]îî•D³¹¥G‘{öhÐÊ Vx¤+îpì×·i·ãÂ…M³Óß¹¿Z¶Ýj±ÉiWiÑØeÊ®Q ´ôñxûÿûÊ×ònGeß|ëíãg?þQޥʻ¯O;÷Ý{¯r·ç‘óâÎÊþýKk_°°÷vUê}ŸÞ®5x00·ÿv¯3¤ùkO¦ÌjzEHµU >bÝ»Jê©y‡å®)wòÂ:k6Ÿ§ÜéJ™@áwŠ&U·IÿBû¢Í/X"õK }Ôù%Mß|˜â«8ü´ù–ÐßôË[SÍ‘Ü9†¼ÊI¬ò1Wq~åÂÇ<š?U[Z#¥’S2hRݨN[Æbi3‹`Á˜b©#Hr£Ä\ŒgL~â¸/S[¹ñk~ê9¯þÚ‹Ãf&`&`&`&ð¯_üò×o8Rí³Ÿþd³vµ§W³[’“¦w<^‘Ú©´»îº;.ÿ×Ñ#í„<ý̳òãâûì½geX왯>lXœá…éý—=cã6̇Êôï×?¿KrÓM6Ž[ÓcÍçœû—Ü_¹Þ ŽýöÝ;~úóÕã/éÀÞ½{§œKãâK.‰µÖšËy¥–…!U«÷Ý~äœÃb0ÞmÉ<¼‹ó¸c?ßüöwãSŸýß|ÐÌŸÎùsÞ9ylzO¤ìêk®ÍtŸñÇSƒÇè{õî•wFÞ÷ÀÑ3Ý3êŒIøí£G™ßù•¾Ç{LÜ|ë­ñÒK/ÇGŽ:²Ù£àÅ1Å:ïêÄ.¼èoéÑíÁ!B+b­}ŸV$·Çš€ ˜€ t|úô̇ã<üÂKqÕ}OFn]ã‚[ßx¼KÚ‘¹ßëå>Þ;¹Óú£â…)ÓãÞg&ĉéQr)·Õ&À6“¡¡“q¡I¤¬ÔÕRWÖÏO M:%}ÒÐè“QçB—+jrhuü•X~Æ’ƒ\šŸxùˆ§]œ75kZ3²-˜‰1-€R7FINÝPªæ8Æ'¿nˆ6ñ´1ùuC4¹ñÊù«ùÉÃ8Éñüòl‘2Q±™€ ˜€ ˜€ ˜@ÄE®¢Ü1ÅfÕ:‡´Ü{ß}ñ¾ÃŽÈï¢ä”§Ÿy¶Ù#Þ;î¸}?¼wräÈñóßþ(‹„• yŒùÌÓÿE¾_ÿöÔ,z®=fL|-v~úsÿßÿÁbÄðáñ•/}1¾ý½“*ÓTm#Jžý§ÓRîïåCzÚbóÍâ‡ßÿn~|ºQò‡'ÿ4´ó’ÇâÓ±A:`çÊËÿ™àá“§Ÿñ§,\"¶žyÚïóÎRb_yõÕtâùw²°ÈÁ6Xï^½â{ßùf>•›ÃoŽøÀa±û®»ä>}ápŸüèñË_ÿ6ŸžÎ½uä‡ò¡7Š©Wrѵ×]ŸÚSòNͽöܽ^x«}­}ŸZMà00w’®%ý‹`ô/t1DJ ‰ôã“öF &á>bè—¦F ýÒÔ4>¹r,ñ\ÄPj^•䪜?¹ÊkÐXÅÐWÕlĈ+&U½xä)ÆQg¡ˆ‹Š—ÐÈ SLj¡_qôa”ªÓG]ùU"± ¤r(޵ Iå©´™€ ,§^žc‡ô]Ž‘b&`ïüœ9¸×[z³ã§Îo—?‹ó)Õé]”«¤S»ký1˜Wípúwñõ`-\¸0ŸV]-žCsúõë[s®zyé#7ë,¾Ó±µ1míç=“ì¬lÔx,œuµ6†¼}úôY®{oëšY{½ïS#ãc&`&ðöXYŸçæ.\½ÓŽHDšj†ˆ3g~ú÷¯gŠ)¤S-¶–ï­¸··ã÷º™Ó¦ÆÀó}׺wù‹ïö桃 :(ñx)]%ÑÓ$0ê8à¥üŠ£Nmip”~ŒqòѦŽO~46åÅGNõ§jY¸Ä_œƒ6:ò¤juc‚Ö¬2mÐæ†”‹ãcµhÝ,}Ä1F±©š\¹xåe,±6‰+Î/É]ySwtñ;(Á`30007—'LëëZ™y_R5±±V<»ô¸ª™ˆ©Ö׈¯VÞFÆ6ÓšÐX™‡÷9rµfmÍ[Ì·"c‹yŠõzß§bœë&`&`ïN­=®˜Ó¯WõïßÄZ¿ë’h)ýMzú— ­ L‚%uŒXâ(I£“ÆÆin”\ÒÛ¨k>âÉ%]Ž:>úñ)Nù“+ûȯ¹ñUµV £ˆåÒ¢éÒäÊC‚" £Î%—üô Tª–oZ7q˜òªÞæù 'å„þb&`&`&`&`&`&`&`&`…@i7e¥nÖ®ô·ÄR‚i-ý¯.î¢X+°¨|JX”€Èäøh ”ÔZrsÉ¢`YüsqQM%mÄO  š¯Íó·Ä¦|60L`Ñ’¥1uÖܺƒÙéQ‚q¯L‹¹ t€Yótó.Îý³æ-hÞÑ@‹Pä~eÆì¢b&`&`&`&`&`&`­èÚi­}ëoi}lJ,êv,óâ¹zÖH"!†H(ÑP>JDHEŒ힤6ý—ª¹Ÿ’•̯>rh=ÊÁ)F¹OIîJÄkÞ¥zv?ùl&`íÀ=éÔ¶Ÿþó¦òÊ8Ýmèªbí5Å;o«õïSîûñßoˆGÆOŽï¹_l0lõ²¿-æûÃ5wÄÞ%vL'ÇUÚ_‰ÿýÆ8v¯m‭֯ì®Û^ºtYœðç+c«µ‡Å×Û£n¬;MÀLÀLÀLÀLÀLÀL ¼¾0úZZ™42mDS“8¨âñ£¿áÓ…:&ýM::[ž,•ŠÑ8JÍ¡ùKYG.âY#õº&A°nP©³¸H&ÐB©k"-V>Úôi,~攩8•ŠO!åœÔ1r§‹¶æ£Ž©­zgvPZ¤ÌlüÅÚ5É-ƬKÒÞ§L›Ó©nw>=>¾ü¾Ýb³QCóÚG­¾jL|mf ê×»]ß‹g&`&`&`&`&`&`oŠG¼%R¢‘IK£Ž!HÒ'-NB¥4³J þ¢Ñn'cF~ú4'¥|øµÆÊ¯uЮk”Å×Mi±šI´`J奥”qÔñ«M¹1%FJ.>.‰™”:µ›ùK<1*S5·™/ç´8 › ´ë L;&e>ÿRüè’âŒkïŽ_PtK‡1|t­ò¥—&`&`&`&`&`&`&ðN'Àá„Éо¸dè^èlÒÖÐÛðÑfB¥„CéfÒÐhÇ•õ³TJ£d<%ãÉ«ñÕæÇÇE<%9¥’[kJÕÚ&!±vDó›Ñ☠cÁZHñ&GŒæ`qÄRbÊÁê}”ø”S1Œ×ò >ó‹‘ÓZôMI"³wP6¡ñWèX6=4vÞ`TÜúøóÁcÙ<Žýçï‹»ž~1NþÈþ1 wϸâÞ'âªtÍ[´86¾F~,›GÂç§öy7ÝŒ›¼srÛu‡Ç'öÝ>zv×£Hﲜ?üÛõñôKSb½¡«ÅçÜ)ú§œÕìò»y6fÌ™\3Žß{ÛX¥o¯úß'_ˆ o}0fÌ];¥õÚLÀLÀLÀLÀLÀLÀLàÍ$°t)rX¶júø¥—¡•I$¤,êoÒæðIÇ“€(-u•ÅIUNæÑ8Å©­ñŠÅ)GS«ÆW 5º³›D\‚@I˜P‹ O1—ê”Ä@RãÕGr>.êôsѦ,ίMó—¶Á¦a60ŽF`“‘Cò’_œ2#—³ÓÁ5SfΉeË^G_œ¾áÞ²jÿ8t»cæÜù1)=þ]p˃I؈œc×Z-‹œÿ¸ó‘ܧ/Üú@$7±f°[óŒëîVW³òw>šÄÎûbÍýâÐí7΂æ)—ß’cžyyjüêò[ciZÏ[oMoZg³n˜€ ˜€ ˜€ ˜€ ˜€ ˜ÀJ Àûýù¬òN3î‰{{7YÅ#Þhe2Õ)WªþVg~þ#”x©u·(ߨNÔ¢«…£øÝG$ÔEÑ)¥Ša7£B,[Li¶ _qç¥rI&¦­K¹Ùy©ùõÍ(ÎõzçÎú^i—&`…€v)N©rr÷óé´lì m6ÌBä!ÛmT¾­£vÛ">°ã&Y€\–vQû›‹â¡$B¹ëåêŒÅ¾rÎi—拱p1?®Þ0þá»4 ”#VX>ô†5ýöÊÛ³zÕ}Oæ@_9t÷µú*ñ¾´†£yÁ \300000•D`@ïn1eÖ¢tðh÷wŒ Çg4î‰{{7Yéõ…Ò¿(ÑǤ«Ñæ’þ¦M‚ú€[G[Zã0´4´6L¹©ãã¢_ó0^1êO®œ³§9ˆÕ<ÄUµFJ%ÒäJÄ"˜€Ô™˜…@~ƈ|ÜFŸË»%©“‹8Å0–:>Œºúh+'uÆó_(cÈÝ#]ÅØÔ´™€ t$ÓÓ#ÕXñ$o­Ý¡ƒs•Àw;2ößrýXgÈ ìëÑ­ktëÚ%ØáøÊŒ9Ñ«{·˜9o¡†æ²xÐΘ5Å ¯N×fÏkóêŒÙùqñYi쉹:÷Í_Ôô#mrê›vL’qëâ?ˆdþb&`&`&`&`&°ò îÛ=f-X§-ˆ‹%¿¬üu­È zvëûtþ=ß•%š™t6•|c%.¢‘á§Ä‡éÏY :Õ›¶â¥¥ÉWÔÓÈ'ý|š“1ʋɧqÌ¡¾PíK#¥Æ)‰%âcaø¸yêô'×.G|ºaê\ÄVŽÃG^õ§j6rã'ŸæçfFy(›×ÆQìÞE™hØL xdüËyÕì`¬´õ×Z=عø÷;‰[҉߼«òkïß#¶\{­xtüäøý5wäwF®5¨Ì]¸(úôàoÕ­?1’±Û²š M‘ïºÑ˜ráë¬98ý#¿8º§ m&`&`&`&`&`&ÐÞð9g@¯nùjokózÚF ´ƒý ãC("#VÖ¿R½Œ|ù+ MãhcjSÇ‡Ž†IÓ‡]õ©¿Ö¢ù™G›ñ©?Usn|Õ?p‘¬R '‘.&*.T~|Š×‚Ÿ¶ÄJâÉA,uÝ,qŠOÕ#A’¶nHcñi>ò0¾¸‹“²™@$À{!o{â…Xc`ߨ&rSÍ8ü†‹÷Q~ïÂëâîô˜öc†Æ/.»%†®Ú/~~Ì{£wnqŸ¯l±ƒ²˜oÂÔÑ9ýëÍNÍWÒÎHÙê齓슜6g^ìµé:r—ËUûöާ&M‰y çy–,Õ½rˆ+&`&`&`&`&`&`&ðf¨Ô¿ÐÁÐÉÐÑ0JÚ|0EïC;£N‰I#Ž\ŠÇO zãuág¼æ¥¤ŸX™ò“?1”O?9ëZ#¥&Ò+K&ÄGɅɇ_7‚¿xcÊC~ÖA㸉b~Ý~.MÕf¥½oO;,/½ë±¼ûqÇõGÆé$mDÍCÒiÛ—¥Ãm>{Ú?c³t’÷ži÷ã=ÏL,ß>?xlüÇÿ¸1¾,¶Z{X·÷6åþb…ÃtxôûÆGžË’÷éÑ=öM¢eÏnÝ’x96‹›¬ãž$Z¾?Í»ðòn—"×MÀLÀLÀLÀLÀLÀV‚@)ÅH £” ý6‚$méf©ZÖÕ¨kŒüèjhièvuÆ’G9iË_9¥Ë)Vyˆ/ŽÓøf¥„ÂfΊ†TÔ¢HˆO ‘à(Q’áY,•Še1ø‹ ÖÍʧõ0Fu=.yðaÅ>‡qжtéÒËýÊDÄfËAà©—çÄØ!}—cäÛ7döü…ùñêÊj8‘a‘CljÙâ%KóOIDÍÖŒL OcX}u”ä#£Ž¯ø€#õ•R7–ªå8ꚟ:¦1M-5xGè׫úá7•kW»yNúnÔø¶"s5:ãLÀLÀLÀLÀLÀLÀL H $Zè½@IDATõ740ô:ijÒØ(õA·(6§XiqèfŠaŒêä`.ú5†%ý˜ôBêÕæW•ÄÕ4‰‚5R‹âX cµ(ú¥„OŸ.Å œÆ§òÍS× Dʉ1Yîù¥<“Ìf&`&`&`&`&`&`&`&`‰@áÉàv«¿%žõô¿¢ÖW}#%I˜DV¬#FJpä}˜úQ^‹ `.úŠÂ%â#m„HJòi ±jkŽä*÷SoËüÄÛLÀLÀLÀLÀLÀLÀLÀLÀL ÃH¯/d­ÒÆÚ»þVMÿÃW×Z H£€B+11Rl3¡RaQb¦ÄÅb~ƨŸ:}C‹|%S51Äkk)ý´@Û2 ·™€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜@Ç!Ð¥ RX6ikÒ×Ú£þÆ%¦R—þ×t5¾ÔˆIH$–:&¡LŒQ/¶ñ©Ró!,’§_ŒynSÊâüÔË%mNc*çW;…Ö6µfE!R‹¦‚&/ НÌ+?sëÄITÔzèç"¿nD7yæOÃl&`&`&`&`&`&`&`&`&Ðqtê„ –­#èoÒ ‹ú_ùt#•¥ÁJ±]) 2†É$2R×äŒ#^>ùñu+õ!8ÒV^ÅHÑ%¯vORg>.[žùÓp› ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ tHÒÚ´øö¨¿±&DTJm0”þ§uW-û1‰‘ÄJ@¤Î$â¡r©¤ €ô3Ž:â£â%ñs‹Ÿ~‰–Ô1õ«.#óçþb&`&`&`&`&`&`&`&`@é–-=z{Óß*õ?Ö‡žGɺëšn¦^DÀbLQT¤ŸK"¢&•ÐX‰“øH>æ§¿âRµ,jR§Ó8Í­ùèçR»ÙüË–á¶™€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜@Ç#P(ëê_鮤«)N:7ü–ëo­ÌÏšêZ#%7DœnPIå+ö#*NB"ýøhSçb»§vO¦j6õQ’›‹|Å6y¼¡ù Ïé§¡600000000è8J¥ô1´±v§¿¥5±¾Jý OÚ^ªÖ6D¾ÖŒ $.j;"ãäg„HÆàãb¡”øŠ±©™}Ä“—‹8bˆ-^©™ûšß%¸l&`&`&`&`&`&`&`&`‘@çÎHdÙÚ­þ–VWKÿ“Xº…êEù«wg¯!" b¨Ÿø5¹^€‰Ft/DIJéc©šMmévò£¿i\1FýÒ߇)7mépEý1”Œ•)·Jù¥'â¯i k¤ňÉiKT±P‹*ª·,Xã‰ÕbˆÍûSKýÅú‡¯¾åšßï ÌýÅLÀLÀLÀLÀLÀLÀLÀL (í Dk·ú[Z›ô»Jý5·jR@[ Lš¡q‚R)"҇Ȩ~J|ŒGx¤12Ž_Q˜$Ns¦j®ÓÖüŒ#†RqêS¾eÞA™èØL`ø±Â€ç¡&`&`&`&`&`&`&°‚J;(¥¡}Iÿ·¸Ôf# ú—45úd¯8üäÁ¤«©N,}ø)¥å)Nóã×DÆ Çi~ú'S\ùm굌 ¸HL‰éX„ü”,‚É+¤ñŒã¢_ïŠLÕòêÄC¿rã#w1}Ì/1’~ê̯zg¿ƒ2Ѱ™À ;¤ï ŒöP0xgðqÞÙß_ß ˜€ ˜€ ˜€ ´]»æý…Òˤ‘!>J‹c™ÚLH]MÂ!úšêÒÌè'4¼TÍcðK“RãÕOŒ49ê2rbÊÏúXíºVLR+d\J¦6%ã©cÄH0ÔÇC?&Wù€"S|š§˜GcZßä©K00000000ŽF ôúÂv­¿%¦EÝÄÒ%nÖÅ®àºA¥NbuI$¤­<øŠýø¹€ˆŸÅC)ŸÆG]%ýÔeÔ•›’ØâXâÔVÞAI‡ÍLÀLÀLÀLÀLÀLÀLÀLÀ:ŠG¼Y~ ý+ùÐͤµ½íú[ÅüÒå(¥ç¥jm+ €µ¢¤€JT$Žq´ÙáÈd\2 ±}“C)%Nãi3^0SµKÅ*'mŒÐXÃó{e050000000èxºtÉrX»ÖßUt;izèvÔÑþŠú_jV7´fÄè"9u‰– %& "$1´%VJÅÕœÊA¬ÄL|Å…Ófc(Û<á(ö4Üf&`&`&`&`&`&`&`&`‡ÀÒ¥ÈaÙÚ­þ–V‡n‡¦'ý¯¨õåÅ×û"±°^ "!— HDÔÄøÉSÌ¥:% ‚¤Æ«Oâ#&}\ÔéçRÞâüøÚ4¿ÊDÌf&`&`&`&`&`&`&`&Ð! T<â-]{Qr¥êouæGËCC¬kº‘ºA¥NnT"!;™D¢!%ýEáqZmÇ¡dnJ]´2ÉÓ8êäð†çïܹ-·È4600000000hJ¯/D+“fÖîô·´¶JýO›Y«t¾š@Qˆ×%¿áfb|,ˆ:â"±Z%>ÅKœb5ž8ú0riÚš›R~åÓøÊùg300000000G $P¶¦µýMúŸ´;ô;„ÕºFp£F2D@%e,“âÃU×䴻љ âaQí%ýÄiáäÔâúˆa÷e›æ/Åž†ÙLÀLÀLÀLÀLÀLÀLÀLÀL c( ”ÒÜÚ¥þ–ˆ" b謵¨ÿá«kE°V 1ܼÄ¢(ÉåBJ,— Qca,VñË8|µk2¹ÊÂ¥rR¶i~?â F› ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜€ ˜@& =­]êo‰kQÿC¿Óz¥ÿÕE/u³^¡vV»ƒŸ’ “¿„Rmå"¿„KÆé&T§Ÿqò3^cS5Ï…xâ´rvâE¢%¥95m&`&`&`&`&`&`&`&`&ÐqÉ‘æ…þ…VÔìÐÍðcÒLj—iC 44╃ºô7in*¯y)57~Æcø¥÷Ñ&Fúm§^ÕŠƒ«$'1\Lª‰)ucÔ U›-B‹&& †©ä†iÇQ—i müÌ-hŠ£äqoÝ`Íù-N&J600000000I  PÖÔ¿Ò¡•q­ý­ÁùSXucÑ­" ¢¢DJ` âçÂ(ñÉh'q“ºM|˜rOy¨còi.•ÿŸ½ó“³8ÒI«œs@Ò"@ä ’‘s4Á˜Œ g0Æ`ßÃÙÇÏ>ŸÏömlcŸÁø9|d°1˜`rd’„@A $”sØÕJú÷¯wÞ¡wvfvFZI³Úªçù¶»««ª«ßùvvçîþè#çVâÃ%[â;Õ·å JßæPqqGÀpGÀpGÀpGÀhqdJò†óŸFNŒK\›ø7qeØä³—ýˆx7ñŠ“rmØ1¾Æÿ†-:.ù…j–³#Bì‚¢  B”AÒDB3KR§Ÿ { J¡$!¢Š—B.èS|Dl#íÍ’ÇÇÖÅpGÀpGÀpGÀpGÀhQdvÉ!ðc\•È¿‰ÿ)™òä^PJ!( *$RQÌ(:úEJbOŸ.ì©Ó/_JDä#u¶l#LF‚–¦J§%ï[¼›—Ž€#à8Ž€#à8Ž€#à8Ž€#à8- dgpÅòoÓbü_Êõå…’¯)!ˆElÓ:d¤ÃtQ?äcšcÑG©:ä#uˆHJâÉ[µ5FPeû©—3>ö.Ž€#à8Ž€#à8Ž€#à8Ž€#à8Ž@‹A`ݺ¸&PÜX¥óoâü(ÓzQ¼1lJ†Vd >bl#B™6Ä¢ÈL‘‹i||ÔO>ùPç"žHÉP‚ ö™ŠK´œñƒ¹‹#à8Ž€#à8Ž€#à8Ž€#à8Ž€#Ðr¨ª‚*‹"nMüšx²JâßÈ‘¼¸¨‹ÿ Õ¢ó [Ô÷ˆ$¤EdDXj` ªi#$„P 0ˆEú‰!{%N)äCD¤lé×ÖotƒzÚ6ßâqqGÀpGÀpGÀp¶ ó—×ÚŠ5ë¬kǶ¶<”.å!б][ëÑ¥½uïXxò|Së -Y±¦Î–®®³š:蜖-àÒ³s;ëÖ©Ý&áÒÒPÈ<$'å»TgñžDÜ%ý¼àüò©ªÙ…~ÔÑ樂Ÿø:ú¥×‚AîD鈨M=_w­~ù•vy¥‚’dD—‚2}€¡I…jLŽ5ItØ*y%,_-MUòØ3ì)ED†jv@_Z'm„ºÆŠ ÿá8•@ÍÚu¶tU­¬­³µëê…ÛWµ±®ÚYÏ.í¬c{Þ:\GÀpGÀpG RX°¢6þ/?¬ogëЮm·) Ûžä”9+lx¿Î[xÔÆÃ­[¿Á–¬\kuëÖ[Ÿ®í”¨Y´j­ARòZTµMi—T˜¸@~׆yõÝ\*lZM¦“Ùâ ß%ŒÎŒ6’ùð,L¼]PE¡¿&\âS¾L"qıQ'–x¹PcðìÙ¡ÓøòCGl¯|_ã`“WpjJÒI¦¶$Å .`4’£øô!´KÉÑ'JlñÆ2ÕS—”:¾ì½t F€o„æ.­±é VÛâðǦ¶nCüCÊSêèèÃ&óíÑ&Ïfé²å6åÝ÷mùŠyc­X¹2ö/Yº4o¿+GÀpGÀpG 1ËW¯³A=;Z§öm­m 'Óò­]“ÈÄ^€[V>nŠ,þý»wØ&ÈIpæÃ¼Z“d¶xW:ÿVŒÿkòå*… „DmêzŸ¡.‘½Ú*¥gÌ´N¿ÈHåC?ñŲ2¦|7füàîâ8•ˆ„ã‡‹ÖØ’@B6%Ø`Û$åsc_²Ï_õU{ùo¯çö͉“bÿcO=›·ß•Ž€#à8Ž€#à8Ž€#Ðõáÿûa+®>À»”d+7EðßVN¦4.i¼–POŽ/l ü›nZqx@ÜäÛ€Áb¯ATuÉH]ƒé¤GÇšdÊtÕ%qeýM?q!,UdžK~ò)güàîâ8•ŠÀ¼eµ¶ªVßE4%¶ø¸8Ž€#à8Ž€#à8Ž@å"À‡úæXXP¹3ôÌ-Š€¸6 ÅB#Õ£ÛšüãC¢RÂÛQrÁå•& 2Þ"iŠ@¤Î Ì‚b©TE?~Ôµ½;HIô\ØcG?vuDýªK_Êø1€ÿpÊD€3'KY9™›=>øº8Ž€#à8Ž€#à8Ž@å"¬üªÜ$=3G ÂHˆþJæßrù?x?ø<Ê&?¼§KC ½€" e#RQD!útP|D4Rjudn,’äBdO]“¢O$¤rÐØØáC?zæ#’3Æ[¿~½µm«!B¯‹#àT<gcß=ùUß4™;¾ýÃ?ÿ›½õödÛ}×QöÝo}Ízõì™7è[ïL¶_ßô{›úþ6°;ë´“í3Ÿ>5Ú®­«³_þæwöø3ÏZMM­í¿ïÞÖ¯o6tˆ]ð™3óÆs¥#à8Ž€#à8Ž€#à8Ž€#P A)þ Þ‹º>Ló¡˜:¥úB5òd”ˆˆ1ú©Ë®M|1is‰g ÕØOltˆì5}蔇ìÄãá£ØÔóŠÌÛ™Q;‚é¢Kº´_ÉaGr\éĨsA$ýõQ›‹xi›¸L)i|ÿ¶¦,ÿéT*<­{ceS|Ó1o¼ùVëÕ£‡Þ{Ïpåköß¿üŸ´;[Ÿ>s–}é¿eoOy׎s¨­\µÊ~ú«ì®?þ9ÚüáÎ{íÞþb's”]tîÙ1Ö3/¼h=ºwÏÆðŠ#à8Ž@鼞Qöí·ÛØ‘cÛÚÈÇë/êèèsqGÀpG 5 !(ÅÁUÿr"?ñÔ¹àðÄí…ja)e%E4ŠPy¨¶˜Z¦=ûÞ)E&’¨’јN@öš„l‰¡<ˆÅ¥66ŒÏ\4¾úÚ9APqq*µëøuÝ8ÙßtÄ+ÿîsÙŽ—\ù{vì8[SS“šÄúîº×êÂ*ÉëÿëquäÚµkíäs.²[n¿ËÎ=ëÓ6ñíwlà€þöW]í'LzÇ>ž;ÏN=áØF±\á8Ž€#PHÈÛ?äß¼†2m¥Ù´•õ} Ý`?ÜmãÿŽ4Œì-GÀpGÀp*dg°VøÀñOm8381ž: _†ÐGr0åßB3k¯ìÐÓ§ˆ¡1dƒN1[ã3V:¾b¡c®‚"Ç‚¡‚jòØjP’  °AÁ>½ú©sa#û4a’–=6Äf M¦äñÙâíâ8Ž@1ôë—íµÓH[·.<„gÞü¬N•wߟfUUU¶çn»FUûöím·]v¶%K—Ùü mäˆá¶xÉR›ˆÉ³>4V\ެ!w/GÀpJDà’Wó““¹î˜Øº8Ž€#à8Ž€#°-#!(+š øÃß!qºÄï©/äûÁäJ‘…©=:HCÈBäb@‘ˆü·(=6+$±!1dªYB›ô¿Mì`lËßWPÔ\ F }Uú«^^¢›â[h¤¶mêßfÖåùr£&¬ªìС½µ—¤[×®±ZS[k—^tžõgN^ñ÷ß°ó.»Òz†­Ý×|ñ2™zé8Ž€#P¬œ|vaé°ÅÇÅpG`ÛD€­­ëÃUHØÕä²ù¨Y ã²µ`'_ŠæßB~ü¢Šÿƒ/·'þ/¨ M ¤Áô %}”$ÀÀ”Ä„PäBèÇŸRzÚøÒÆž6"=q°‡ÀŽØi¼ÐÌÚj|âà'ŸhÏ›™“”ÀåâT&];´³Úºû§ß斦ψÖ7wêØÁzöèfíÃÿ·• …ÿÇÿþ[ÿjΞc/=þ—)ñ€Ë^÷K›>c–uëÖÍN?ùxûÒeŸÛªÉ]½fuîÔ©Až-µñÄ›ïÚÝcß´Å+V[ç°@cÏáƒìóGïoý{vk©SjÑygx-ñ_Ìþ  æÞ ¡¸7lúe ý)ÿF¿85ùUŒ‡=6”Š£’X¹ãU6ùʆ¾¼RÊ»Ž‚(y%Eò‡@DT¦ÀN E£Ìâ!L ›Ô=>èƤŸRñð“N¾*5þV}s ù¹8Ž@ôìÒίÚ8‚ßæ?>øPxM·ðð›©ÆSºO8æÈðÏYÇF¡Ï Oë;îûþ_gçŸ}¦ÿªÍ [Á?sú©që÷œ@Nò!ÿëÿò½påëÓ»W<«òü³Ïð÷¢FhºÂp¶&µaµÉ¼‹+†˜,î¬jÙ%¾NP– ›;8Ž€#E€ÿcW¯©‰_\ è×Û:„㌶¦L~÷=ûڵߵE‹—4JcÁÂEvÕ׿m}zõ´oþýÕöú„‰öwÝgíªÚÙ/½¸‘ýæV|4{¶ý4¥9É1Pßþæ×²Då¼ùóí;ßýýëµß´!Ûm·¹Si–øoNŸm7þuœ ïßÛNÙoW›½h™=;é;jÏ l„Ë’|™ ï)I)~LÕo'=ü›üRõ‹ÃQlÚ\øèÃ7}èTâ+Ql•Ò‹OD_P4@AƒÐ‘HÄ">$E²P± I’°ü5© Š}Øá#[ôØà'Ÿê艉`—ޝwOÅŦŠ3(“ÃDѹ8Ž@!б}•õêÒÞ–”IRâƒï¦JÛðMñÈêöï|?ž=yè§´üòó†=hÿÑví×¾j¿ýßÿ³ïþè'Ö¹s';ë´“íªË?í;tè`ƒÂJÊ#ÇbË—¯°©ï½o¿úíÍñŸ»sÎ8-oLW:Ž€#°5X¾|eÅ‘“àðZXA¹±²)¾;¦û9Ž€#°-"Â߉¾}zmµé±hà+ÿô/ÖµsÛ~ØP›9ëùÜ÷ÀCÆÖîoýÃ5ö©ö³ÓO:>üïýýñÁ‡íóœkÃJÐ-)wÝýG;óŒÓí°C±›Ãg…'Ÿ|ÚN=å$« gÛÿôºëí‚óÏi1ä$¸½2uV„ï«§±aýêïƒóÛÇzwÛø/·äë±-Ž•YA 'ÿ&> þK‡c82ú)¹l±׆NýâØðçFÉ… ýÔ5mbéƒ8utô£“âUÔ»¢"R±¨Q¦SÁH?B;·¤ÔO)"R ã#!qôè4IêÄPÜPuÚô?M^vêS¼õNN”\ G`@a›÷z[UËÛEÓÒ¥C•ᳩrZøG† ¹æŠË‡ukðÏ deîV’SO<θV®Ze]:wÎná® [$ú«ìï.>ß.½ð¼“­(ç|îòðäïÒæü‡#à8[Õá=«evm9ÿš6œÁ¦ø6Œä-GÀp­ýwbðÀ¶÷»Ûׯ¾Ò~ü‹_7"(_{sbü¿ý€ÑûÄ òfLøßýwÝkïM›f»ï2j‹¾ˆÏˆÔaqÌáÛµiÓfÄú­ÿw‡ ß~˜>æÐ-šOs öîœY‚2%'§Í]dÿ÷ÌkF§pìÖgÞËŽßwgûŃÏÛÌùKìûžh]:Ö¯!c«øǽeß<óH1 ·=ðÊÛöÔÄ÷lIØ:¾GØ6þwÇèÄg /Xf%<˜x1ñ_èØ’H[„!6âÔÒíŠò—]0‹~”âÕTÇ–˜xjc£ñÑÁ>NãÓWò‡á4à—W€ QI"I £MI<W¶ÈÄPvG+Ñã£\ð¥ÍD¸?Tc²äñ“e°ø¹8Ž@"À?CûtŠ+)›J•“Øf¾Ajʼä~V?–óMk×.]ä€/ÿ ý¿?Ü’sÍ7®µK¯úûøÏѧœXrnè8Ž€#à8Ž€#à´vúöém×ýç÷ªÃÁy¡˜¿`¡õêÙ3³$ƒþýêÏ0ž7T[¬“ä†ÀÛ‰¸,éèRŒ ‚‹4”ŽR ÈF«éÇ~ùaC?QÉøêXå£LL6ÄB/{Jb£KÀ^ã®kn#ÄvqÍ€¿«{v $e;[ºªÎVÖÖÙÚuõ¿Ú<­›âpædslëÞ éÇ?ýÁwmü«¯Û{L‹À/8çL;(l9a¹‹#à8•„@çð¥ÊªpÎX¥ÉvêlVþ,/;|]GÀpšþNT²,YºÔ¶ ¤`*ݺvÍ%K—¥ê-R¿à¼sìú_ß`>ôH8‹~ p±öÝÿ¡ýÛw¾m,„hiràNÛÛùaK7„â„ésâµãà¾ö/çk];u°^rRølÖɪڶµ}wb?¸çÉhsÜÞ;ÙÍOŽ·—§Î´Ãv«¶¹K–«-?sÈ^¶~ý»?ÄÛ¾/ûÖÙGEHX•yýCcí£…KmHßž- ¦-š/Ç'ÆeqdZ8(²;>€bƒ=zø7tºÐQG°!–x4x¶8X(e#?Ê”“oPGŸ\;baOŽÔ‹J9ÿ¦I2€¥®”¬t´é“/zÆ);•²&Ù˜Ôb`§‹¶Æ£Ž¨­z[VP:I±ñŽ@‹@r@OÞç?¤¦Ò'É9•\.Ž€#àT2Ý»w5¶ïUÚN“Ñá)ÜKPâëâ8Ž€#°éðù™¿•,={öˆG.¥9®X¹26{…¾--< ó?¾÷8,çN^ûïÙEá,Ìá\úý×Ϭ}x ö9gŸ¶ÙÒ©môxg¼§0z”½4y†Ýÿò[öÞœ…vÿ+“ìÂÃ÷µ>ݺØÊ5µqÕäôy‹ãKW­‰äå~#‡ÚÓf[ÍÚ:{iÊÌØwx +ç²ruíZ[¶ªÆ®ý¿G¢~uX”‚|úœ ŒPü‘g‹7üD"™¸4ꄤGJ•âÌr94úS¡ÞN‚B|ú4&¥t蕾Ò+ÚE¥‚’` ’NJÉj@Q”ŠK?w“¥Ž^mÅ ª¬/6bréç™IÉW“|¨c£2Tc›ñÐ;9 .Ž€#à8Ž€#à$ðdÖýûØÒe+âÓZ+…¨<©÷jûóâ.I¦¥WñuqGÀØx &;…•“={t³öõÛI7>ØföЯŸMyï½øËª°ÅaÛ72 ¿Xn­¸íN6t¨í-ücxÒ8Oï^±b¥ýø¿f¿úÅOZGÑ5ÜdžU‘{l?È®¹é~›‘!#ïzáM{püÛÖ¡]•õíÞðïöá»ïWPBR²5|ÇÁýlpŸ6'< Ù.Ô±‘@Üì8hë¾fÊ¥’ËÌ}÷Å%>x6ø1¾ ø;ˆJ‡âÍèWì¸Ð!âã(ùŢğ¸òÏ7¾âaO˜â ‰­œBµ°ˆH,lÑp2JŽÁi¢Ôe‡Æ 9ú(Å`¢:bÊùWà3Áúw¢ú¸ØŸK/JøÛWPŒ‹#à8Ž€#à8)|øì·ŸÐšæ¢ú°P¹ vƒÝþ¡þOVOñò‚¡ì¤{7ò^GÀpmÑ{ïi&½mã_{#>Å›Ïý/Œ{Åztïn;VWoµyŽõ5{óÍ öãÿüw›6}† <І©_5Évè…‹Y¿¾õgenµ$K˜‡ßt «>µªqÙê5Ñ«w×Îq+÷½/N°sÇìmg‡U–ó–®´«û§lÔÑaËw·NíÙIØû/´ËŽ= ö çN‚Á¢«ì˜½vÌÚ{¥4Ö…•¹óBRþKmñepe" )SþMÜ:þá"%mql¡šåõ䫘²W$¦8ò—-zD1ê[~’|S¢¤e§¤EÒ¦Î%ÄÔGIB†$HÁOĤ&F[C‡­ÆO'F,ÅÃŽºâ†j¬ÇÒ J`pqGÀpG e ðÃÝ6ØGa1ä³ õ¯_ñ¼è»ÁðqqGÀض€€\VêLɱ/7T¹Ïž»ÛÙ§Ÿb·ÝóGûÑu×Û¥o¯O˜hÓfÌ´K/<¯¬_6'b ,°›~wK\1Ù±cGëÛ·Í›· œ½¸ÞjjkmåêUauê–ß~^îáP®{ày[²rµV:öèÜÑ^xgz sÄ;Ø‚eõ[éQPg%e*íªÚÚ!» ·ÇÃÓ»«Ú¶±Cw»©Ÿžôýð«“㹓è§Ï_l{w–]ûÙcŒÕš.…àu "žŒzÊ¿‰—£_zlÄ©Ñ/~ìàO%vm.ñqø"²ãŸ4#.Ž=üuDããG ùÐ×HJ!(å”þ—H’ M¥_6"'ƒ*ú°ÄM=¢d5ÅÒhëRlMŸ|B5ê7´ ½‹#à8Ž€#à8Ž@ËAàÖý6Ø·Ãç¦VR²rÒÉÉ–óºz¦Ž€#à”ƒÀ/oø6iò”¬Ë×ÿå{¶ÓÕvëׇUˆ}ì~òCûÁOIJsѹgÛ.¹ k¿%+œ;ù³_üÚxXÛ»‘þaúá‡j_ûæ?ÇÄ\xþ¹Ö>±RéÂVÿ«O>Äîxþ {rÂ{ñ¼ê~=ºÚ5§ŸÒÍ9’; êkló¾ÿåIvêþ»Úðþ w1°…›§wïVSöÓ‘\ίä)àOO|ßž +,!%¤e§€‹æ°µÊ̳UÄQ‰W£Í%þM$$m$׎¶¸6üqyÔ›ºø=ú5þ²Q?¶ÄLí4¶»¼‚QS¢@J@ö$‘&Ä`i"Ÿôˆ˜Y•A•eR©C•“¸S¥ø”Äæ¢OB[>èôÛŽ=OØXæûépqò˜2g…Ü­|G÷pG • Àûäð~7ëlg,Xm#4<ßi³XAÁ_[jvÏGmì¥ÅmlÖªúĆ(î½ÁβÁFû?+èÕòTGÀø÷ç­ÚbŸ#V­Zm;wj¶³›ûoûš55VVn,9¹©ÿlÊkQ[·ÎêÖ­·.Eµ|ò/_]cÃ6pVL–+G+‚·ðDð ñVnãujîÿÀjsÿ_·tÑëÕ«Wœw¡¹KŸY5±YÕ»wï3Bcf¸Xø'¾ .Ž®&S‡‡CGŸHB|°G“?m‘ŽÔJbKhutÄ¡..0T³)ýŒ¯Øø¡+*)¹WÔ0t*H(Q%‡>\«Ñ¥.lsýê@<ô\L=1‡_ò]Çrj_EpqGÀpG …!9º§þ·maÉ{ºŽ€#à8[.]6ï…›:‰Ná<Æ–*<‡+Ÿt[¿7V l6ÅcÇmÉ~Òþ áEdD²üW¨Ã—‰£ƒÓ?QâÐä'®Mí`šåø¨‹Ó‹=›xAôÊEã3–%¢S¨ÆØè°)(Jª AèÀ†Á)0w ÐÂN:P&B²S1H&*3¨â„4¾âb/_l½l(£89)$¼tGÀpGÀpGÀpGÀh¡å¿Âœ¶*ÿ–3>¼œòM9¼‚З²‚R 'ó]ƒ".©#Ò¡WBè!!ÕV,⋸Ɉêôã§ÉÑ'ßPv²ÇN¹³ Kbµ<ãJòª å¦Kv‹åÆ8h×ÅpGÀpGÀpGÀpG õ áŽÄµQÂQ¦œ¼zDü™D Å¡a¯ÔÅ¿‰sS‰¿¸6J=q%؈¿C'õ7*K!(5€&© $®É“z;ôué5~Ò)f®¿úÑ3>u.„’嬜]I8iž,k%ÆúJ$'—,YbsæÌ±µkµ"7dZ‚l¿ýöñ¬‚LÝÄpGÀpGÀpGÀpG`@ !(Å£1+qa”ÒÑÁ¿Ñ†¤-Þ,Tc‘ulàÕàÒRÒ=q“¶ÿ”ä/'[ÅÁ>õ“ƒR¤aeNCɉ$Tè¹J‘’jc‡õ”© ÍhI*Ž–Nc©Ä8" SÆg,tÒ·­´3(׬Yc3gδvíÚYÿþýK:Ÿ¥K—ZM çº8Ž€#à8Ž€#à8Ž€#à8Ž€#à´’Ý·ð]ð_”ü|"þM¼º|öò£ï©˜òx)׆ãj,ê\ø¢KýB3ËÍI ËzMò³‚’é€Ô%é„I‘N>$Bc)YõÑÆ¡Ž`¯èR¦–šX¨fí¨k|ê¶¹ºØ±5,_¾<ÏjÈnÝJ{:²Ê­™wk»®®ÎV­Ze=zôhMÓö¹:Ž€#à8Ž€#à8Ž€#à8Ž@"Ù,>®‹ ¾NœmqnâÍ´P>ìd+.ÞL6ø¨Ž=cÑ/Jt”ô#â ©ç_qTbWP¬)a˜&…&N‚\$†­€Pâøa«Ä‡~‘¡·l³22M­–”ŸJÆÁ–Rã3mÊxUÚoVt"¬ ÜÜòãÿØ4cuïÞÝ `ûí·Ÿ9rs¿Åã¯[·Î>úè#:thI+SI0ŸÏ=÷ÜcÓ§O·‹.ºÈ† ¶Åçá:Ž€#à8Ž€#à8Ž€#°­ ÀʯJû\¾­`ëóh=$€ÿE)^L¼›J¸681ÚâÐRç Ò_OP…Jø7ˆªT'ÿÔV\cs¥¢ñ‰¥ñéÇ'‹®‘àДDÂ6­“ŒæM@ÉQªÎä©+yT1ŽÚ}³œññmuÒ¾}{;ôÐCã5|øðxöåÝwßmO<ñÄ6‡¤âm·ÝVÖVø|>Œ«[!t]GÀpGÀpGÀp6>È;9¹qØ5—WUÛ6¶n½hšæŠºuã0æÕš„ÅUAÄU:ÿ&Î/åÿ¨•R–ñ€‚BRçNÝbCÕº1©²Q,µ±•Ž˜"%)bRI‰/(¢|°‹ï™Rä'6­VX©yøá‡gç¿bÅ »ýöÛmüøñ¶Ë.»ÄÕ†ÙÎ^Y°`AÙ3ÈçsôÑG—‹#à8Ž€#à8Ž€#à8Ž@ù´mÓÆjëÖ[‡v|\ÿäƒzù‘Z·GsqÝ;·³ùËk­÷Û©&̇yµ&©ªE–åãÄÁƒ©“:gÍL_Ä”C ÍØ«)±Gˆ‰-àŠŸ#¦V\†j´ÅŽKB.ß—Ú˯AYê+J 4aWèI„’‰p¥öJ’’>Dä&1d¯”è)`eK?¹kLA?—ÚþmM#ν<ꨣìÞ{ïµW^y%KPBX¾þúëÉ Â£>Úœu7nœM˜0ÁV®\iÛm·üñV[[k÷ÝwŸí¿ÿþvàFÛ‡~8nþ⿟R~óÍ7Û!‡b“'OŽ«7Ù^¾÷Þ{Ûƒ>Ûƒ ²ÓN;-ûtò?üО~úi›7ožõîÝ;æ1bÄãNb3ƦM›fï¾û®õìÙÓN>ùd2dˆ;Ö^{íµ˜,+G/¹ä’Ø~ã7ìÕW_µ… ÆrÊ)q«{!Ÿ'Ÿ|Ò¦L™bŸÿüç­K—.¶zõjC÷ÁÄûi‡v°cŽ9Æ:uêÔd^Y½â8Ž€#à8Ž€#à8Ž@+A {ç*ûxi èÑ!’”–.å!·dåZ뱉D\Ÿ.ímÅš:›µpµÕÒ¸¥KÇ@z÷ ˜tëT*ÕÒg\Ÿæ!9)ߥºˆG Å…QÒÏ Ÿ¦v¨Æ6%‚~-åðÄ×Ñ/=`cË/²tÄGÔ¦Ž "[ê¹|º¼RÊ+J²×¥I’}€Au„ä¸4ItØ*ylùjijúŽÅ°§ªÙq}i]`aK]cÑvÉ ÀVoD«_zé%{æ™g"á·óÎ;ÛÛo¿møÃìŠ+®ˆÄá‹/¾hÏ>ûl$3GÉ»I“&Yuuu£§‹ópž8Î/õGyÄvÝu×,)øÎ;ïı ú¦Nj?þ¸sÎ91¶hCL|ðÁ‘èd;:ytìØ1Æ‚åCäIˆÎ+¯¼2ž¯Ù¡Cý­±Š=2kÖ¬8>gn¢'ïûï¿ß.¿üò‚>’ä­ó;ï¼óNûøã ¢”9AÔ.Z´È.¾øâì å“ðŽ€#à8Ž€#à8Ž€#à´"úuëW¹ÍZ¸ÆºvlkË׈£h ÌX°z«' ×#‹Ý;ŠjÙ¸”à†YmØÚVnZ•ë•Ùâ-¾ ¾‹‹_,ñoð_âÀÄÛUn¢šp‰ÄW¶úå$Ž86êô‹— ÕÈá±’Rvè4¾üÐ[ãk Æ×8Øä%—·3£$ ‚¦v$…08Â`JD“H“èGO,Æ•½âk ÅÀ—zªW=¨#(Љu|¸4>¥Ky\"á (»víj¬zdÙ0[¿yP «&Y)I ixá…ƇϰeœsDx M)²ÓN;ÙgœW\^wÝuqE"!‡¼Þpà ‘D$ã@ ž}öÙÖ·o߸2óç?ÿ¹½õÖ[ñÁ>Ø@žwÞyTcÅS]ycƒà'&¯ñ£ÿ¨G€‡àð„Û@ÚÁÄÏž=»Þ üd%#Ä$+YeH¿VfBE+#YضnÆáÁ9<¨'ˆÆ‘gÂÙš²—]vYŒÉ6ræŸ|ûP4,9!à³ãŽ;fëT´‚3*ý‡#à8Ž€#à8Ž€#à8Ž€#à8›d‘ü—Ⱦ”cTñsªÃ¿A6RÖ†‹~.ø2ño”6po¹ü›8<|Ÿq©§ãÓ/ÿÜ~Å&…E¤^a‹úÄ8 Ê t$ÁäHŒ>ü(EDjBA{.DöÔÓIãƒ(ú¹Ð3ã±bNÛ‰C_ÅȲeËâj­è+–˜V1–b›‡U~Ï=÷\Tsî"O¢†P<à€²Oðæ,Æ7ß|Óþò—¿Ä³_~ùåh?zôhk×®í±Ç6qâD{þùçãjØÞ 9ǹ!)yÐ Û‰ÿþûïç¦Pr›§óà¶xë©à<™›'w§O/²a.«œ¥ÉSǰãA7<(•|>i?ãáÉçäÁuވ϶q¼£–©×GÀpGÀpGÀpGÀh.2¥ø/ñnpmˆx7Jõ¡‡'“ˆ{£Ÿº|áÚÄçŸ6—x¶PýâßÔ&†Æ¢Ê_D<uŦžWJ!( ÈÀ F@ „Žº’ÀŽä”4zùÊtŒ+Ò‚^—tŠGLúˆCÑøèS{bk¼õ¥®”#à–±Þe劈Êrü ÐÆŽ]8§¢âQ«é8öØcãŠBÈ;ˆÈ=zØi§ÉHõ‡§y¿ð qkøž{î ;¢óØcÙ<`C† ‰¤åøñããxåþ€ä<ýôÓU<ˆb¹ºººä-Þ©¥¬d&çˆåIÞ·ÜrK$!A9[S¯C>Ÿ4oHÉsÏ=×}ôј}Ìó¤“NŠä­”)Z^wGÀpGÀpGÀpæF Ãaˆÿ‚ó§ÆPpg)gGÁŽLOßw†ž ‘Nd#múÄ¿QGG[uâË>Ë¿elÔÍh‡‡m“‚c)¢Dd¯¤'It´Iœd¹ØÆM©ÉÈ?¨²  “>µW<ü”1(‰E_:&þô¡çBÐ/âý±U!?ØnÌJCV奦)–vxƒtËÝú\Ìocúx ŽÎXÌõ‡ …ëÔ©Sƒ.~QÈ1}PLƒhÕ›\åJîÈ-â< §äúä³c5*„·&”Ïf[ÓñðHζöªú|G 9à}rx¿ÎͲQ¬ VÛÈ…ÿ†5rp…#à8Ž€#à8Ž@Ù¼?oÕfÿ¿né¢Ù£ô -¨“>CJÆy°;4ì=-4f… >LD%\YýC4êy8úáÓDRJ'ž?tðgèDzŠ| ªØ'=6òG{ÈJò@ˆ‹(>¥t±#÷G)ì‚S*I J !¨ÄhKÐÓf ’ iD¶Ô!$™(¶ÄW©=þø`£É(.1‰¡ÐcK¬8~¥mñ¹ÅjT¹VŒ"'I|óåÌ/Os’“Œ•K‚¢+Urç@~ÅÈIâæúäkcÈÒ|q\ç8Ž€#à8Ž€#à8Ž€#à8Ž@©dŽ.„ë‚#ƒGã’À‰[ƒƒ+ãJ¹5l8‡Éåß°Ãê~ =ö*åK[ü^äßB[þ;¨¢¤1¥kT2h)BpD‰PG§¤IQ2$É¥¤4ÈDã"ýØÑF°#&}ˆôØqá/Q[±Ñk|ê1®˜g• <‘zÑ¢EqÛ1ç–r>&« ]GÀpGÀpGÀpGÀpZìè ÿ%N þK$e.¯†-ü™ìi‹SIŸ84tê\ùø?=Ý›ð%ck|ôÒÁíÑÖxéAÝXJ!( (2‘ Œ(JMŒ’˜šP¨F;ü±“^¥’ÒkB"4™¸&£ñe«ñ‰ƒŸ|¢=Kb+‰¤deàöÛoosæÌ‰g!†|K–RVù•Ì GÀpGÀpGÀpGÀp* ¯%þ‹|á¿àÅ )áíúщ{ÃqH6ô‹SÆ~qjòªh‹=6”W%±rǪlò• }y…šQòJŠ$䈨L'€ŠF™ÄC˜6©/z|Ð#ŒI?¥âá'|Ujü’V(†8[TxÂ4O®­­Í>¬¥©¸ l %ïwGÀpGÀpGÀpG`ÛB 9ÞKÛ¹Åi²j‹·“þM~©úÅ¿á‡`‹mñp)ÿ‡J|%Š­Rzñ‰è Š(h:r‰XćdhC*@¡#I–¿&T±;|d‹^““É+.uôÄDˆŸŽ¯íߊ‹MU¥AIRˆŽõ8øOGÀpGÀpGÀpGÀpÂdVP‰Á¿‰ƒÿ’ˆ0¤®Œ Á;Jüõ‹cÃGœ%—ø6ê{b‰—£ŽŽ~t²Sü Š:âklty¥IƒÄ [.%M—Wú IŒ:—ž"„ zúT¨f'­É£ÃQ\ÕË¿”3ãHþÃpGÀpGÀpGÀpGÀ¨02+(sy³Šâßd"L ñEQM ÀB†)ó)bQ"ƒ££- ÄÖ›+%$SÂR+ƒIv;uâòS¨Æ+{üdlçâ8Ž€#à8Ž€#à8Ž€#à8Ž€#à´ÚµƒZ«lþ-äǢĔ·a“gŤ‚RD¡HBµ)ñ×2NÆÁF«'E4¦~ØÐhe¥âAJJCD%ã¤qäÓäø™e°Šë¥#à8Ž€#à8Ž€#à8Ž€#à8Ž€#ÐbàøÂ Í¿…üRÞŽ|Å9ЍDWPd\Ð éÀÒQ—HBÚŠƒ.íGψèIJéäƒu•ôS—PWlJlS_ìÔV[_A ,.Ž€#à8Ž€#à8Ž€#à8Ž€#à8-<[¼ñ_a^ðfâÚ¶8ÿ–3¾x9Jñy¡ZXR°•P‘ŠØáG[Û¹PH,ߤD`J±“?müf¨fm‰#[ŤP4Ròø¾‚²0ÿé8Ž€#à8Ž€#à8Ž€#à8Ž€#Ðò¨ªŠt<™¸2&QQü[ÈÞNœ¼urLù¿ÐÌ/84%Øè"8u‘–" À‚„ĆNÐR@IDAT¶ÈJ±¸S1°%Y]š8müð¡,{|_APsqGÀpGÀpGÀpGÀh‘¬[¥bù·¼œžø¿”ë‹ÉûQÊA•" '% ÑÑN Lt"!)IR‰¢Püô L[%:l5~:1b)vÔ7T?‚²%¬¢äf««¦Àè†ÃO3ìxlûGÀpGÀpGÀpGÀpÖ‰@ž-Þb,Å›‰“ Ô'^~x4ø@ñn*±Chs‰ƒsCd§8è4.%zˆ-êˆÆÇinô7RJ9ˆ ¤M’ M¥_6"'ƒ*úðpœ\B‘>%«‰(–&@[—bChj|Aý†¶m.ê*ê¤äÂ… méÒ¥¶zõê¹uîÜÙzöìi}ûöu²²2ÞpGÀpGÀpGÀpG õ Yx'þ‹~ Â+r_™Rü›HÈúUpíð׆?".ºbS¿G¿ÆÿF©~l‰™Úi âiìòJ)¥¥ L$!1¨+``ôøé˜BŸ’í©ãƒl𥮉PW_¨fÇ¡N,K|ˆÝ1\©mhVŽ,^¼ØæÌ™WM²b²W¯^Ö±#)›ÕÔÔØŠ+ìã?¶ ØàÁƒ­wïÞ•“¼gâ8Ž€#à8[÷ç­Ú¢ãù`Ž€#à8Ž€#à8•ƒ@† Ly6ñmââÄÑ¡‡CÓâ=ñnpeØ gá v´e/.Mº”O#ãOcⓎ…/"ü4V}oŸ/U4E¢#1tL’:}éàZåˆN¦Î…m®º|€=ñ4>“0ŠC©ñ×ñ(öJ[E9oÞ¼H>BL6¬ ù(sÖ¬Y¶víZ0`@˜Zù’y}ÅáPþLÜÃpGÀhŒÜ­uNÜgí8Ž€#à8ŽÀB`Êœ[h¤ò‡É”ð_¼$#’å¿B¾ t"áÍÄ¡É6¢6uqqÔſѨO¼ zå¢ñG‹Ñ©?T£?:l J)¥'.J•ì•8  ¹([b`K[“ÅNö¡'$B’¶&$_t8ø§9T)éÈÊÈ.]ºØðáí}{¦—_X5Ù­[7›1cFôÁ¶œ•”sçεG}4®ÔäfîׯŸqĶãŽ;æ°´Ë—/·U«VÙÀ+ OÁpGÀpGÀpGÀpŠ@ —ÿ‚ƒ'ƒGC(iÿÁ÷ÁQ§DÄ¿aG,Ù£Ç> ]èñ׸”´Ñc+Q|â¡Ç†Rþô³¨hbFâ"0ñI“–^§“Ç>Mš>MT¥&B\b`¯¼¨ÓÏ$5NYãWÒS¼9s’mݬœLÉIŽƒ~òäÉöÎ;ïØüùóM«!%±Å›äÉM’ŠË{î¹'n?øàƒ âø•,Ï=÷œ=ùä“•œ¢çæ8Ž€#à8Ž€#à8Ž€#à8ŽÀC yH\ZEòo!¯¦ø¿¢x•³‚r‡ Ž^äa¨f™RÕ±G4~Ò)f®¿úÑCHм¤P²œ•³+©G¤&u–%c}fl¨n}á8‘lëÖÊIn²éÓ§Gò°S§NF"2bÁ–s(ÙêMŒR¶zÏž=ÛX8fÌ;ì°ÃbHÊJ"lcR9?8sSØätyÓpGÀpGÀpGÀpV‡@BPŠGqa”ÒÑÁ¿ÑÖb?ñfA94JD>Ô±WƒKƒ·C¨£'ŽbÒ–à/ˆñr²UœÐÕ`\ÚD¤a£ŽD¡äD*ô\eÊ”ÒÆ.êL½Æž6¡D4Qé4–Jlˆ#2õ'±ÑIß¶’Πäiݬ„L·i×ÖÖF2²OŸ>6tèÐH N›6->Ù›¾à`-ú@\£‚2:…l'çF†¨M‰¿[o½5¶Ï?ÿüh:uêT{â‰'ì˜cޱQ£FÙŸÿüç¸=~§v2V5®\¹ÒF·ˆãÐT?O%g%ä|ÇÞa‡blHØ5kÖØÍ7ßlGuT\-úÚk¯Å:9/ýþ÷¿[ÑE®Æ$ý‡#à8Ž€#à8Ž€#à8Ž€#à8­ d±|—ø4P€?ã×&þM¼6ùìåG?"ÞMD£â¤\vŒ¯±Ä¿a‹ŽK~¡šå戤„e½&ù© ‰ªQ• Ê i"¦¦Ÿ { J¡$!¢Š—B.èS|Dl#íÍ’ÇÇv« [³!í8S2žÜ=räH2dHTC$ʆmÚ© 'F)Û¼ d;w¶÷Þ{Ïn¼ñF7n\ôU¼eË–Å–jC†B~R"’o¿ý¶=õÔS6bĈxfæ‹/¾hˆ¥ôßyç6qâDëß¿¿õíÛ×&L˜·œãË/cA|Ž;Öªªªâœ!i!”ÁøàƒÑ—þh([Ð/¿üòøðŒ jÇo•Ç|ôóŽ€#à8Ž€#à8Ž€#à8Ž€#à´fàY2"þ‹R¼<*! Ô‡–êX G?œšþ Ž0Õ᫘Ò3.±;—˜’-±4~¨FŽOþ´ó M A4!lÓ:ÉÐæªß‹üI? § (9JÕ™¾-}ôQ¢6ulÙR×xÊ]^)… $Y‚ëRP£0裎—&‰ND"ulùji*6&À8”""éÓ8€¾´.°°¥®±houéÙ³g<›‘3!ó}zÈvÛm×(W|ø_ê–hΛ|ùå— B‡íÕ3gÎŒ+\ø‰ðð>„?ÿüóñ!6”¹Â/²ÁŸÈ^{í•5+Ôχ`HžÎÍMìÞ|óMcþœ3©–Ù@I“ÒœuIެútÙv€ÀºâŠ+²¼ýöÛm·Ýv³}öÙ'êÒ'Ïgr*¬(>øàƒË"_¸õ`ªœpۼꪫâ—ü.sVë]wÝe—]vYü²¡Ò’æý‚/GDPnÌk´5çĽqàn3ï?Ü3<ô¬‚’÷lþNA ®Zµj“Ië|¯' ‚¿ñoäëv#à8Ž€#à8Ž€#àdÈìtßßÅg&þ þK˜x» ŠÿÆv^q€)_&‘8âØ¨K¼\¨F³+e‡Nã˱5¾òa|ƒM^Qry;3Êt’©I! Ž0˜Ñ$HBB?zâ1®ì_‰+¾ÔS½êAAQLì¨ãÃ¥ñ)+FXY¸`Á›3gN$ðrÉ“O ôðáC¾V'æ³KutP\!9yòd›>}z$ !':ê¨hvä‘GÆ\^xá…øÁû\’’ÕZœ‹É“¶Yy‰/£¤Xÿ¹çžWÕ<óÌ3Ñœü'tRœC1‚’‘’Ø *©§üŸÆ§Ä—KÜœ|rÇW;˜–RJa`.„6 0IA/Â1T³ýÔS‘¾šuD¤b:uÙŸ6—â Óä©+¦úƒª²„jðàÁñA8l/…XHIÊQ£F5J2[VO²ú¥”íÝÀ8å”Sìä“O6¶÷Aú¤ÂJEV®±Íâ²ñSŸúTjë矾qÞ+!óI¡~žÔ}É%—M.ßþö·ó…³½÷ÞÛöÜsÏø¡µœ³6óså6…+kYïû.]ºØGшŒ„tôgë*«Ãy䑸º™ßÇ]wÝÕ;ì°,vœû¿ÿû¿qû8+’%[Öy°Ôþûïïelï¾ûîH*jõ1«¡É÷ÒK/¿—lO?óÌ3#1ϱ <Ôª©Õ¼ ün#…bË‹÷pb%5¿ßé—Äl*oæúÄOd¿<8·x ¾Fàñúë¯Ç/ª««ãœÁ€×ˆoÿøräwÞ‰çòòEÄÑGm¼o ?þx\-ÎVqæÀû[ÚñÍ•©S§Fìy!‰£•‚7Ýt“wÜqîìn¹å–øþÈvy [ÿY¥ÎêÏ;ï¼3®Påý‘|ÈqáÂ…‘¸c˜`†ÅäÈ‘Üo|¡Ã¹©¬Bä˜V¼òïñl]†ˆW?qÁ‰œN?ýt{ã7¢ï‡zhv5±ðæþ§÷J½gË•ô¬fçõ†ÔæÞ"Çb±¼ïó»À=Æ<6† ,ö:óå_F!·Ýv[üÄ[cÿõ¯ÍûÚ“/Óøò˜ì8à5æwLÁ×ç©§žŠ÷ŠsqGÀpGÀpZ:ð*ÉåߤOKñbpj"SþM¶Øq~MmñqÔÓ1©Cx"§¾õÉOb¥1èÑŸXåÔÜ”((‰’ ‚’ÂÀ\õKO›«}Æ€8—xJœ ‚©Æy‰žòIAÌ7~0mí­.¬ ãƒâÇ?|A4Z-‘‰9ÉÃs Ù›7q.9™Úë“]!r²”þ!ùðÎåâ¤@>° ñÁŒÄg>ó™hÂX 52 3ÎÑË,…}ú­Î`…(#&[¨ù]ãw3È&Ž9Б ôAMš4ɾøÅ/Æ/Èçì³ÏŽgCBøA“íêäÆï¼„cU+«Û ÆØR Á'’NöùJ'~·DTæ‹1kÖ¬ˆS¡¼D¬ÑÉÃüR)–78Aàñ@¯3Î8#Î"‘œŽ?þxûýïÏ$„ôÔ{LúAñÞvá…ÆßucÈ3¾4h‚ƒŒæõ†p|õÕWãŠlâAJغ öœSÊkDLÎ+eë;_Òð:Aæ»o &‰+‚’{…mÐäÀ땾Fäá aÇX‰ÂD7:óâµ<묳b?ä)ïÝ]t‘ñ~Î!O9/ú™Ó¾ð…HºÑÏ=sõÕWG"”ûHs†ÔÆ“{™<ÐËã VÁr”û²)äã÷‡{Ÿø‚úR­)ß´¿Øë .p@\¥Úi§E7}‘ÅëÊ—kù^{î[°æË1Ž !7¾,à½LÉ“ßyÞtï¥9yÝpGÀpGÀhÁˆgÓÄ›årbüÓg¦•“âåàÕôpmâÜð§Oœ`UG¯øùÆ—N6ŒçGl.‘;”?T‹ ÷4ÔT¢ Ðf`D¡®˜€$éǺV^b(è¹°ÇŽ~MŒ:¢~Õ¥/eü ’~@@h…$Šù@ÆÊ.F‚m)¤EsΑ•;¹FHã7ÕŸÚzÝh ðx( Ä+éø]É'òüî@È@`h•e>[tØ/Z´ÈX­y£ßÍÔ"âb Ô‹Ô!©X ÁÁ©Äª¸R…ã˜Da!RÿOúS$¥~ûÛßF2‘¤:ã‘qrcË ’”m»ûî»o$èøB²±Tá Îb¬Ä#_‘±øCš)D"ùåbŒÍŠ<¶³Ú”ד8n¬¨”€auuuô=zt$Ÿ8"#0†˜$V1ƒ1y•ðÚå»oˆ AÈ}‚@ʱڑ{&Ÿp¼Æ‘G_#Æxÿý÷ãêOHeƇ,f•!D­†Æ—;¬r„èƒ#×ô¾¥Ÿã*!E!¹XŽÌ¼!ÔGŒÛŽÜ§¬t”‹±‰Ïë@ÞM‘vµ|Àø¯'¸€O9ÒÔëÌyíòâ’°5ßk¯˜üîñºñšC0“¯¾ˆ€¤„Ü+?"Dˆzé8Ž€#à8Ž€#ÐÒЗ0Jæßrù?x?ø)I&}¡2%g Ù°’ " R÷†\â/7F±¼ ÂxoÔêÁBcÒ›<6æ½ òŽ×.}9ïÑI¹ïƒ‚ ²ŽœSaä)(˜*´Ú<½o ¹8kò›òÔSOU˜Fe.q) Ò¿/ZÑJ_>a…î[úaJÉÅœ™'¹CTJÀR2ŸÂ+Ÿm®"’Ø¿"ÙyÍ ðËyXÎÆ¼Î¹¹ÐNç) Ü'Z ¯Ÿþaãw„ßcGÀpGÀpG`[B óÿ®ø/x/êpmüuJõ…jäÉ(qoôS—/\´Ä¿ÑæÏª±ŸØèÙk,úÐ)Ù‰ÇÃ]ÚF×@J!( @¢ F@ ¤ä•v$§¤ÑËÚø c\‘–èôº¤SYÉŇVV~ |ÀÒ‡ÔJÍÝór*H'βƒLyôÑGã9‰Ÿýìg ¦ ñödˆGVö±e¸ºººÁVbœ!Aî¹çžH¬AxêœHˆA¤TØ&.Âßåt‹wjWNUc¹¤d1ÿby‰Àá|ÍBR,oņ,J ºB±R=+òða[ºˆcþØB€1Çr’Lš:S±PLVQ>÷Üsñµâ>àu/UÀŽÜõwF«#éãþk.až` ™®±š+v‡1XY .#FŒH»ì±Ç‹$®Î½lЙ§Ñœ¯³Â“¿œåé_’ /GÀpGÀpZ‚RüD—85¦w–rvÔìáÈôômqgè¹éD6Ò¦1ÄÅ¡£ž:ñežv¡ñ‰m“¢DŠ’©ÄV £Gļ2pÚØ ã"qJt©mhFöŒ£Icƒmz…fƒ1hs~˜càæÈ> s99Ù\¨zœÖˆ[oyó†„c¥¡V¦‚Ó° Ò Ò‘ß?H\a{.ñ8³’‡àHxX Ç4èìJÆçbë.‚[‰ƒ³/9÷pKH±¼ õX=ÉÙ|9¢3 Ó¼Šå ‘F/¾øbt!«íDÄ‚=$¨tŠ Î¬Å—~0¡Îkƹ‡åÛ¡9 TÇa‹­íéêºbñ/†8ÇR®aE-÷ŠÎîä˜Î»V—6§@òšA¦fþ1‰÷˜æÝÔX¼èøjK;÷1[£Sa%*÷Gx°*8½Øò _ª”ò:k»9+N•W±øü=ç¾fųì9Û³ÜíçÅÆð>GÀpGÀpG àÿëŒä¿BÿVåߊŒ/>PsÈ[6þÞØL(PŠ!¥TâIYQô´E:"[êÜ ¸)Y)‚Röøãƒ }ˆâbC å€[r‹ãoÌ Ÿàëâ8-/HV{Aèp;K•)rŽÖáÌDü9¿.A‰-Ä„TJ¤UWWÛ1Çc÷Ýw_$ãøÃÁjKˆäC‰}¿þõ¯#qÅj¿·Þz+ömÎMåuâ‰'Úý÷ßo×]w]\Ê RˆTI±¼!}y¸O?gÛ;8³-^4!ûxBó¸q㌠@æ¥^<ì„mø¼G³}š­ùå{¬<ø…U¯¡p¬Âäu*E !Þ E(—⇠°2—UºÌ’–y’Os ÷#G 0ÖÏþ󸊒c8¾ á~eeäÏ~ö³8OüDärŸH ú8CUÄ¡ô”àĽ?}úôx.eÚW¨ÞÔëÌx½~÷»ßÅU¬ŸûÜç …ÊêùýáÞâ,V}™pðÁgû½â8Ž€#à8Ž€#àl‹d>kÁuÁ‘Á£‰¨dºð_âÖàÅàʸàÐÄ­aSÿdÙOý‰£Äê~ ½ø?J.ÙÓDþ-SÒ§±éCÒ˜õšÂ‹±›>x1¸8™pg´)±C¯†-:µe“ö)žžîM›ØR*žÆ¤M]±i#ŒSP ›’˜)0eš”!&¶©=þØKŸN{Úˆôš$ŸšƒØi¼ÐÌÚ{¡ò‰ö¼ zqqrqmHªBDa¡Ù7Etp¾àG}TtEf±1·9É|‹åÕÑÕTÞ…bZ…šâÏ{sSã§öÅêMåYÌwSûJ™ë¦Ž!ÿM+}­š wåU¬lêuNó*'í#fS¿³©½×#P·~­=3÷öð‡·Ú»ËJß¾ß8’kG 5!°S½íä¡—Ø‘ϲvm ÁY&ëê¬îÕg¬nì#¶~æ'¼+É×G Õ"Ðvû¬Ý¡'Y»ýŽ LQ)”Ö¶U†×ÿŤàÁàÐR’’~tâÞ°AàÕd/n.åßè§&ü°Õ•òØ(cчh|êÊý²¡/¯”òj*ˆ’§Í &DDe:ì”P4Êü ¤°I}Ñk êŒI?¥âá'|Ujü­²š&äåâ8Û|ÙqÖYg9)²½®>G µ"pÛ´ŸØ½Ó~]ÿßVkÁçí8e#À¿˜ô5ûhÕö¹‘ß.Û?uXûÈí¶ö‰{ý}(Å뎀#Ð$|¡Q;ã]Û0o¶µ?õ’&í·ƒd5%¼¤$¥ø1MSmñvÒÿÉ/µQ¿ø7üŦ-.åÿÐaC‰¯D±UJ/>}AIXÐ tȆ@ N[ Ú…J*eoIXþØ*l±CèOmèÃÁ^}5>[=]GÀØTx:øˆ#65Œû;Ž€#P<>ûÎOþs«ˆŒ< GÀh1„Okñ=d®{ùqÚD ÝÝhµ„÷¡øҊȬ „«Xþ-ä&þ.—ÿ#ç&EÎMl¹RÒ2Q$e¨Æ>XU±­Ø²GÁ=ýøH䯽XÅ4¿kœG¦‰yé8Ž€#à8Ž@%"°´va%¦å99Ž@ A 9ÞC6¬XÚBfëi:Ž@%"ÐÚÞC2+(sy³ŠâßÂ}’.DÌÇÿ½•R°!p!*E jµ$m3ª:ñSB2%,ÓCKðQ.øŠªñÊ?Yº8Ž€#à8Ž€#à8Ž€#à8Ž€#à8-ÌYôpbË¿…Ü %SÞN„%¼`“"R°˜!$!—HBµ)ñ'ê6´Dcêš±ŸR++[‰bˆ¨dœ4Ž|š?³ Vq½tGÀpGÀpGÀpGÀpZ ™ã +š `¦¼ØŠsQYo5Êtb ªK$!mÅA—ö£çDô$‹ ¥tòÁŽºJú©K¨+6%¶©/vj«¿­¯ GÀpGÀpGÀpGÀp–ˆ@ž-Þø¯0/x3qm[œË_¼¥ø¼P-,)XÈJ ¨HEìð£Í Gã’Ë7)˜R­¤(Úø ÌPÍÚG¶ŠI¡Ä)y|_AY˜ÿtZ#+W®´uëôÒ(ÎË–-+ßi3zL›6ÍV¯^Ý,#ð­ãòåË­5}qõá‡Z¥½¦ÍòbzGÀpGÀpG !PUé°ŠæßÂËÁ‡o.øBx;êp)ÿšù‡¦]§.ÒR„¡ÈD ‰ m‘•ØÒÖ˜Š­ÈLtiâ´ñDzìñ[ÓÑ€‹#àd¨««³o¼Ñž|òÉm“wß}w³ÎeÊ”)ö›ßüÆf̘±YÇ)|Þ¼yÈ´{ï½×æÎ[È¥dýûï¿o×_½Ýu×]›oÍš56kÖ¬’ÇÞš†>ú¨1wGÀpGÀpGÀh¹$ o*– èÂÛÁéQ’gÊõ…fqYXÌ ’K ˆDÔÀ"ÓXªS’Ä¢üÕ'òQÑ¡Ÿ:ý\Лޮ¬ñ  ˆ¹8­>øàƒm·ÝvÛ&fÏ{Ùý÷ß¿Yç2dÈ;ðÀmàÀ›uœBÁÇg}ôQ¡îMÒ?ñÄvØa‡Ù¾ð4hÐ&Åš9s¦½úê«›ÃGÀpGÀpGÀ( ·%žL¼îªSnUþ­Èøä ‡XT4‘¢F™N&ª ’• "Ò’þ”¸D‡¡ÍÃq(›RmˆLb£CäGؼäñÛ¶-gŠ ãâ8Û ”C‡Ý&¦³bÅ ËŒ¼ÙæÓ­[7;ꨣ¬S§N›mŒb7×Vdp[²d‰AÀ6‡l®<›#7á8­®íºÛÈî{ZÏ}ãätÛUmøÞëK›6mc>ƒ:o_0™ºïnCºìP°ß;G`Û@ M¿Ááý&|>oÓÆÚimúþ²¹í¬MÿæùŸoÛ@Ýg±­ 9¾®LœYÅño!·\þO‹ÉU<_Á—¤”ÿ\H¡‚ bPg`E—èñ¡ :½#чÒ!\ÔéÇN6øR×D¨«/T³1©ãß>\ø»c¸RÛÐtqJD`êÔ©6vìX[µj•A’}ôÑ6lذ˜êM7ÝdÇwœ½ôÒKqknß¾}í”SN‰v·Ür‹|òÉ HȇzÈúõëgtÝqǶ×^{Ùî»ïn?þx\¸páB›0aBÔCÆA\± |öìÙÆ¹;qÄÖ±#o!f…ÆïÓ§q>â›o¾ã¾þúë1§ÓO?ÝÞxã¨g硇jûì³OŒÅ²üçŸÞ&OžÏAÜn»íâܺté’µÃ;ØøñããY‰£F²N8ÁæÌ™cO?ýtŒqÛm·œÎ=÷\«©©±GyÄ>þøckß¾½íºë®q•`4Ìü`Þ½zõ2Î"d[2óc%!81gæžýû÷·E‹ÙwÞi—]vY$) aÆÖù3Ï<3æDÜóÎ;/’lAçuÓîÝ»Ûþûïo{ï½w̤P,:ù6ðOúS|}ñKÈåêêêè;þüF¯?y#Åîú9‡T+OÙî NÜOÌ·˜/ÛÜy­»sçÎñžÜyçíå—_¶×^{ÍÖ®]k·ß~{œ7÷ËÝwßm»ì²K¼¯wñâÅñþ»ôÒK£!ÌŠå@œÚÚZ+vŸpÀöÊ+¯ØÄ‰ã=ÃêP^Ïž={âÞ@XõɼÎ:묬ž×›ß=÷Ü3ž×Zèþäw³©{-Ô+Ž€#P6_ßýWvÄ 3ùMX<Ö®}íÜFúT±[¯ƒì;{ßb¿ú{pÖÍvá_·£Æ>÷Âh[T3/5Ý*õÎU]íç>b/Î{Ø~8ñм9ü÷þ¶÷–M°zõ“÷§¼†®tŠC ÃÅ_³v£ÈæµaÍ*Û0g¦­{ço¶ö©?†Oéõt@»CN´ç\euÏ=hµº)k_N¥MÇÎÖék×Ùº7_´š[~”×µÓWÿËÖÏzÏÖ\ÿ­¼ý®tZ*‚2åÙÄ·Á…Áʼn£C‡¦ô#ü2bƒž…ƒØÑ–½¸4éR>x\ŒO<‰O:¾ˆtòÓXõ½~¦0ɪˆ$4 ¾ ¦É2IÕ58mHCÁ61èÇŽ::âФùaˆe¿¹W‘¤‹#àl<l—}ðÁí˜cޱ/ùËvüñÇGB b ì‚°;餓ìòË/ÓSO=e:tˆÄdºÕvéÒ¥ööÛoG¢_b@ð œùÅû9çœcûí·ŸqN% „æ—¾ô¥HºAHAÞI O?þLªlf…¤rõÕWÛ!‡‰QÌ‚@„²…ùüóÏãA²=üðñO± ‚.ºè¢Hú½óÎ;q>NQÈi§f§žzj$S_xá…ˆÇUW]ea#FŒˆ6éæ ¡6f̘HpˆûMy»?K¹×4Ž—Ž€#°ñ<5ç^»sÚϳ×SsîÛø`îé8ŽÀD nüS¶ö±»l](­c'kòEÖéë?·6ºÄ,6ÌûÈ6¬ ;‚æÌØ‚YùPŽÀ¶ƒ@† çV‘ü[@Û#Wø;ñèŠJJ2ĆÉS0%%C3K"¢ÇN}¤$Bb$«1,~èD<е ª,q©˜”eï[¼ÑŨ\X-·ýöÛGb…}¬¦ëÑ£G$”5ä«!›Xí¥‡¦Œ=:„"3Y¹È Ä|«ÇˆÅêKH'V.2tbE!Ĥ+/Y ñ#)4>ýH¬>S  ‰ÅjLVoò‡dÁ‚‘ücµåN;íÉ,æ ù¦‰ñ©O}*®Þ„”æJ,æŽ7BlV=òpVjÕiìL~@\²b"•”¬el¶rƒ'þ…$3ÙA$â –` Ê NÈ;òåìÏ‘#GÆÕò)‹~rc>äÄü˜0ßë_ʽC V@"`HlòkÊ—U‘`„-¯'ä«"ÁŽ×‰üˆ¥Øq€&~äbÖT Wì>'Haˆfrá5€„.W T‹ÝŸ¥ÞkåŽëöŽ€#Ð'æÜe·}ð“ìõ䜻xËp E`Ý+OÚÚGn³Ú?þÖÖüä«V÷â#ÖvÐöÖþÄócÆëÞ›h«¯½ÀêÆ=V¡3ð´€ø´Šäß‚)ÿ§|ÅÿYìf1#‚œÌwŒˆKêˆtè•zHHµ‹ø".26ªÓŸ&GŸ|C5ÚÉ;åBÌ6|°rqÊDB(}B5„ZïÞ½ó& A$B‹-ÛX”¬+$¹Ä%+.!vÒ/2 )ú Ír%?·â a>*©Cþ@¢R²"3]ÅFÞ™£OúƒxškªWýð÷gŸ}6n%f›8D+U1!&„¢¤Ø|°ÉÅL~c©€äg*—¬”Š¥þRÊ4ßrï4~S¾Ì‡û "Y¯Ê4N9õ\ÌšÊA±›ºÏÙ¶ÍÊaî-î³É³©ûscî5åï¥#àlçVÕŽßî<ûÎëÚG«>°vmÛÛ?go.k¿|çëEƒ·Ýùv^õWìæwÿÃÆÎ«ßÀYßÛç6ƒ ½gú¯øŸ¹ý•q¬ÞÄø7N½6nïÚ®‡ýò ¿Úí\g£ûnõ;Á殙ǟ²ôµc‡î{Ø¥;^k;÷ØÇÖ¬[ewMÿ…=üá­ÙøUmªbÿ‘ƒÎ¶ÕëVØo¦üsã…lZéÞ¾—]±ó÷mß>‡Yíú{löv÷ô_Úú úX’Z{Ýp*ð¼öß[Õ>c¬Ý˜S­ö/·ZUõ®ÖἯØÚ¿ÞauÌlÓ¥›u8ûJk»Ëè°Õf•Õ½ö¬­}4ìD G"µ;ø„àw²µ0ÔÖ<Ójï¿ÿ³§}2ÍðÿuûÓ>oíö?ʬf•ÕÞ{ƒ­›úæ'ýI­M—îÖþ¬Ë­jÔ¾a?f­Õ½ü¸­}<|ùþ/wqZ™ÏŒâÚ(¹‰)õÇâ‹¢º¹Å¥„˜ŠCÃ^1¨‹Ã'½B3˵¡×ØèñGÐW‚ mô¥l£"÷Ç'ŸRs{>ikMRAI\“g Šz„ºô ?é3×_ý蟋:BÉ^:ØêÄIódé 1Ö;9Ppq*VƱk#¬.{î¹çâj6È«êêê’Ã@±R2ý"C«3sɤ’ƒ0„ì„ä\IV6‡0_¶F{ì±qõ[¬™ÿÖxÈ xA¸¥ÂJ¾æÆ1¿)÷N1_¶cßzë­ñÜIV´rþøÇ?N‡nT‡ˆÖVéFÅrÈu)tŸC¢r%çNB®O™2Åxby>ˆ×‘¹ýMÝŸÜ»•r¯åæîmG`[B OÇ–>LfîêYÖ½]/ÐiX$&5WÚ½:ôS³`9~Áöå]~h§»,KPBn×¥Ú^YP¼‡œ?;â»xä?>O„•›§û‚ýÓ7Äs!Ûr‘1¿ºëOlâ’qöÒüGmÌÀÓìk»_o_|qLX Ð6žƒ 1‰ïAý޳+GýÀ¦,}Ýæ¬ž‡80è&/{Í^œÿP AÏ·k÷ú}þ…lU]ÿm¿ðßß÷Ž˜#Ä$rAõ?ØÊº¥ñŒÍ¨ðŽ€#PùÔ¬±õ3ßµª@@¶0$|rïhmú 0ËlùnòÅV5úp[ûôŸÂ§öu‘@¬{â^k3|ëðÙ/‡3,_µº™ïY»ýްŸû¦­ùÑUÙ9Wí~ µé:%žEÙîSÇY‡ËþÙÖ|÷RãüË*uüÒ¿‡‡ælgëÆÕ¿çµ?á|Û°ze< ³­7 G !(Å£‘±¸0JéáÈàßhCHÒæJûC3Št4°WƒKKIOôÄQLÚüS’S¼œlûÔOþ J97Pæ4"Ñt²%tº0§NRºhkròcB)9)[ú±GðÑE[cQ¦q°Oý ŽÏŠ%GÀ¨\Ø>;iÒ¤x†!YB °]8]eX,{LÂù<ܧa;8Ä>AØ.Íy—ÕÕÕqee9±š²åËÊGmIgË0ÛÒKm%feŸüy@ 8ACX%Ê L>þñÉDÞV]‹ðüFpùÄ'>‘V%¾å-oI·_wÝuiÅ$¤o4²ÊuëÖ¥]sÍ5és„4åöu½Å»–9B0ƒŸ/⳨&û³ïT˳?y‘ŸÄ0ÏÒÔ­ÿäYÈ[³?ð¤}Ž•…¼‰¹ÿó?ÿs"·ù¬ó›ßTK?=ã²Ö}¿Ò~ÉI\=«”ï@%’’yðlÐÏ~ö³ɪç„*Éjû'·º*ûšò28¸yû×mçÀæâÔFdž‹õÙV¾½ùsvîÚ‹W^šVM5o½}èîwMqǪÍöÆù¶°y©ýÃ×¥¾¶ÆÂcNV: ¹whgÒuþSƒÆæ¾‡“®ÃoÇÞ9¸Åv nµŽÆ…vÜ‚Sl]ÇÓSßÂl•çîÁm~Ž/\®oèþuêišŽrª=ÅÅvÆÒ—¤úmkŒÛÌC@`n!P·pIJx|Ïv«[¼|JòcßkGŸ`mñ¯é–ï‘=–ïvûe«ü–k¬®­p,ªëX`ã½]Ÿþ›ÛTøäœÀLþ§~uáZ²áôç'²=«8ëÚ Ïv—]”À\@`âîàœ㤠_Wø28184ôâÍàÓÄ¿a'[ì¨Ã›É†1ªãƒqôk %:Ê }’/tUÒ•Æ—•ØU‚Í$" ó¤£À$È0Ø %Î8lEtÊý"½šnÙæÖì„á;ßù΢[nÙ}ßûÞWlS¹êª«Šmæ™uþùç§¹æ·b—úbÐÅ_\KåÒK/M«óô¬K@6ÞûÊ•Λ[–Ù$ëÖ­³w¿ûÝ©É »<ÿÒ±SЉôÅl¬ TŽê«äKý”mÓùçP«ôó¯¶ïÈ7ä^>/é«åMꬆdŒç­çÈË·½ímSæÉgå•W¦Ï•[ïˆLIé<¤¯–ƒlT–ÛÏY=Éãùîh•-8Jòï:žÑ )ÊœJ÷õjûg-ûšbF³Gà›>m¿ÞóÓÙ;(3òç»dÛ69Aùjkih÷[¥»íæí×—±4{¼wƒ}ëä›Ã¹è~ «üsÝJ\~ì»gXŽõ'²²´?oM uér=ïñ_=}wîú¾mê}°Ø‰ß@ ˜Cø›¼ëöØþLɱ훭¡„ úú§ÓŠIž7ÙtÁë¬ñ¬ólàïþ“ÿŠo°æ7¿'Ýò=¾Ó_ü× ßüä/#Ç óGML“‰¾Ñ{nãÛ6Mv N֣̲kwñ_”âÅ8eó%P צ6:$×ñ? ôéIàßàscåSzââ‹Øl¹È_ŠO?c4žvY©… ,uB@œ#$C`„çARW? cGRˆ’£Dñi+yüi¢ôÓ&¾Æz5µ%vlÄWÝ«ÉOiîèC@àE@ÄΓ‘Þ¹òÿ8©D(Î4÷RâûÙúš)ÖlûËåX«¯Ù~û³ïT+¯RîåæYÉW%ÒÏvã¹X™)WÅ¡„ˆ¬&ÕöÏCm_«6è DÌÍoZœ¦Ä­Óû"¬ZüŽ?Ëñòcßc/_}¹ýpëÿµÁÑ©dßÖþ62>bK[Wú i¾0ͽbOë˜PŽ—äðüÊ@ 84hðõÇŸšnŸ®?Îïˆ9ò÷cÆ[»ËIÓK.õ[º»ltÃÄ#q|µãØ®­Ö¸Àÿ3†_ôÜí±f½q{v©Ô¯?ÅŸõB÷·r7½äwm¼¯ÛF~sk©™ñ¼Éñ]Û¬ñÜ—;•3hcmH/æ1Iϯ æÜ5!|CàÎàÊà¾Ä“y5Õ)e£:¼YΡ¡ÿщ="N>MýøÇ¶Ø±IÄש-Úâûr{ÙM)k!(€£<áœ0DO”y`ÙÓ‡PjBL”~ü Ã¿|P yùÀ‘-ýä®˜Š¡øj§ÿ30$@ @ ˜kܲý‰0ä­Ù§,~®ŒŸµû»~¹OÓà‘{°¡±{¸çî²c?³áo­ÁßÖ}ÞªË Ú;Ò夥¿¸ÂßÌÍó)«Ém;¿mvÿÊWi¾Û^ã/Û¹vãÇý…;bUãîÚ}³½éØ?K/ëáÍÞ¼û*÷Ý«îb946hÿã®+ÒÛÇoÝ»Œ7ˆs«÷'üë¢MT@àÐC ñÌ—¤¤xÑÍøæG «#¿çŒ'25çºEˬée¯÷uX­é­ÚC×|ÌXÉŠËúuO·Ö«ÞŸžE9ü£ë­yåZ,jÀɃW¼Ñê–®Hc?ÿ!3kø4ñ•›ƒŸø+k~íy¬78›Pï·›o²áë>5Í4À¡ŽÀÄ#Pà¼Äw©žÉÔGI?\|šÚ^MmJ=üZÎù‰¯£_zø7lù"J‡DmêØ ²¥^Êç¡++“ßò²ÝII²8×&K`ÐÇ=c²!AÚJT¶®J6”L±, Å_رá‡8l""½šú öy ü0VÒê•Uþ!–ÈŽ¬¢ ŠÜ·¥ÇN\Ux0uE£è@à)ŒÇɵËÚ*îì?àÇ⋾wÔAÍ9œxZýù‘c~»6$ã¾ÊqóO±õMûÈ=ïñ¶in!ç–îŗÚÔqý£=ÆíÙå¤ÎŸ×Þ0/=³\©®¹¾Åšê›k¶/탋ÀõçmÚ¯}WM}Öö~9‹ÁsÞ$/¬ÑËoŠ“p"‘ÛÓ^Ϙ,vNTü6rÞÞ=>ÐWÚS¾ÝØluµÛ—÷ÚC öú# <×u»wϯç±J•Þ™"½žËÌ»ººÌßÀ‹ó î âN ~Œ“/Ü|B6¸5¾ Ÿ¦“²e<"½6Ɖ`Ìcˆ—£[Ú²#7ÆÉ?~«~¯–œÌ$8Ós[‚!" ¦D4 ’Ð_J{ùW ù`¬ú½:¥N›øò‰uư)>eH @ @ 0§`%ãl…U‡?ò[·g’q¿4ïö7äÎBº‡÷TÅó0yIO­ÂjJ¶@ 8LNN2Õ±±â½+Î܉˚ÉIœøjÊñrϬ :C ‰[¼á¾ÄÁ¡ÁyÁ¯A2ŠoC?†P‡#£_üš¸7•Œ£/'½™ÆR"9ÿ§ø”Œe7'Ÿ¥ñÕvÓÊR AI"fCh““$zŽ^-öSÏEã«ÉQGD*æ1¨Ëÿ´Ùä&O]>Õïª@ @ @à©ÀO¶ß`_Ýø±²·T?µ‘‰Ù@ s­ªôlKù·r/§&r0çß4;6¸6ø5q|9G=I]«.ÇUS_¹:cŠaÞÀñL"§$Ê„ä”D‹(¤­~éi³q;6‚ÅÅŸg‚zýŒ!†ÈKÆáCcrËÅwÓ)yÑ @ @ žRܼýëO©ùÆd@ ÃÐÚÿ”@IDATñlšžx³RNLCÔÊIñrðjz¸6qnŒÏ9?½‡qèå¿\|édÃ8=|³‰¥T|¯NNÁ©D M`D¡.Ÿ€$égu­¼ÄPгiâôkbÔõ«.}-ñ“ƒø‡'{öì±-[¶ÌÙÉmÚ´)=O„ ŒŒŒXMsá$Õd.ãÒÛÛk££•Ï]9f¥ ÛC=Tu|é˜h@ @ @ p(#=òPæßJù?x?ø<ÊÊ?ð&€™8Ñ,[ˆÌ;sRQD¤HDј“‰ØŠ|ÄñéG/;¯&ùQ|Slū̟_‡/¿ýíoí‡?üáœà7Þh6lHùßpà öoÿöo3Îå¾ûî³~ô£öè£&Û{ì±Ç¦Œ›«¸@Ò~ìc³ï}ï{Sæ“7¾ým[ìƒæªbâöË_þ² Æ3ËŠ D%@ @ æ4eUþË'(^MvâјûAçßfˆONUE·ZW3bBL$'±GGô€€¤"ué5VmÆ #®–š¢CÐk“Nþä?\ñØç·‡e÷éÓÀ!‹À©§žj«W¯ž1?lÎ:ë,[±bE²Ý¸q£Ý}÷ݶfÍšÇêþvÅsÎ9簘ˡŽuä@ @ s ‚Rüܘ85&–svZ©‡=·l£×&îÍUE>ð‹ ýâßò˜ªãKöØÑf£®¶W“8%§ûÞÑ)Q%+¢QgŒìÑÑ/Û<>¾Ø”6Äg.Н¾Æ (•@ ˜¬[·ÎØf’ŽŽ{ñ‹_\4›évï¢á©@P†@ @ @ ¨¯‡Kÿ…ÀÁ}ц3)Cú¨ÃÝåü›7‹öŒ•zÚâá(|(mêèäSüñ‰•Ç—/tÄa«(µ”rNI0(¨&ªÄiKH–61H‚¤ÙR‡drØæ çöŒg 6šŒüâô)/J|¥øÜâ}® C ;ï¼3ݪ|饗Óúâ¿h'Ÿ|²rÊ)öðÃÛ]wÝeÇ{¬ýìg?³îîn;ñÄíe/{YÙïöÞ½{í»ßý®mÛ¶ÍXwÚi§ÙÙgŸ|sK4·sKt{{»½ð…/L„`%}1!¯Tò{Ï=÷ØOúS{ÃÞ`mmméÙ‡W_}uÊü¹eû /´Ÿüä'¶yóf[¼x±½â¯°#Ž8"wŸê·ß~{zžæ«^õªÔVLž±É\N:é${Á ^`Fo}ë[6?ÿùÏg/~þóŸO+0™W.ßøÆ7¬¥¥ÅÎ;ï¼¢šÛÄï¸ã»üòË‹:*<ð@Ê•ØóçÏ·3Î8#a(ý›ßüæ"îàø™Ï|Æ^ýêWÛ¢E‹ìÇ?þ±Ý{ï½Æÿîyä‘vþùç'œù ùË_+Doºé¦„ÑÛßþvûÎw¾“VîÚµË~õ«_¥~ˆ×/|á ©Î>€Ðwë­·¦çs®[·Î†††’ž?̛ϔù477ÛqÇWì£Â³,«åµ/ûÖÇÑæ8 ›—ZçЮ9>‹H?ž,8†ì¯Ôu,´ñžÎýuã@à)ŠÇ§’LðZp]pdðhlø/qkðe" sn ý*åß°c6ÔC<öôSj,múÆåã‰IîSºi%Ak‘‹J„1è”4É š I²))M2‘ÄØðE?vJ;|Ò‡Hã%jË7zŧžüÆ J  ] ›J_ Ó××—ˆ'²æ™„÷ß¿­\¹ÒÞøÆ7/ˆãvæg<ãS&Y¹…þâ‹/NDÙ—¾ô%cÅ!ºë®».‘f}|zK%½œÏä—ç=B¶óG?úQ"!'ˆ>ÈIUòàù‰Ä»òÊ+­ôø”cÁ¼!!O8á»ä’KÒ\ Œÿx¨…|æ3Ÿi|¼X†MMùa²0ƒ§?ýéö•¯|%µóæÍKÊÛn»Ížö´§ &þòË믿>ŽGuT"ý˜>Éã›ßüfú,Àùõ¯HÙ¥K—¦yíØ±#µ .L¤ Äèk^óšôBƒ7¤%D.Á 6äñÚ×¾6პ¹ˆ„„Ü3æÆ<¶nÝš^‚ƒ9 ñ I )ʼr¡¿Z^µî[¹Ï¨‡çùzûÊÃÿyI·G[ñò®ž."§÷Ljp˜Û)¸’¢M¥ô N?:Jt²Uâô11Dc±ÁVB/Ù§ø÷ékìA+‡íÖ_l°k¿s]÷ŸÙ·¾}»ýø?±;o»Ãî}p£=¸i·=¾m¯íêì³ÑxqÏAûÂñá‰+ãXÉ*@ˆÊ£>:­,-oo†ì<æ˜clçΉ¸ƒÈÔ U !á À Ñ–,Y’\TÒËÿL~YÉs YYùÊW¾RCSùœç<'­0dä¹çž›ò‚h­&Ä„Àc5$ã !èJLÀ‡þ É¿Ü<˜ë/~ñ‹¤†ÐƒÔdEc.¬Ä„,\»vm"B‰ÇŠDÆ‘ö¬„” Ö³ž•SV"üñ [üCpB.Šæ„ Nä¢ggâróE/zQ"É¿TðË37!|!g©Ë¢’ôôÓOOñø_EˆT ý3åUë¾%ŸQ‡ —¯{ýéÉï·ãœv¸L)æO38vp Ù_iºð÷¬ù÷þÄê>~]Åø@ x !À1ƒcǧ’L,n§vÈñožùÁßQ–ò⽫¼@Î$‰ˆÈCÚ ¦ñ"UÒφ-I¨îÕ¢ˆt™ÅH1¨ã‡þÜã¤ÓX•Š_¼ÑmŠ ŽØí¿ÙhûÒm6Ò¹ÓÖ/é¶e-ÖR?nmŽN»¯Z¶r5µ/°ù˶E«µ]{ûm^k-]Øa­­Í%¯pÎ@*‰øÊçɪB;È= rÝöËj>ÞøÍ°!.YÍqWI/3ùåvqH²›o¾9½¼F«5>/µzU¢"Hó~ÕYáÉ-ÖrBx©Î÷¿ÿýôn«‡l«\ˆ¹~ýú\•ˆCÈW„9ò†íÝ»w§¨¼)›Õ”¾¬ÍWÃSÿIÁ N¥Q\MȉÛûË Ÿ q!CËI-y•Ž«´o•ÚE;˜ë4Ö7ÙKW½.ms}.‘ ÌQ­ñ¬—¦mŽÎ Ò@à C@¿«< ?u;·ø6å¡¶x;éáß4.·Q¿ø7Æ!ØbG[<\Îÿ¡Ã†’±ùV)=ñåGºi¥LëÈ¥ŽhK@²P¾ I’°ÆçÉЇcdëÕâä4†IÉ/uôøDð‘Ç×}ò‹M?^Ö3(ûúG­·§ßz‡GíˆÅÍ¶Ì ÇSvYkS‡u´µ[[C“ŽÛè` öì±{vZWç.;òøS­»¿Åövï´e‹æÙŠeÕ 3‘@àpE€ï§nçÝß9òD¾ó<§q☦¸dõåë^÷:ƒ¼ñÆÓ³*/»ì²´*³œ^ƒgòËKjxžãK^ò’tk3«ý–-[¦áSÊM›6¥¶VNéÌô‹€;Ç0VCBÎòOnk¾òÊ+³h…ªbæÜR­\!!{YE  É Ê­ë<”Õ—R {!BˉÏJý3¯rù„.@ @ ƒÀÄï[81ø7ñað_†ôÕ±!âß(¨_cĹQ²aC?uÅ£MLñrÔÑÑNvò瘝Ã?vUeFƒl4¶lJš.—ú IŒ:÷¨#Ø §_@yµ8iMãùU}Ÿãˆö)“ìϨg×;8nCc£6ÜPoGt´ÚÙGÙi‹wØ‘­ãvÌQëìé'fÇ®;É–,Zn-õ¾¢rÄZFzlhÇcöØý¿²†ñQkjmµžþaÛ¾»ÛFp<€È㼆ÿâ%+_³næVgž©ÿaÚ°aCº­ÔÑCl—ÕrÕô©ÓÿTó‹¿n¸!‘ugžyfZeH¢TÂ­ÎØ1/ˆLnQé'›Òrݺu‰ô»å–[R+F¹¥šça– óÌ$«Ë ÇÂg?ûÙ‰@eE"¤k©ðB¡»ï¾;=ç‘>ðbÓó4ÑqK7/bN¬¨D8Y2Tñ¹…=¿<Îâ8ìÀ”[ºYÁ‰ðY³z’— ·ÄóÂ"ÉþæÅŠXæ@ @ À“‰¿õ\Jy³CŠóüD˜VâÿªBÈdf`‰½~y\zJ’€…ˆ¤Ž.ïgý¬xL{©1^M:lè×X}ʃ~úÄÜÒbfS|ˆåVR¹ý¬dphĺ{GÌïà¶>RϰûïÝaËzï³Ñ½ÙТ#}£¶§kg"†Ç{m`¼ÇF(»‡œÐôÕ”~‹÷’#lÙšõV7Ö`NRnéöU˜ó¬¥©–dV©Ç @àD€y¬îûìg?›ˆC=÷p6ɲЗ­°:òCúPúîóŒCnåf•Ï#ä…+[l¼-»’>_Í/+!ÆXµ‰<ÿùÏ·O~ò“±ø¼ç=/éXaù/ÿò/éögµ‹.º(é«ý<åÅ/¼˜R“|y>d¹ÿtá6kˆ»|à‰L¼à‚ ʺ†h„DäÍÜådݺuéMß×\sM:~‹Û´!%ØËªU«¦¬<+“[È?þñ§U•`vÎ9çhجK^Ä3-y:D,¤(+5%/ùËíÚk¯µ~ðƒ‰t}éK_šVˆª¶yqîà™œÜ²ÎœC@ @ @ x²à÷• œü—82V¯ÀÁ‡!Á‰!؉0„_S]œ™üä|:6ü¡h¼ú±A'þ$GüÈK~¼Z^0œI”˜c/êš 6$G`|ÓÆ6L1é'[ÚÔeC)W‹Ï}Ò¯¸²Çý‘þ#óë4„ð"V;ò£IöÕÛàÈ =üÓlø¡_XÝP¿5-XdG­\e ÍM6ä/…ö7û6ØÓí«.‡|Õ¥Y¿ßÞ¶övÒ/ò[Àë¬Ég3Z7n¼âˆEÖÒLê!À“À}[zìÄUOH"¬ ävárÜlà¥,Li¹° >­žT_%½úUVò«þÒòïÿþïiʭϬò›8±”šUms |i¾åÌdAË*Ì+®¸¢Üð)º™|M1Î`éÛê+Ĥ€{5ìØJ?ë<þlòÂ'1ärå9Eýð@€ãäÚe…7Ó¬=º³ÿ ;¬9„ß@ @ užˆëºÎÝ;Ó ~cTú!=¿a$Ü©¶xñâK¼ý¨opn""! 1TI JìØàËÐi,mÙIצºW‹‚a q4V>鯶6µ•|c[V0¨U°•cJ¥.?èdC=|z%H)Æ`§1”ô˯WS]¾)±ÉÇb£¶úëóƒý‘{|Øx8É0fƒcã>™:îí¶½;¶¤äõ͉`©óÏˉˆz·ioj³6ßÌ_ê1Þ˜Š‘>Û¹u³ß ÙgN< §gðõúsñ}|ûþ¤c9‹äÒ"'b©aEŒrd_%}) •ü–Ú•¶9ÉT#ØJíóv¹|ó~Õ«Ùq,äVhnC¯Eªùª6žyhr’x3aWî³ÎóœM^øÔÅAî+ê@ @ @ <‘Lp[p]âɦñ_}âÚžpþ­$¾x9åLYUjYªW:)­–¤T ‚B)¿ôî ôjc‡oDc±³‹Ž6ã(y`œb2–:6*½šÚÄK>ÔËßÞs¯mÛ¶ÍV¬Xå·>úÊÈÑñ ºv'-y¬ÞŽY³’¹„ÓÐ efK MsŠƒŠ·[s›ù“-¬j<ûì³ÓmâOv.?@ @ @ v¸ÛÐî‹Mï_&nM«i3€[ÀµP¼™84ÚØ±%þÌKñq”Œ§d<~5¾\|tlØSâ“qò‡tUEDb5£|2JNŽIX‰ä“”~?ØR"òÁDu&:ù” ã5¿ŸxØ"øE”‹>'™÷ÿ”{üå ÷=p¿ÍëXà/ÛxÔ†ýVÃ~_í¸|ù6°×W<ú oZ:æû­ÜÖï·qw÷ XCÝ.[ÐÖbcãc6äYÖù­«|æ~+wÝxƒø­ƒc~{ø¸òÕ–c¾Ú’ôÉwÓÖ¶|éB›×~poKˆÅŸ9‡€^Ú‘?‡oÎMâ)”pþüÆ'sÚ¬jÌ_vódæ±@ @ @ jG€Ç…MH9þ‹.ôâËàÊà×àÒ(sþMÜ:ñx”´Å±yµHNj¬|Gãd§¶ÆË="…V…¿"+t'µ’–’iH›:›S% A’ utLˆq"&0mM ¶ŠŸO _ò‡uùõjª§ò@”<²Ño/lõgD¶XC[³¯šÜá/Áé¶Žùó=†“Š Í6Hv~KéöÛ­s[§ßÞ=hK´Ûà°?wÒŸ3¹tÕj[¼t 91É­ÞãuévÖq_a9îääø8ÏËcª~븗mÙn'·–9„@ @ @ @ %«‰È—&@[›|Ch*¾ÀÈßßgÚ Ûc›·¦ævk¨÷gL¶4[sKŸ56õÙÖ­Ûí·wþÔvoÙhæ/Èéïí±m[·ÙH߀­X¾ÄN~Ú ¶ññ]¾m³ö…ÛšKméò¥V×<ÏZ—8ÙYWxÞxS³:19j£op`ÌßZ»ÅÖsTzqà„@ @ @ @ <õ˜x|¡ø/Jø1x0êÚÄ¿‰„¤”ÚÑׯX. ® ‘oêâ÷èWÆËFýØâ3·S l»²R A)G .G" ñA]‰Ñ3F€HÇDú”,Ï–¤ÎìdÃXêšuõyµ‡:ã!,ƒïßr[oî»<úÈ#ÖÙ¹Çß²Ôhc­£Ö½w¿xÈæwtX§¯–üÚW¿j­ ¾x²Åöx_sC½µ¶ŒÛ–‡µ{|dÿЈuûíà­»º¬o`Ðúî}ÈšZçY]S‹­?éT{ñ…ë]ïœí¨?¯Ò_çÝ××c}ÝNRöÚ';׬>rß“>@#n»óvÒñëmáVІ< øóKyÖäLD;o æÙ”ãå$OƼ#f @ @ @ ˜ (sžM|›¸8qtèáдxO¼\6èY8ˆmÙ‹K“.çÓðÇF|ü)&còXŒE¤Ó8Å*ôVø›¬`RTˆ$±Ód™¤ê N[·q£ Œ£ú±£ŽNl«W§ŒØâ>ҸÙÙøØ#‰$3^Œ3o^›ßÚÝnóÛ[íÔ“Ÿf'p‚³vÍ_°Èêê[¬±¹ÍV-^f«—,³=ç¹V?âÇm´wÐ6ûŠËŽŽÅ~+wõîÜaþæNk­³ŽÖf[°`ž |UeÇt²iÀvî:°oô¾ñ{ß·ç^pQÍp¼ç¿ü»ÿÁ 5ÛW2üƒ?y½ÿ#ÿZ¶û Wþ¡}û¦”í{ª+;;;íþûï·®®®¡-eH @ @ @ >L”ðbÈ!É¿y^˜Ü¹BȉÿCWUj!(±aò"sRçò;mô!’‰‘¬ì,ãЉIkëª"q)Ÿ”û¦•g©&½ýCÖÔ²À‰ÇIÎ|[¼x±µ9 9oþ[´b¥væÙé¶ì• [må¢Vkmnô•”öÂSÖÛ;~÷<»àŒSm~›“š :ÃÖ4Vg NPÕÞl‹†züÖñFknm÷²ÞWÉYWÏ^¿¥üûõo~iwýê—ÕR›3}<+á+×}Ý HCjC`Ø_¢´iÓ&kjjrR»cÆAóýy¨Ø2fÄ_â@ @ @ ‡âÓIþÍ‘¿§|ÅÿUý0ÄnV3’#ØÎrÀ §dC¤C¯„Г¤Úò…—ŒÓ$T§?Ÿã5Ö«):ì±S.ø¬Ûß—äôöYÛ¼y6~‹57ÙÞ=›mtxÀÚ:¦p§Ÿz²í}è[ØRgG.h³-ÛwØÂ¶¥vÁ9§ÚªŽQ»àì“ìáMÙöþQ¿¼Õ†û:mÞ‚V_uy¢yá…6Ò0bƒƒ]6ì/Óá–¿üåϬ¿¿ÏV®Zî+2™Îá!ǽÆþîƒÿd'w¬{ÌÚÃcRqÛ·o7ÞÒµfÍ|ÀÌ_SlV¯^mø# ¶mÛ–ê1½p@ @ @ OÙKrÄyAÁƒåœ|šˆ$ñcØK´ PöòA]ü›87•ŒW\JÅFÏx½ø>ÚØˆ¿£­ñÔËÊÌÌÇdMRNI\“'!ôvèêÒ+㤓ÏÒñêGÏ„ž&NÉíÞ<»’:~unÇÇØÄ2X¯ÎNlõ²U6:6d;wì¶=»·Z‡¯ˆlnõÛ¹}– çµÚ9gœb ë†lëÆ¶¢µÞžvôZ[»x‰õíÞck—,´Kž{†mxl›µ.Zd«Žð[ÁÛšl|þk^é„Ò£Úè ““CÃvÏÝw{œa[ºd±¯†kô‰ žÙå>ӨǷlµôßì×wß›â=ïì³ìÿà?ؼööâÐzÄ>õù«í¾6غµGÛý³«ìè£VÛmwüÜ®ûæ·ìœ3ϰ/\óU{ù.{ÑóϵÿüÎÿd úø‹nìµ—\d·Ü~‡ýù_ý/ûä?}ÐÚÛÛ&;'j_¾öz»ã—wÙßý÷ÿRì{ÇŸý¥½ü¥/¶W^ðR{ÿ?ý«At>øð#öã[nM/zû•o±—Ÿ÷âdϪÃúßí{?¸ÙÆÆFý%E'Ù{Þñv[ì¸+ßÖg×\wƒ-[ºÄþ¿¿ø3ûê 7Úußø–µøs¯|óïÙ%¯|yòEÿÔ¿ÛµÐWËþÉÛ¯´=ïÜb^³Âc öìÙãµû­ÿ j…-c»jÕªŸ[Y³ã0 @ @ @ ' Œ Ì‰"qa”ÒÑÁ¿Ñ†¤-ÞÌ«©N‰h ulD(æ¤'züÈ'm ã!€Ð1F¼œlåÇ»¦Ä¥=M4xZG¦HDŸ·eKè´aJ¤´ÑÖä4Ž åä¤léÇaŒ6ÚŠ•O?Øçã*ÆßßgP[£GìëöÕ“;¬k÷NëéÚk;·o¶No ÷Y_çnëÙ»ÓëFl~kƒ¯¢ÜbŸûæMö¹›î°koºÅWDî±V¶Øñ+:lé‚z[Ð4žnÃíîq³Çºví¶î¹ÛWXÖÛ ¿…¼ÕWÃ5û³(›‹°Å•A'EßñÞ¿L¤ã×>ÿÉD >ôÈ£öWÿûSÝvÇöïþS»ú“ó°´Ø?þë'Rÿ ¿å7ûÌ÷ìµ}èïíÿ÷ÿtbðÇö­ïý`Êx5 ŠÿûûÞVþõû?,õ”²¯¿ßöîú¼En—ï÷Å ]Ý=öé/|)…_ùÌ'ìÒ‹_iÿëq}wêÿ°çöëßÞcÿôc×~þSNfeýþL}Êbòêÿó1~èÓíÞóçæiÙ _ú¬]qùëì>òQÛ±sW²¿ýŽ_Ø]y…Ýx͹ .½þ²£'Bz{{`³… Y¥»oÂÆö=A¹î[va@ @ @ ûŠÀAɰŠü—÷UâßÐÃÁå<š¸7JDüeî§fþ­dœüá[üõŠ¢  ¼C¤¢œçc˜É"ô³aφÐF(IH«(ƒN€xµTnÃ’Ø3Ný^Mãk‰í¬¤¾nغ;wøs&{mx°Ç3IÏœè³.'%·lyÜvîÞa»»:mWg—ÕÍk·¾–»å¾ì[wÜe·ûKf:=í¾–&›÷^ÛÛßëö]þ–î~趺¡^¿%w£Ík·%~‹xÏ©ô­a|ЉÊY¥\Ó [nÿ™õ8ö¯xczKôÓN<ÞÞøº×Ø~r«ç·»èãòË^m«W­L«:YÉx_öÒVA¾ù ¯µ¿þi'o§ŸúŒª/Õéè˜gûßþÒ~rÛívõW¯+ÆØ—ÊsŸs¦=çÙ§û›Ò[쵯ºÈo¶G6>–߯ùJÈçŸ{¶?t¯ýöÞû yëÏî´á‰g2’/+1ÛÚZ½<ÏW}î´K^ñòôìÆ‹_ñ2«w¶ò¡G7¦tþè÷¯°gžr²59Y|Éï¼ÜIæÛ¸éñ}IuÖ¶Ì iñ9–ôÕúýC@ @ @ @`î#0qwp-üדͿ)>|’óM…¿µP`8͉D±«•" )‰mž„Æa+¢Sãù¨`e¨Ë§WÓ½œ6¢ñÄÑÄŸ8è)Ó¶¿·x/]2ÏvízÌêêmt|ÀßÖÝfã#ãNÕ[›‘[·m¶­;wÚp[³ °pËN[¼`¾-÷Û‚ÇGëüù’¶}[§mkn²£¶Ûxý°õtúD›m¨o§Ï`ÄÚ;ÜW=Sô7|¸]Vö-\¸ÈuNÆÇÆ}…góÖmvÄòeSnÇ~ú‰'¤`[ýmãË–,™˜[¿EžMëtÅ<'gE–ëG·þØcì?_õûëø°<¯’íLzˆF>øáá‘ôb"^³Ùo[ït¢Xrùe—Úx™7¹·ûmúH³ßÚ@D6ùg42Anñç8^ÿÍï$¢sÈo¿GF<Î!Úg³ÿ!™ö˜cŽ™ÒÎ#y_Ô@ @ @ @`î!½Zü¥x1x280•j‹CËuôkq¡WS]œm„±ò ÿ†_Ä.L^™ÙâWñébŒÆÓ.+"ËvN(KçÉhB0=ÔÕOÂØ‘¢ä(ÑcG|ÚJ^þ(é§$¾âyµHJR—¶z%u„ø¥¹§Ž}ùsÄŠ¶§s‹8‘ØÔê«ÚZ뜄·†w˸ ú‹m¶ïÚk¾:p~Ûpjá.@àI@ ¾¾ÎŸQí×^†@ @ x¸ÖâšëP^¢ëY$þ ’±”ÿÒrþqðcâÿ¸Ý’~ø4ñvøÿ¦øÊù8Ù»:ño¥üzlÒøøAW•£Ã`&!9#uÆÐ&™„ÐD›@lš ö¹MNþ”œ¥?ÆåISg,`2–~Ú¹/ñÝ|ßåˆ#ް¶ùc¶hY³¿Çoï®óÛ¼ûl÷Þ]vÏ=œäërÒr¾ û³'wïµÅËŽ°•kVÛâåK­}Ñ[~ä*k[°Ðß¾ÀvwúíÓ=V×ÐæÏnì·O}ê+~»óo졇vØÆÍ=¶i[¿mÞ>dÛvØÎÎq[¸lí¾'\2‚[“yv"·<ÿßë¿á/²yn²àå6¼ùù߯¾ÆIÑñtûò®¹6Ý>½|ÙÒ/¾ù§øûi'·YKÖùÛ½7<ô°Ýã·ÇyN7ýèf{|óuW-¹=ûU~»öGÿϧÓs11Þäc¯õ—àì«ðbvµó_ô‚DNþä¶Ÿ%ŒðÃ3*yVå.ò½þؾè¸!¬å9˜÷N½I9‹?m¼„ÉWvîõ[Õµ"²7ͼ ‡±ø @ 8<èðg¾ìèJ$åá1£˜E @ ‡“\kqÍu¨Jö2bqk‡2ÿFŽâ©‹ÿ« o­è‹$ÄuHB6‘* ªÀ$ЇPÒ‡@,ÒÙË%zD>d-uòfÓDƒ~ùóªp¯ô~Èš#O²¿s}º½wl´Á†‡|eÛ˜ v¦çŒÖù­Úc é™’cNPõŒŽ[÷è˜íõgLrëwsO‹íô•’]Cã60ä«0}•`?o²ÞWP ÷Ûãïq–~¾µ¶Ïó•õi5ãˆ3ãì|K—±™†~öê¯Ø'?÷ÅôœÅ—¾ðùvåß:xãÿæ¯ìo?øÃfxdØÎ>ãÙö¾wþñ~ǬÅäèßü×?·+ßñ®¢ù9g>Û.xÉ‹ì÷]·hÑÂT?ן9Y«ü¡¿Ñû#ûÿíµWüAºe¢î-o¸¬ÖáE»gvеúH»ìŠ·Ù¢… 윳Î0žÑ‰<ìϨ„è=ϱämëßûáÍÆË…^ÿêK¬««;½!üˆeËì¤ã×ýͦ²Ì}lÞ¼Ù¶³êÕWñÖ"Ør›ûêÕ«k1›@ 9‚À¢¶Fëõkˆ»úlÈïâ @ @ 8p47Öù±þnh¦CS&/Áwq1Ȧ:‹÷$ºP¤„ £„CƒÇá-¡þ-çüÄ¿a#=Àȧtò§6öØ ²¥®xØc[QjaïDD€M 0–>À@ϳ"eCPÚJT¶ôK˜ c¹5;‘’òOɦ=òãÕd¯´ñC¿„%d+ýCüº³-¿zí×íΟßåÏ:ôÕiMÖßç+bëÆ­·ÛW¸9ɸåÑGüÞ»Ò‹IŽ:r…~âQÖîÏkÜ»»Ó6<ºÉºú‡}Õd³ ŽŒú wöZû¼ù6ù*ëõ¹Ç·ÞVúKhš[ +Þ†‡‡©zÚ©'Û+/<¶)OÇËpÚü¶áŒqŸÒϽ*öO1~ÜæÝÜÔ4ë|X}É›°çwtìW¶N8Bäò|Ê\úúú“ˬÙô,KV¬òlÌý>>ø  8ÇsŒÍŸ?¿ªËn“ù#<âoZoµõë×ï71¯`÷mé±WíŽòe ‡#'×.‹Uë‡ãgs @ @à@#й{§/ÈZ”~³WZP'}~GeWW—ùB¦ßñ|6Mäñ§Æ&nnM¼ý´E8rk7ä·†Ãѧ~¯ù>é)eG?1‹?ÄQ ÚÔÆÀåa'ޱê÷jyaÐL"‡¥vÓDI€:¶"iS§Q?6JŽ>ùW‰-ã°ÉûÑc#!>R-~Áb?þž}ö™~·¿QÙWcòB–:O¯n¼ÞI½klåÅ7 ¾rÍWNöô[÷ ¿õÛ?öN¯ïõ[«ëÛæÛXC‹2 þÖì>kôçòŒÉ'”›|ܘÃâÏŸl¨óUšý6ê$h£ÏþÌ3Nߌ§å-Û•ÈI,yvcµþ©Þ~«™ZKtn÷Þ_r’8 ýeG¥ä$zHK ¸‰œDw ÈIüp@:úè£ÓŠZˆGVSNžu÷Yçî]¾ªoØŸiØè„`“µ·úÁ}¶ãψ??pÈoÏmll²³ü¶fÞ°òÔF ÅÉlVC>öØc¶Ó°k×®ôlI=_²ßßÏÆÿª°bB3'KŸÚèÅì@ @ @ íªôÙÀi±ž8±ÒI¢‡?ƒsƒSC¨Ã·Á¯!âÞdK[ŸÆªO1Ñ«ŽúÅëÑ–à+÷ž6úŠ‚ã™DNI”ÀrJ"ˆ&UhMöçzÆèpü(®¢€±¡Ÿ1°Å.×ã{_â»ùþÉùç=×xð{pC§øs êê<%ÃSÝhßVÛf¾ÚnÔŸ=9ä+"÷võúËnöØü¶uÇ^_¡Øîoíî°1^%oïnôÛÄ÷ø ËöÅ>IŸ%·$w÷ôø*ÌQ;fíJ{Ñ ž»ÉÆèÃHÊãŽ;.½0‡àpëºÞÔÍjž¯Ž]¼xqq‰øa3ñ˜H @ @ @ ää<zñfè%èÄ­Á³ÑÖ&b“ž ^ Â’ñ9çÇ­ÜôËFþËÅ—N6Ä‚ÇÃ7>JbT… &:DÒ¤®`& ‚hRš€À>O;Ú&°¼šËý0^Ì.>h#Ô‹HÇ8ÍE“Ïû©ï·\úª‹ìCþç>¿U»ÞŸ‘Øì« ëÇ›¬Ñoánö·&øó#óæævëö7qos2©ß–-õ‡­v,pòrõ¤Oe«¯†k_¼Ä–Ö-O϶±ymíö»½b¿ó ‡‘l¬–äE8/ÊþåðštÌ&@ @ @ Ùó(E.ƒåüvðspfu85‘z~$}piâßd-u6qmÔÅá¡ÿG\êy|ú5¾´_>ܤ²0h&Qb¹‚Ò§M“`’ Ú²C'!>vôQJ4iÚô!èØ[ñäWí)ñÇüÖé%+ýVïÿøûWøªµöôlBOŽ58IÔâÄâ‚E‹}ed³ÕÕ;iä++çù Z:üÅ&Ç\"‘vìÚ™nãnð[¸¹•{ÔWK’/?pšç·è¾Éß°½bÅþ¿¹û@Í7üz@H699Îää¡÷ùDF@ @ @ ÀF`‚ ¬ÊyLñj¹RÙWþqûÄ¿•‰‡'·§ö´²‚‡Øi‚r*]Þ/rM„~µ©³±ÚÖ7gÕ'{üã¶bá§ð¼{loì@“8Ç®;ÆÞõÎwØÑGé/¶ñçPúKRZ\ìñgPv÷ùs(y®¤”{»{l`dÈZ¼oplÄoéî±®^·´®ž¾t[÷n¿eròè5k쮼Âoï>ÚS @ @ @ @  L”Uù/·¤k£Î‡VÊ¿åýðm¥ü›88ñojçññ9…ó6ýâãòøâö¼»²è¶èÊ…JžQòj“‰³‘ ‰Ð§ûÞ¥wU1YM4Ÿ€nG§~üä>s`ÔG|Æ2†~åÕx  J÷m«\eïv’òú¯Ó~úÓ[ý%9~ûö¸ ø³);»lphÀúúÓ—.wo÷îíwBsÈÚ==u6ûÛ¿y#ø¹çœe¯¹ô܆@ @ @ @ S¨÷rÿ%ÎŒ’6œ˜nç'&î® ô²g,"ç´E0R"ô)&mêèð)[ùƒˆ”¯œëCG›­¢ÔBPÊ9¥V(*(¥’"ˆô$‰^ýô‘0ÉÈ:=‡Û[àÏ|ýk^io¼üõvܱë\Bá)@ @ @ @à°B`‚ׂ )Iü—7(áËàßÄÁQ"²¡žóo´ÅÁaCâ×ð‡=mú5VD¥«Šq5^±éCJ}´%k!(¢ )é”4I ”Ø›’¢d,d"Br´å—6"â‘>½6l5ž>µ£xƒ~dü`¬ ,¸.ü]¹b…½úw/± _ö2»ñÆoÙOr‹ÝõË_Û}÷ßo]{mhxÌ–/_nÇ®;ÎNÖiöœçœe/}é‹ÓÛ—s?Q@ @ @ @ J˜xY®ÈGx2ø/HBñi*±AàϨçzñt”ô‰Ëƒ_“u¶œ³£-þ=¶¥ü:6üâ{ÚŒU\¯V–ZJæ‰á¡¤RS©$¼+õ‹p$1ù“ “(š¨& Øiâ^MBÌ<'ì™ ¥Æ+½õø`“”Äioo³KýVm¶@ @ @ @ @à@ 0Ák‰Ã%ü¼˜HJ¸³œ+£Ÿ ¡OöØÀ—‰O§&}âÓ¼-ce«ø´ñ'®Dñ©ãƒM>dC_YÁéL‚“¶HÏ  @ @ @ @ æ"+(áÇàßø³ÙðoâØrþM|újåßj¯œSâ•þ(x¥þ\-‰B"j2”òCËBar©³éÁØ §Ÿ1¥Æc›û¡Ÿ8úhcW.>~˜´âS”ŽBH @ @ @ ÀœD`b¥ø0æ@Ž ®Œ:üÜœ}l´%Ø`/½ÈCéÑQ—_JD<œÚøÿ¦ø",?çÿ’“j业 ‰kB* Ž(8mMÉ©ŽÿMã±ßVŽS?eŸ:[NˆâÎIɪ•ÿâ`&Q¢JRmJ%@Á†„(I°òqÞ,&,¢Rþ°E'è'÷£1”ØR"Нiô‰xAN!tü @ @ @ @ ,/ÿ%¢PÜ™ø7ñjâÈèGÊñoŒAä#çßr?âßfÃÿ‰sQYˆXᯌ+tOQcË´)IÚòƒ.ïGÏÆ„ÑC b#"Æ`G]¾CDuÆj\>6ùõÓ®‹”‚%Ê@ @ @ @ ¹†@™[¼™‚ø/qiðfâßD8Š£‡F©1”âß¼šDüÜ[%þM¾ÔϘ<¾ô”ò—œWúƒÑLÂ$MŠ:ãh3a‚±IH’å›”L)vO›ñšŒW‹¶ø‘­â*% !Œ§­>l•Ôø±‚Ò‘ @ @ @ @ ˜“44$:¬ÿ&žŒyÁ·±•òoô‰3£Žäü›úàØRþ âßd‹ÝþmÂFvâí°Éù?Æ•Ì$Øh#u‘‡B R² ê"+±¥­˜ò-É"èòÄi3Ž1”ØR'D>ñ‹P²±|•±N2«Ë[!@ @ @ @ ÀB`tT4X‘÷ÿ%þ Lü¼ý9ׯl÷…› ÿGGÌJñÉ¡¢0h&a’‰ì›0¤N@fÒøÑĽšÚ*Ñ311…²Ã>PÀRÇW>ÅWy|7Mcó1ʇ2nñ¡@ @ @ @ 9‰@É-ÞâÌÄ1'tHοÉNü[¾È¯”ÃVü~U/Çÿ)ñÄñ¡£Î8Æ#²£Mìª"ãªFÒF@MZ‰SÒŸ—è`h•mV7R›Rm|æ“Ñ8W'ßÊùa¼Æ)>öèÇêëëYFI;$@ @ @ @ ˜3ðè‰ÇÂ…iËù/ñhºë˜¹å¼Ùlù7bà¿âîKñ)Õ—ÇG'ÞP6®ª,"ü*[ˆA1|Økc z-!%0AIˆ-'*½™lÑÉ[Æh”JšA‡¢ØôËVqð…@\RÇv¼»»{»—!@ @ @ @ ÀœC ³³s‡'-®¬”ÿ/&î-çߘ/üãÐSŠSójâëàÒrø7JéGü]î; y`«±Œ¯'›ieî`Zg‰gLBNKP£Iº*%/û¦ ;"yôŒ£úñ¡\(e#«’ÍÐDmÙ)>mÕé§Þ×]wÝM#$@ @ @ @ ˜KŒÙƒ>øϹß7ø´RþKü ñoðb¥ü›ÈEìðC?B]Â8Kü"øÃNœ[%þ/­¸=ƳU•D"ʶù¢Í²Æ2¿ÔYÒ%”ô±iœ> ì‰C_‹om:ü*OúsÉsËãcÃ|Ñ)~>/ù#¾ôªc(7Å”ú)eC¹äø+>}øÈñ÷fÊK8ÑV|ìÚÄ(}-ñÉ)âþ±ÿÅ÷/Ž?“ç??,Æñ×1ˆóOáüÈþçßqýÁÞ×_qý×ßñû#~ÅïÏÂù€ó¢xƒøý}pùq3ø<>›œ¡Íïéôû×UE^ßv¾¨³)>~t”|Sj H‰@FöùƳ {}Öº„6qðKQ|ÚÊ'½ÆÉF>˜#ñKsÂø¦ aB?"éåGßÍ¿`]òWNKÔÓšrJ0 DÑc§ jLþa ÙÒ–õÉ'mùÐdð…Ž A¯¶ìÑS׌”ÒÉ–6ùã"%¾•ŸúqòGŸôØË¿|ó!`‹ž~‰Ú²×XÆáa,ýè(%¹­t”èó\˜›vtôy|µKãˇ›G|Ç ðý/¾qü‰ã/g„‚Äùgò¼ L(uîÔ¹5οqý×_…ë_}'ô}Q;®?'k€…Ž!à×ÿ“ÇÙ8ÿê›SØGÀƒëÒ\´ïè»çŸ8ÿÄù§¶ó޵úñ½BÇw)ÿýG]6Ôõ”ÞUI°÷DÔ± ù˜Ü¿ê|^ãóºt¥zÚlø•=uü—øÌA'[ÚØç}Ô±Aäct¼AO=u6 zDctüa¬tÉ L[úb™lEeIE6$¡„0Q²è˜{J%äÕb¿ÆH'±(úÚlL– JÐ)lu|›º6ìùÒxùÈcêƒÀŒCW:ú5^$~±Eˆ(uìeKIÉwÕåŧ®¼Ôùôj²Edž(¾bÉVã±Ñœ©—ÆW?óøì|# û߇øþÅñGÇTö Kãø qþ‰óoáº!®? ß+âúkòúWÇLŠëÏÂ1³tÿˆëïøý¿¿â÷Wüþ*œGu|œÍï/¾Gœ‡åCçô¬Nħú¼šÎIùñWßCJ_ªËWê˜øƒ/ùã\§ñÒAOR>±a |’ô^-ö«N‰`‹”òOŒEJ¯?ÐË?ýÊ_öè#þ }~MKrD§ÏÚÊÃUIòñ(ä«Ð[á¯È· ÝÓÔrª$O’Ñäè JB‰[ÆSÇ–ª{5õ˧ì öŠOÝvM ;ñjñƒcý꫟>ìØW%>èWÍ[ú´CR—ÐÇ„qÔÙ°ÅŽù¨.[ùW\áê¦i ml‰/[¯&?C?B©|è#¶rÍ㻺h+ŸòñA§ `øÇþß¿8þÄñ7Î?:Wrvà<®sfœAdòš‚:ØÄõG\Åõçä5¿®ÿù~èZ]ÇKâút ×ßñû#~Åï¯}ýýÅqƒã)ÇÛüü#.†£ >ikÿÒñ—ë:‰ü(¾liCøQbC‰¨¤ŽméõO_ñä“R¹2_ʶ¸0ÆÑWËõgéüuþQÎøÇ¢|Tb“÷‹#6}Êé¼Zœ³ò§¬($T‹D(I€D•¬úÔö®b"Ø"C‰=¢ähË9éÃÀtlš¶ÚQry|ìécC I¼B:IDAT£~êèi#òA;·“ º<>zÅWN”äHG[Ú”ê§ÎF<úµ‰%;ô¢4/âçc©çã°—”‹OŸâçù(>:Ÿè_~#~àûßÉïx|ÿ xðWÇ êqü™~ü—8þNî¥ç¿8ÿ°‡Äù7®?âú+®?ãú;~®t]¿¿â÷Wüþ*\#q­3ýþg“_[æŠÿ‘Þuý‘_Ÿ¢©ß6ŒÅ6÷§ºJåW)>ýôÉΫ‰gB§ø[K|Æ›qš—|3"ÿ_”zÕá¿„úrÇù¢?ŠC›ºpP_nÍ4ÉLë,QÈ– :Hª.ÔÎ'‚U)×L_ØÓ§MþéÏÊõªS2./½™ÅÜ_^§{üçÀ—ó¡Ü°ÃsTÝ«E,èCdƒ/‰úh«Ÿºâ):D6ôkg•?aŒ]Ž»ÆP úˉôŠ/Œe+_ôk§ŽøtÿÉ}>ö¿É諭3ñý‹ãO Çgt^Q)}œ Ç8ÿpÐþ¡ci\ÄõW\¾qý]À!®¿ãú;ÿ.è\‘—qý×_ì#ºÎÔu…JéµUºþbŸ*=ÿhßÂulJKüá=±òÍ›E½öYÅÈíäS·~3Ž~åª:ztˆJüá›¶|æ¹H Rãè*<¥ÇF9ª¤¡Û'eöGvÂ(ëšZ…h›Ir2ǹSú4qùÂm%ÂD— ý#êÇ6Ÿ0·Î©¹_E’çS->öŠ1(ß«©M tôÉŽ}øÈçK€>í^M>(å‡~>|â2&!ÿòëÝiœì䗱Нüéc£-;¯Nñ¯yTНq¿ð9‚˜þ…ýHûGìñý‹ãOá¸ÇßÂ1’c[œâü«ó„ïqýáèÚ/®¿ LJ¸þ,ý¯ïM\Çõ7¿‘øý¿¿â÷çÁýý ¾|ÏJ¿â„èϯkóß¿Ø Œ-µC/~‡>õC>âƒvޟǧÑ8ìÔ¯ø´©+wù×ïJý”è°«Ä?a#ÿ²ÓÜhSÇ"Ž‹¶ŽS¹Õ£y0Á^~’¢Ü9-×'ÎIL’·•0}²¡_ÁsÿêÇVýØÊ%"6ª+¥>l“Æa“ƒ†M†â»zJ|ù`¼âc#=%¾(sðÕvuêg¼vtêW‰o~ØÓF°EÔ¦ŽDcT–Æ/XMÎþÒøØhþø!¾bæØ(¾|0ôøˆøS?0B„¯°Ë?ÿ¼?ðý/¾qü‰ão:l¦ó‰Ž:ÏèŠEœâü«ý"®?âú+®?ãú“ãÇ8Oè¢sG\Oþ†Ìñ³¸þŒëϧâõ§Žð#ûÊ¿ð3¨ëø#¿”ô³áŸ8èØdO½R|}&n2e¬Žu:ÆQjß꣭ø”’<¾ø§¼:~Æ‘£½6úòxŠ¡~Ʊ‘OîCùa—¼YY0œIpHy"ÔKPêzAuìIL㼚D «%uÆPªÎ¸|¬Æa—ûÏãó€Nå’Å¡”ŸrñéW|ì±EðÅK¿|É2ó×Î¥ïÃFØ”b¡ÄŇvºÜÆä:ü O|ç1ôk%6¹½ê®N9çñ¥£DäGñå_>"~àûßäw\ß}oâûÇŸ8þÆù'?_ªÎù•ójœ'¯„ %¢ãh\ðÐùEûP\ÅõW\ÅõÇDÇ7ãú3®?ãú³öëO¾?ºÖàû¤ï‘tù÷Lß-•Ø«{ùBdž×zªë» ïÃ1\/ a,6ùxo¦6ãÑÓˆBùÍǡÆ2¯cƒTŠ_è½ü繕›¿t*±×üñC^ˆæÊüɱª`´/‚sMR h¼ÚßÒ©ÌÇ“†jãJÆ"”¥ýò§Rq°Cò±ø“âË?m„qÔªò ½ìµcà¿T‡-ºÒ±yîØÐÎmò6;(¢8ô!²W~µÄg¬æ+{ù’ÚßÀŸ="ö?}?âûWØâø3õü ã)¥ö'aÇß8ÿÄùwêµ›¾#ùw&®?&qýÁ2yLóoSøÞ qþ)|gâü;ù]a¿ˆëÉci\ÅõÇDÇ ê|Gòëü;Ãù—ýFýùùÇÕINþÄ é:_ù~'=öô!Š_hMò/´«ÿéÏÿÔ•%BlåG[zÅW¾ä@]ù)gùW?>4&÷¡ºx5ÚòÅüÍ( Þ!ˆ„`yèI:O$Ÿ„&H©ÕƒŒ‘OMR~ÕÎmä[:ÚøÓ<ˆG±èKã»*éù {DqôaÐFTRWžÚ ðE½r—?ú´)Gv4å§>Åc<’·±‘ž:B»\|úð­ñØÓV F°÷¿€!¸° /᜷闞:øÇþß¿Éc(Xè;ÇŸÉã Ç‹8þÆùGûAœãú#®¿âú3®¿ãú›ë%Î lùï oNiÓ¯k+êHüþˆßñûãàÿþG¤ï_áÛWøþåÇp8Ùè÷mêlúžãOç¯õ"?¥ËúΫ)ýü ÚɘÄg¬âSæ×ŸšãèCÄsá›ñjSgCT—?ÅÀ–>Å£®y{5Õ)k„&Ãx ôlJH·B‡~DcóØšöŒE°“žR1¨36O]zÅGW6úòœ¼™üËžvŸ¶võÑÖ<õAâ[óC‡¿¼Ï›Iˆ"Ê {_mlT ‡¼ÑüÐ1GJùòjbIW.¾b`¬8*Õ—·©#øŒø…ý LÿØÿØô]ój’øþMbÇŸÂñ=?þëË΢ã¬JõåmêHãüçß8ÿr,á˜×qýÁ~×B&qý5¹OÄõW\±Äõg“ÑuµŽ™j«,wý­UâtÊåþd«ï:¯C¶´E^ª{âЦŽƒvîOzW§~Æ•ÆG'þ ŸôË—W‹1Ô§ØôQÇ–1lذ1ÅR”ôÕ$ï«(‰äÁñ(èÙ‘$¢ i”ðÔÙ#²Ã¡­x´5yú5–~ôøÉÇ+W'½Êü9”§Vcð/ÊM㉧~tK]ãKç/=ù1&]ù{µ8ì4ô´ñŸW<éòüòñìø¢”-e¥øôåã½ñƒÀrÿaŸýoòxÃw+¾“Ç×üøÇŸ8þÆù'οqý1yþŒë¯¸þŒëïò¿â÷GüþʯýÒ:~Nü¾ΣñûëÀÿþÔo¸rLJ|Úï¸+ñ;ùXl®yùœ(µyµHÐ1vÈ·<¿!ü1FŸ7:¡Ò‹;¢ý²W>Š_l•œ½ÆPG°c¼$Ï }¹ùçöŒgŽš‹üÔ\ ÜZäÁ Êx’„~ô´©«‰¡ƒ”ÁA )/úäS“uUQ°eŒlTV‹¯¸²U|ôø"6}ˆæBlõ¡Ó†­všÜÖÕÅùPg,B¾âáW¾Ðã‹ØÊ-Ç/Ÿ'c„‡âË–Ñ|¨ËFñó\ËÅg¬bÈOÄŸü\„Mþùþ…}.ö?¾qñýÓq,âø “Çÿ8þN+âüS8ײèüç߸þÐ5†Ž£qý×_ú] }#®?'¯-âú{òœÊ¹DÇ W(ã÷(Î-àÃ>×qýÁ>€Ìtý¥ë293FûRþßxJÆp¼–¯Î¸ÿ1N>KÏÿ´k9þk<ñøþÓF(óý_v⟰¡Ÿ6±¨OXÑÆŸrD¯ó¾ØÐ)ŽW‹ñå]U!ྈ3†d‚)`é$èW Æ"šuÙkâ²A€&IILÙ(¾«’ôò§|(;ù½|¡g z¥¤_ãd#²w“d#[éå›~tÌQ~ù”žXÊMqUÊFcä=¾ËÅÇÉmU/?¿°ïw•Â1ð/ìOÚÀ%ö¿øþÅñ§pìä{Á1<¨#ùwEõ8þN?ÿÅù'οqý×\»ëºK¥Ž£qý×_œCue¿ˆëϸþŒëϸþä\Áqr_®¿óã cKÏ?®J‚çÅḃèøCY_¾!4UÇ?¹/úЗ‹BõÜzÅW^”Õæ¯~ÙȧòÏy9bѯTºª˜þ„z߈챩Yp¶¯B¢¥&¦$r$$[êCe ›ÀÐ\•D¾e/ßš À _}Ê¥ñå‡Áí èdC]9çöÄTü|gP®*åÛ¼Ž_×|Ùañ…¨O1i—úP»\|Í9Ÿ×ó4 ÿø°ˆý¯€CþÑ÷MǵãûW8†‚‡Ž{qü)ïóý'¯³wé;ÇßÂw-Îqþóoá» cC~ÌÐù&Î?…c§ðˆóoœ¹î`ˆëÉß™;òãG^ç(£cL\€Füþ‹ë¯Ù_霬ãíüûÇ9ª–“:·åméK>g{þS>äŒðùçñ4•¹½æFÉx7êò¥ÒU3òoØÌ("öf4Ì J“pè©BMöA}ØÉF¥&­‰3ù¼ÎXÚ²§M]mW?±ËÅG§1²uU›6ý¥‚ò§¡_¢¹Ðæ¹²‘ol±¡dC¯<½š=1è#>uå¨>¥d“È:âKfŸq?ðýoê± ¾…ãSâøçe'Ïåqþë¸þšü^Äõçäùr_®ÿãú;~Äï¯øý¿¿žØß_à­sV¹ï:úõûG×Àð/âŒ8ÿKïÕd/_”’œ'BÇ}Þúøb+oü+gú$šŸø'ÚÔå®6:ü Ê[uJúØd¯ØôÕ,ûJP*q‘†´ ¬d˜}²+>¶|²ØäþðE>9o&Ÿò+{•è±—/ÙU‹Ov“ïœèJEsÔ‡bbK]}ø¥Î<ò½rÓ)e]žƒbѧùxôˆú” 6"/éW|å†]Äücÿ+|wâûÇQ"Ž?qüóOœ ÇþÆõGáú,t×_LÀA"lâú³°Ÿ€M\O~wâ÷Çäoüø×ßqýͱ3~Î$|7žŒë¯BôýÿýÃ÷™ã>s€ˆ¤­k(}×5?3õù£§ž‹Æ¢“}~ü-}pbyŒÒóý—ý ß²§D”›öGtÔÙ‹Æ)¼~ÅÈs'¶â{ufɘٺ j—O€$*‰&† uMPà0]þ@OµéCò¼5†ºò‘­ÚĤÿ‘>tô¼Ð=ölz¦W‹ñÑS>±g'Á¶’à›XlÔÙù¡ŽN>ð­1ÔÙò¼Tg¼âçsquC‰à Ûjñó9DüÀ?ö¿Â÷%¾qü‰ãoœtÎÍ÷…8ÿÆõG\®ãú“+íIá8!‰ëïøý¿¿â÷güþ.ü®æxˆðÐqòÉâ”K¥øôç×|´u-Hþcùl5Jm^-ŒØc›ûT|HEù@dž½ê^V'6Æ KÅeœò!&vòG›>µ½š|ˆï¢/Ÿ¾4ç¼ÎxôØRÏE±Ñåõܦl½ôB¢¬Q‰’$ØJIà´IQ™QÌ|"ôc«‰Qâ‹M×İ•Žz¹ø²-_¾õAº‹4ÙÒ–Mž#ýÄC(‰¡\4–’|G?e.C)f›:"[µñ…N1[Dväª>JÚy|Ù¹º˜3ºjñ‰ñÿØÿâûÇŽœ“ÇØB+Ž¿:¯Äùgr߈óo\ÄõW\Æõ÷äï'8oRçz"~Äï¯øý9ù]à»Á1Ñw$~?±¿¿Ù‘jøÓ'‘¸ô|f´Kÿ²Õõ!¥nñ–ÏjŸ?6ê÷jÓõ·ò(wýA›òÀ¾ÔåN]¶Ê]>–~„²”S>ô3Fã4}Í"²°æ™!4/•‰¢§$“Õ„)51M(·÷îÔOYê‚>’r¥ñù°ä#ŒÓx¯EvôåB›>é)å;úðILÅeç@‡Rj®Ôg_±°S=/Kýåñ©Ï_*¶a?eßоæÕb]:ÊØÿ ßða¿‰ïßäñ/Ž?qüóOõó?ÇÐ8ÿN=·Äù7ο\WðÝе†W‹ué(ãú#®?8^ qýןqý×ßâ_ö÷÷‡ÎA…£Ká\C]çúÙˆƒŽã[i|qRês“¢­Æ¡CÔÆu6‰êy©óŸbkÿW^\‹ÿÂceK‰¨,´¦^¯–ö—ÚÒOL„RsUŽ©£–? œT H²ôS’ P²¡IGâÌ«E@ÐåB_”ŒÏý¨ÏÕE)O<[}P”ŠOŸ$ÏÚÔóø+ߨ±IèGб3Pb‹ž69²•Ž—|©í¦EÜòy”­âc;›øŒC"þägÖÚïÁ—vàï ¸°Ÿh£û(Žñý›<ŽÔzü‹ãOaÿ‰ãïä16Ž¿qþ‰óïÔëØ¸þ˜óøØ“÷à³3PÊ'¶ª{µ=þJí ´±¯$ŒS,ÕåŸ<´S*WùÅ–M¶Ôe“Ç—w'¡ írñÑEüÀ?ö¿Â÷@ß­øþM=æÄñgò¼¥}$Ž¿“ç¿8ÿL=ÿÇù7¿ú(|wâú£p©k1Ž©qýןqý×ßqý×ßtm¿?îï¾oµœùtm«ó¶>#Jù¡6B Q‰lᘨ³i<Ÿ;ä"ù “-uÆË–2÷çÍ)}¥ñ±ÍíO,tÊ›RñµÿaÇ&|°§žû£®üTb#aü> Ág#ÿ¯½3ÚŽcÅ¡èÓüÿÿÎÓ°‹ì.u¥ÛŽíØq2[kUBHpJIîÅ(“pÒNž2,xòXã¼ü[Õc¡Ì‡1c&.uÓo›:Ä8H›Ú‡‡,rð´^x´'=²¯½«nô:¿i}8šº(‘Ó lÃþr®Å¹Ñ¶Ž äÚÔ³¹{=Êx‹åÙ×ËþFRÌÃ?ÿÓØCî­ößÆÂ¸²wMñÇX ÅßíbÂ>êü9?¾Àƒ§ó÷ô“ò…gNùGù‡¾PþQþUþ¹ãcùwù·ñø(™kÒ~Kþ.ƾ%ÿð. _„Ôá¼ø[“s¢œù/mæ®ÛŒ‡oÜ·Tïê:ô8Ž~ú tÈ¿Þ?iÞ71öµû/ÆAØP7ã¬Ó?ç&™w‘ ñ[3'3ëèQ§`ù"èCÖ€âäÏ8ÆÐ–L=sáÈȧœÄu2æ™}ú e™²s€§£cý0žvC?ãX%ý”8Çä¹YÐ3å´/ou/~ꇡ[>%c Jô¨ƒ{“÷È>úÄʱ‹•ý…8ƒ‡þ'.ù_û˜QÎXSü)þvþÜï‰Îß#Tù†¹FùGùWùçΩÜì¿ öŽ9Ë?Ë?ý.)ÿ.ÿ&f@”åßb$x¼'ÿÏ·Æßeî!þónËËÊyÿÃ\µå¼ÑÅ8÷9òÒŒÿSž~ß?kpýóþI}SnÚG‡ëwÞð°ïèg míÛÇHêÞÜ7þªìÃîÄÕÁ„™:|ÚNpòûXœÁ•KÅéHŒƒèHJ@°oU¶ö•¥Ä&:‘—?íS·þ«ýÅ:ˆ>äÐi:ÔI©ÖA}ÎK®yYt#K]¹U=t£Ò>ý’öÑ©}ú¯ö§ÞGöë”Æ:©göûÙµ…ÿé+ù_û¯øsÿg\-þîJÜ„¦¯LœèëüÙ~¿ Ðù[þ·ý€_âCùWùgù÷yV–ßçĉ™W”•±G~Wþ‰]ã÷)Þ©¸)Éá®þ§,÷Oþ_½ÍõëÎwi£Gòüãîˆ:ºÐ?sêÕ<øÚGvê˜ö©Ó'6«zË;áÑçü§í#C?z¨k‹:Ï»hz—‚ƒÐÃ3Á¥ÍÄßIÒ¯, ð¥ÉÁFr?¿?èoÿµÿÚÅŸâïý÷.qóÙ÷ûåÙ÷1ÕqÔ!Ï+ÆQ§”Ѓ<ø{‰NögÛqê@ÞØÎxÉq¶)‹¼cßœ—ëEžºz(!dç£}úÔ븹>xÚ™ódÜ/ }”˜°D݇ÉALåõrOì§íK[Õc s¤_äÃÃ%$´íƒ¯¾iYç‡ D¿öcuHí#§¬åbúÁ6%}Ø@‡mÖ©ƒºóPNçtìÕ>|íRw­ÖµO›G½”Ú‡‰ù)§}úàeÿô=p;ëá¿1°Èÿ¶¿€ûªýWü)þ® ±Èº[{¿¸O:6`Óù[þQþUþE®écì,ÿÜçHùwù·¹vß}½õûß®ù'|xêCƘëÝɼ‡A^Ù)§^s9ô¨Û:òæ½þMJeÔ‰,rØÔ.%ýÈòЯ¼<×@9¿?蟤.æÈ9«㔥®,}Ô}ÔoIÿ›‰äç# %q2x Y€}<€`}Uïˆ,º¦ÌÔ‰¼ºsÚ§ß5:·—ì#C?6¬c}Òœ+<õNûŒ‘ÐÙ¯îÍÝŽ©Œ6•®©qôCÈOûÚ˜òê²Ïö¡`ý¸1h£‹~ä)³þÓŸ–K䀰¨ýWü)þî½Àyá3ã…ç}¶÷¨ÎŸÎßûóÿà)ÿ(ÿÂÊ?Ë?çy¹_@å_å_å_{/”½/ÿ$¶Ìx2ï^:èÃ÷Àz†?ú¦²ú,õ«}ôò ïzÿ²¤Ž.åC{ÆKíÃç¡-1Î Iêö#£}×ÅdxèW~UouÆCŒ™²òηü ä3HMžä"•¿¶‘s¡Œ›ÀÐæFØÅϵø§Î¼(/()‘}$¯ÞÕ}ô+«mù³îiýÞT#ãÚ(§ýÕ<ž20lSB®kbIŸö'žÈÓçšÙ$Ÿ}ÛþÛó¿íí¿Cñç<‹Š¿?¿;Ïy”O”ì˜Yþuæ¶æ¿ü¥üsû ¿åŸåŸîü¡üî/=ʿʿþTþ…/>ó?ø<3ÿqÿ²§9û<ÿƒ(•q¼²‡ÀÖ;å¸Xäž >¤>ô{5Ç0ú•ö&OÙ%zØt|ûÐóhžŒ¹ÖÝzǯ@¾cèÝ&.&6Až ¤tòÊ/Ö xêÈ0¢Ž@SÒ¶¾ªO Ð«}JøÓ>ã”U¯v,áóØÖ¥ç ǹ~ú±å|Wõ˜3<xPöÃ?ÿkÿŠ¿ž#?Û8Ádž³ž½”“ϹÚù{bRþQþUþy~+˜“—ïo.êû£ï¾?úþøªïâ dΦÿ£¯ù/sƒ›ùß䣋qæ„”žô©>mˆ’ö3ûôÓ§Nõ¡ R2Ìžýðy&Ñï8øÎSüå19KtbŸ6užik5ßGNö}£ÏQS t’Lܲ-)ãÂDБ£.è’ȶsP^ÝÞ2ûçü”Ѿ:)¯úì›|tªÏyLûÔ±sjž:(¯òb¤ŒØi 9Èqì3VÝìÿPq“qýêœsÌþéà$‰Uø‹È.õüoïíößÏñOÑGŠ?g¼.þï}ƒo@??ì‰òŸíǦX?åç¾Êò}–x¶–”\¿?Ý/úHùWù×ôc(~Rþuž3æ¤àòèü%G;ñ$þòß•ôþ<%dÄwÖáiË Ñ«=í`Ó;ˆiÈ`WÎIùiÓ¾Y¢Ã±Œ!N@Ú1/ÛÜý;u[Ÿýoª3ùßI,Î`çbÔÏdéwÒêÚ<¼(^0uǯêAòh`‹ñðt„i_]ðxiêÒæbND?}öc_Œä1R§uík—þI:öés uÿã¨ê‡çú©kÎOYl© û´Õ¾óÕ>ú¥ìoÿðß>•ÿ¾ÐþÛ‘¢øSüíüÙñ±ó·ü£ü«ü³üÛ¯ˆ]>ûþéûcãÓ÷×ù}¡çôýÙ÷'÷?ÐW|cçz?Ãý¶áÓ}ÿ.öÃûeéÇ—¡©Ÿ=oüûïªOýÈ2Æû§GöÕÉ8òï™{RǾäxJè:ÆüÝ1ö?ÃýêtÞ”ÎA=Øz7¹øw+Yˆ“b¢Ä\Ä\öi .:Yˆ>túЦ_ö/Öq‘©ãÑI[uRÂc~Ó>|ìû¢Võ&GÝ—q|ì¸~êzý–ÔµO]›Ì‡:¤¬úÕ¥S"ÃXÚÈb_ÙUýÉ>ü~Öýz9«½'¢>ñ·_û«ûîþyüØóþZ2îWοi_óýÓoû??t®â i†söÌž÷_è ŸÖ:ö¥„l‹òÎ~│k‹ú‡ˆ þ.bⓘ<Äd¡9é ’}ö»hÚ<¾Hø‚Ä_ uûWã!Ç9yÈ9_mM=ÖÁʹ0V Îxl¨ž6¨ÓÙï¼ÐGßköÑÅóûØÈþaQøoò¿ö_ñ§øÛù³}ÀóÕ3Ÿ(I½ó·ü£ü«üÓø@\xkþ_þÝ÷Gß_윾¿úþÜ~ð·|ò¾<ÿ}wÏâ¿w=ÈKòæþ‡÷Þõ3þö¥ziO}«y»r¾ð°GÛ‡¶äú(Í‘CÞ¼ÿ’¯}ú©Û¶\¬Ñï¼ d&LÜÉR¿’ù‹}ÓO=”ŒQÿœëbßæNýÃô»/(™ (x,†ö$lóò]àì0ty‰ÈXÚêE§:”S†ñô©>ãƒ,%ÕÿrÎcUyQªÇ—1íÓ¯}Ç;Æ6%c ×§}ÆNûÎ Y/,µq´‘³ÜäkúÄU¹Åº[;rŒ}d_Úcží쇿~•ÿµÿŠ?ÅßΟ3èü-ÿ(ÿÚûÁ<üÑü–<’§ü³ü»ï½ü®so¸oà÷ýµ@XÔ÷gßÿî÷Çg|âkè‡ÜóìÂ&|ÈûäáCô9Ž¿…ìwÓ~ÿu}ê¹Úw~Ø÷ûË8AŸã”c<Ï$í3Ž9Ñv®Œ‡hsO&)g¿ë§ßõÙ瘕.êCJ f²LÔI#âÄYä\ÀÑçKLdUàˆR}ʨƒö#ûS‡ÿÖ~‰ÞìRW†ñΞ|mЦN9í¯æÑf,ÎAß»šGûÑúí£t ò¦ujˆ6äXì»øö£+ûg¯‰ X‰!uð Ø8åÛÚç¾Ð7(Ý;ÅŸâoç;bÓ~éÿž—æŸôÍqô›_M›Ög¿û‹bœwlòl3κ²”È©Èö´9ïß¶ÔoúÅÈW`K ¨Š æE0KÆXGŽº 2Fr¼%|ê³.ÿªªzdêÜhOûð¦}úµÏÐižo¶á‹õ´>åÔ¯MÆ(‹œý¬EûØùûŒÉþa‘˜Rÿü¯ýÇN8ãKñççXYü=ý£ó§ó·ücç]å_åŸåßûÛ<Š\Lf»ïŸs pêûãô¿oË?ö•òÏï—ÎÈœ˜= ]ã¼·Ü¿ð¾!ï*Ðïþ nÞA MûÔ¡Gñyu"cüaŒûÎR”ÊQÒÖîªôˆg߇J&¹0lh‹ð¼ ¤²Ÿ1`{‚¡>Æ¢ûg|yÖëÃX.,yéz´çyêW‡sŸ|t8Ž™)o?%zq‚«}ú õ8ŸiGÝ®yû§Ì´oùì‡þ×þ+þÜÇb#d-þn<<_â9K±ÌÿNìò?¼bã¡QZ§±Ýþ‘‡¾c›Ø%>sß²ÿÿÅßs?¸/Ü/ÿ;¶o‰-ÅßÎýÁóež)øÈµßý4K}©øSü™q¿ÐOþåøëÚù¿÷DÊÌýeü£GûÏ1³TŸ¸RÒ?u­æ-/ô¾ ¶‘CžGr›\À,±+ÐeÓ Gš€QŸàÓïK¥ºmúÔMéŸ^RGvöMÎÑù85ä6m yeiÓ§þiƒõs{ ÏþU=ä)áAÓ>mùÖi_íÓŸýßðÇk¶Ïåí¿âOñ·óç<ˆœ—çlçïÆÃ¼G\ÄÈ<ƒ~| ¼àÉ/ÿ8ý«üc9Æ"p(ÿ(ÿ(ÿ(ÿ(ÿ8Ïbcù(lL(ÿ–ü‹ó 2ßy-ÿáþ‰ÿƒ÷#ÿ7BŸùÔ³ü >2öcŸúÕþb¶(‘™÷_޵Rž9‹mKd°a?íO!ýå¥,NðYØ\,¢ð˜›”º@¯êO±¾}¾_ |íÓG{ÒÕ¾ºœ%cè£ÍêèÖmAûôC®Iû®ù¹~u1¢_ºÚG&ûá¯Ïäí¿âOñ·ó§ó·üãÌ ÈŸÊ¿öÙXþ¹³éòïCß}õýy~gû-Õ÷÷víçûîÎûüÆ;!öÓKù—wBSÞ{ž×ü½óìzvÿƒ>ÏúU=òÆ©ô,þkÃYÆ:OÚŸFøUÄ}°I(*¸«z~A£ßàI> ñ’à»Ú’òö1†gÎÁ›dtò̾ռé§>_ r¶ÑOšöçü\‹ö-ËŸ$"«ú$êÈ<[¿ý®ñjß~ôQç˜ä,ú³@rÈ–ø…ÿãý'>ùßöö߯_â‰þA›:TüÙ8÷™ÙùÓù[þ±c‚1’–ñ³ü£üƒ À(çùZþµYTþµqàwúm|†*ÿÚ8”•3žåŸô[ÝOxŽñ—~ø<Ðkñ‡~ï‚(ÑÍýÏ´¯ú©#ãùoŸö)éçQ~Ur¾”Ú|¶ÿà•¨Ãw~Œ§®žUý\ba_MÚ8qáð'XðرÌžŒê²d /T½”ÿùÑ^ÅÁ×¶mBû¾«}Ú<ÌG£Í8_Þ´ë<¦}纆ºœ/:¯öщÇüª}íRBà…ˆR=”Ù?Bøã!Û—ñ‹ü¯ýWü)þvþÜŸ™¯ÿ¿g^ÁyRþQþUþÉN(ÿîûãþ,éû«ï/ò…¾ÿ¿ÏýǼÓá˜w#Ñç7¼)KÛýl ïzÿƒNÆñ(7ß¿|¿¿¯öi_ïŸC®>óOô?³¿ºûØaœ÷êržæÿÈ Í‰ÁeÄEcú$ ÌOyeDêÈçt̪c¯kT–~êÚG¿Ÿ6¨ý99çAÝ>Ê«ý9Otá4õ«}øÚG×Ô=mRGVyÚèFŸãéËþáGøo0Ä"ÿÛ{< J÷Oû¯øSü=ÏÖΟ#D?¿åœžå_åŸåß}ôýuæÏÄÆ¾?Ïœ<úþÜxˆÅ¿ôýÉ·ÂõþçÚžï_y÷ˆ9åüþtY*C)Ío¿Y‘ç¡ )¯¬ýâ¿¥öï3ûŒùRrò_jô‡1lš`À¤Dúr¶‡x®Ã½X7yêê²´yf¾4úœ‡ö÷È><ÆÚG)1†>Èú´ïú¯ö‘”e6l¿¶~Ç3Öú,ÑG;û÷ïL ±ïðÛþ¿‰¥<Êü¯ý‡Š?óüÅ' cEñwãÑùÓùÃ^p?”ÿ½œÿ?f,‘GYþQþ”””œù{2VzÞü¿ç⯀ %˜X£-!OäØY*küAÈ@ÊÂS7¥:)çý—2‹}ãiX§t<öm3¢„9ƾÍýâ_/ö¾Øìaî †s8úy|‘e}xÉò©CöÉ÷ÅÐ9d eµG?õéÔí_Õ©K;þ•[t“µ¯Šœ2ŽÑ>mëì3v®½k¢œãíËþ‰{øãÛßó¿½Ý3í¿ûø“Š?ÅßΟóœïüÝÑ¡üãÌÁ²ÔŸÿö#ù~ásxØGyµo®Ÿ:„Nd•G×\míZ.Öm ²ÎÇqô)9ù/5:Œ ó‚Ù¦.Øô)_©ÓÇ¥+užù¿s‡ï?Û^Õƒ,öhó’å¯êAWû0‘¿ŽÃ²èàùïzÐ…,|çÇñ”ôCÔ!ÛÖiO9ç«}¬O´Y?„ÌKëŸëÍþYøoŽ_ü'ÿkÿθ\ü½?';:Ë?Ê¿Ê?wžPþÝ÷Gß_ç·TߟÏïúþ>óê?}ÿàýù=ï†:}òWõvƒ }ø8ßÉÔy~eÿ£5«—qz¤ùý­}W¿Áþ<ùƒPçÿÚþC/$þÎksÿÀ¯ú¦2É Hh‚Nݶõù‚Žë‡ñðuù”Œ“Yi꾌iôCŒE†—;í¯æÑv.oµ?]oµ?çB=ûo{ÿáÿ1ÿÏÿÚÿø@ñoûAñ·øû–ü£ó§óç#ùgçoçoçoùGùWùq üó{矼#ˆ3ŸwõÖü1×3ÿ¥ø¯½5ìG 9æ%ûÈ`Ï»1Æýb’ß…Žùt žýôñ@”¬>È‹DµœcšÿĆ6²êÕàQ‡ÿ̾²Käæ|Úå¢Ó¹MÝÈÒç\è{¯}×ïuí«}eêDNr~È9mÚ'Ÿ9Ç#Gžüìß¿p{q¿ÕuóäÄY¹ðߨ‰Kþwî3|§ýWü)þvþtþžq±ü£ücæßå_åŸåß§7öý ›üÆêû«ïO¿3¿Ã÷·s¸–Ì‘ý Ÿòµû§—Î?u£Çµ¯êí¾‚:4ûh?¦}ç…=õ¡>¥õUýpßKÀ˜›uJ“_8¼+2à ¶AÍñ$Çôóè«z#åóÎkÖQ õoC|°}W0 œ—D}ö­æí…ÊG¢íX/l(­#£ÎYw}`¤eµã\l[z)B[ŒµI ÑÇ3uÒV'åköÕÍ8õ¯êq J úÔ;í/öíâ•úœKöÃ?ÿ{yÿ·ÿˆ;Î6àqÿÅßΟÎß3N3Ü+ÆÑòsŸ”•••‰ä3>;›}ÿõý[þýûòoöx’dzǨÛö\žw5Ô!Ë·äÿîiÆ[W7í¹ÿÑ«}ä©CÈÏ÷Ï8H=Î_9ûfIýÛ‘îÛMlLPy$A¿mÚ¾^4e6góÔ©ó©ŸjÇ"?_:måÐ;uÛ~fÇR/²ÈáDÚ£Íã|Võ¶äì·Ž.ô@ðæ\ài‹’‡[{iÚמúm# ±Ú¤m~ö‹Â?ÿkÿ±‚=A|€(yŠ? ±©ø»Ïâ¦çMçÏÆD<ð”Îß3¦èå§_”íxZþUþUþuÆJvEù׎ åŸåßïýþp‘{HÖ‰73æØ6gsÿ)ó«÷?ÞaO]æ<Ú¦Ôv<ÿà9æjI9õÉÿ–% ñßF€¸¾ŒYg-®Éà ¤OYê¾Dy¼8É628 ã‘2ò}ùê»ÚGçä)=H{´íƒ‡>Æi'ûç;ÿÓ/ô™üom–Es¯ÑnÿÝc¢¿ðŽâoçÏöÎßòccùWùgùwß}ß}öý©ôý}î s†|£ƒüsÞ?=:ø^oÎ~­ÓýþU7}Ž£þW‹ù+&ûd’®ðçK†ï ´”‡*x:|êÊQB8ÌäQ·{—˜ŽÀêÞ†ËGþj_‡D–:óGN×9Ñ?ç°š7‡ÔNöÃ?ÿkÿv¬4.;:ÏÜ‚<¢ücç[å_åŸåß}ôýÕ÷gßßgŽÐýÃÆBŸxïý ¹X^óOx~—\±^]?ÝIMûŒƒÐ ѦߜN[Î]yd£/FàåL‚ñ¢|±ãÇ/ЬŽk[=ŽUnÚ“§ ¥ã²þùß¹Ü#í¿É5Þ\ÛÆq3ÖDdã8ñ GÜŠ¿Åßâï¹Ü5Åßâ/1òzÞ\ÛÆQý¦ógÇÓyÞˆ‰QŠ[çOçOçϹÜ#??ÄÈëysmGõcmñWD¶M<è¹âvJÿE5á/šòÓ©zƒÌ‹âñÆxÞ áÉG!/Ó¶7Ùðá1}¾ð9–út mg?üñ ˆRÿÊÿN,Čć²ýwkŠ?ÅßΟû=aì ìü%‚n*ÿØþPþUþEl€f¬(ÿ:s-q£ò¯ÓOÊ?ïÏÚòÏòÏòÏû=aì¤üŽù'óú'ˆàó//Ƈuqðê@¾4Ûô]/DC?ò”XŽuŒåê:ˆö´ë!_{ê°­e·¦ìƒOøçøAû¯øcì4fZ3iO?ñLÏ1´-†ÊŠ%ý<Åßâ/~Pü=÷•{ÆÒ=C»øsâdLãMñg{‹xèCb¥/ÑÏSü-þâÅß3®¸g,Ý3´‹¿'NÆp1Þ··ˆ‡>$Vúý<SüõÝÎ=àzþêò_» |ô2æKÃñpHÉk›Î+1ÆÏ:zt䫞ëƉuöAcÓ7¸áŸÿµÿ~lU\cIñgLjâoçOçïÞ ×sô3ˆ&å;¦–mø½ú¼Îߣë^êüíüeß”””” ®çèõÌà4ùù±çjÿ Ú?·°' zôRI8LÈ®ý´q6#ÔÊ›%ãÁRÇ4‰¡4ˆéTÚR~‰d…ÿÆÁ_},ÿÛûHqOY¶ÿŠ?ÆS|‡½C©OL,·óŠ:8AÅ߃¿Åß½::ˆ£{b–Äâïö°àÊΟEçïéË-nû…:~á3“ÜcÅßâoñwï ÷Ä,Ù?ÿêùÃ:y¤Y—÷Ï–¼ÔèÞ&Úó0±ÎÉrŽñ€1Y±Ïq´ @¼tÀãÉ~øë/ù_û_€Œ»Uü1îZÎ=SüÝçKçϹoðÈ}Ôù»ñ(ÿ8}¢ü«ü³ü»ü{æå_ûœðÜÜ­òOóNËé3åŸåŸä—Ïòo|&z€@”@¬«ã|ä[Ž!GUgœ|d Zð­SòÌDÈ~x¾£ëxl8fê‚O›rÒuü“ýKß©˜„ÿö¢«ÿä§Ï̽Ôþ+þàÅßyúœ—>3¾SŒµî#FÊ+þnÅJüŠ¿Åßò¿'Œ”¿¿úÁŽœãçôêî%ã«¼ÎŸÇøuþœ>3}©øóÿf¼±n<¡´~í³]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Ÿ…Àÿã²ñ8<7òTIEND®B`‚flippercloud-flipper-532c39a/examples/000077500000000000000000000000001440444367400200245ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/active_record/000077500000000000000000000000001440444367400226355ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/active_record/ar_setup.rb000066400000000000000000000013701440444367400250050ustar00rootroot00000000000000require 'bundler/setup' require 'active_record' ActiveRecord::Base.establish_connection({ adapter: 'sqlite3', database: ':memory:', }) ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_features ( id integer PRIMARY KEY, key text NOT NULL UNIQUE, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_gates ( id integer PRIMARY KEY, feature_key text NOT NULL, key text NOT NULL, value text DEFAULT NULL, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value) SQL flippercloud-flipper-532c39a/examples/active_record/basic.rb000066400000000000000000000005111440444367400242400ustar00rootroot00000000000000require_relative "./ar_setup" # Requires the flipper-active_record gem to be installed. require 'flipper/adapters/active_record' Flipper[:stats].enable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end Flipper[:stats].disable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end flippercloud-flipper-532c39a/examples/active_record/internals.rb000066400000000000000000000063751440444367400251740ustar00rootroot00000000000000require_relative "./ar_setup" # Requires the flipper-active_record gem to be installed. require 'flipper/adapters/active_record' # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) Flipper[:stats].enable Flipper[:stats].enable_group :admins Flipper[:stats].enable_group :early_access Flipper[:stats].enable_actor User.new('25') Flipper[:stats].enable_actor User.new('90') Flipper[:stats].enable_actor User.new('180') Flipper[:stats].enable_percentage_of_time 15 Flipper[:stats].enable_percentage_of_actors 45 Flipper[:search].enable puts 'all rows in features table' pp Flipper::Adapters::ActiveRecord::Feature.all # [#, # #] puts puts 'all rows in gates table' pp Flipper::Adapters::ActiveRecord::Gate.all # [#, # #, # #, # #, # #, # #, # #, # #, # #] puts puts 'flipper get of feature' pp Flipper.adapter.get(Flipper[:stats]) # flipper get of feature flippercloud-flipper-532c39a/examples/api/000077500000000000000000000000001440444367400205755ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/api/basic.ru000066400000000000000000000006161440444367400222310ustar00rootroot00000000000000# # Usage: # # if you want it to not reload and be really fast # bin/rackup examples/api/basic.ru -p 9999 # # # if you want reloading # bin/shotgun examples/api/basic.ru -p 9999 # # http://localhost:9999/ # require 'bundler/setup' require "flipper/api" require "flipper/adapters/pstore" # You can uncomment this to get some default data: # Flipper.enable :logging run Flipper::Api.app flippercloud-flipper-532c39a/examples/api/custom_memoized.ru000066400000000000000000000017151440444367400243540ustar00rootroot00000000000000# # Usage: # # if you want it to not reload and be really fast # bin/rackup examples/api/custom_memoized.ru -p 9999 # # # if you want reloading # bin/shotgun examples/api/custom_memoized.ru -p 9999 # # http://localhost:9999/ # require 'bundler/setup' require "active_support/notifications" require "flipper/api" require "flipper/adapters/pstore" adapter = Flipper::Adapters::Instrumented.new( Flipper::Adapters::PStore.new, instrumenter: ActiveSupport::Notifications, ) flipper = Flipper.new(adapter) ActiveSupport::Notifications.subscribe(/.*/, ->(*args) { name, start, finish, id, data = args case name when "adapter_operation.flipper" p data[:adapter_name] => data[:operation] end }) # You can uncomment this to get some default data: # flipper[:logging].enable_percentage_of_time 5 run Flipper::Api.app(flipper) { |builder| builder.use Flipper::Middleware::SetupEnv, flipper builder.use Flipper::Middleware::Memoizer, preload: true } flippercloud-flipper-532c39a/examples/api/memoized.ru000066400000000000000000000017371440444367400227660ustar00rootroot00000000000000# # Usage: # # if you want it to not reload and be really fast # bin/rackup examples/api/memoized.ru -p 9999 # # # if you want reloading # bin/shotgun examples/api/memoized.ru -p 9999 # # http://localhost:9999/ # require 'bundler/setup' require "active_support/notifications" require "flipper/api" require "flipper/adapters/pstore" Flipper.configure do |config| config.adapter { Flipper::Adapters::Instrumented.new( Flipper::Adapters::PStore.new, instrumenter: ActiveSupport::Notifications, ) } end ActiveSupport::Notifications.subscribe(/.*/, ->(*args) { name, start, finish, id, data = args case name when "adapter_operation.flipper" p data[:adapter_name] => data[:operation] end }) Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? } # You can uncomment this to get some default data: # Flipper.enable :logging run Flipper::Api.app { |builder| builder.use Flipper::Middleware::Memoizer, preload: true } flippercloud-flipper-532c39a/examples/basic.rb000066400000000000000000000005111440444367400214270ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' # check if search is enabled if Flipper.enabled?(:search) puts 'Search away!' else puts 'No search for you!' end puts 'Enabling Search...' Flipper.enable(:search) # check if search is enabled if Flipper.enabled?(:search) puts 'Search away!' else puts 'No search for you!' end flippercloud-flipper-532c39a/examples/cloud/000077500000000000000000000000001440444367400211325ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/cloud/app.ru000066400000000000000000000006751440444367400222720ustar00rootroot00000000000000# Usage (from the repo root): # env FLIPPER_CLOUD_TOKEN= FLIPPER_CLOUD_SYNC_SECRET= bundle exec rackup examples/cloud/app.ru -p 9999 # env FLIPPER_CLOUD_TOKEN= FLIPPER_CLOUD_SYNC_SECRET= bundle exec shotgun examples/cloud/app.ru -p 9999 # http://localhost:9999/ require 'bundler/setup' require 'flipper/cloud' Flipper.configure do |config| config.default { Flipper::Cloud.new } end run Flipper::Cloud.app flippercloud-flipper-532c39a/examples/cloud/basic.rb000066400000000000000000000006021440444367400225360ustar00rootroot00000000000000# Usage (from the repo root): # env FLIPPER_CLOUD_TOKEN= bundle exec ruby examples/cloud/basic.rb require_relative "./cloud_setup" require 'bundler/setup' require 'flipper/cloud' Flipper[:stats].enable if Flipper[:stats].enabled? puts 'Enabled!' else puts 'Disabled!' end Flipper[:stats].disable if Flipper[:stats].enabled? puts 'Enabled!' else puts 'Disabled!' end flippercloud-flipper-532c39a/examples/cloud/cloud_setup.rb000066400000000000000000000002231440444367400240020ustar00rootroot00000000000000if ENV["FLIPPER_CLOUD_TOKEN"].nil? || ENV["FLIPPER_CLOUD_TOKEN"].empty? warn "FLIPPER_CLOUD_TOKEN missing so skipping cloud example." exit end flippercloud-flipper-532c39a/examples/cloud/forked.rb000066400000000000000000000012541440444367400227330ustar00rootroot00000000000000# Usage (from the repo root): # env FLIPPER_CLOUD_TOKEN= bundle exec ruby examples/cloud/threaded.rb require_relative "./cloud_setup" require 'bundler/setup' require 'flipper/cloud' pids = 5.times.map do |n| fork { # Check every second to see if the feature is enabled threads = [] 5.times do threads << Thread.new do loop do sleep rand if Flipper[:stats].enabled? puts "#{Process.pid} #{Time.now.to_i} Enabled!" else puts "#{Process.pid} #{Time.now.to_i} Disabled!" end end end end threads.map(&:join) } end pids.each do |pid| Process.waitpid pid, 0 end flippercloud-flipper-532c39a/examples/cloud/import.rb000066400000000000000000000007121440444367400227710ustar00rootroot00000000000000# Usage (from the repo root): # env FLIPPER_CLOUD_TOKEN= bundle exec ruby examples/cloud/import.rb require_relative "./cloud_setup" require 'bundler/setup' require 'flipper' require 'flipper/cloud' Flipper.enable(:test) Flipper.enable(:search) Flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker")) Flipper.enable_percentage_of_time(:logging, 5) cloud = Flipper::Cloud.new # makes cloud identical to memory flipper cloud.import(Flipper) flippercloud-flipper-532c39a/examples/cloud/threaded.rb000066400000000000000000000015061440444367400232410ustar00rootroot00000000000000# Usage (from the repo root): # env FLIPPER_CLOUD_TOKEN= bundle exec ruby examples/cloud/threaded.rb require_relative "./cloud_setup" require 'bundler/setup' require 'flipper/cloud' require "active_support/notifications" require "active_support/isolated_execution_state" ActiveSupport::Notifications.subscribe(/poller\.flipper/) do |*args| p args: args end Flipper.configure do |config| config.default { Flipper::Cloud.new(local_adapter: config.adapter, instrumenter: ActiveSupport::Notifications) } end # Check every second to see if the feature is enabled threads = [] 10.times do threads << Thread.new do loop do sleep rand if Flipper[:stats].enabled? puts "#{Time.now.to_i} Enabled!" else puts "#{Time.now.to_i} Disabled!" end end end end threads.map(&:join) flippercloud-flipper-532c39a/examples/configuring_default.rb000066400000000000000000000010371440444367400243700ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' # sets up default adapter so Flipper works like Flipper::DSL Flipper.configure do |config| config.adapter { Flipper::Adapters::Memory.new } end puts Flipper.enabled?(:search) # => false Flipper.enable(:search) puts Flipper.enabled?(:search) # => true Flipper.disable(:search) enabled_actor = Flipper::Actor.new("1") disabled_actor = Flipper::Actor.new("2") Flipper.enable_actor(:search, enabled_actor) puts Flipper.enabled?(:search, enabled_actor) puts Flipper.enabled?(:search, disabled_actor) flippercloud-flipper-532c39a/examples/dsl.rb000066400000000000000000000035661440444367400211450ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' # create a thing with an identifier class Person < Struct.new(:id) include Flipper::Identifier end person = Person.new(1) puts "Stats are disabled by default\n\n" # is a feature enabled puts "flipper.enabled? :stats: #{Flipper.enabled? :stats}" # is a feature on or off for a particular person puts "Flipper.enabled? :stats, person: #{Flipper.enabled? :stats, person}" # get at a feature puts "\nYou can also get an individual feature like this:\nstats = Flipper[:stats]\n\n" stats = Flipper[:stats] # is that feature enabled puts "stats.enabled?: #{stats.enabled?}" # is that feature enabled for a particular person puts "stats.enabled? person: #{stats.enabled? person}" # enable a feature by name puts "\nEnabling stats\n\n" Flipper.enable :stats # or, you can use the feature to enable stats.enable puts "stats.enabled?: #{stats.enabled?}" puts "stats.enabled? person: #{stats.enabled? person}" # oh, no, let's turn this baby off puts "\nDisabling stats\n\n" Flipper.disable :stats # or we can disable using feature obviously stats.disable puts "stats.enabled?: #{stats.enabled?}" puts "stats.enabled? person: #{stats.enabled? person}" puts # get an instance of the percentage of time type set to 5 puts Flipper.time(5).inspect # get an instance of the percentage of actors type set to 15 puts Flipper.actors(15).inspect # get an instance of an actor using an object that responds to flipper_id responds_to_flipper_id = Struct.new(:flipper_id).new(10) puts Flipper.actor(responds_to_flipper_id).inspect # get an instance of an actor using an object thing = Struct.new(:flipper_id).new(22) puts Flipper.actor(thing).inspect # register a top level group admins = Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? } puts admins.inspect # get instance of registered group by name puts Flipper.group(:admins).inspect flippercloud-flipper-532c39a/examples/enabled_for_actor.rb000066400000000000000000000013571440444367400240070ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' # Some class that represents what will be trying to do something class User attr_reader :id def initialize(id, admin) @id = id @admin = admin end def admin? @admin end # Must respond to flipper_id alias_method :flipper_id, :id end user1 = User.new(1, true) user2 = User.new(2, false) Flipper.register :admins do |actor| actor.admin? end Flipper.enable :search Flipper.enable_actor :stats, user1 Flipper.enable_percentage_of_actors :pro_stats, 50 Flipper.enable_group :tweets, :admins Flipper.enable_actor :posts, user2 pp Flipper.features.select { |feature| feature.enabled?(user1) }.map(&:name) pp Flipper.features.select { |feature| feature.enabled?(user2) }.map(&:name) flippercloud-flipper-532c39a/examples/group.rb000066400000000000000000000014241440444367400215060ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' stats = Flipper[:stats] # Register group Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end # Some class that represents actor that will be trying to do something class User attr_reader :id def initialize(id, admin) @id = id @admin = admin end # Must respond to flipper_id alias_method :flipper_id, :id def admin? @admin == true end end admin = User.new(1, true) non_admin = User.new(2, false) puts "Stats for admin: #{stats.enabled?(admin)}" puts "Stats for non_admin: #{stats.enabled?(non_admin)}" puts "\nEnabling Stats for admins...\n\n" stats.enable_group :admins puts "Stats for admin: #{stats.enabled?(admin)}" puts "Stats for non_admin: #{stats.enabled?(non_admin)}" flippercloud-flipper-532c39a/examples/group_dynamic_lookup.rb000066400000000000000000000031541440444367400246050ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' stats = Flipper[:stats] # Register group Flipper.register(:enabled_team_member) do |actor, context| combos = context.actors_value.map { |flipper_id| flipper_id.split(";", 2) } team_names = combos.select { |class_name, id| class_name == "Team" }.map { |class_name, id| id } teams = team_names.map { |name| Team.find(name) } teams.any? { |team| team.member?(actor) } end # Some class that represents actor that will be trying to do something class User < Struct.new(:id) include Flipper::Identifier end class Team include Flipper::Identifier attr_reader :name def self.all @all ||= {} end def self.find(name) all.fetch(name.to_s) end def initialize(name, members) @name = name.to_s @members = members self.class.all[@name] = self end def id @name end def member?(actor) @members.map(&:id).include?(actor.id) end end jnunemaker = User.new("jnunemaker") jbarnette = User.new("jbarnette") aroben = User.new("aroben") core_app = Team.new(:core_app, [jbarnette, jnunemaker]) feature_flags = Team.new(:feature_flags, [aroben, jnunemaker]) stats.enable_actor jbarnette actors = [jbarnette, jnunemaker, aroben] actors.each do |actor| if stats.enabled?(actor) puts "stats are enabled for #{actor.id}" else puts "stats are NOT enabled for #{actor.id}" end end puts "enabling team_actor group" stats.enable_actor core_app stats.enable_group :enabled_team_member actors.each do |actor| if stats.enabled?(actor) puts "stats are enabled for #{actor.id}" else puts "stats are NOT enabled for #{actor.id}" end end flippercloud-flipper-532c39a/examples/group_with_members.rb000066400000000000000000000030271440444367400242540ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' stats = Flipper[:stats] # Register group Flipper.register(:team_actor) do |actor| actor.is_a?(TeamActor) && actor.allowed? end # Some class that represents actor that will be trying to do something class User < Struct.new(:id) include Flipper::Identifier end class Team attr_reader :name def initialize(name, members) @name = name @members = members end def id @name end def member?(actor) @members.include?(actor) end end class TeamActor def initialize(team, actor) @team = team @actor = actor end def allowed? @team.member?(@actor) end def flipper_id "TeamActor:#{@team.id}:#{@actor.id}" end end jnunemaker = User.new(1) jbarnette = User.new(2) aroben = User.new(3) core_app = Team.new(:core_app, [jbarnette, jnunemaker]) feature_flags = Team.new(:feature_flags, [aroben, jnunemaker]) core_nunes = TeamActor.new(core_app, jnunemaker) core_roben = TeamActor.new(core_app, aroben) if stats.enabled?(core_nunes) puts "stats are enabled for jnunemaker" else puts "stats are NOT enabled for jnunemaker" end if stats.enabled?(core_roben) puts "stats are enabled for aroben" else puts "stats are NOT enabled for aroben" end puts "enabling team_actor group" stats.enable_group :team_actor if stats.enabled?(core_nunes) puts "stats are enabled for jnunemaker" else puts "stats are NOT enabled for jnunemaker" end if stats.enabled?(core_roben) puts "stats are enabled for aroben" else puts "stats are NOT enabled for aroben" end flippercloud-flipper-532c39a/examples/importing.rb000066400000000000000000000020441440444367400223610ustar00rootroot00000000000000require 'bundler/setup' require_relative 'active_record/ar_setup' require 'flipper' require 'flipper/adapters/redis' require 'flipper/adapters/active_record' # Say you are using redis... redis_adapter = Flipper::Adapters::Redis.new(Redis.new) redis_flipper = Flipper.new(redis_adapter) # And redis has some stuff enabled... redis_flipper.enable(:search) redis_flipper.enable_percentage_of_time(:verbose_logging, 5) redis_flipper.enable_percentage_of_actors(:new_feature, 5) redis_flipper.enable_actor(:issues, Flipper::Actor.new('1')) redis_flipper.enable_actor(:issues, Flipper::Actor.new('2')) redis_flipper.enable_group(:request_tracing, :staff) # And you would like to switch to active record... ar_adapter = Flipper::Adapters::ActiveRecord.new ar_flipper = Flipper.new(ar_adapter) # NOTE: This wipes active record clean and copies features/gates from redis into active record. ar_flipper.import(redis_flipper) # active record is now identical to redis. ar_flipper.features.each do |feature| pp feature: feature.key, values: feature.gate_values end flippercloud-flipper-532c39a/examples/individual_actor.rb000066400000000000000000000010741440444367400236730ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' stats = Flipper[:stats] # Some class that represents what will be trying to do something class User attr_reader :id def initialize(id) @id = id end # Must respond to flipper_id alias_method :flipper_id, :id end user1 = User.new(1) user2 = User.new(2) puts "Stats for user1: #{stats.enabled?(user1)}" puts "Stats for user2: #{stats.enabled?(user2)}" puts "\nEnabling stats for user1...\n\n" stats.enable(user1) puts "Stats for user1: #{stats.enabled?(user1)}" puts "Stats for user2: #{stats.enabled?(user2)}" flippercloud-flipper-532c39a/examples/instrumentation.rb000066400000000000000000000017551440444367400236240ustar00rootroot00000000000000require 'bundler/setup' require 'securerandom' require 'active_support/isolated_execution_state' require 'active_support/notifications' class FlipperSubscriber def call(*args) event = ActiveSupport::Notifications::Event.new(*args) puts event.inspect end ActiveSupport::Notifications.subscribe(/flipper/, new) end require 'flipper' require 'flipper/adapters/instrumented' # pick an adapter adapter = Flipper::Adapters::Memory.new # instrument it if you want, if not you still get the feature instrumentation instrumented = Flipper::Adapters::Instrumented.new(adapter, :instrumenter => ActiveSupport::Notifications) # get a handy dsl instance flipper = Flipper.new(instrumented, :instrumenter => ActiveSupport::Notifications) # grab a feature search = flipper[:search] perform = lambda do # check if that feature is enabled if search.enabled? puts 'Search away!' else puts 'No search for you!' end end perform.call puts 'Enabling Search...' search.enable perform.call flippercloud-flipper-532c39a/examples/instrumentation_last_accessed_at.rb000066400000000000000000000016141440444367400271570ustar00rootroot00000000000000# Quick example of how to keep track of when a feature was last checked. require 'bundler/setup' require 'securerandom' require 'active_support/isolated_execution_state' require 'active_support/notifications' require 'flipper' class FlipperSubscriber def self.stats @stats ||= {} end def call(name, start, finish, id, payload) if payload[:operation] == :enabled? feature_name = payload.fetch(:feature_name) self.class.stats[feature_name] = Time.now end end ActiveSupport::Notifications.subscribe(/feature_operation.flipper/, new) end Flipper.configure do |config| config.default { Flipper.new(config.adapter, instrumenter: ActiveSupport::Notifications) } end Flipper.enabled?(:search) Flipper.enabled?(:booyeah) Flipper.enabled?(:hooray) sleep 1 Flipper.enabled?(:booyeah) Flipper.enabled?(:hooray) sleep 1 Flipper.enabled?(:hooray) pp FlipperSubscriber.stats flippercloud-flipper-532c39a/examples/memoizing.rb000066400000000000000000000017061440444367400223530ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'flipper/adapters/operation_logger' require 'flipper/instrumentation/log_subscriber' Flipper.configure do |config| config.adapter do # pick an adapter, this uses memory, any will do Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) end end Flipper.enable(:foo) Flipper.enable(:bar) Flipper.disable(:baz) Flipper.disable(:wick) # reset the operation logging adapter to empty for clarity Flipper.adapter.reset # Turn on memoization (the memoizing middleware does this per request). Flipper.memoize = true # Preload all the features. Flipper.preload_all # Do as many feature checks as your heart desires. %w[foo bar baz wick].each do |name| Flipper.enabled?(name) end # See that only one operation exists, a get_all (which is the preload_all). pp Flipper.adapter.operations # [#] flippercloud-flipper-532c39a/examples/mongo/000077500000000000000000000000001440444367400211435ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/mongo/basic.rb000066400000000000000000000005451440444367400225550ustar00rootroot00000000000000require 'bundler/setup' require 'logger' ENV["FLIPPER_MONGO_URL"] ||= "mongodb://127.0.0.1:#{ENV["MONGODB_PORT"] || 27017}" require 'flipper/adapters/mongo' Flipper[:stats].enable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end Flipper[:stats].disable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end flippercloud-flipper-532c39a/examples/mongo/internals.rb000066400000000000000000000026231440444367400234720ustar00rootroot00000000000000require 'bundler/setup' require 'pp' require 'logger' ENV["FLIPPER_MONGO_URL"] ||= "mongodb://127.0.0.1:#{ENV["MONGODB_PORT"] || 27017}" require 'flipper/adapters/mongo' # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) Flipper[:stats].enable Flipper[:stats].enable_group :admins Flipper[:stats].enable_group :early_access Flipper[:stats].enable_actor User.new('25') Flipper[:stats].enable_actor User.new('90') Flipper[:stats].enable_actor User.new('180') Flipper[:stats].enable_percentage_of_time 15 Flipper[:stats].enable_percentage_of_actors 45 Flipper[:search].enable puts 'all docs in collection' pp Flipper.adapter.adapter.collection.find.to_a # all docs in collection # [{"_id"=>"stats", # "actors"=>["25", "90", "180"], # "boolean"=>"true", # "groups"=>["admins", "early_access"], # "percentage_of_actors"=>"45", # "percentage_of_time"=>"15"}, # {"_id"=>"flipper_features", "features"=>["stats", "search"]}, # {"_id"=>"search", "boolean"=>"true"}] puts puts 'flipper get of feature' pp Flipper.adapter.get(Flipper[:stats]) # flipper get of feature # {:boolean=>"true", # :groups=>#, # :actors=>#, # :percentage_of_actors=>"45", # :percentage_of_time=>"15"} flippercloud-flipper-532c39a/examples/percentage_of_actors.rb000066400000000000000000000013771440444367400245350ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' stats = Flipper[:stats] # Some class that represents what will be trying to do something class User < Struct.new(:id) include Flipper::Identifier end total = 100_000 # create array of fake users users = (1..total).map { |n| User.new(n) } perform_test = lambda { |number| Flipper.enable_percentage_of_actors :stats, number enabled = users.map { |user| Flipper.enabled?(:stats, user) ? true : nil }.compact actual = (enabled.size / total.to_f * 100).round(3) puts "percentage: #{actual.to_s.rjust(6, ' ')} vs #{number.to_s.rjust(3, ' ')}" } puts "percentage: Actual vs Hoped For" [0.001, 0.01, 0.1, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number| perform_test.call number end flippercloud-flipper-532c39a/examples/percentage_of_actors_enabled_check.rb000066400000000000000000000012041440444367400273310ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' # Some class that represents what will be trying to do something class User attr_reader :id def initialize(id) @id = id end # Must respond to flipper_id alias_method :flipper_id, :id end # checking a bunch total = 20_000 enabled = [] percentage_enabled = 10 feature = Flipper[:data_migration] feature.enable_percentage_of_actors 10 (1..total).each do |id| user = User.new(id) if feature.enabled? user enabled << user end end p actual: enabled.size, expected: total * (percentage_enabled * 0.01) # checking one user = User.new(1) p user_1_enabled: feature.enabled?(user) flippercloud-flipper-532c39a/examples/percentage_of_actors_group.rb000066400000000000000000000021301440444367400257350ustar00rootroot00000000000000# This example shows how to setup a group that enables a feature for a # percentage of actors. It could be combined with other logic to enable a # feature for actors in a particular location or on a particular plan, but only # for a percentage of them. The percentage is a constant, but could easily be # plucked from memcached, redis, mysql or whatever. require 'bundler/setup' require 'flipper' # Some class that represents what will be trying to do something class User < Struct.new(:id) include Flipper::Identifier end PERCENTAGE = 50 Flipper.register(:experimental) do |actor| if actor.respond_to?(:flipper_id) Zlib.crc32(actor.flipper_id.to_s) % 100 < PERCENTAGE else false end end # enable the experimental group Flipper.enable_group :stats, :experimental # create a bunch of fake users and see how many are enabled total = 10_000 users = (1..total).map { |n| User.new(n) } enabled = users.map { |user| Flipper.enabled?(:stats, user) ? true : nil }.compact # show the results actual = (enabled.size / total.to_f * 100).round puts "percentage: #{actual} vs hoped for: #{PERCENTAGE}" flippercloud-flipper-532c39a/examples/percentage_of_time.rb000066400000000000000000000011561440444367400241730ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' logging = Flipper[:logging] perform_test = lambda do |number| logging.enable_percentage_of_time number total = 100_000 enabled = [] disabled = [] enabled = (1..total).map { |n| logging.enabled? ? true : nil }.compact actual = (enabled.size / total.to_f * 100).round(3) # puts "#{enabled.size} / #{total}" puts "percentage: #{actual.to_s.rjust(6, ' ')} vs #{number.to_s.rjust(3, ' ')}" end puts "percentage: Actual vs Hoped For" [0.001, 0.01, 0.1, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number| perform_test.call number end flippercloud-flipper-532c39a/examples/redis/000077500000000000000000000000001440444367400211325ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/redis/basic.rb000066400000000000000000000004221440444367400225360ustar00rootroot00000000000000require 'bundler/setup' require 'logger' require 'flipper/adapters/redis' Flipper[:stats].enable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end Flipper[:stats].disable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end flippercloud-flipper-532c39a/examples/redis/internals.rb000066400000000000000000000030001440444367400234470ustar00rootroot00000000000000require 'bundler/setup' require 'pp' require 'logger' require 'flipper/adapters/redis' client = Redis.new # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) Flipper[:stats].enable Flipper[:stats].enable_group :admins Flipper[:stats].enable_group :early_access Flipper[:stats].enable_actor User.new('25') Flipper[:stats].enable_actor User.new('90') Flipper[:stats].enable_actor User.new('180') Flipper[:stats].enable_percentage_of_time 15 Flipper[:stats].enable_percentage_of_actors 45 Flipper[:search].enable print 'all keys: ' pp client.keys # all keys: ["stats", "flipper_features", "search"] puts print "known flipper features: " pp client.smembers("flipper_features") # known flipper features: ["stats", "search"] puts puts 'stats keys' pp client.hgetall('stats') # stats keys # {"boolean"=>"true", # "groups/admins"=>"1", # "actors/25"=>"1", # "percentage_of_time"=>"15", # "percentage_of_actors"=>"45", # "groups/early_access"=>"1", # "actors/90"=>"1", # "actors/180"=>"1"} puts puts 'search keys' pp client.hgetall('search') # search keys # {"boolean"=>"true"} puts puts 'flipper get of feature' pp Flipper.adapter.get(Flipper[:stats]) # flipper get of feature # {:boolean=>"true", # :groups=>#, # :actors=>#, # :percentage_of_actors=>"45", # :percentage_of_time=>"15"} flippercloud-flipper-532c39a/examples/redis/namespaced.rb000066400000000000000000000021171440444367400235600ustar00rootroot00000000000000require 'bundler/setup' require 'redis-namespace' require 'flipper/adapters/redis' options = {url: 'redis://127.0.0.1:6379'} if ENV['REDIS_URL'] options[:url] = ENV['REDIS_URL'] end client = Redis.new(options) namespaced_client = Redis::Namespace.new(:flipper_namespace, redis: client) adapter = Flipper::Adapters::Redis.new(namespaced_client) flipper = Flipper.new(adapter) # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) flipper[:stats].enable flipper[:stats].enable_group :admins flipper[:stats].enable_group :early_access flipper[:stats].enable_actor User.new('25') flipper[:stats].enable_actor User.new('90') flipper[:stats].enable_actor User.new('180') flipper[:stats].enable_percentage_of_time 15 flipper[:stats].enable_percentage_of_actors 45 flipper[:search].enable print 'all keys: ' pp client.keys # all keys: ["stats", "flipper_features", "search"] puts puts 'notice how all the keys are namespaced' flippercloud-flipper-532c39a/examples/rollout/000077500000000000000000000000001440444367400215245ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/rollout/basic.rb000066400000000000000000000006741440444367400231410ustar00rootroot00000000000000require 'bundler/setup' require 'redis' require 'rollout' require 'flipper' require 'flipper/adapters/rollout' redis = Redis.new rollout = Rollout.new(redis) rollout.activate(:stats) adapter = Flipper::Adapters::Rollout.new(rollout) flipper = Flipper.new(adapter) if flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end rollout.deactivate(:stats) if flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end flippercloud-flipper-532c39a/examples/rollout/import.rb000066400000000000000000000017251440444367400233700ustar00rootroot00000000000000require 'bundler/setup' require 'redis' require 'rollout' require 'flipper' require 'flipper/adapters/redis' require 'flipper/adapters/rollout' # setup redis, rollout and rollout flipper redis = Redis.new rollout = Rollout.new(redis) rollout_adapter = Flipper::Adapters::Rollout.new(rollout) rollout_flipper = Flipper.new(rollout_adapter) # setup flipper default instance Flipper.configure do |config| config.adapter { Flipper::Adapters::Redis.new(redis) } end # flush redis so we have clean state for script redis.flushdb # activate some rollout stuff to show that importing works rollout.activate(:stats) rollout.activate_user(:search, Struct.new(:id).new(1)) rollout.activate_group(:admin, :admins) # import rollout into redis flipper Flipper.import(rollout_flipper) # demonstrate that the rollout enablements made it into flipper p Flipper[:stats].boolean_value # true p Flipper[:search].actors_value # # p Flipper[:admin].groups_value # # flippercloud-flipper-532c39a/examples/sequel/000077500000000000000000000000001440444367400213225ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/sequel/basic.rb000066400000000000000000000007331440444367400227330ustar00rootroot00000000000000require 'bundler/setup' require 'sequel' Sequel::Model.db = Sequel.sqlite(':memory:') Sequel.extension :migration, :core_extensions require 'generators/flipper/templates/sequel_migration' CreateFlipperTablesSequel.new(Sequel::Model.db).up require 'flipper/adapters/sequel' Flipper[:stats].enable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end Flipper[:stats].disable if Flipper[:stats].enabled? puts "Enabled!" else puts "Disabled!" end flippercloud-flipper-532c39a/examples/sequel/internals.rb000066400000000000000000000062371440444367400236560ustar00rootroot00000000000000require 'bundler/setup' require 'sequel' Sequel::Model.db = Sequel.sqlite(':memory:') Sequel.extension :migration, :core_extensions require 'generators/flipper/templates/sequel_migration' CreateFlipperTablesSequel.new(Sequel::Model.db).up require 'flipper/adapters/sequel' # Register a few groups. Flipper.register(:admins) { |thing| thing.admin? } Flipper.register(:early_access) { |thing| thing.early_access? } # Create a user class that has flipper_id instance method. User = Struct.new(:flipper_id) Flipper[:stats].enable Flipper[:stats].enable_group :admins Flipper[:stats].enable_group :early_access Flipper[:stats].enable_actor User.new('25') Flipper[:stats].enable_actor User.new('90') Flipper[:stats].enable_actor User.new('180') Flipper[:stats].enable_percentage_of_time 15 Flipper[:stats].enable_percentage_of_actors 45 Flipper[:search].enable puts 'all rows in features table' pp Flipper::Adapters::Sequel::Feature.all #[#"stats", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"search", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>] puts puts 'all rows in gates table' pp Flipper::Adapters::Sequel::Gate.all # [#"stats", :key=>"boolean", :value=>"true", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"groups", :value=>"admins", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"groups", :value=>"early_access", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"actors", :value=>"25", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"actors", :value=>"90", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"actors", :value=>"180", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"percentage_of_time", :value=>"15", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"stats", :key=>"percentage_of_actors", :value=>"45", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>, # #"search", :key=>"boolean", :value=>"true", :created_at=>2016-11-19 13:57:48 -0500, :updated_at=>2016-11-19 13:57:48 -0500}>] puts puts 'flipper get of feature' pp Flipper.adapter.get(Flipper[:stats]) # {:boolean=>"true", # :groups=>#, # :actors=>#, # :percentage_of_actors=>"45", # :percentage_of_time=>"15"} flippercloud-flipper-532c39a/examples/ui/000077500000000000000000000000001440444367400204415ustar00rootroot00000000000000flippercloud-flipper-532c39a/examples/ui/authorization.ru000066400000000000000000000023041440444367400237100ustar00rootroot00000000000000# # Usage: # bundle exec rackup examples/ui/authorization.ru -p 9999 # bundle exec shotgun examples/ui/authorization.ru -p 9999 # http://localhost:9999/ # require 'bundler/setup' require "flipper/ui" require "flipper/adapters/pstore" Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? } # Example middleware to allow reading the Flipper UI but nothing else. class FlipperReadOnlyMiddleware def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) if request.get? @app.call(env) else [401, {}, ["You can only look"]] end end end # You can uncomment these to get some default data: # Flipper.enable(:search_performance_another_long_thing) # Flipper.disable(:gauges_tracking) # Flipper.disable(:unused) # Flipper.enable_actor(:suits, Flipper::Actor.new('1')) # Flipper.enable_actor(:suits, Flipper::Actor.new('6')) # Flipper.enable_group(:secrets, :admins) # Flipper.enable_percentage_of_time(:logging, 5) # Flipper.enable_percentage_of_actors(:new_cache, 15) # Flipper.add("a/b") run Flipper::UI.app { |builder| builder.use Rack::Session::Cookie, secret: "_super_secret" builder.use FlipperReadOnlyMiddleware } flippercloud-flipper-532c39a/examples/ui/basic.ru000066400000000000000000000037201440444367400220740ustar00rootroot00000000000000# # Usage: # # if you want it to not reload and be really fast # bin/rackup examples/ui/basic.ru -p 9999 # # # if you want reloading # bin/shotgun examples/ui/basic.ru -p 9999 # # http://localhost:9999/ # require 'bundler/setup' require "flipper/ui" require "flipper/adapters/pstore" Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? } Flipper.register(:early_access) { |actor| actor.respond_to?(:early?) && actor.early? } Flipper::UI.configure do |config| # config.banner_text = 'Production Environment' # config.banner_class = 'danger' config.feature_creation_enabled = true config.feature_removal_enabled = true config.cloud_recommendation = true config.confirm_fully_enable = true # config.show_feature_description_in_list = true config.descriptions_source = lambda do |_keys| { "search_performance_another_long_thing" => "Just to test feature name length.", "gauges_tracking" => "Should we track page views with gaug.es.", "unused" => "Not used.", "suits" => "Are suits necessary in business?", "secrets" => "Secrets are lies.", "logging" => "Log all the things.", "new_cache" => "Like the old cache but newer.", "a/b" => "Why would someone use a slash? I don't know but someone did. Let's make this really long so they regret using slashes. Please don't use slashes.", } end end # You can uncomment these to get some default data: # Flipper.enable(:search_performance_another_long_thing) # Flipper.disable(:gauges_tracking) # Flipper.disable(:unused) # Flipper.enable_actor(:suits, Flipper::Actor.new('1')) # Flipper.enable_actor(:suits, Flipper::Actor.new('6')) # Flipper.enable_group(:secrets, :admins) # Flipper.enable_group(:secrets, :early_access) # Flipper.enable_percentage_of_time(:logging, 5) # Flipper.enable_percentage_of_actors(:new_cache, 15) # Flipper.add("a/b") run Flipper::UI.app { |builder| builder.use Rack::Session::Cookie, secret: "_super_secret" } flippercloud-flipper-532c39a/examples/ui/read_only.ru000066400000000000000000000022721440444367400227700ustar00rootroot00000000000000# # Usage: # # if you want it to not reload and be really fast # bin/rackup examples/ui/read_only.ru -p 9999 # # # if you want reloading # bin/shotgun examples/ui/read_only.ru -p 9999 # # http://localhost:9999/ # require 'bundler/setup' require "flipper/ui" require "flipper/adapters/pstore" Flipper.register(:admins) { |actor| actor.respond_to?(:admin?) && actor.admin? } Flipper.register(:early_access) { |actor| actor.respond_to?(:early?) && actor.early? } Flipper::UI.configure do |config| config.banner_text = 'Read only mode.' config.banner_class = 'danger' config.read_only = true end # You can uncomment these to get some default data: # Flipper.enable(:search_performance_another_long_thing) # Flipper.disable(:gauges_tracking) # Flipper.disable(:unused) # Flipper.enable_actor(:suits, Flipper::Actor.new('1')) # Flipper.enable_actor(:suits, Flipper::Actor.new('6')) # Flipper.enable_group(:secrets, :admins) # Flipper.enable_group(:secrets, :early_access) # Flipper.enable_percentage_of_time(:logging, 5) # Flipper.enable_percentage_of_actors(:new_cache, 15) # Flipper.add("a/b") run Flipper::UI.app { |builder| builder.use Rack::Session::Cookie, secret: "_super_secret" } flippercloud-flipper-532c39a/flipper-active_record.gemspec000066400000000000000000000020731440444367400240250ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_active_record_files = lambda do |file| file =~ /active_record/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'ActiveRecord adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' extra_files = [ 'lib/generators/flipper/templates/migration.erb', 'lib/flipper/version.rb', ] gem.files = `git ls-files`.split("\n").select(&flipper_active_record_files) + extra_files gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_active_record_files) gem.name = 'flipper-active_record' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'activerecord', '>= 4.2', '< 8' end flippercloud-flipper-532c39a/flipper-active_support_cache_store.gemspec000066400000000000000000000020601440444367400266160ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_active_support_cache_store_files = lambda do |file| file =~ /active_support_cache_store/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'ActiveSupport::Cache store adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_active_support_cache_store_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_active_support_cache_store_files) gem.name = 'flipper-active_support_cache_store' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'activesupport', '>= 4.2', '< 8' end flippercloud-flipper-532c39a/flipper-api.gemspec000066400000000000000000000016541440444367400217710ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_api_files = lambda do |file| file =~ %r{(flipper)[\/-]api} end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'API for the Flipper gem' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_api_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_api_files) gem.name = 'flipper-api' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'rack', '>= 1.4', '< 3' gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" end flippercloud-flipper-532c39a/flipper-cloud.gemspec000066400000000000000000000017231440444367400223230ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_cloud_files = lambda do |file| file =~ /cloud/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'FlipperCloud.io adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' extra_files = [ 'lib/flipper/version.rb', ] gem.files = `git ls-files`.split("\n").select(&flipper_cloud_files) + extra_files gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_cloud_files) gem.name = 'flipper-cloud' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency "brow", "~> 0.4.1" end flippercloud-flipper-532c39a/flipper-dalli.gemspec000066400000000000000000000016521440444367400223030ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_dalli_files = lambda do |file| file =~ /dalli/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Dalli adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_dalli_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_dalli_files) gem.name = 'flipper-dalli' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'dalli', '>= 2.0', '< 4' end flippercloud-flipper-532c39a/flipper-moneta.gemspec000066400000000000000000000015171440444367400225010ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) flipper_moneta_files = lambda do |file| file =~ /moneta/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Moneta adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_moneta_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_moneta_files) gem.name = 'flipper-moneta' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'moneta', '>= 0.7.0', '< 1.2' end flippercloud-flipper-532c39a/flipper-mongo.gemspec000066400000000000000000000016431440444367400223350ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_mongo_files = lambda do |file| file =~ /mongo/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Mongo adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_mongo_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_mongo_files) gem.name = 'flipper-mongo' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'mongo', '~> 2.0' end flippercloud-flipper-532c39a/flipper-redis.gemspec000066400000000000000000000016521440444367400223240ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_redis_files = lambda do |file| file =~ /redis/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Redis adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_redis_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_redis_files) gem.name = 'flipper-redis' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'redis', '>= 3.0', '< 6' end flippercloud-flipper-532c39a/flipper-rollout.gemspec000066400000000000000000000017371440444367400227220ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_rollout_files = lambda do |file| file =~ /rollout/ end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Rollout adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_rollout_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_rollout_files) gem.name = 'flipper-rollout' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'redis', '>= 2.2', '< 5' gem.add_dependency 'rollout', "~> 2.0" end flippercloud-flipper-532c39a/flipper-sequel.gemspec000066400000000000000000000017161440444367400225150ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_sequel_files = ->(file) { file =~ /sequel/ } Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Sequel adapter for Flipper' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' extra_files = [ 'lib/flipper/version.rb', ] gem.files = `git ls-files`.split("\n").select(&flipper_sequel_files) + extra_files gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_sequel_files) gem.name = 'flipper-sequel' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'sequel', '>= 4.0.0', '< 6' end flippercloud-flipper-532c39a/flipper-ui.gemspec000066400000000000000000000021171440444367400216300ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) flipper_ui_files = lambda do |file| file =~ %r{(docs|examples|flipper)[\/-]ui} end Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'UI for the Flipper gem' gem.license = 'MIT' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.files = `git ls-files`.split("\n").select(&flipper_ui_files) + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_ui_files) gem.name = 'flipper-ui' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'rack', '>= 1.4', '< 3' gem.add_dependency 'rack-protection', '>= 1.5.3', '<= 4.0.0' gem.add_dependency 'flipper', "~> #{Flipper::VERSION}" gem.add_dependency 'erubi', '>= 1.0.0', '< 2.0.0' gem.add_dependency 'sanitize', '< 7' end flippercloud-flipper-532c39a/flipper.gemspec000066400000000000000000000022661440444367400212220ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path('../lib/flipper/version', __FILE__) require File.expand_path('../lib/flipper/metadata', __FILE__) plugin_files = [] plugin_test_files = [] Dir['flipper-*.gemspec'].map do |gemspec| spec = eval(File.read(gemspec)) plugin_files << spec.files plugin_test_files << spec.files end ignored_files = plugin_files ignored_files << Dir['script/*'] ignored_files << '.gitignore' ignored_files << 'Guardfile' ignored_files.flatten!.uniq! ignored_test_files = plugin_test_files ignored_test_files.flatten!.uniq! Gem::Specification.new do |gem| gem.authors = ['John Nunemaker'] gem.email = ['nunemaker@gmail.com'] gem.summary = 'Feature flipper for ANYTHING' gem.homepage = 'https://github.com/jnunemaker/flipper' gem.license = 'MIT' gem.files = `git ls-files`.split("\n") - ignored_files + ['lib/flipper/version.rb'] gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - ignored_test_files gem.name = 'flipper' gem.require_paths = ['lib'] gem.version = Flipper::VERSION gem.metadata = Flipper::METADATA gem.add_dependency 'concurrent-ruby', '< 2' end flippercloud-flipper-532c39a/lib/000077500000000000000000000000001440444367400167545ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper-active_record.rb000066400000000000000000000000511440444367400235450ustar00rootroot00000000000000require 'flipper/adapters/active_record' flippercloud-flipper-532c39a/lib/flipper-active_support_cache_store.rb000066400000000000000000000000661440444367400263500ustar00rootroot00000000000000require 'flipper/adapters/active_support_cache_store' flippercloud-flipper-532c39a/lib/flipper-api.rb000066400000000000000000000000261440444367400215070ustar00rootroot00000000000000require 'flipper/api' flippercloud-flipper-532c39a/lib/flipper-cloud.rb000066400000000000000000000000301440444367400220370ustar00rootroot00000000000000require "flipper/cloud" flippercloud-flipper-532c39a/lib/flipper-dalli.rb000066400000000000000000000000411440444367400220200ustar00rootroot00000000000000require 'flipper/adapters/dalli' flippercloud-flipper-532c39a/lib/flipper-mongo.rb000066400000000000000000000000411440444367400220520ustar00rootroot00000000000000require 'flipper/adapters/mongo' flippercloud-flipper-532c39a/lib/flipper-redis.rb000066400000000000000000000000411440444367400220410ustar00rootroot00000000000000require 'flipper/adapters/redis' flippercloud-flipper-532c39a/lib/flipper-sequel.rb000066400000000000000000000000421440444367400222320ustar00rootroot00000000000000require 'flipper/adapters/sequel' flippercloud-flipper-532c39a/lib/flipper-ui.rb000066400000000000000000000000251440444367400213520ustar00rootroot00000000000000require 'flipper/ui' flippercloud-flipper-532c39a/lib/flipper.rb000066400000000000000000000116621440444367400207500ustar00rootroot00000000000000require "forwardable" module Flipper extend self extend Forwardable # Private: The namespace for all instrumented events. InstrumentationNamespace = :flipper # Public: Start here. Given an adapter returns a handy DSL to all the flipper # goodness. To see supported options, check out dsl.rb. def new(adapter, options = {}) DSL.new(adapter, options) end # Public: Configure flipper. # # Flipper.configure do |config| # config.adapter { ... } # end # # Yields Flipper::Configuration instance. def configure yield configuration if block_given? end # Public: Returns Flipper::Configuration instance. def configuration @configuration ||= Configuration.new end # Public: Sets Flipper::Configuration instance. def configuration=(configuration) # need to reset flipper instance if configuration changes self.instance = nil @configuration = configuration end # Public: Default per thread flipper instance if configured. You should not # need to use this directly as most of the Flipper::DSL methods are delegated # from Flipper module itself. Instead of doing Flipper.instance.enabled?(:search), # you can use Flipper.enabled?(:search) for the same result. # # Returns Flipper::DSL instance. def instance Thread.current[:flipper_instance] ||= configuration.default end # Public: Set the flipper instance. It is most common to use the # Configuration#default to set this instance, but for things like the test # environment, this writer is actually useful. def instance=(flipper) Thread.current[:flipper_instance] = flipper end # Public: All the methods delegated to instance. These should match the # interface of Flipper::DSL. def_delegators :instance, :enabled?, :enable, :disable, :bool, :boolean, :enable_actor, :disable_actor, :actor, :enable_group, :disable_group, :enable_percentage_of_actors, :disable_percentage_of_actors, :actors, :percentage_of_actors, :enable_percentage_of_time, :disable_percentage_of_time, :time, :percentage_of_time, :features, :feature, :[], :preload, :preload_all, :adapter, :add, :exist?, :remove, :import, :memoize=, :memoizing?, :sync, :sync_secret # For Flipper::Cloud. Will error for OSS Flipper. # Public: Use this to register a group by name. # # name - The Symbol name of the group. # block - The block that should be used to determine if the group matches a # given thing. # # Examples # # Flipper.register(:admins) { |thing| # thing.respond_to?(:admin?) && thing.admin? # } # # Returns a Flipper::Group. # Raises Flipper::DuplicateGroup if the group is already registered. def register(name, &block) group = Types::Group.new(name, &block) groups_registry.add(group.name, group) group rescue Registry::DuplicateKey raise DuplicateGroup, "Group #{name.inspect} has already been registered" end # Public: Returns a Set of registered Types::Group instances. def groups groups_registry.values.to_set end # Public: Returns a Set of symbols where each symbol is a registered # group name. If you just want the names, this is more efficient than doing # `Flipper.groups.map(&:name)`. def group_names groups_registry.keys.to_set end # Public: Clears the group registry. # # Returns nothing. def unregister_groups groups_registry.clear end # Public: Check if a group exists # # Returns boolean def group_exists?(name) groups_registry.key?(name) end # Public: Fetches a group by name. # # name - The Symbol name of the group. # # Examples # # Flipper.group(:admins) # # Returns Flipper::Group. def group(name) groups_registry.get(name) || Types::Group.new(name) end # Internal: Registry of all groups_registry. def groups_registry @groups_registry ||= Registry.new end # Internal: Change the groups_registry registry. def groups_registry=(registry) @groups_registry = registry end end require 'flipper/actor' require 'flipper/adapter' require 'flipper/adapters/memoizable' require 'flipper/adapters/memory' require 'flipper/adapters/instrumented' require 'flipper/configuration' require 'flipper/dsl' require 'flipper/errors' require 'flipper/feature' require 'flipper/gate' require 'flipper/instrumenters/memory' require 'flipper/instrumenters/noop' require 'flipper/identifier' require 'flipper/middleware/memoizer' require 'flipper/middleware/setup_env' require 'flipper/poller' require 'flipper/registry' require 'flipper/type' require 'flipper/types/actor' require 'flipper/types/boolean' require 'flipper/types/group' require 'flipper/types/percentage' require 'flipper/types/percentage_of_actors' require 'flipper/types/percentage_of_time' require 'flipper/typecast' require "flipper/railtie" if defined?(Rails::Railtie) flippercloud-flipper-532c39a/lib/flipper/000077500000000000000000000000001440444367400204155ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/actor.rb000066400000000000000000000006351440444367400220560ustar00rootroot00000000000000# Simple class for turning a flipper_id into an actor that can be based # to Flipper::Feature#enabled?. module Flipper class Actor attr_reader :flipper_id def initialize(flipper_id) @flipper_id = flipper_id end def eql?(other) self.class.eql?(other.class) && @flipper_id == other.flipper_id end alias_method :==, :eql? def hash flipper_id.hash end end end flippercloud-flipper-532c39a/lib/flipper/adapter.rb000066400000000000000000000030641440444367400223650ustar00rootroot00000000000000require "set" require "flipper/feature" require "flipper/adapters/sync/synchronizer" module Flipper # Adding a module include so we have some hooks for stuff down the road module Adapter def self.included(base) base.extend(ClassMethods) end module ClassMethods # Public: Default config for a feature's gate values. def default_config { boolean: nil, groups: Set.new, actors: Set.new, percentage_of_actors: nil, percentage_of_time: nil, } end end # Public: Get all features and gate values in one call. Defaults to one call # to features and another to get_multi. Feel free to override per adapter to # make this more efficient. def get_all instances = features.map { |key| Flipper::Feature.new(key, self) } get_multi(instances) end # Public: Get multiple features in one call. Defaults to one get per # feature. Feel free to override per adapter to make this more efficient and # reduce network calls. def get_multi(features) result = {} features.each do |feature| result[feature.key] = get(feature) end result end # Public: Ensure that adapter is in sync with source adapter provided. # # Returns result of Synchronizer#call. def import(source_adapter) Adapters::Sync::Synchronizer.new(self, source_adapter, raise: true).call end # Public: Default config for a feature's gate values. def default_config self.class.default_config end end end flippercloud-flipper-532c39a/lib/flipper/adapters/000077500000000000000000000000001440444367400222205ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/adapters/active_record.rb000066400000000000000000000205401440444367400253570ustar00rootroot00000000000000require 'set' require 'flipper' require 'active_record' module Flipper module Adapters class ActiveRecord include ::Flipper::Adapter # Abstract base class for internal models class Model < ::ActiveRecord::Base self.abstract_class = true end # Private: Do not use outside of this adapter. class Feature < Model self.table_name = [ Model.table_name_prefix, "flipper_features", Model.table_name_suffix, ].join end # Private: Do not use outside of this adapter. class Gate < Model self.table_name = [ Model.table_name_prefix, "flipper_gates", Model.table_name_suffix, ].join end # Public: The name of the adapter. attr_reader :name # Public: Initialize a new ActiveRecord adapter instance. # # name - The Symbol name for this adapter. Optional (default :active_record) # feature_class - The AR class responsible for the features table. # gate_class - The AR class responsible for the gates table. # # Allowing the overriding of name is so you can differentiate multiple # instances of this adapter from each other, if, for some reason, that is # a thing you do. # # Allowing the overriding of the default feature/gate classes means you # can roll your own tables and what not, if you so desire. def initialize(options = {}) @name = options.fetch(:name, :active_record) @feature_class = options.fetch(:feature_class) { Feature } @gate_class = options.fetch(:gate_class) { Gate } end # Public: The set of known features. def features with_connection(@feature_class) { @feature_class.all.map(&:key).to_set } end # Public: Adds a feature to the set of known features. def add(feature) with_connection(@feature_class) do # race condition, but add is only used by enable/disable which happen # super rarely, so it shouldn't matter in practice @feature_class.transaction do unless @feature_class.where(key: feature.key).first begin @feature_class.create! { |f| f.key = feature.key } rescue ::ActiveRecord::RecordNotUnique end end end end true end # Public: Removes a feature from the set of known features. def remove(feature) with_connection(@feature_class) do @feature_class.transaction do @feature_class.where(key: feature.key).destroy_all clear(feature) end end true end # Public: Clears the gate values for a feature. def clear(feature) with_connection(@gate_class) { @gate_class.where(feature_key: feature.key).destroy_all } true end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) gates = with_connection(@gate_class) { @gate_class.where(feature_key: feature.key).pluck(:key, :value) } result_for_gates(feature, gates) end def get_multi(features) with_connection(@gate_class) do gates = @gate_class.where(feature_key: features.map(&:key)).pluck(:feature_key, :key, :value) grouped_gates = gates.inject({}) do |hash, (feature_key, key, value)| hash[feature_key] ||= [] hash[feature_key] << [key, value] hash end result = {} features.each do |feature| result[feature.key] = result_for_gates(feature, grouped_gates[feature.key]) end result end end def get_all with_connection(@feature_class) do # query the gates from the db in a single query features = ::Arel::Table.new(@feature_class.table_name.to_sym) gates = ::Arel::Table.new(@gate_class.table_name.to_sym) rows_query = features.join(gates, ::Arel::Nodes::OuterJoin) .on(features[:key].eq(gates[:feature_key])) .project(features[:key].as('feature_key'), gates[:key], gates[:value]) gates = @feature_class.connection.select_rows(rows_query) # group the gates by feature key grouped_gates = gates.inject({}) do |hash, (feature_key, key, value)| hash[feature_key] ||= [] hash[feature_key] << [key, value] hash end # build up the result hash result = Hash.new { |hash, key| hash[key] = default_config } features = grouped_gates.keys.map { |key| Flipper::Feature.new(key, self) } features.each do |feature| result[feature.key] = result_for_gates(feature, grouped_gates[feature.key]) end result end end # Public: Enables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being enabled for the gate. # # Returns true. def enable(feature, gate, thing) case gate.data_type when :boolean set(feature, gate, thing, clear: true) when :integer set(feature, gate, thing) when :set enable_multi(feature, gate, thing) else unsupported_data_type gate.data_type end true end # Public: Disables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def disable(feature, gate, thing) case gate.data_type when :boolean clear(feature) when :integer set(feature, gate, thing) when :set with_connection(@gate_class) do @gate_class.where(feature_key: feature.key, key: gate.key, value: thing.value).destroy_all end else unsupported_data_type gate.data_type end true end # Private def unsupported_data_type(data_type) raise "#{data_type} is not supported by this adapter" end private def set(feature, gate, thing, options = {}) clear_feature = options.fetch(:clear, false) with_connection(@gate_class) do @gate_class.transaction do clear(feature) if clear_feature @gate_class.where(feature_key: feature.key, key: gate.key).destroy_all begin @gate_class.create! do |g| g.feature_key = feature.key g.key = gate.key g.value = thing.value.to_s end rescue ::ActiveRecord::RecordNotUnique # assume this happened concurrently with the same thing and its fine # see https://github.com/jnunemaker/flipper/issues/544 end end end nil end def enable_multi(feature, gate, thing) with_connection(@gate_class) do @gate_class.create! do |g| g.feature_key = feature.key g.key = gate.key g.value = thing.value.to_s end end nil rescue ::ActiveRecord::RecordNotUnique # already added so no need move on with life end def result_for_gates(feature, gates) result = {} gates ||= [] feature.gates.each do |gate| result[gate.key] = case gate.data_type when :boolean if row = gates.detect { |key, value| !key.nil? && key.to_sym == gate.key } row.last end when :integer if row = gates.detect { |key, value| !key.nil? && key.to_sym == gate.key } row.last end when :set gates.select { |key, value| !key.nil? && key.to_sym == gate.key }.map(&:last).to_set else unsupported_data_type gate.data_type end end result end def with_connection(model = @feature_class, &block) model.connection_pool.with_connection(&block) end end end end Flipper.configure do |config| config.adapter { Flipper::Adapters::ActiveRecord.new } end flippercloud-flipper-532c39a/lib/flipper/adapters/active_support_cache_store.rb000066400000000000000000000100141440444367400301470ustar00rootroot00000000000000require 'flipper' module Flipper module Adapters # Public: Adapter that wraps another adapter with the ability to cache # adapter calls in ActiveSupport::ActiveSupportCacheStore caches. # class ActiveSupportCacheStore include ::Flipper::Adapter Version = 'v1'.freeze Namespace = "flipper/#{Version}".freeze FeaturesKey = "#{Namespace}/features".freeze GetAllKey = "#{Namespace}/get_all".freeze # Private def self.key_for(key) "#{Namespace}/feature/#{key}" end # Internal attr_reader :cache # Public: The name of the adapter. attr_reader :name # Public def initialize(adapter, cache, expires_in: nil, write_through: false) @adapter = adapter @name = :active_support_cache_store @cache = cache @write_options = {} @write_options[:expires_in] = expires_in if expires_in @write_through = write_through end # Public def features read_feature_keys end # Public def add(feature) result = @adapter.add(feature) @cache.delete(FeaturesKey) result end ## Public def remove(feature) result = @adapter.remove(feature) @cache.delete(FeaturesKey) if @write_through @cache.write(key_for(feature.key), default_config, @write_options) else @cache.delete(key_for(feature.key)) end result end ## Public def clear(feature) result = @adapter.clear(feature) @cache.delete(key_for(feature.key)) result end ## Public def get(feature) @cache.fetch(key_for(feature.key), @write_options) do @adapter.get(feature) end end def get_multi(features) read_many_features(features) end def get_all if @cache.write(GetAllKey, Time.now.to_i, @write_options.merge(unless_exist: true)) response = @adapter.get_all response.each do |key, value| @cache.write(key_for(key), value, @write_options) end @cache.write(FeaturesKey, response.keys.to_set, @write_options) response else features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end end ## Public def enable(feature, gate, thing) result = @adapter.enable(feature, gate, thing) if @write_through @cache.write(key_for(feature.key), @adapter.get(feature), @write_options) else @cache.delete(key_for(feature.key)) end result end ## Public def disable(feature, gate, thing) result = @adapter.disable(feature, gate, thing) if @write_through @cache.write(key_for(feature.key), @adapter.get(feature), @write_options) else @cache.delete(key_for(feature.key)) end result end private def key_for(key) self.class.key_for(key) end # Internal: Returns an array of the known feature keys. def read_feature_keys @cache.fetch(FeaturesKey, @write_options) { @adapter.features } end # Internal: Given an array of features, attempts to read through cache in # as few network calls as possible. def read_many_features(features) keys = features.map { |feature| key_for(feature.key) } cache_result = @cache.read_multi(*keys) uncached_features = features.reject { |feature| cache_result[key_for(feature)] } if uncached_features.any? response = @adapter.get_multi(uncached_features) response.each do |key, value| @cache.write(key_for(key), value, @write_options) cache_result[key_for(key)] = value end end result = {} features.each do |feature| result[feature.key] = cache_result[key_for(feature.key)] end result end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/dalli.rb000066400000000000000000000065361440444367400236440ustar00rootroot00000000000000require 'dalli' require 'flipper' module Flipper module Adapters # Public: Adapter that wraps another adapter with the ability to cache # adapter calls in Memcached using the Dalli gem. class Dalli include ::Flipper::Adapter Version = 'v1'.freeze Namespace = "flipper/#{Version}".freeze FeaturesKey = "#{Namespace}/features".freeze GetAllKey = "#{Namespace}/get_all".freeze # Private def self.key_for(key) "#{Namespace}/feature/#{key}" end # Internal attr_reader :cache # Public: The name of the adapter. attr_reader :name # Public: The ttl for all cached data. attr_reader :ttl # Public def initialize(adapter, cache, ttl = 0) @adapter = adapter @name = :dalli @cache = cache @ttl = ttl end # Public def features read_feature_keys end # Public def add(feature) result = @adapter.add(feature) @cache.delete(FeaturesKey) result end # Public def remove(feature) result = @adapter.remove(feature) @cache.delete(FeaturesKey) @cache.delete(key_for(feature.key)) result end # Public def clear(feature) result = @adapter.clear(feature) @cache.delete(key_for(feature.key)) result end # Public def get(feature) @cache.fetch(key_for(feature.key), @ttl) do @adapter.get(feature) end end def get_multi(features) read_many_features(features) end def get_all if @cache.add(GetAllKey, Time.now.to_i, @ttl) response = @adapter.get_all response.each do |key, value| @cache.set(key_for(key), value, @ttl) end @cache.set(FeaturesKey, response.keys.to_set, @ttl) response else features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end end # Public def enable(feature, gate, thing) result = @adapter.enable(feature, gate, thing) @cache.delete(key_for(feature.key)) result end # Public def disable(feature, gate, thing) result = @adapter.disable(feature, gate, thing) @cache.delete(key_for(feature.key)) result end private def key_for(key) self.class.key_for(key) end def read_feature_keys @cache.fetch(FeaturesKey, @ttl) { @adapter.features } end # Internal: Given an array of features, attempts to read through cache in # as few network calls as possible. def read_many_features(features) keys = features.map { |feature| key_for(feature.key) } cache_result = @cache.get_multi(keys) uncached_features = features.reject { |feature| cache_result[key_for(feature.key)] } if uncached_features.any? response = @adapter.get_multi(uncached_features) response.each do |key, value| @cache.set(key_for(key), value, @ttl) cache_result[key_for(key)] = value end end result = {} features.each do |feature| result[feature.key] = cache_result[key_for(feature.key)] end result end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/dual_write.rb000066400000000000000000000026211440444367400247050ustar00rootroot00000000000000module Flipper module Adapters class DualWrite include ::Flipper::Adapter # Public: The name of the adapter. attr_reader :name, :local, :remote # Public: Build a new sync instance. # # local - The local flipper adapter that should serve reads. # remote - The remote flipper adapter that writes should go to first (in # addition to the local adapter). def initialize(local, remote, options = {}) @name = :dual_write @local = local @remote = remote end def features @local.features end def get(feature) @local.get(feature) end def get_multi(features) @local.get_multi(features) end def get_all @local.get_all end def add(feature) @remote.add(feature).tap { @local.add(feature) } end def remove(feature) @remote.remove(feature).tap { @local.remove(feature) } end def clear(feature) @remote.clear(feature).tap { @local.clear(feature) } end def enable(feature, gate, thing) @remote.enable(feature, gate, thing).tap do @local.enable(feature, gate, thing) end end def disable(feature, gate, thing) @remote.disable(feature, gate, thing).tap do @local.disable(feature, gate, thing) end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/failover.rb000066400000000000000000000041541440444367400243600ustar00rootroot00000000000000module Flipper module Adapters class Failover include ::Flipper::Adapter # Public: The name of the adapter. attr_reader :name # Public: Build a new failover instance. # # primary - The primary flipper adapter. # secondary - The secondary flipper adapter which services reads when # the primary adapter is unavailable. # options - Hash of options: # :dual_write - Boolean, whether to update secondary when # primary is updated # :errors - Array of exception types for which to failover def initialize(primary, secondary, options = {}) @name = :failover @primary = primary @secondary = secondary @dual_write = options.fetch(:dual_write, false) @errors = options.fetch(:errors, [ StandardError ]) end def features @primary.features rescue *@errors @secondary.features end def get(feature) @primary.get(feature) rescue *@errors @secondary.get(feature) end def get_multi(features) @primary.get_multi(features) rescue *@errors @secondary.get_multi(features) end def get_all @primary.get_all rescue *@errors @secondary.get_all end def add(feature) @primary.add(feature).tap do @secondary.add(feature) if @dual_write end end def remove(feature) @primary.remove(feature).tap do @secondary.remove(feature) if @dual_write end end def clear(feature) @primary.clear(feature).tap do @secondary.clear(feature) if @dual_write end end def enable(feature, gate, thing) @primary.enable(feature, gate, thing).tap do @secondary.enable(feature, gate, thing) if @dual_write end end def disable(feature, gate, thing) @primary.disable(feature, gate, thing).tap do @secondary.disable(feature, gate, thing) if @dual_write end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/failsafe.rb000066400000000000000000000027551440444367400243300ustar00rootroot00000000000000module Flipper module Adapters class Failsafe include ::Flipper::Adapter # Public: The name of the adapter. attr_reader :name # Public: Build a new Failsafe instance. # # adapter - Flipper adapter to guard. # options - Hash of options: # :errors - Array of exception types for which to fail safe def initialize(adapter, options = {}) @adapter = adapter @errors = options.fetch(:errors, [StandardError]) @name = :failsafe end def features @adapter.features rescue *@errors Set.new end def add(feature) @adapter.add(feature) rescue *@errors false end def remove(feature) @adapter.remove(feature) rescue *@errors false end def clear(feature) @adapter.clear(feature) rescue *@errors false end def get(feature) @adapter.get(feature) rescue *@errors {} end def get_multi(features) @adapter.get_multi(features) rescue *@errors {} end def get_all @adapter.get_all rescue *@errors {} end def enable(feature, gate, thing) @adapter.enable(feature, gate, thing) rescue *@errors false end def disable(feature, gate, thing) @adapter.disable(feature, gate, thing) rescue *@errors false end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/http.rb000066400000000000000000000130021440444367400235200ustar00rootroot00000000000000require 'net/http' require 'json' require 'set' require 'flipper' require 'flipper/adapters/http/error' require 'flipper/adapters/http/client' module Flipper module Adapters class Http include Flipper::Adapter attr_reader :name, :client def initialize(options = {}) @client = Client.new(url: options.fetch(:url), headers: options[:headers], basic_auth_username: options[:basic_auth_username], basic_auth_password: options[:basic_auth_password], read_timeout: options[:read_timeout], open_timeout: options[:open_timeout], write_timeout: options[:write_timeout], max_retries: options[:max_retries], debug_output: options[:debug_output]) @name = :http end def get(feature) response = @client.get("/features/#{feature.key}") if response.is_a?(Net::HTTPOK) parsed_response = JSON.parse(response.body) result_for_feature(feature, parsed_response.fetch('gates')) elsif response.is_a?(Net::HTTPNotFound) default_config else raise Error, response end end def get_multi(features) csv_keys = features.map(&:key).join(',') response = @client.get("/features?keys=#{csv_keys}") raise Error, response unless response.is_a?(Net::HTTPOK) parsed_response = JSON.parse(response.body) parsed_features = parsed_response.fetch('features') gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash| hash[parsed_feature['key']] = parsed_feature['gates'] hash end result = {} features.each do |feature| result[feature.key] = result_for_feature(feature, gates_by_key[feature.key]) end result end def get_all response = @client.get("/features") raise Error, response unless response.is_a?(Net::HTTPOK) parsed_response = JSON.parse(response.body) parsed_features = parsed_response.fetch('features') gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash| hash[parsed_feature['key']] = parsed_feature['gates'] hash end result = {} gates_by_key.keys.each do |key| feature = Feature.new(key, self) result[feature.key] = result_for_feature(feature, gates_by_key[feature.key]) end result end def features response = @client.get('/features') raise Error, response unless response.is_a?(Net::HTTPOK) parsed_response = JSON.parse(response.body) parsed_response['features'].map { |feature| feature['key'] }.to_set end def add(feature) body = JSON.generate(name: feature.key) response = @client.post('/features', body) raise Error, response unless response.is_a?(Net::HTTPOK) true end def remove(feature) response = @client.delete("/features/#{feature.key}") raise Error, response unless response.is_a?(Net::HTTPNoContent) true end def enable(feature, gate, thing) body = request_body_for_gate(gate, thing.value.to_s) query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : "" response = @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body) raise Error, response unless response.is_a?(Net::HTTPOK) true end def disable(feature, gate, thing) body = request_body_for_gate(gate, thing.value.to_s) query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : "" response = case gate.key when :percentage_of_actors, :percentage_of_time @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body) else @client.delete("/features/#{feature.key}/#{gate.key}#{query_string}", body) end raise Error, response unless response.is_a?(Net::HTTPOK) true end def clear(feature) response = @client.delete("/features/#{feature.key}/clear") raise Error, response unless response.is_a?(Net::HTTPNoContent) true end private def request_body_for_gate(gate, value) data = case gate.key when :boolean {} when :groups { name: value } when :actors { flipper_id: value } when :percentage_of_actors, :percentage_of_time { percentage: value } else raise "#{gate.key} is not a valid flipper gate key" end JSON.generate(data) end def result_for_feature(feature, api_gates) api_gates ||= [] result = default_config feature.gates.each do |gate| api_gate = api_gates.detect { |ag| ag['key'] == gate.key.to_s } result[gate.key] = value_for_gate(gate, api_gate) if api_gate end result end def value_for_gate(gate, api_gate) value = api_gate['value'] case gate.data_type when :boolean, :integer value ? value.to_s : value when :set value ? value.to_set : Set.new else unsupported_data_type(gate.data_type) end end def unsupported_data_type(data_type) raise "#{data_type} is not supported by this adapter" end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/http/000077500000000000000000000000001440444367400231775ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/adapters/http/client.rb000066400000000000000000000065241440444367400250110ustar00rootroot00000000000000require 'uri' require 'openssl' require 'flipper/version' module Flipper module Adapters class Http class Client DEFAULT_HEADERS = { 'Content-Type' => 'application/json', 'Accept' => 'application/json', 'User-Agent' => "Flipper HTTP Adapter v#{VERSION}", }.freeze HTTPS_SCHEME = "https".freeze attr_reader :uri, :headers attr_reader :basic_auth_username, :basic_auth_password attr_reader :read_timeout, :open_timeout, :write_timeout, :max_retries, :debug_output def initialize(options = {}) @uri = URI(options.fetch(:url)) @headers = DEFAULT_HEADERS.merge(options[:headers] || {}) @basic_auth_username = options[:basic_auth_username] @basic_auth_password = options[:basic_auth_password] @read_timeout = options[:read_timeout] @open_timeout = options[:open_timeout] @write_timeout = options[:write_timeout] @max_retries = options.key?(:max_retries) ? options[:max_retries] : 0 @debug_output = options[:debug_output] end def get(path) perform Net::HTTP::Get, path, @headers end def post(path, body = nil) perform Net::HTTP::Post, path, @headers, body: body end def delete(path, body = nil) perform Net::HTTP::Delete, path, @headers, body: body end private def perform(http_method, path, headers = {}, options = {}) uri = uri_for_path(path) http = build_http(uri) request = build_request(http_method, uri, headers, options) http.request(request) end def uri_for_path(path) uri = @uri.dup path_uri = URI(path) uri.path += path_uri.path uri.query = "#{uri.query}&#{path_uri.query}" if path_uri.query uri end def build_http(uri) http = Net::HTTP.new(uri.host, uri.port) http.read_timeout = @read_timeout if @read_timeout http.open_timeout = @open_timeout if @open_timeout http.max_retries = @max_retries if @max_retries http.write_timeout = @write_timeout if @write_timeout http.set_debug_output(@debug_output) if @debug_output if uri.scheme == HTTPS_SCHEME http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER end http end def build_request(http_method, uri, headers, options) request_headers = { "Client-Language" => "ruby", "Client-Language-Version" => "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})", "Client-Platform" => RUBY_PLATFORM, "Client-Engine" => defined?(RUBY_ENGINE) ? RUBY_ENGINE : "", "Client-Pid" => Process.pid.to_s, "Client-Thread" => Thread.current.object_id.to_s, "Client-Hostname" => Socket.gethostname, }.merge(headers) body = options[:body] request = http_method.new(uri.request_uri) request.initialize_http_header(request_headers) request.body = body if body if @basic_auth_username && @basic_auth_password request.basic_auth(@basic_auth_username, @basic_auth_password) end request end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/http/error.rb000066400000000000000000000012461440444367400246600ustar00rootroot00000000000000require "json" module Flipper module Adapters class Http class Error < StandardError attr_reader :response def initialize(response) @response = response message = "Failed with status: #{response.code}" begin data = JSON.parse(response.body) if error_message = data["message"] message << "\n\n#{data["message"]}" end if more_info = data["more_info"] message << "\n#{data["more_info"]}" end rescue => exception # welp we tried end super(message) end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/instrumented.rb000066400000000000000000000101251440444367400252650ustar00rootroot00000000000000require 'delegate' module Flipper module Adapters # Internal: Adapter that wraps another adapter and instruments all adapter # operations. class Instrumented < SimpleDelegator include ::Flipper::Adapter # Private: The name of instrumentation events. InstrumentationName = "adapter_operation.#{InstrumentationNamespace}".freeze # Private: What is used to instrument all the things. attr_reader :instrumenter # Public: The name of the adapter. attr_reader :name # Internal: Initializes a new adapter instance. # # adapter - Vanilla adapter instance to wrap. # # options - The Hash of options. # :instrumenter - What to use to instrument all the things. # def initialize(adapter, options = {}) super(adapter) @adapter = adapter @name = :instrumented @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) end # Public def features default_payload = { operation: :features, adapter_name: @adapter.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.features end end # Public def add(feature) default_payload = { operation: :add, adapter_name: @adapter.name, feature_name: feature.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.add(feature) end end # Public def remove(feature) default_payload = { operation: :remove, adapter_name: @adapter.name, feature_name: feature.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.remove(feature) end end # Public def clear(feature) default_payload = { operation: :clear, adapter_name: @adapter.name, feature_name: feature.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.clear(feature) end end # Public def get(feature) default_payload = { operation: :get, adapter_name: @adapter.name, feature_name: feature.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.get(feature) end end def get_multi(features) default_payload = { operation: :get_multi, adapter_name: @adapter.name, feature_names: features.map(&:name), } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.get_multi(features) end end def get_all default_payload = { operation: :get_all, adapter_name: @adapter.name, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.get_all end end # Public def enable(feature, gate, thing) default_payload = { operation: :enable, adapter_name: @adapter.name, feature_name: feature.name, gate_name: gate.name, thing_value: thing.value, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.enable(feature, gate, thing) end end # Public def disable(feature, gate, thing) default_payload = { operation: :disable, adapter_name: @adapter.name, feature_name: feature.name, gate_name: gate.name, thing_value: thing.value, } @instrumenter.instrument(InstrumentationName, default_payload) do |payload| payload[:result] = @adapter.disable(feature, gate, thing) end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/memoizable.rb000066400000000000000000000076251440444367400247030ustar00rootroot00000000000000require 'delegate' module Flipper module Adapters # Internal: Adapter that wraps another adapter with the ability to memoize # adapter calls in memory. Used by flipper dsl and the memoizer middleware # to make it possible to memoize adapter calls for the duration of a request. class Memoizable < SimpleDelegator include ::Flipper::Adapter FeaturesKey = :flipper_features GetAllKey = :all_memoized # Internal attr_reader :cache # Public: The name of the adapter. attr_reader :name # Internal: The adapter this adapter is wrapping. attr_reader :adapter # Private def self.key_for(key) "feature/#{key}" end # Public def initialize(adapter, cache = nil) super(adapter) @adapter = adapter @name = :memoizable @cache = cache || {} @memoize = false end # Public def features if memoizing? cache.fetch(FeaturesKey) { cache[FeaturesKey] = @adapter.features } else @adapter.features end end # Public def add(feature) @adapter.add(feature).tap { expire_features_set } end # Public def remove(feature) @adapter.remove(feature).tap do expire_features_set expire_feature(feature) end end # Public def clear(feature) @adapter.clear(feature).tap { expire_feature(feature) } end # Public def get(feature) if memoizing? cache.fetch(key_for(feature.key)) { cache[key_for(feature.key)] = @adapter.get(feature) } else @adapter.get(feature) end end # Public def get_multi(features) if memoizing? uncached_features = features.reject { |feature| cache[key_for(feature.key)] } if uncached_features.any? response = @adapter.get_multi(uncached_features) response.each do |key, hash| cache[key_for(key)] = hash end end result = {} features.each do |feature| result[feature.key] = cache[key_for(feature.key)] end result else @adapter.get_multi(features) end end def get_all if memoizing? response = nil if cache[GetAllKey] response = {} cache[FeaturesKey].each do |key| response[key] = cache[key_for(key)] end else response = @adapter.get_all response.each do |key, value| cache[key_for(key)] = value end cache[FeaturesKey] = response.keys.to_set cache[GetAllKey] = true end # Ensures that looking up other features that do not exist doesn't # result in N+1 adapter calls. response.default_proc = ->(memo, key) { memo[key] = default_config } response else @adapter.get_all end end # Public def enable(feature, gate, thing) @adapter.enable(feature, gate, thing).tap { expire_feature(feature) } end # Public def disable(feature, gate, thing) @adapter.disable(feature, gate, thing).tap { expire_feature(feature) } end # Internal: Turns local caching on/off. # # value - The Boolean that decides if local caching is on. def memoize=(value) cache.clear @memoize = value end # Internal: Returns true for using local cache, false for not. def memoizing? !!@memoize end private def key_for(key) self.class.key_for(key) end def expire_feature(feature) cache.delete(key_for(feature.key)) if memoizing? end def expire_features_set cache.delete(FeaturesKey) if memoizing? end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/memory.rb000066400000000000000000000062651440444367400240660ustar00rootroot00000000000000require 'concurrent/atomic/read_write_lock' module Flipper module Adapters # Public: Adapter for storing everything in memory. # Useful for tests/specs. class Memory include ::Flipper::Adapter FeaturesKey = :features # Public: The name of the adapter. attr_reader :name # Public def initialize(source = nil) @source = Hash.new.update(source || {}) @name = :memory @lock = Concurrent::ReadWriteLock.new end # Public: The set of known features. def features @lock.with_read_lock { @source.keys }.to_set end # Public: Adds a feature to the set of known features. def add(feature) @lock.with_write_lock { @source[feature.key] ||= default_config } true end # Public: Removes a feature from the set of known features and clears # all the values for the feature. def remove(feature) @lock.with_write_lock { @source.delete(feature.key) } true end # Public: Clears all the gate values for a feature. def clear(feature) @lock.with_write_lock { @source[feature.key] = default_config } true end # Public def get(feature) @lock.with_read_lock { @source[feature.key] } || default_config end def get_multi(features) @lock.with_read_lock do result = {} features.each do |feature| result[feature.key] = @source[feature.key] || default_config end result end end def get_all @lock.with_read_lock { @source.to_h } end # Public def enable(feature, gate, thing) @lock.with_write_lock do @source[feature.key] ||= default_config case gate.data_type when :boolean @source[feature.key] = default_config @source[feature.key][gate.key] = thing.value.to_s when :integer @source[feature.key][gate.key] = thing.value.to_s when :set @source[feature.key][gate.key] << thing.value.to_s else raise "#{gate} is not supported by this adapter yet" end true end end # Public def disable(feature, gate, thing) @lock.with_write_lock do @source[feature.key] ||= default_config case gate.data_type when :boolean @source[feature.key] = default_config when :integer @source[feature.key][gate.key] = thing.value.to_s when :set @source[feature.key][gate.key].delete thing.value.to_s else raise "#{gate} is not supported by this adapter yet" end true end end # Public def inspect attributes = [ 'name=:memory', "source=#{@source.inspect}", ] "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>" end # Public: a more efficient implementation of import for this adapter def import(source_adapter) get_all = source_adapter.get_all @lock.with_write_lock { @source.replace(get_all) } end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/moneta.rb000066400000000000000000000055211440444367400240330ustar00rootroot00000000000000require 'moneta' module Flipper module Adapters class Moneta include ::Flipper::Adapter FEATURES_KEY = :flipper_features # Public: The name of the adapter. attr_reader :name # Public def initialize(moneta) @moneta = moneta @name = :moneta end # Public: The set of known features def features moneta[FEATURES_KEY] || Set.new end # Public: Adds a feature to the set of known features. def add(feature) moneta[FEATURES_KEY] = features << feature.key.to_s true end # Public: Removes a feature from the set of known features and clears # all the values for the feature. def remove(feature) moneta[FEATURES_KEY] = features.delete(feature.key.to_s) moneta.delete(key(feature.key)) true end # Public: Clears all the gate values for a feature. def clear(feature) moneta[key(feature.key)] = default_config true end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) default_config.merge(moneta[key(feature.key)].to_h) end # Public: Enables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being enabled for the gate. # # Returns true. def enable(feature, gate, thing) case gate.data_type when :boolean clear(feature) result = get(feature) result[gate.key] = thing.value.to_s moneta[key(feature.key)] = result when :integer result = get(feature) result[gate.key] = thing.value.to_s moneta[key(feature.key)] = result when :set result = get(feature) result[gate.key] << thing.value.to_s moneta[key(feature.key)] = result end true end # Public: Disables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def disable(feature, gate, thing) case gate.data_type when :boolean clear(feature) when :integer result = get(feature) result[gate.key] = thing.value.to_s moneta[key(feature.key)] = result when :set result = get(feature) result[gate.key] = result[gate.key].delete(thing.value.to_s) moneta[key(feature.key)] = result end true end private def key(feature_key) "#{FEATURES_KEY}/#{feature_key}" end attr_reader :moneta end end end flippercloud-flipper-532c39a/lib/flipper/adapters/mongo.rb000066400000000000000000000116351440444367400236720ustar00rootroot00000000000000require 'set' require 'flipper' require 'mongo' module Flipper module Adapters class Mongo include ::Flipper::Adapter # Private: The key that stores the set of known features. FeaturesKey = :flipper_features # Public: The name of the adapter. attr_reader :name # Public: The name of the collection storing the feature data. attr_reader :collection def initialize(collection) @collection = collection @name = :mongo end # Public: The set of known features. def features read_feature_keys end # Public: Adds a feature to the set of known features. def add(feature) update FeaturesKey, '$addToSet' => { 'features' => feature.key } true end # Public: Removes a feature from the set of known features. def remove(feature) update FeaturesKey, '$pull' => { 'features' => feature.key } clear feature true end # Public: Clears all the gate values for a feature. def clear(feature) delete feature.key true end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) doc = find(feature.key) result_for_feature(feature, doc) end def get_multi(features) read_many_features(features) end def get_all features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end # Public: Enables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def enable(feature, gate, thing) case gate.data_type when :boolean clear(feature) update feature.key, '$set' => { gate.key.to_s => thing.value.to_s, } when :integer update feature.key, '$set' => { gate.key.to_s => thing.value.to_s, } when :set update feature.key, '$addToSet' => { gate.key.to_s => thing.value.to_s, } else unsupported_data_type gate.data_type end true end # Public: Disables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def disable(feature, gate, thing) case gate.data_type when :boolean delete feature.key when :integer update feature.key, '$set' => { gate.key.to_s => thing.value.to_s } when :set update feature.key, '$pull' => { gate.key.to_s => thing.value.to_s } else unsupported_data_type gate.data_type end true end private def read_feature_keys find(FeaturesKey).fetch('features') { Set.new }.to_set end def read_many_features(features) docs = find_many(features.map(&:key)) result = {} features.each do |feature| result[feature.key] = result_for_feature(feature, docs[feature.key]) end result end # Private def unsupported_data_type(data_type) raise "#{data_type} is not supported by this adapter" end # Private def find(key) @collection.find(_id: key.to_s).limit(1).first || {} end def find_many(keys) docs = @collection.find(_id: { '$in' => keys }).to_a result = Hash.new { |hash, key| hash[key] = {} } docs.each do |doc| result[doc['_id']] = doc end result end # Private def update(key, updates) options = { upsert: true } @collection.find(_id: key.to_s).update_one(updates, options) end # Private def delete(key) @collection.find(_id: key.to_s).delete_one end def result_for_feature(feature, doc) result = {} feature.gates.each do |gate| result[gate.key] = case gate.data_type when :boolean, :integer doc[gate.key.to_s] when :set doc.fetch(gate.key.to_s) { Set.new }.to_set else unsupported_data_type gate.data_type end end result end end end end Flipper.configure do |config| config.adapter do url = ENV["FLIPPER_MONGO_URL"] || ENV["MONGO_URL"] collection = ENV["FLIPPER_MONGO_COLLECTION"] || "flipper" unless url raise ArgumentError, "The MONGO_URL environment variable must be set. For example: mongodb://127.0.0.1:27017/flipper" end Flipper::Adapters::Mongo.new(Mongo::Client.new(url)[collection]) end end flippercloud-flipper-532c39a/lib/flipper/adapters/operation_logger.rb000066400000000000000000000063651440444367400261160ustar00rootroot00000000000000require 'delegate' module Flipper module Adapters # Public: Adapter that wraps another adapter and stores the operations. # # Useful in tests to verify calls and such. Never use outside of testing. class OperationLogger < SimpleDelegator include ::Flipper::Adapter class Operation attr_reader :type, :args def initialize(type, args) @type = type @args = args end end OperationTypes = [ :features, :add, :remove, :clear, :get, :get_multi, :get_all, :enable, :disable, ].freeze # Internal: An array of the operations that have happened. attr_reader :operations # Internal: The name of the adapter. attr_reader :name # Public def initialize(adapter, operations = nil) super(adapter) @adapter = adapter @name = :operation_logger @operations = operations || [] end # Public: The set of known features. def features @operations << Operation.new(:features, []) @adapter.features end # Public: Adds a feature to the set of known features. def add(feature) @operations << Operation.new(:add, [feature]) @adapter.add(feature) end # Public: Removes a feature from the set of known features and clears # all the values for the feature. def remove(feature) @operations << Operation.new(:remove, [feature]) @adapter.remove(feature) end # Public: Clears all the gate values for a feature. def clear(feature) @operations << Operation.new(:clear, [feature]) @adapter.clear(feature) end # Public def get(feature) @operations << Operation.new(:get, [feature]) @adapter.get(feature) end # Public def get_multi(features) @operations << Operation.new(:get_multi, [features]) @adapter.get_multi(features) end # Public def get_all @operations << Operation.new(:get_all, []) @adapter.get_all end # Public def enable(feature, gate, thing) @operations << Operation.new(:enable, [feature, gate, thing]) @adapter.enable(feature, gate, thing) end # Public def disable(feature, gate, thing) @operations << Operation.new(:disable, [feature, gate, thing]) @adapter.disable(feature, gate, thing) end # Public: Count the number of times a certain operation happened. def count(type) type(type).size end # Public: Get all operations of a certain type. def type(type) @operations.select { |operation| operation.type == type } end # Public: Get the last operation of a certain type. def last(type) @operations.reverse.find { |operation| operation.type == type } end # Public: Resets the operation log to empty def reset @operations.clear end def inspect inspect_id = ::Kernel::format "%x", (object_id * 2) %(#<#{self.class}:0x#{inspect_id} @name=#{name.inspect}, @operations=#{@operations.inspect}, @adapter=#{@adapter.inspect}>) end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/poll.rb000066400000000000000000000017151440444367400235170ustar00rootroot00000000000000require 'flipper/adapters/sync/synchronizer' require 'flipper/poller' module Flipper module Adapters class Poll extend Forwardable include ::Flipper::Adapter # Deprecated Poller = ::Flipper::Poller # Public: The name of the adapter. attr_reader :name, :adapter, :poller def_delegators :synced_adapter, :features, :get, :get_multi, :get_all, :add, :remove, :clear, :enable, :disable def initialize(poller, adapter) @name = :poll @adapter = adapter @poller = poller @last_synced_at = 0 @poller.start end private def synced_adapter @poller.start poller_last_synced_at = @poller.last_synced_at.value if poller_last_synced_at > @last_synced_at Flipper::Adapters::Sync::Synchronizer.new(@adapter, @poller.adapter).call @last_synced_at = poller_last_synced_at end @adapter end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/poll/000077500000000000000000000000001440444367400231665ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/adapters/poll/poller.rb000066400000000000000000000002101440444367400250010ustar00rootroot00000000000000warn "DEPRECATION WARNING: Flipper::Adapters::Poll::Poller is deprecated. Use Flipper::Poller instead." require 'flipper/adapters/poll' flippercloud-flipper-532c39a/lib/flipper/adapters/pstore.rb000066400000000000000000000114561440444367400240700ustar00rootroot00000000000000require 'pstore' require 'set' require 'flipper' module Flipper module Adapters # Public: Adapter based on Ruby's pstore database. Perfect for when a local # file is good enough for storing features. class PStore include ::Flipper::Adapter FeaturesKey = :flipper_features # Public: The name of the adapter. attr_reader :name # Public: The path to where the file is stored. attr_reader :path # Public def initialize(path = 'flipper.pstore', thread_safe = true) @name = :pstore @path = path @store = ::PStore.new(path, thread_safe) end # Public: The set of known features. def features @store.transaction do read_feature_keys end end # Public: Adds a feature to the set of known features. def add(feature) @store.transaction do set_add FeaturesKey, feature.key end true end # Public: Removes a feature from the set of known features and clears # all the values for the feature. def remove(feature) @store.transaction do set_delete FeaturesKey, feature.key clear_gates(feature) end true end # Public: Clears all the gate values for a feature. def clear(feature) @store.transaction do clear_gates(feature) end true end # Public def get(feature) @store.transaction do result_for_feature(feature) end end def get_multi(features) @store.transaction do read_many_features(features) end end def get_all @store.transaction do features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end end # Public def enable(feature, gate, thing) @store.transaction do case gate.data_type when :boolean clear_gates(feature) write key(feature, gate), thing.value.to_s when :integer write key(feature, gate), thing.value.to_s when :set set_add key(feature, gate), thing.value.to_s else raise "#{gate} is not supported by this adapter yet" end end true end # Public def disable(feature, gate, thing) case gate.data_type when :boolean clear(feature) when :integer @store.transaction do write key(feature, gate), thing.value.to_s end when :set @store.transaction do set_delete key(feature, gate), thing.value.to_s end else raise "#{gate} is not supported by this adapter yet" end true end # Public def inspect attributes = [ "name=#{@name.inspect}", "path=#{@path.inspect}", "store=#{@store}", ] "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>" end private def clear_gates(feature) feature.gates.each do |gate| delete key(feature, gate) end end def read_feature_keys set_members FeaturesKey end def read_many_features(features) result = {} features.each do |feature| result[feature.key] = result_for_feature(feature) end result end def result_for_feature(feature) result = {} feature.gates.each do |gate| result[gate.key] = case gate.data_type when :boolean, :integer read key(feature, gate) when :set set_members key(feature, gate) else raise "#{gate} is not supported by this adapter yet" end end result end # Private def key(feature, gate) "#{feature.key}/#{gate.key}" end # Private def read(key) @store[key.to_s] end # Private def write(key, value) @store[key.to_s] = value.to_s end # Private def delete(key) @store.delete(key.to_s) end # Private def set_add(key, value) set_members(key) do |members| members.add(value.to_s) end end # Private def set_delete(key, value) set_members(key) do |members| members.delete(value.to_s) end end # Private def set_members(key) key = key.to_s @store[key] ||= Set.new if block_given? yield @store[key] else @store[key] end end end end end Flipper.configure do |config| config.adapter { Flipper::Adapters::PStore.new } end flippercloud-flipper-532c39a/lib/flipper/adapters/read_only.rb000066400000000000000000000022041440444367400245170ustar00rootroot00000000000000require 'flipper' module Flipper module Adapters # Public: Adapter that wraps another adapter and raises for any writes. class ReadOnly include ::Flipper::Adapter class WriteAttempted < Error def initialize(message = nil) super(message || 'write attempted while in read only mode') end end # Internal: The name of the adapter. attr_reader :name # Public def initialize(adapter) @adapter = adapter @name = :read_only end def features @adapter.features end def get(feature) @adapter.get(feature) end def get_multi(features) @adapter.get_multi(features) end def get_all @adapter.get_all end def add(_feature) raise WriteAttempted end def remove(_feature) raise WriteAttempted end def clear(_feature) raise WriteAttempted end def enable(_feature, _gate, _thing) raise WriteAttempted end def disable(_feature, _gate, _thing) raise WriteAttempted end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/redis.rb000066400000000000000000000122441440444367400236560ustar00rootroot00000000000000require 'set' require 'redis' require 'flipper' module Flipper module Adapters class Redis include ::Flipper::Adapter # Private: The key that stores the set of known features. FeaturesKey = :flipper_features # Public: The name of the adapter. attr_reader :name # Public: Initializes a Redis flipper adapter. # # client - The Redis client to use. Feel free to namespace it. def initialize(client) @client = client @name = :redis end # Public: The set of known features. def features read_feature_keys end # Public: Adds a feature to the set of known features. def add(feature) if redis_sadd_returns_boolean? @client.sadd? FeaturesKey, feature.key else @client.sadd FeaturesKey, feature.key end true end # Public: Removes a feature from the set of known features. def remove(feature) if redis_sadd_returns_boolean? @client.srem? FeaturesKey, feature.key else @client.srem FeaturesKey, feature.key end @client.del feature.key true end # Public: Clears the gate values for a feature. def clear(feature) @client.del feature.key true end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) doc = doc_for(feature) result_for_feature(feature, doc) end def get_multi(features) read_many_features(features) end def get_all features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end # Public: Enables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being enabled for the gate. # # Returns true. def enable(feature, gate, thing) case gate.data_type when :boolean clear(feature) @client.hset feature.key, gate.key, thing.value.to_s when :integer @client.hset feature.key, gate.key, thing.value.to_s when :set @client.hset feature.key, to_field(gate, thing), 1 else unsupported_data_type gate.data_type end true end # Public: Disables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def disable(feature, gate, thing) case gate.data_type when :boolean @client.del feature.key when :integer @client.hset feature.key, gate.key, thing.value.to_s when :set @client.hdel feature.key, to_field(gate, thing) else unsupported_data_type gate.data_type end true end private def redis_sadd_returns_boolean? @client.class.respond_to?(:sadd_returns_boolean) && @client.class.sadd_returns_boolean end def read_many_features(features) docs = docs_for(features) result = {} features.zip(docs) do |feature, doc| result[feature.key] = result_for_feature(feature, doc) end result end def read_feature_keys @client.smembers(FeaturesKey).to_set end # Private: Gets a hash of fields => values for the given feature. # # Returns a Hash of fields => values. def doc_for(feature, pipeline: @client) pipeline.hgetall(feature.key) end def docs_for(features) @client.pipelined do |pipeline| features.each do |feature| doc_for(feature, pipeline: pipeline) end end end def result_for_feature(feature, doc) result = {} fields = doc.keys feature.gates.each do |gate| result[gate.key] = case gate.data_type when :boolean, :integer doc[gate.key.to_s] when :set fields_to_gate_value fields, gate else unsupported_data_type gate.data_type end end result end # Private: Converts gate and thing to hash key. def to_field(gate, thing) "#{gate.key}/#{thing.value}" end # Private: Returns a set of values given an array of fields and a gate. # # Returns a Set of the values enabled for the gate. def fields_to_gate_value(fields, gate) regex = %r{^#{Regexp.escape(gate.key.to_s)}/} keys = fields.grep(regex) values = keys.map { |key| key.split('/', 2).last } values.to_set end # Private def unsupported_data_type(data_type) raise "#{data_type} is not supported by this adapter" end end end end Flipper.configure do |config| config.adapter do client = Redis.new(url: ENV["FLIPPER_REDIS_URL"] || ENV["REDIS_URL"]) Flipper::Adapters::Redis.new(client) end end flippercloud-flipper-532c39a/lib/flipper/adapters/redis_cache.rb000066400000000000000000000072251440444367400250040ustar00rootroot00000000000000require 'redis' require 'flipper' module Flipper module Adapters # Public: Adapter that wraps another adapter with the ability to cache # adapter calls in Redis class RedisCache include ::Flipper::Adapter Version = 'v1'.freeze Namespace = "flipper/#{Version}".freeze FeaturesKey = "#{Namespace}/features".freeze GetAllKey = "#{Namespace}/get_all".freeze # Private def self.key_for(key) "#{Namespace}/feature/#{key}" end # Internal attr_reader :cache # Public: The name of the adapter. attr_reader :name # Public def initialize(adapter, cache, ttl = 3600) @adapter = adapter @name = :redis_cache @cache = cache @ttl = ttl end # Public def features read_feature_keys end # Public def add(feature) result = @adapter.add(feature) @cache.del(FeaturesKey) result end # Public def remove(feature) result = @adapter.remove(feature) @cache.del(FeaturesKey) @cache.del(key_for(feature.key)) result end # Public def clear(feature) result = @adapter.clear(feature) @cache.del(key_for(feature.key)) result end # Public def get(feature) fetch(key_for(feature.key)) do @adapter.get(feature) end end def get_multi(features) read_many_features(features) end def get_all if @cache.setnx(GetAllKey, Time.now.to_i) @cache.expire(GetAllKey, @ttl) response = @adapter.get_all response.each do |key, value| set_with_ttl key_for(key), value end set_with_ttl FeaturesKey, response.keys.to_set response else features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) } read_many_features(features) end end # Public def enable(feature, gate, thing) result = @adapter.enable(feature, gate, thing) @cache.del(key_for(feature.key)) result end # Public def disable(feature, gate, thing) result = @adapter.disable(feature, gate, thing) @cache.del(key_for(feature.key)) result end private def key_for(key) self.class.key_for(key) end def read_feature_keys fetch(FeaturesKey) { @adapter.features } end def read_many_features(features) keys = features.map(&:key) cache_result = Hash[keys.zip(multi_cache_get(keys))] uncached_features = features.reject { |feature| cache_result[feature.key] } if uncached_features.any? response = @adapter.get_multi(uncached_features) response.each do |key, value| set_with_ttl(key_for(key), value) cache_result[key] = value end end result = {} features.each do |feature| result[feature.key] = cache_result[feature.key] end result end def fetch(cache_key) cached = @cache.get(cache_key) if cached Marshal.load(cached) else to_cache = yield set_with_ttl(cache_key, to_cache) to_cache end end def set_with_ttl(key, value) @cache.setex(key, @ttl, Marshal.dump(value)) end def multi_cache_get(keys) return [] if keys.empty? cache_keys = keys.map { |key| key_for(key) } @cache.mget(*cache_keys).map do |value| value ? Marshal.load(value) : nil end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/rollout.rb000066400000000000000000000041521440444367400242470ustar00rootroot00000000000000require 'flipper' module Flipper module Adapters class Rollout include Adapter class AdapterMethodNotSupportedError < Error def initialize(message = 'unsupported method called for import adapter') super(message) end end # Public: The name of the adapter. attr_reader :name def initialize(rollout) @rollout = rollout @name = :rollout end # Public: The set of known features. def features @rollout.features end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) rollout_feature = @rollout.get(feature.key) return default_config if rollout_feature.nil? boolean = nil groups = Set.new(rollout_feature.groups) actors = Set.new(rollout_feature.users) percentage_of_actors = case rollout_feature.percentage when 100 boolean = true groups = Set.new actors = Set.new nil when 0 nil else rollout_feature.percentage end { boolean: boolean, groups: groups, actors: actors, percentage_of_actors: percentage_of_actors, percentage_of_time: nil, } end def add(_feature) raise AdapterMethodNotSupportedError end def remove(_feature) raise AdapterMethodNotSupportedError end def clear(_feature) raise AdapterMethodNotSupportedError end def enable(_feature, _gate, _thing) raise AdapterMethodNotSupportedError end def disable(_feature, _gate, _thing) raise AdapterMethodNotSupportedError end def import(_source_adapter) raise AdapterMethodNotSupportedError end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/sequel.rb000066400000000000000000000156021440444367400240470ustar00rootroot00000000000000require 'set' require 'flipper' require 'sequel' module Flipper module Adapters class Sequel include ::Flipper::Adapter begin old = ::Sequel::Model.require_valid_table ::Sequel::Model.require_valid_table = false # Private: Do not use outside of this adapter. class Feature < ::Sequel::Model(:flipper_features) unrestrict_primary_key plugin :timestamps, update_on_create: true end # Private: Do not use outside of this adapter. class Gate < ::Sequel::Model(:flipper_gates) unrestrict_primary_key plugin :timestamps, update_on_create: true end ensure ::Sequel::Model.require_valid_table = old end # Public: The name of the adapter. attr_reader :name # Public: Initialize a new Sequel adapter instance. # # name - The Symbol name for this adapter. Optional (default :active_record) # feature_class - The AR class responsible for the features table. # gate_class - The AR class responsible for the gates table. # # Allowing the overriding of name is so you can differentiate multiple # instances of this adapter from each other, if, for some reason, that is # a thing you do. # # Allowing the overriding of the default feature/gate classes means you # can roll your own tables and what not, if you so desire. def initialize(options = {}) @name = options.fetch(:name, :sequel) @feature_class = options.fetch(:feature_class) { Feature } @gate_class = options.fetch(:gate_class) { Gate } end # Public: The set of known features. def features @feature_class.all.map(&:key).to_set end # Public: Adds a feature to the set of known features. def add(feature) # race condition, but add is only used by enable/disable which happen # super rarely, so it shouldn't matter in practice @feature_class.find_or_create(key: feature.key.to_s) true end # Public: Removes a feature from the set of known features. def remove(feature) @feature_class.db.transaction do @feature_class.where(key: feature.key.to_s).delete clear(feature) end true end # Public: Clears the gate values for a feature. def clear(feature) @gate_class.where(feature_key: feature.key.to_s).delete true end # Public: Gets the values for all gates for a given feature. # # Returns a Hash of Flipper::Gate#key => value. def get(feature) db_gates = @gate_class.where(feature_key: feature.key.to_s).all result_for_feature(feature, db_gates) end def get_multi(features) db_gates = @gate_class.where(feature_key: features.map(&:key)).to_a grouped_db_gates = db_gates.group_by(&:feature_key) result = {} features.each do |feature| result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key]) end result end def get_all feature_table = @feature_class.table_name.to_sym gate_table = @gate_class.table_name.to_sym features_sql = @feature_class.select(:key.qualify(feature_table).as(:feature_key)) .select_append(:key.qualify(gate_table)) .select_append(:value.qualify(gate_table)) .left_join(@gate_class.table_name.to_sym, feature_key: :key) .sql db_gates = @gate_class.fetch(features_sql).to_a grouped_db_gates = db_gates.group_by(&:feature_key) result = Hash.new { |hash, key| hash[key] = default_config } features = grouped_db_gates.keys.map { |key| Flipper::Feature.new(key, self) } features.each do |feature| result[feature.key] = result_for_feature(feature, grouped_db_gates[feature.key]) end result end # Public: Enables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def enable(feature, gate, thing) case gate.data_type when :boolean set(feature, gate, thing, clear: true) when :integer set(feature, gate, thing) when :set begin @gate_class.create(gate_attrs(feature, gate, thing)) rescue ::Sequel::UniqueConstraintViolation end else unsupported_data_type gate.data_type end true end # Public: Disables a gate for a given thing. # # feature - The Flipper::Feature for the gate. # gate - The Flipper::Gate to disable. # thing - The Flipper::Type being disabled for the gate. # # Returns true. def disable(feature, gate, thing) case gate.data_type when :boolean clear(feature) when :integer set(feature, gate, thing) when :set @gate_class.where(gate_attrs(feature, gate, thing)) .delete else unsupported_data_type gate.data_type end true end private def unsupported_data_type(data_type) raise "#{data_type} is not supported by this adapter" end def set(feature, gate, thing, options = {}) clear_feature = options.fetch(:clear, false) args = { feature_key: feature.key, key: gate.key.to_s, } @gate_class.db.transaction do clear(feature) if clear_feature @gate_class.where(args).delete begin @gate_class.create(gate_attrs(feature, gate, thing)) rescue ::Sequel::UniqueConstraintViolation end end end def gate_attrs(feature, gate, thing) { feature_key: feature.key.to_s, key: gate.key.to_s, value: thing.value.to_s, } end def result_for_feature(feature, db_gates) db_gates ||= [] feature.gates.each_with_object({}) do |gate, result| result[gate.key] = case gate.data_type when :boolean if detected_db_gate = db_gates.detect { |db_gate| db_gate.key == gate.key.to_s } detected_db_gate.value end when :integer if detected_db_gate = db_gates.detect { |db_gate| db_gate.key == gate.key.to_s } detected_db_gate.value end when :set db_gates.select { |db_gate| db_gate.key == gate.key.to_s }.map(&:value).to_set else unsupported_data_type gate.data_type end end end end end end Flipper.configure do |config| config.adapter { Flipper::Adapters::Sequel.new } end Sequel::Model.include Flipper::Identifier flippercloud-flipper-532c39a/lib/flipper/adapters/sync.rb000066400000000000000000000046441440444367400235310ustar00rootroot00000000000000require "flipper/adapters/sync/synchronizer" require "flipper/adapters/sync/interval_synchronizer" module Flipper module Adapters # TODO: Syncing should happen in a background thread on a regular interval # rather than in the main thread only when reads happen. class Sync include ::Flipper::Adapter # Public: The name of the adapter. attr_reader :name # Public: The synchronizer that will keep the local and remote in sync. attr_reader :synchronizer # Public: Build a new sync instance. # # local - The local flipper adapter that should serve reads. # remote - The remote flipper adapter that should serve writes and update # the local on an interval. # interval - The Float or Integer number of seconds between syncs from # remote to local. Default value is set in IntervalSynchronizer. def initialize(local, remote, options = {}) @name = :sync @local = local @remote = remote @synchronizer = options.fetch(:synchronizer) do sync_options = { raise: false, } instrumenter = options[:instrumenter] sync_options[:instrumenter] = instrumenter if instrumenter synchronizer = Synchronizer.new(@local, @remote, sync_options) IntervalSynchronizer.new(synchronizer, interval: options[:interval]) end synchronize end def features synchronize @local.features end def get(feature) synchronize @local.get(feature) end def get_multi(features) synchronize @local.get_multi(features) end def get_all synchronize @local.get_all end def add(feature) @remote.add(feature).tap { @local.add(feature) } end def remove(feature) @remote.remove(feature).tap { @local.remove(feature) } end def clear(feature) @remote.clear(feature).tap { @local.clear(feature) } end def enable(feature, gate, thing) @remote.enable(feature, gate, thing).tap do @local.enable(feature, gate, thing) end end def disable(feature, gate, thing) @remote.disable(feature, gate, thing).tap do @local.disable(feature, gate, thing) end end private def synchronize @synchronizer.call end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/sync/000077500000000000000000000000001440444367400231745ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/adapters/sync/feature_synchronizer.rb000066400000000000000000000071571440444367400300030ustar00rootroot00000000000000require "flipper/actor" require "flipper/gate_values" module Flipper module Adapters class Sync # Internal: Given a feature, local gate values and remote gate values, # makes the local equal to the remote. class FeatureSynchronizer extend Forwardable def_delegator :@local_gate_values, :boolean, :local_boolean def_delegator :@local_gate_values, :actors, :local_actors def_delegator :@local_gate_values, :groups, :local_groups def_delegator :@local_gate_values, :percentage_of_actors, :local_percentage_of_actors def_delegator :@local_gate_values, :percentage_of_time, :local_percentage_of_time def_delegator :@remote_gate_values, :boolean, :remote_boolean def_delegator :@remote_gate_values, :actors, :remote_actors def_delegator :@remote_gate_values, :groups, :remote_groups def_delegator :@remote_gate_values, :percentage_of_actors, :remote_percentage_of_actors def_delegator :@remote_gate_values, :percentage_of_time, :remote_percentage_of_time def initialize(feature, local_gate_values, remote_gate_values) @feature = feature @local_gate_values = local_gate_values @remote_gate_values = remote_gate_values end def call if remote_disabled? return if local_disabled? @feature.disable elsif remote_boolean_enabled? return if local_boolean_enabled? @feature.enable else @feature.disable if local_boolean_enabled? sync_actors sync_groups sync_percentage_of_actors sync_percentage_of_time end end private def sync_actors remote_actors_added = remote_actors - local_actors remote_actors_added.each do |flipper_id| @feature.enable_actor Actor.new(flipper_id) end remote_actors_removed = local_actors - remote_actors remote_actors_removed.each do |flipper_id| @feature.disable_actor Actor.new(flipper_id) end end def sync_groups remote_groups_added = remote_groups - local_groups remote_groups_added.each do |group_name| @feature.enable_group group_name end remote_groups_removed = local_groups - remote_groups remote_groups_removed.each do |group_name| @feature.disable_group group_name end end def sync_percentage_of_actors return if local_percentage_of_actors == remote_percentage_of_actors @feature.enable_percentage_of_actors remote_percentage_of_actors end def sync_percentage_of_time return if local_percentage_of_time == remote_percentage_of_time @feature.enable_percentage_of_time remote_percentage_of_time end def default_config @default_config ||= @feature.adapter.default_config end def default_gate_values @default_gate_values ||= GateValues.new(default_config) end def default_gate_values?(gate_values) gate_values == default_gate_values end def local_disabled? default_gate_values? @local_gate_values end def remote_disabled? default_gate_values? @remote_gate_values end def local_boolean_enabled? local_boolean end def remote_boolean_enabled? remote_boolean end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/sync/interval_synchronizer.rb000066400000000000000000000026161440444367400301670ustar00rootroot00000000000000module Flipper module Adapters class Sync # Internal: Wraps a Synchronizer instance and only invokes it every # N seconds. class IntervalSynchronizer # Private: Number of seconds between syncs (default: 10). DEFAULT_INTERVAL = 10 # Public: The Float or Integer number of seconds between invocations of # the wrapped synchronizer. attr_reader :interval # Public: Initializes a new interval synchronizer. # # synchronizer - The Synchronizer to call when the interval has passed. # interval - The Integer number of seconds between invocations of # the wrapped synchronizer. def initialize(synchronizer, interval: nil) @synchronizer = synchronizer @interval = interval || DEFAULT_INTERVAL # TODO: add jitter to this so all processes booting at the same time # don't phone home at the same time. @last_sync_at = 0 end def call return unless time_to_sync? @last_sync_at = now @synchronizer.call nil end private def time_to_sync? seconds_since_last_sync = now - @last_sync_at seconds_since_last_sync >= @interval end def now Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) end end end end end flippercloud-flipper-532c39a/lib/flipper/adapters/sync/synchronizer.rb000066400000000000000000000047501440444367400262640ustar00rootroot00000000000000require "flipper/feature" require "flipper/gate_values" require "flipper/adapters/sync/feature_synchronizer" module Flipper module Adapters class Sync # Public: Given a local and remote adapter, it can update the local to # match the remote doing only the necessary enable/disable operations. class Synchronizer # Public: Initializes a new synchronizer. # # local - The Flipper adapter to get in sync with the remote. # remote - The Flipper adapter that is source of truth that the local # adapter should be brought in line with. # options - The Hash of options. # :instrumenter - The instrumenter used to instrument. # :raise - Should errors be raised (default: true). def initialize(local, remote, options = {}) @local = local @remote = remote @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) @raise = options.fetch(:raise, true) end # Public: Forces a sync. def call @instrumenter.instrument("synchronizer_call.flipper") { sync } end private def sync local_get_all = @local.get_all remote_get_all = @remote.get_all # Sync all the gate values. remote_get_all.each do |feature_key, remote_gates_hash| feature = Feature.new(feature_key, @local) # Check if feature_key is in hash before accessing to prevent unintended hash modification local_gates_hash = local_get_all.key?(feature_key) ? local_get_all[feature_key] : @local.default_config local_gate_values = GateValues.new(local_gates_hash) remote_gate_values = GateValues.new(remote_gates_hash) FeatureSynchronizer.new(feature, local_gate_values, remote_gate_values).call end # Add features that are missing in local and present in remote. features_to_add = remote_get_all.keys - local_get_all.keys features_to_add.each { |key| Feature.new(key, @local).add } # Remove features that are present in local and missing in remote. features_to_remove = local_get_all.keys - remote_get_all.keys features_to_remove.each { |key| Feature.new(key, @local).remove } nil rescue => exception @instrumenter.instrument("synchronizer_exception.flipper", exception: exception) raise if @raise end end end end end flippercloud-flipper-532c39a/lib/flipper/api.rb000066400000000000000000000014311440444367400215120ustar00rootroot00000000000000require 'rack' require 'flipper' require 'flipper/api/middleware' require 'flipper/api/json_params' module Flipper module Api CONTENT_TYPE = 'application/json'.freeze def self.app(flipper = nil, options = {}) env_key = options.fetch(:env_key, 'flipper') app = ->(_) { [404, { 'Content-Type'.freeze => CONTENT_TYPE }, ['{}'.freeze]] } builder = Rack::Builder.new yield builder if block_given? builder.use Flipper::Api::JsonParams builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key builder.use Flipper::Api::Middleware, env_key: env_key builder.run app klass = self app = builder.to_app app.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output app end end end flippercloud-flipper-532c39a/lib/flipper/api/000077500000000000000000000000001440444367400211665ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/api/action.rb000066400000000000000000000115011440444367400227660ustar00rootroot00000000000000require 'forwardable' require 'flipper/api/error' require 'flipper/api/error_response' require 'json' module Flipper module Api class Action module FeatureNameFromRoute def feature_name @feature_name ||= begin match = request.path_info.match(self.class.route_regex) match ? Rack::Utils.unescape(match[:feature_name]) : nil end end private :feature_name end extend Forwardable VALID_REQUEST_METHOD_NAMES = Set.new([ 'get'.freeze, 'post'.freeze, 'put'.freeze, 'delete'.freeze, ]).freeze # Public: Call this in subclasses so the action knows its route. # # regex - The Regexp that this action should run for. # # Returns nothing. def self.route(regex) @route_regex = regex end # Internal: Does this action's route match the path. def self.route_match?(path) path.match(route_regex) end # Internal: The regex that matches which routes this action will work for. def self.route_regex @route_regex || raise("#{name}.route is not set") end # Internal: Initializes and runs an action for a given request. # # flipper - The Flipper::DSL instance. # request - The Rack::Request that was sent. # # Returns result of Action#run. def self.run(flipper, request) new(flipper, request).run end # Public: The instance of the Flipper::DSL the middleware was # initialized with. attr_reader :flipper # Public: The Rack::Request to provide a response for. attr_reader :request # Public: The params for the request. def_delegator :@request, :params def initialize(flipper, request) @flipper = flipper @request = request @code = 200 @headers = { 'Content-Type' => Api::CONTENT_TYPE } end # Public: Runs the request method for the provided request. # # Returns whatever the request method returns in the action. def run if valid_request_method? && respond_to?(request_method_name) catch(:halt) { send(request_method_name) } else raise Api::RequestMethodNotSupported, "#{self.class} does not support request method #{request_method_name.inspect}" end end # Public: Runs another action from within the request method of a # different action. # # action_class - The class of the other action to run. # # Examples # # run_other_action Home # # => result of running Home action # # Returns result of other action. def run_other_action(action_class) action_class.new(flipper, request).run end # Public: Call this with a response to immediately stop the current action # and respond however you want. # # response - The response you would like to return. def halt(response) throw :halt, response end # Public: Call this with a json serializable object (i.e. Hash) # to serialize object and respond to request # # object - json serializable object # status - http status code def json_response(object, status = 200) header 'Content-Type', Api::CONTENT_TYPE status(status) body = JSON.dump(object) halt [@code, @headers, [body]] end # Public: Call this with an ErrorResponse::ERRORS key to respond # with the serialized error object as response body # # error_key - key to lookup error object def json_error_response(error_key) error = ErrorResponse::ERRORS.fetch(error_key.to_sym) json_response(error.as_json, error.http_status) end # Public: Set the status code for the response. # # code - The Integer code you would like the response to return. def status(code) @code = code.to_i end # Public: Set a header. # # name - The String name of the header. # value - The value of the header. def header(name, value) @headers[name] = value end private # Private: Returns the request method converted to an action method. def request_method_name @request_method_name ||= @request.request_method.downcase end # Private: split request path by "/" # Example: "features/feature_name" => ['features', 'feature_name'] def path_parts @request.path.split('/') end def valid_request_method? VALID_REQUEST_METHOD_NAMES.include?(request_method_name) end end end end flippercloud-flipper-532c39a/lib/flipper/api/action_collection.rb000066400000000000000000000007121440444367400252030ustar00rootroot00000000000000module Flipper module Api # Internal: Used to detect the action that should be used in the middleware. class ActionCollection def initialize @action_classes = [] end def add(action_class) @action_classes << action_class end def action_for_request(request) @action_classes.detect do |action_class| action_class.route_match?(request.path_info) end end end end end flippercloud-flipper-532c39a/lib/flipper/api/error.rb000066400000000000000000000004451440444367400226470ustar00rootroot00000000000000module Flipper module Api # All flipper api errors inherit from this. Error = Class.new(StandardError) # Raised when a request method (get, post, etc.) is called for an action # that does not know how to handle it. RequestMethodNotSupported = Class.new(Error) end end flippercloud-flipper-532c39a/lib/flipper/api/error_response.rb000066400000000000000000000017431440444367400245670ustar00rootroot00000000000000module Flipper module Api module ErrorResponse class Error attr_reader :http_status def initialize(code, message, http_status) @code = code @message = message @more_info = 'https://flippercloud.io/docs/api#error-code-reference' @http_status = http_status end def as_json { code: @code, message: @message, more_info: @more_info, } end end ERRORS = { feature_not_found: Error.new(1, 'Feature not found.', 404), group_not_registered: Error.new(2, 'Group not registered.', 404), percentage_invalid: Error.new(3, 'Percentage must be a positive number less than or equal to 100.', 422), flipper_id_invalid: Error.new(4, 'Required parameter flipper_id is missing.', 422), name_invalid: Error.new(5, 'Required parameter name is missing.', 422), }.freeze end end end flippercloud-flipper-532c39a/lib/flipper/api/json_params.rb000066400000000000000000000025171440444367400240340ustar00rootroot00000000000000require 'rack/utils' module Flipper module Api class JsonParams include Rack::Utils def initialize(app) @app = app end CONTENT_TYPE = 'CONTENT_TYPE'.freeze QUERY_STRING = 'QUERY_STRING'.freeze REQUEST_BODY = 'rack.input'.freeze # Public: Merge request body params with query string params # This way can access all params with Rack::Request#params # Rack does not add application/json params to Rack::Request#params # Allows app to handle x-www-url-form-encoded / application/json request # parameters the same way def call(env) if env[CONTENT_TYPE] == 'application/json' body = env[REQUEST_BODY].read env[REQUEST_BODY].rewind update_params(env, body) end @app.call(env) end private # Rails 3.2.2.1 Rack version does not have Rack::Request#update_param # Rack 1.5.0 adds update_param # This method accomplishes similar functionality def update_params(env, data) return if data.empty? parsed_request_body = JSON.parse(data) parsed_query_string = parse_query(env[QUERY_STRING]) parsed_query_string.merge!(parsed_request_body) parameters = build_query(parsed_query_string) env[QUERY_STRING] = parameters end end end end flippercloud-flipper-532c39a/lib/flipper/api/middleware.rb000066400000000000000000000025551440444367400236370ustar00rootroot00000000000000require 'rack' require 'flipper/api/action_collection' # Require all V1 actions automatically. Pathname(__FILE__).dirname.join('v1/actions').each_child(false) do |name| require "flipper/api/v1/actions/#{name}" end module Flipper module Api class Middleware def initialize(app, options = {}) @app = app @env_key = options.fetch(:env_key, 'flipper') @action_collection = ActionCollection.new @action_collection.add Api::V1::Actions::PercentageOfTimeGate @action_collection.add Api::V1::Actions::PercentageOfActorsGate @action_collection.add Api::V1::Actions::ActorsGate @action_collection.add Api::V1::Actions::GroupsGate @action_collection.add Api::V1::Actions::BooleanGate @action_collection.add Api::V1::Actions::ClearFeature @action_collection.add Api::V1::Actions::Actors @action_collection.add Api::V1::Actions::Feature @action_collection.add Api::V1::Actions::Features end def call(env) dup.call!(env) end def call!(env) request = Rack::Request.new(env) action_class = @action_collection.action_for_request(request) if action_class.nil? @app.call(env) else flipper = env.fetch(@env_key) { Flipper } action_class.run(flipper, request) end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/000077500000000000000000000000001440444367400215145ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/000077500000000000000000000000001440444367400231545ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/actors.rb000066400000000000000000000020061440444367400247720ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/actor' module Flipper module Api module V1 module Actions class Actors < Api::Action route %r{\A/actors/(?.*)/?\Z} def get keys = params['keys'] features = if keys names = keys.split(',') if names.empty? [] else flipper.preload(names) end else flipper.features end actor = Flipper::Actor.new(flipper_id) decorated_actor = Decorators::Actor.new(actor, features) json_response(decorated_actor.as_json) end private def flipper_id match = request.path_info.match(self.class.route_regex) match ? match[:flipper_id] : nil end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/actors_gate.rb000066400000000000000000000022351440444367400257760ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class ActorsGate < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/actors/?\Z} def post ensure_valid_params feature = flipper[feature_name] actor = Actor.new(flipper_id) feature.enable_actor(actor) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end def delete ensure_valid_params feature = flipper[feature_name] actor = Actor.new(flipper_id) feature.disable_actor(actor) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end private def ensure_valid_params json_error_response(:flipper_id_invalid) if flipper_id.nil? end def flipper_id @flipper_id ||= params['flipper_id'] end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/boolean_gate.rb000066400000000000000000000014231440444367400261200ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class BooleanGate < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/boolean/?\Z} def post feature = flipper[feature_name] feature.enable decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end def delete feature = flipper[feature_name.to_sym] feature.disable decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/clear_feature.rb000066400000000000000000000007151440444367400263050ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class ClearFeature < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/clear/?\Z} def delete feature = flipper[feature_name] feature.clear json_response({}, 204) end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/feature.rb000066400000000000000000000016111440444367400251330ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class Feature < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/?\Z} def get return json_error_response(:feature_not_found) unless feature_exists?(feature_name) exclude_gates = params['exclude_gates']&.downcase == "true" feature = Decorators::Feature.new(flipper[feature_name]) json_response(feature.as_json(exclude_gates: exclude_gates)) end def delete flipper.remove(feature_name) json_response({}, 204) end private def feature_exists?(feature_name) flipper.features.map(&:key).include?(feature_name) end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/features.rb000066400000000000000000000027051440444367400253230ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class Features < Api::Action route %r{\A/features/?\Z} def get keys = params['keys'] exclude_gates = params['exclude_gates']&.downcase == "true" features = if keys names = keys.split(',') if names.empty? [] else existing_feature_names = names.keep_if do |feature_name| feature_exists?(feature_name) end flipper.preload(existing_feature_names) end else flipper.features end decorated_features = features.map do |feature| Decorators::Feature.new(feature).as_json(exclude_gates: exclude_gates) end json_response(features: decorated_features) end def post feature_name = params.fetch('name') { json_error_response(:name_invalid) } feature = flipper[feature_name] feature.add decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end private def feature_exists?(feature_name) flipper.features.map(&:key).include?(feature_name) end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/groups_gate.rb000066400000000000000000000031201440444367400260140ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class GroupsGate < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/groups/?\Z} def post ensure_valid_params feature = flipper[feature_name] feature.enable_group(group_name) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end def delete ensure_valid_params feature = flipper[feature_name] feature.disable_group(group_name) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end private def ensure_valid_params if group_name.nil? || group_name.empty? json_error_response(:name_invalid) end return if allow_unregistered_groups? return if Flipper.group_exists?(group_name) json_error_response(:group_not_registered) end def allow_unregistered_groups? allow_unregistered_groups = params['allow_unregistered_groups'] allow_unregistered_groups && allow_unregistered_groups == 'true' end def disallow_unregistered_groups? !allow_unregistered_groups? end def group_name @group_name ||= params['name'] end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/percentage_of_actors_gate.rb000066400000000000000000000027041440444367400306600ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class PercentageOfActorsGate < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/percentage_of_actors/?\Z} def post if percentage < 0 || percentage > 100 json_error_response(:percentage_invalid) end feature = flipper[feature_name] feature.enable_percentage_of_actors(percentage) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end def delete feature = flipper[feature_name] feature.disable_percentage_of_actors decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end private def percentage_param @percentage_param ||= params['percentage'].to_s end def percentage @percentage ||= begin unless percentage_param.match(/\d/) raise ArgumentError, "invalid numeric value: #{percentage_param}" end Flipper::Types::Percentage.new(percentage_param).value rescue ArgumentError, TypeError -1 end end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/actions/percentage_of_time_gate.rb000066400000000000000000000026751440444367400303320ustar00rootroot00000000000000require 'flipper/api/action' require 'flipper/api/v1/decorators/feature' module Flipper module Api module V1 module Actions class PercentageOfTimeGate < Api::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/percentage_of_time/?\Z} def post if percentage < 0 || percentage > 100 json_error_response(:percentage_invalid) end feature = flipper[feature_name] feature.enable_percentage_of_time(percentage) decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end def delete feature = flipper[feature_name] feature.disable_percentage_of_time decorated_feature = Decorators::Feature.new(feature) json_response(decorated_feature.as_json, 200) end private def percentage_param @percentage_param ||= params['percentage'].to_s end def percentage @percentage ||= begin unless percentage_param.match(/\d/) raise ArgumentError, "invalid numeric value: #{percentage_param}" end Flipper::Types::Percentage.new(percentage_param).value rescue ArgumentError, TypeError -1 end end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/decorators/000077500000000000000000000000001440444367400236615ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/api/v1/decorators/actor.rb000066400000000000000000000014311440444367400253150ustar00rootroot00000000000000module Flipper module Api module V1 module Decorators class Actor < SimpleDelegator # Public: the actor and features. attr_reader :actor, :features def initialize(actor, features) @actor = actor @features = features end def as_json { 'flipper_id' => actor.flipper_id, 'features' => features_data, } end private def features_data features.each_with_object({}) do |feature, features_hash| features_hash[feature.name] = { 'enabled' => feature.enabled?(actor), } features_hash end end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/decorators/feature.rb000066400000000000000000000014501440444367400256410ustar00rootroot00000000000000require 'delegate' require 'flipper/api/v1/decorators/gate' module Flipper module Api module V1 module Decorators class Feature < SimpleDelegator # Public: The feature being decorated. alias_method :feature, :__getobj__ # Public: Returns instance as hash that is ready to be json dumped. def as_json(exclude_gates: false) result = { 'key' => key, 'state' => state.to_s, } unless exclude_gates gate_values = feature.adapter.get(self) result['gates'] = gates.map do |gate| Decorators::Gate.new(gate, gate_values[gate.key]).as_json end end result end end end end end end flippercloud-flipper-532c39a/lib/flipper/api/v1/decorators/gate.rb000066400000000000000000000013701440444367400251270ustar00rootroot00000000000000module Flipper module Api module V1 module Decorators class Gate < SimpleDelegator # Public the gate being decorated alias_method :gate, :__getobj__ # Public: the value for the gate from the adapter. attr_reader :value def initialize(gate, value = nil) super gate @value = value end def as_json { 'key' => gate.key.to_s, 'name' => gate.name.to_s, 'value' => value_as_json, } end private # json doesn't like sets def value_as_json data_type == :set ? value.to_a : value end end end end end end flippercloud-flipper-532c39a/lib/flipper/cloud.rb000066400000000000000000000037751440444367400220640ustar00rootroot00000000000000require "flipper" require "flipper/middleware/setup_env" require "flipper/middleware/memoizer" require "flipper/cloud/configuration" require "flipper/cloud/dsl" require "flipper/cloud/middleware" require "flipper/cloud/engine" if defined?(Rails::Engine) module Flipper module Cloud # Public: Returns a new Flipper instance with an http adapter correctly # configured for flipper cloud. # # token - The String token for the environment from the website. # options - The Hash of options. See Flipper::Cloud::Configuration. # block - The block that configuration will be yielded to allowing you to # customize this cloud instance and its adapter. def self.new(options = {}) configuration = Configuration.new(options) yield configuration if block_given? DSL.new(configuration) end def self.app(flipper = nil, options = {}) env_key = options.fetch(:env_key, 'flipper') memoizer_options = options.fetch(:memoizer_options, {}) app = ->(_) { [404, { 'Content-Type'.freeze => 'application/json'.freeze }, ['{}'.freeze]] } builder = Rack::Builder.new yield builder if block_given? builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key builder.use Flipper::Middleware::Memoizer, memoizer_options.merge(env_key: env_key) builder.use Flipper::Cloud::Middleware, env_key: env_key builder.run app klass = self app = builder.to_app app.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output app end # Private: Configure Flipper to use Cloud by default def self.set_default Flipper.configure do |config| config.default do if ENV["FLIPPER_CLOUD_TOKEN"] self.new(local_adapter: config.adapter) else warn "Missing FLIPPER_CLOUD_TOKEN environment variable. Disabling Flipper::Cloud." Flipper.new(config.adapter) end end end end end end Flipper::Cloud.set_default flippercloud-flipper-532c39a/lib/flipper/cloud/000077500000000000000000000000001440444367400215235ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/cloud/configuration.rb000066400000000000000000000153241440444367400247240ustar00rootroot00000000000000require "socket" require "flipper/adapters/http" require "flipper/adapters/poll" require "flipper/poller" require "flipper/adapters/memory" require "flipper/adapters/dual_write" require "flipper/adapters/sync/synchronizer" require "flipper/cloud/instrumenter" require "brow" module Flipper module Cloud class Configuration # The set of valid ways that syncing can happpen. VALID_SYNC_METHODS = Set[ :poll, :webhook, ].freeze DEFAULT_URL = "https://www.flippercloud.io/adapter".freeze # Private: Keeps track of brow instances so they can be shared across # threads. def self.brow_instances @brow_instances ||= Concurrent::Map.new end # Public: The token corresponding to an environment on flippercloud.io. attr_accessor :token # Public: The url for http adapter. Really should only be customized for # development work. Feel free to forget you ever saw this. attr_reader :url # Public: net/http read timeout for all http requests (default: 5). attr_accessor :read_timeout # Public: net/http open timeout for all http requests (default: 5). attr_accessor :open_timeout # Public: net/http write timeout for all http requests (default: 5). attr_accessor :write_timeout # Public: IO stream to send debug output too. Off by default. # # # for example, this would send all http request information to STDOUT # configuration = Flipper::Cloud::Configuration.new # configuration.debug_output = STDOUT attr_accessor :debug_output # Public: Instrumenter to use for the Flipper instance returned by # Flipper::Cloud.new (default: Flipper::Instrumenters::Noop). # # # for example, to use active support notifications you could do: # configuration = Flipper::Cloud::Configuration.new # configuration.instrumenter = ActiveSupport::Notifications attr_accessor :instrumenter # Public: Local adapter that all reads should go to in order to ensure # latency is low and resiliency is high. This adapter is automatically # kept in sync with cloud. # # # for example, to use active record you could do: # configuration = Flipper::Cloud::Configuration.new # configuration.local_adapter = Flipper::Adapters::ActiveRecord.new attr_accessor :local_adapter # Public: The Integer or Float number of seconds between attempts to bring # the local in sync with cloud (default: 10). attr_accessor :sync_interval # Public: The secret used to verify if syncs in the middleware should # occur or not. attr_accessor :sync_secret def initialize(options = {}) @token = options.fetch(:token) { ENV["FLIPPER_CLOUD_TOKEN"] } if @token.nil? raise ArgumentError, "Flipper::Cloud token is missing. Please set FLIPPER_CLOUD_TOKEN or provide the token (e.g. Flipper::Cloud.new(token: 'token'))." end @read_timeout = options.fetch(:read_timeout) { ENV.fetch("FLIPPER_CLOUD_READ_TIMEOUT", 5).to_f } @open_timeout = options.fetch(:open_timeout) { ENV.fetch("FLIPPER_CLOUD_OPEN_TIMEOUT", 5).to_f } @write_timeout = options.fetch(:write_timeout) { ENV.fetch("FLIPPER_CLOUD_WRITE_TIMEOUT", 5).to_f } @sync_interval = options.fetch(:sync_interval) { ENV.fetch("FLIPPER_CLOUD_SYNC_INTERVAL", 10).to_f } @sync_secret = options.fetch(:sync_secret) { ENV["FLIPPER_CLOUD_SYNC_SECRET"] } @local_adapter = options.fetch(:local_adapter) { Adapters::Memory.new } @debug_output = options[:debug_output] @adapter_block = ->(adapter) { adapter } self.url = options.fetch(:url) { ENV.fetch("FLIPPER_CLOUD_URL", DEFAULT_URL) } instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) # This is alpha. Don't use this unless you are me. And you are not me. cloud_instrument = options.fetch(:cloud_instrument) { ENV["FLIPPER_CLOUD_INSTRUMENT"] == "1" } @instrumenter = if cloud_instrument Instrumenter.new(brow: brow, instrumenter: instrumenter) else instrumenter end end # Public: Read or customize the http adapter. Calling without a block will # perform a read. Calling with a block yields the cloud adapter # for customization. # # # for example, to instrument the http calls, you can wrap the http # # adapter with the intsrumented adapter # configuration = Flipper::Cloud::Configuration.new # configuration.adapter do |adapter| # Flipper::Adapters::Instrumented.new(adapter) # end # def adapter(&block) if block_given? @adapter_block = block else @adapter_block.call app_adapter end end # Public: Set url for the http adapter. attr_writer :url def sync Flipper::Adapters::Sync::Synchronizer.new(local_adapter, http_adapter, { instrumenter: instrumenter, interval: sync_interval, }).call end def brow self.class.brow_instances.compute_if_absent(url + token) do uri = URI.parse(url) uri.path = "#{uri.path}/events".squeeze("/") Brow::Client.new({ url: uri.to_s, headers: { "Accept" => "application/json", "Content-Type" => "application/json", "User-Agent" => "Flipper v#{VERSION} via Brow v#{Brow::VERSION}", "Flipper-Cloud-Token" => @token, } }) end end # Public: The method that will be used to synchronize local adapter with # cloud. (default: :poll, will be :webhook if sync_secret is set). def sync_method sync_secret ? :webhook : :poll end private def app_adapter sync_method == :webhook ? dual_write_adapter : poll_adapter end def dual_write_adapter Flipper::Adapters::DualWrite.new(local_adapter, http_adapter) end def poller Flipper::Poller.get(@url + @token, { interval: sync_interval, remote_adapter: http_adapter, instrumenter: instrumenter, }).tap(&:start) end def poll_adapter Flipper::Adapters::Poll.new(poller, dual_write_adapter) end def http_adapter Flipper::Adapters::Http.new({ url: @url, read_timeout: @read_timeout, open_timeout: @open_timeout, write_timeout: @write_timeout, max_retries: 0, # we'll handle retries ourselves debug_output: @debug_output, headers: { "Flipper-Cloud-Token" => @token, }, }) end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/dsl.rb000066400000000000000000000012751440444367400226370ustar00rootroot00000000000000require 'forwardable' module Flipper module Cloud class DSL < SimpleDelegator attr_reader :cloud_configuration def initialize(cloud_configuration) @cloud_configuration = cloud_configuration super Flipper.new(@cloud_configuration.adapter, instrumenter: @cloud_configuration.instrumenter) end def sync @cloud_configuration.sync end def sync_secret @cloud_configuration.sync_secret end def inspect inspect_id = ::Kernel::format "%x", (object_id * 2) %(#<#{self.class}:0x#{inspect_id} @cloud_configuration=#{cloud_configuration.inspect}, flipper=#{__getobj__.inspect}>) end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/engine.rb000066400000000000000000000014771440444367400233260ustar00rootroot00000000000000require "flipper/railtie" module Flipper module Cloud class Engine < Rails::Engine paths["config/routes.rb"] = ["lib/flipper/cloud/routes.rb"] config.before_configuration do config.flipper.cloud_path = "_flipper" end initializer "flipper.cloud.default", before: :load_config_initializers do |app| Flipper.configure do |config| config.default do if ENV["FLIPPER_CLOUD_TOKEN"] Flipper::Cloud.new( local_adapter: config.adapter, instrumenter: app.config.flipper.instrumenter ) else warn "Missing FLIPPER_CLOUD_TOKEN environment variable. Disabling Flipper::Cloud." Flipper.new(config.adapter) end end end end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/instrumenter.rb000066400000000000000000000020651440444367400246120ustar00rootroot00000000000000require "delegate" require "flipper/instrumenters/noop" module Flipper module Cloud class Instrumenter < SimpleDelegator def initialize(options = {}) @brow = options.fetch(:brow) @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) super @instrumenter end def instrument(name, payload = {}, &block) result = @instrumenter.instrument(name, payload, &block) push name, payload result end private def push(name, payload) return unless name == Flipper::Feature::InstrumentationName return unless :enabled? == payload[:operation] dimensions = { "feature" => payload[:feature_name].to_s, "result" => payload[:result].to_s, } if (thing = payload[:thing]) dimensions["flipper_id"] = thing.value.to_s end event = { type: "enabled", dimensions: dimensions, measures: {}, ts: Time.now.utc, } @brow.push event end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/message_verifier.rb000066400000000000000000000065371440444367400254020ustar00rootroot00000000000000require "openssl" require "digest/sha2" module Flipper module Cloud class MessageVerifier class InvalidSignature < StandardError; end DEFAULT_VERSION = "v1" def self.header(signature, timestamp, version = DEFAULT_VERSION) raise ArgumentError, "timestamp should be an instance of Time" unless timestamp.is_a?(Time) raise ArgumentError, "signature should be a string" unless signature.is_a?(String) "t=#{timestamp.to_i},#{version}=#{signature}" end def initialize(secret:, version: DEFAULT_VERSION) @secret = secret @version = version || DEFAULT_VERSION raise ArgumentError, "secret should be a string" unless @secret.is_a?(String) raise ArgumentError, "version should be a string" unless @version.is_a?(String) end def generate(payload, timestamp) raise ArgumentError, "timestamp should be an instance of Time" unless timestamp.is_a?(Time) raise ArgumentError, "payload should be a string" unless payload.is_a?(String) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), @secret, "#{timestamp.to_i}.#{payload}") end def header(signature, timestamp) self.class.header(signature, timestamp, @version) end # Public: Verifies the signature header for a given payload. # # Raises a InvalidSignature in the following cases: # - the header does not match the expected format # - no signatures found with the expected scheme # - no signatures matching the expected signature # - a tolerance is provided and the timestamp is not within the # tolerance # # Returns true otherwise. def verify(payload, header, tolerance: nil) begin timestamp, signatures = get_timestamp_and_signatures(header) rescue StandardError raise InvalidSignature, "Unable to extract timestamp and signatures from header" end if signatures.empty? raise InvalidSignature, "No signatures found with expected version #{@version}" end expected_sig = generate(payload, timestamp) unless signatures.any? { |s| secure_compare(expected_sig, s) } raise InvalidSignature, "No signatures found matching the expected signature for payload" end if tolerance && timestamp < Time.now - tolerance raise InvalidSignature, "Timestamp outside the tolerance zone (#{Time.at(timestamp)})" end true end private # Extracts the timestamp and the signature(s) with the desired version # from the header def get_timestamp_and_signatures(header) list_items = header.split(/,\s*/).map { |i| i.split("=", 2) } timestamp = Integer(list_items.select { |i| i[0] == "t" }[0][1]) signatures = list_items.select { |i| i[0] == @version }.map { |i| i[1] } [Time.at(timestamp), signatures] end # Private def fixed_length_secure_compare(a, b) raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end # Private def secure_compare(a, b) fixed_length_secure_compare(::Digest::SHA256.digest(a), ::Digest::SHA256.digest(b)) && a == b end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/middleware.rb000066400000000000000000000037351440444367400241750ustar00rootroot00000000000000# frozen_string_literal: true require "flipper/cloud/message_verifier" module Flipper module Cloud class Middleware # Internal: The path to match for webhook requests. WEBHOOK_PATH = %r{\A/webhooks\/?\Z} # Internal: The root path to match for requests. ROOT_PATH = %r{\A/\Z} def initialize(app, options = {}) @app = app @env_key = options.fetch(:env_key, 'flipper') end def call(env) dup.call!(env) end def call!(env) request = Rack::Request.new(env) if request.post? && (request.path_info.match(ROOT_PATH) || request.path_info.match(WEBHOOK_PATH)) status = 200 headers = { "Content-Type" => "application/json", } body = "{}" payload = request.body.read signature = request.env["HTTP_FLIPPER_CLOUD_SIGNATURE"] flipper = env.fetch(@env_key) begin message_verifier = MessageVerifier.new(secret: flipper.sync_secret) if message_verifier.verify(payload, signature) begin flipper.sync body = JSON.generate({ groups: Flipper.group_names.map { |name| {name: name}} }) rescue Flipper::Adapters::Http::Error => error status = error.response.code.to_i == 402 ? 402 : 500 headers["Flipper-Cloud-Response-Error-Class"] = error.class.name headers["Flipper-Cloud-Response-Error-Message"] = error.message rescue => error status = 500 headers["Flipper-Cloud-Response-Error-Class"] = error.class.name headers["Flipper-Cloud-Response-Error-Message"] = error.message end end rescue MessageVerifier::InvalidSignature status = 400 end [status, headers, [body]] else @app.call(env) end end end end end flippercloud-flipper-532c39a/lib/flipper/cloud/routes.rb000066400000000000000000000005751440444367400234000ustar00rootroot00000000000000# Default routes loaded by Flipper::Cloud::Engine Rails.application.routes.draw do if ENV["FLIPPER_CLOUD_TOKEN"] && ENV["FLIPPER_CLOUD_SYNC_SECRET"] config = Rails.application.config.flipper cloud_app = Flipper::Cloud.app(nil, env_key: config.env_key, memoizer_options: { preload: config.preload } ) mount cloud_app, at: config.cloud_path end end flippercloud-flipper-532c39a/lib/flipper/configuration.rb000066400000000000000000000034451440444367400236170ustar00rootroot00000000000000module Flipper class Configuration def initialize(options = {}) @default = -> { Flipper.new(adapter) } @adapter = -> { Flipper::Adapters::Memory.new } end # The default adapter to use. # # Pass a block to assign the adapter, and invoke without a block to # return the configured adapter instance. # # Flipper.configure do |config| # config.adapter # => instance of default Memory adapter # # # Configure it to use the ActiveRecord adapter # config.adapter do # require "flipper/adapters/active_record" # Flipper::Adapters::ActiveRecord.new # end # # config.adapter # => instance of ActiveRecord adapter # end # def adapter(&block) if block_given? @adapter = block else @adapter.call end end # Controls the default instance for flipper. When used with a block it # assigns a new default block to use to generate an instance. When used # without a block, it performs a block invocation and returns the result. # # configuration = Flipper::Configuration.new # configuration.default # => Flipper::DSL instance using Memory adapter # # # sets the default block to generate a new instance using ActiveRecord adapter # configuration.default do # require "flipper/adapters/active_record" # Flipper.new(Flipper::Adapters::ActiveRecord.new) # end # # configuration.default # => Flipper::DSL instance using ActiveRecord adapter # # Returns result of default block invocation if called without block. If # called with block, assigns the default block. def default(&block) if block_given? @default = block else @default.call end end end end flippercloud-flipper-532c39a/lib/flipper/dsl.rb000066400000000000000000000204621440444367400215300ustar00rootroot00000000000000require 'forwardable' module Flipper class DSL extend Forwardable # Private attr_reader :adapter # Private: What is being used to instrument all the things. attr_reader :instrumenter def_delegators :@adapter, :memoize=, :memoizing? # Public: Returns a new instance of the DSL. # # adapter - The adapter that this DSL instance should use. # options - The Hash of options. # :instrumenter - What should be used to instrument all the things. # :memoize - Should adapter be wrapped by memoize adapter or not. def initialize(adapter, options = {}) @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) memoize = options.fetch(:memoize, true) adapter = Adapters::Memoizable.new(adapter) if memoize @adapter = adapter @memoized_features = {} end # Public: Check if a feature is enabled. # # name - The String or Symbol name of the feature. # args - The args passed through to the enabled check. # # Returns true if feature is enabled, false if not. def enabled?(name, *args) feature(name).enabled?(*args) end # Public: Enable a feature. # # name - The String or Symbol name of the feature. # args - The args passed through to the feature instance enable call. # # Returns the result of the feature instance enable call. def enable(name, *args) feature(name).enable(*args) end # Public: Enable a feature for an actor. # # name - The String or Symbol name of the feature. # actor - a Flipper::Types::Actor instance or an object that responds # to flipper_id. # # Returns result of Feature#enable. def enable_actor(name, actor) feature(name).enable_actor(actor) end # Public: Enable a feature for a group. # # name - The String or Symbol name of the feature. # group - a Flipper::Types::Group instance or a String or Symbol name of a # registered group. # # Returns result of Feature#enable. def enable_group(name, group) feature(name).enable_group(group) end # Public: Enable a feature a percentage of time. # # name - The String or Symbol name of the feature. # percentage - a Flipper::Types::PercentageOfTime instance or an object # that responds to to_i. # # Returns result of Feature#enable. def enable_percentage_of_time(name, percentage) feature(name).enable_percentage_of_time(percentage) end # Public: Enable a feature for a percentage of actors. # # name - The String or Symbol name of the feature. # percentage - a Flipper::Types::PercentageOfActors instance or an object # that responds to to_i. # # Returns result of Feature#enable. def enable_percentage_of_actors(name, percentage) feature(name).enable_percentage_of_actors(percentage) end # Public: Disable a feature. # # name - The String or Symbol name of the feature. # args - The args passed through to the feature instance enable call. # # Returns the result of the feature instance disable call. def disable(name, *args) feature(name).disable(*args) end # Public: Disable a feature for an actor. # # name - The String or Symbol name of the feature. # actor - a Flipper::Types::Actor instance or an object that responds # to flipper_id. # # Returns result of disable. def disable_actor(name, actor) feature(name).disable_actor(actor) end # Public: Disable a feature for a group. # # name - The String or Symbol name of the feature. # group - a Flipper::Types::Group instance or a String or Symbol name of a # registered group. # # Returns result of disable. def disable_group(name, group) feature(name).disable_group(group) end # Public: Disable a feature a percentage of time. # # name - The String or Symbol name of the feature. # percentage - a Flipper::Types::PercentageOfTime instance or an object # that responds to to_i. # # Returns result of disable. def disable_percentage_of_time(name) feature(name).disable_percentage_of_time end # Public: Disable a feature for a percentage of actors. # # name - The String or Symbol name of the feature. # percentage - a Flipper::Types::PercentageOfActors instance or an object # that responds to to_i. # # Returns result of disable. def disable_percentage_of_actors(name) feature(name).disable_percentage_of_actors end # Public: Add a feature. # # name - The String or Symbol name of the feature. # # Returns result of add. def add(name) feature(name).add end # Public: Has a feature been added in the adapter. # # name - The String or Symbol name of the feature. # # Returns true if added else false. def exist?(name) feature(name).exist? end # Public: Remove a feature. # # name - The String or Symbol name of the feature. # # Returns result of remove. def remove(name) feature(name).remove end # Public: Access a feature instance by name. # # name - The String or Symbol name of the feature. # # Returns an instance of Flipper::Feature. def feature(name) if !name.is_a?(String) && !name.is_a?(Symbol) raise ArgumentError, "#{name} must be a String or Symbol" end @memoized_features[name.to_sym] ||= Feature.new(name, @adapter, instrumenter: instrumenter) end # Public: Preload the features with the given names. # # names - An Array of String or Symbol names of the features. # # Returns an Array of Flipper::Feature. def preload(names) features = names.map { |name| feature(name) } @adapter.get_multi(features) features end # Public: Preload all the adapters features. # # Returns an Array of Flipper::Feature. def preload_all keys = @adapter.get_all.keys keys.map { |key| feature(key) } end # Public: Shortcut access to a feature instance by name. # # name - The String or Symbol name of the feature. # # Returns an instance of Flipper::Feature. alias_method :[], :feature # Public: Shortcut for getting a boolean type instance. # # value - The true or false value for the boolean. # # Returns a Flipper::Types::Boolean instance. def boolean(value = true) Types::Boolean.new(value) end # Public: Even shorter shortcut for getting a boolean type instance. # # value - The true or false value for the boolean. # # Returns a Flipper::Types::Boolean instance. alias_method :bool, :boolean # Public: Access a flipper group by name. # # name - The String or Symbol name of the feature. # # Returns an instance of Flipper::Group. def group(name) Flipper.group(name) end # Public: Wraps an object as a flipper actor. # # thing - The object that you would like to wrap. # # Returns an instance of Flipper::Types::Actor. # Raises ArgumentError if thing does not respond to `flipper_id`. def actor(thing) Types::Actor.new(thing) end # Public: Shortcut for getting a percentage of time instance. # # number - The percentage of time that should be enabled. # # Returns Flipper::Types::PercentageOfTime. def time(number) Types::PercentageOfTime.new(number) end alias_method :percentage_of_time, :time # Public: Shortcut for getting a percentage of actors instance. # # number - The percentage of actors that should be enabled. # # Returns Flipper::Types::PercentageOfActors. def actors(number) Types::PercentageOfActors.new(number) end alias_method :percentage_of_actors, :actors # Public: Returns a Set of the known features for this adapter. # # Returns Set of Flipper::Feature instances. def features adapter.features.map { |name| feature(name) }.to_set end def import(flipper) adapter.import(flipper.adapter) end # Cloud DSL method that does nothing for open source version. def sync end # Cloud DSL method that does nothing for open source version. def sync_secret end end end flippercloud-flipper-532c39a/lib/flipper/errors.rb000066400000000000000000000012161440444367400222560ustar00rootroot00000000000000module Flipper # Top level error that all other errors inherit from. class Error < StandardError; end # Raised when gate can not be found for a thing. class GateNotFound < Error def initialize(thing) super "Could not find gate for #{thing.inspect}" end end # Raised when attempting to declare a group name that has already been used. class DuplicateGroup < Error; end # Raised when an invalid value is set to a configuration property class InvalidConfigurationValue < Flipper::Error def initialize(message = nil) default = "Configuration value is not valid." super(message || default) end end end flippercloud-flipper-532c39a/lib/flipper/feature.rb000066400000000000000000000244751440444367400224110ustar00rootroot00000000000000require 'flipper/errors' require 'flipper/type' require 'flipper/gate' require 'flipper/feature_check_context' require 'flipper/gate_values' module Flipper class Feature # Private: The name of feature instrumentation events. InstrumentationName = "feature_operation.#{InstrumentationNamespace}".freeze # Public: The name of the feature. attr_reader :name # Public: Name converted to value safe for adapter. attr_reader :key # Private: The adapter this feature should use. attr_reader :adapter # Private: What is being used to instrument all the things. attr_reader :instrumenter # Internal: Initializes a new feature instance. # # name - The Symbol or String name of the feature. # adapter - The adapter that will be used to store details about this feature. # # options - The Hash of options. # :instrumenter - What to use to instrument all the things. # def initialize(name, adapter, options = {}) @name = name @key = name.to_s @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) @adapter = adapter end # Public: Enable this feature for something. # # Returns the result of Adapter#enable. def enable(thing = true) instrument(:enable) do |payload| adapter.add self gate = gate_for(thing) wrapped_thing = gate.wrap(thing) payload[:gate_name] = gate.name payload[:thing] = wrapped_thing adapter.enable self, gate, wrapped_thing end end # Public: Disable this feature for something. # # Returns the result of Adapter#disable. def disable(thing = false) instrument(:disable) do |payload| adapter.add self gate = gate_for(thing) wrapped_thing = gate.wrap(thing) payload[:gate_name] = gate.name payload[:thing] = wrapped_thing adapter.disable self, gate, wrapped_thing end end # Public: Adds this feature. # # Returns the result of Adapter#add. def add instrument(:add) { adapter.add(self) } end # Public: Does this feature exist in the adapter. # # Returns true if exists in adapter else false. def exist? instrument(:exist?) { adapter.features.include?(key) } end # Public: Removes this feature. # # Returns the result of Adapter#remove. def remove instrument(:remove) { adapter.remove(self) } end # Public: Clears all gate values for this feature. # # Returns the result of Adapter#clear. def clear instrument(:clear) { adapter.clear(self) } end # Public: Check if a feature is enabled for a thing. # # Returns true if enabled, false if not. def enabled?(thing = nil) thing = Types::Actor.wrap(thing) unless thing.nil? instrument(:enabled?, thing: thing) do |payload| context = FeatureCheckContext.new( feature_name: @name, values: gate_values, thing: thing ) if open_gate = gates.detect { |gate| gate.open?(context) } payload[:gate_name] = open_gate.name true else false end end end # Public: Enables a feature for an actor. # # actor - a Flipper::Types::Actor instance or an object that responds # to flipper_id. # # Returns result of enable. def enable_actor(actor) enable Types::Actor.wrap(actor) end # Public: Enables a feature for a group. # # group - a Flipper::Types::Group instance or a String or Symbol name of a # registered group. # # Returns result of enable. def enable_group(group) enable Types::Group.wrap(group) end # Public: Enables a feature a percentage of time. # # percentage - a Flipper::Types::PercentageOfTime instance or an object that # responds to to_i. # # Returns result of enable. def enable_percentage_of_time(percentage) enable Types::PercentageOfTime.wrap(percentage) end # Public: Enables a feature for a percentage of actors. # # percentage - a Flipper::Types::PercentageOfTime instance or an object that # responds to to_i. # # Returns result of enable. def enable_percentage_of_actors(percentage) enable Types::PercentageOfActors.wrap(percentage) end # Public: Disables a feature for an actor. # # actor - a Flipper::Types::Actor instance or an object that responds # to flipper_id. # # Returns result of disable. def disable_actor(actor) disable Types::Actor.wrap(actor) end # Public: Disables a feature for a group. # # group - a Flipper::Types::Group instance or a String or Symbol name of a # registered group. # # Returns result of disable. def disable_group(group) disable Types::Group.wrap(group) end # Public: Disables a feature a percentage of time. # # percentage - a Flipper::Types::PercentageOfTime instance or an object that # responds to to_i. # # Returns result of disable. def disable_percentage_of_time disable Types::PercentageOfTime.new(0) end # Public: Disables a feature for a percentage of actors. # # percentage - a Flipper::Types::PercentageOfTime instance or an object that # responds to to_i. # # Returns result of disable. def disable_percentage_of_actors disable Types::PercentageOfActors.new(0) end # Public: Returns state for feature (:on, :off, or :conditional). def state values = gate_values boolean = gate(:boolean) non_boolean_gates = gates - [boolean] if values.boolean || values.percentage_of_time == 100 :on elsif non_boolean_gates.detect { |gate| gate.enabled?(values.send(gate.key)) } :conditional else :off end end # Public: Is the feature fully enabled. def on? state == :on end # Public: Is the feature fully disabled. def off? state == :off end # Public: Is the feature conditionally enabled for a given actor, group, # percentage of actors or percentage of the time. def conditional? state == :conditional end # Public: Returns the raw gate values stored by the adapter. def gate_values adapter_values = adapter.get(self) GateValues.new(adapter_values) end # Public: Get groups enabled for this feature. # # Returns Set of Flipper::Types::Group instances. def enabled_groups groups_value.map { |name| Flipper.group(name) }.to_set end alias_method :groups, :enabled_groups # Public: Get groups not enabled for this feature. # # Returns Set of Flipper::Types::Group instances. def disabled_groups Flipper.groups - enabled_groups end # Public: Get the adapter value for the groups gate. # # Returns Set of String group names. def groups_value gate_values.groups end # Public: Get the adapter value for the actors gate. # # Returns Set of String flipper_id's. def actors_value gate_values.actors end # Public: Get the adapter value for the boolean gate. # # Returns true or false. def boolean_value gate_values.boolean end # Public: Get the adapter value for the percentage of actors gate. # # Returns Integer greater than or equal to 0 and less than or equal to 100. def percentage_of_actors_value gate_values.percentage_of_actors end # Public: Get the adapter value for the percentage of time gate. # # Returns Integer greater than or equal to 0 and less than or equal to 100. def percentage_of_time_value gate_values.percentage_of_time end # Public: Get the gates that have been enabled for the feature. # # Returns an Array of Flipper::Gate instances. def enabled_gates values = gate_values gates.select { |gate| gate.enabled?(values.send(gate.key)) } end # Public: Get the names of the enabled gates. # # Returns an Array of gate names. def enabled_gate_names enabled_gates.map(&:name) end # Public: Get the gates that have not been enabled for the feature. # # Returns an Array of Flipper::Gate instances. def disabled_gates gates - enabled_gates end # Public: Get the names of the disabled gates. # # Returns an Array of gate names. def disabled_gate_names disabled_gates.map(&:name) end # Public: Returns the string representation of the feature. def to_s name.to_s end # Public: Identifier to be used in the url (a rails-ism). def to_param to_s end # Public: Pretty string version for debugging. def inspect attributes = [ "name=#{name.inspect}", "state=#{state.inspect}", "enabled_gate_names=#{enabled_gate_names.inspect}", "adapter=#{adapter.name.inspect}", ] "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>" end # Public: Get all the gates used to determine enabled/disabled for the feature. # # Returns an array of gates def gates @gates ||= gates_hash.values.freeze end def gates_hash @gates_hash ||= { boolean: Gates::Boolean.new, actor: Gates::Actor.new, percentage_of_actors: Gates::PercentageOfActors.new, percentage_of_time: Gates::PercentageOfTime.new, group: Gates::Group.new, }.freeze end # Public: Find a gate by name. # # Returns a Flipper::Gate if found, nil if not. def gate(name) gates_hash[name.to_sym] end # Public: Find the gate that protects a thing. # # thing - The object for which you would like to find a gate # # Returns a Flipper::Gate. # Raises Flipper::GateNotFound if no gate found for thing def gate_for(thing) gates.detect { |gate| gate.protects?(thing) } || raise(GateNotFound, thing) end private # Private: Instrument a feature operation. def instrument(operation, initial_payload = {}) @instrumenter.instrument(InstrumentationName, initial_payload) do |payload| payload[:feature_name] = name payload[:operation] = operation payload[:result] = yield(payload) if block_given? end end end end flippercloud-flipper-532c39a/lib/flipper/feature_check_context.rb000066400000000000000000000022201440444367400252720ustar00rootroot00000000000000module Flipper class FeatureCheckContext # Public: The name of the feature. attr_reader :feature_name # Public: The GateValues instance that keeps track of the values for the # gates for the feature. attr_reader :values # Public: The thing we want to know if a feature is enabled for. attr_reader :thing def initialize(feature_name:, values:, thing:) @feature_name = feature_name @values = values @thing = thing end # Public: Convenience method for groups value like Feature has. def groups_value values.groups end # Public: Convenience method for actors value value like Feature has. def actors_value values.actors end # Public: Convenience method for boolean value value like Feature has. def boolean_value values.boolean end # Public: Convenience method for percentage of actors value like Feature has. def percentage_of_actors_value values.percentage_of_actors end # Public: Convenience method for percentage of time value like Feature has. def percentage_of_time_value values.percentage_of_time end end end flippercloud-flipper-532c39a/lib/flipper/gate.rb000066400000000000000000000027331440444367400216670ustar00rootroot00000000000000module Flipper class Gate # Public def initialize(options = {}) end # Public: The name of the gate. Implemented in subclass. def name raise 'Not implemented' end # Private: Name converted to value safe for adapter. Implemented in subclass. def key raise 'Not implemented' end def data_type raise 'Not implemented' end def enabled?(_value) raise 'Not implemented' end # Internal: Check if a gate is open for a thing. Implemented in subclass. # # Returns true if gate open for thing, false if not. def open?(_thing, _value, _options = {}) false end # Internal: Check if a gate is protects a thing. Implemented in subclass. # # Returns true if gate protects thing, false if not. def protects?(_thing) false end # Internal: Allows gate to wrap thing using one of the supported flipper # types so adapters always get something that responds to value. def wrap(thing) thing end # Public: Pretty string version for debugging. def inspect attributes = [ "name=#{name.inspect}", "key=#{key.inspect}", "data_type=#{data_type.inspect}", ] "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>" end end end require 'flipper/gates/actor' require 'flipper/gates/boolean' require 'flipper/gates/group' require 'flipper/gates/percentage_of_actors' require 'flipper/gates/percentage_of_time' flippercloud-flipper-532c39a/lib/flipper/gate_values.rb000066400000000000000000000016661440444367400232520ustar00rootroot00000000000000require 'set' require 'flipper/typecast' module Flipper class GateValues attr_reader :boolean attr_reader :actors attr_reader :groups attr_reader :percentage_of_actors attr_reader :percentage_of_time def initialize(adapter_values) @boolean = Typecast.to_boolean(adapter_values[:boolean]) @actors = Typecast.to_set(adapter_values[:actors]) @groups = Typecast.to_set(adapter_values[:groups]) @percentage_of_actors = Typecast.to_percentage(adapter_values[:percentage_of_actors]) @percentage_of_time = Typecast.to_percentage(adapter_values[:percentage_of_time]) end def eql?(other) self.class.eql?(other.class) && boolean == other.boolean && actors == other.actors && groups == other.groups && percentage_of_actors == other.percentage_of_actors && percentage_of_time == other.percentage_of_time end alias_method :==, :eql? end end flippercloud-flipper-532c39a/lib/flipper/gates/000077500000000000000000000000001440444367400215205ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/gates/actor.rb000066400000000000000000000014311440444367400231540ustar00rootroot00000000000000module Flipper module Gates class Actor < Gate # Internal: The name of the gate. Used for instrumentation, etc. def name :actor end # Internal: Name converted to value safe for adapter. def key :actors end def data_type :set end def enabled?(value) !value.empty? end # Internal: Checks if the gate is open for a thing. # # Returns true if gate open for thing, false if not. def open?(context) return false if context.thing.nil? context.values.actors.include?(context.thing.value) end def wrap(thing) Types::Actor.wrap(thing) end def protects?(thing) Types::Actor.wrappable?(thing) end end end end flippercloud-flipper-532c39a/lib/flipper/gates/boolean.rb000066400000000000000000000015511440444367400234660ustar00rootroot00000000000000module Flipper module Gates class Boolean < Gate # Internal: The name of the gate. Used for instrumentation, etc. def name :boolean end # Internal: Name converted to value safe for adapter. def key :boolean end def data_type :boolean end def enabled?(value) !!value end # Internal: Checks if the gate is open for a thing. # # Returns true if explicitly set to true, false if explicitly set to false # or nil if not explicitly set. def open?(context) context.values.boolean end def wrap(thing) Types::Boolean.wrap(thing) end def protects?(thing) case thing when Types::Boolean, TrueClass, FalseClass true else false end end end end end flippercloud-flipper-532c39a/lib/flipper/gates/group.rb000066400000000000000000000015461440444367400232070ustar00rootroot00000000000000module Flipper module Gates class Group < Gate # Internal: The name of the gate. Used for instrumentation, etc. def name :group end # Internal: Name converted to value safe for adapter. def key :groups end def data_type :set end def enabled?(value) !value.empty? end # Internal: Checks if the gate is open for a thing. # # Returns true if gate open for thing, false if not. def open?(context) return false if context.thing.nil? context.values.groups.any? do |name| Flipper.group(name).match?(context.thing, context) end end def wrap(thing) Types::Group.wrap(thing) end def protects?(thing) thing.is_a?(Types::Group) || thing.is_a?(Symbol) end end end end flippercloud-flipper-532c39a/lib/flipper/gates/percentage_of_actors.rb000066400000000000000000000020501440444367400262160ustar00rootroot00000000000000require 'zlib' module Flipper module Gates class PercentageOfActors < Gate # Internal: The name of the gate. Used for instrumentation, etc. def name :percentage_of_actors end # Internal: Name converted to value safe for adapter. def key :percentage_of_actors end def data_type :integer end def enabled?(value) value > 0 end # Private: this constant is used to support up to 3 decimal places # in percentages. SCALING_FACTOR = 1_000 private_constant :SCALING_FACTOR # Internal: Checks if the gate is open for a thing. # # Returns true if gate open for thing, false if not. def open?(context) return false if context.thing.nil? id = "#{context.feature_name}#{context.thing.value}" Zlib.crc32(id) % (100 * SCALING_FACTOR) < context.values.percentage_of_actors * SCALING_FACTOR end def protects?(thing) thing.is_a?(Types::PercentageOfActors) end end end end flippercloud-flipper-532c39a/lib/flipper/gates/percentage_of_time.rb000066400000000000000000000013361440444367400256670ustar00rootroot00000000000000module Flipper module Gates class PercentageOfTime < Gate # Internal: The name of the gate. Used for instrumentation, etc. def name :percentage_of_time end # Internal: Name converted to value safe for adapter. def key :percentage_of_time end def data_type :integer end def enabled?(value) value > 0 end # Internal: Checks if the gate is open for a thing. # # Returns true if gate open for thing, false if not. def open?(context) rand < (context.values.percentage_of_time / 100.0) end def protects?(thing) thing.is_a?(Flipper::Types::PercentageOfTime) end end end end flippercloud-flipper-532c39a/lib/flipper/identifier.rb000066400000000000000000000005511440444367400230650ustar00rootroot00000000000000module Flipper # A default implementation of `#flipper_id` for actors. # # class User < Struct.new(:id) # include Flipper::Identifier # end # # user = User.new(99) # Flipper.enable :thing, user # Flipper.enabled? :thing, user #=> true # module Identifier def flipper_id "#{self.class.name};#{id}" end end end flippercloud-flipper-532c39a/lib/flipper/instrumentation/000077500000000000000000000000001440444367400236605ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/instrumentation/log_subscriber.rb000066400000000000000000000044541440444367400272200ustar00rootroot00000000000000require 'securerandom' require 'active_support/notifications' require 'active_support/log_subscriber' module Flipper module Instrumentation class LogSubscriber < ::ActiveSupport::LogSubscriber # Logs a feature operation. # # Example Output # # flipper[:search].enabled?(user) # # Flipper feature(search) enabled? false (1.2ms) [ thing=... ] # # Returns nothing. def feature_operation(event) return unless logger.debug? feature_name = event.payload[:feature_name] gate_name = event.payload[:gate_name] operation = event.payload[:operation] result = event.payload[:result] thing = event.payload[:thing] description = "Flipper feature(#{feature_name}) #{operation} #{result.inspect}" details = "thing=#{thing.inspect}" details += " gate_name=#{gate_name}" unless gate_name.nil? name = '%s (%.1fms)' % [description, event.duration] debug " #{color(name, CYAN, true)} [ #{details} ]" end # Logs an adapter operation. If operation is for a feature, then that # feature is included in log output. # # Example Output # # # log output for adapter operation with feature # # Flipper feature(search) adapter(memory) enable (0.0ms) [ result=...] # # # log output for adapter operation with no feature # # Flipper adapter(memory) features (0.0ms) [ result=... ] # # Returns nothing. def adapter_operation(event) return unless logger.debug? feature_name = event.payload[:feature_name] adapter_name = event.payload[:adapter_name] gate_name = event.payload[:gate_name] operation = event.payload[:operation] result = event.payload[:result] description = 'Flipper ' description << "feature(#{feature_name}) " unless feature_name.nil? description << "adapter(#{adapter_name}) " description << "#{operation} " details = "result=#{result.inspect}" name = '%s (%.1fms)' % [description, event.duration] debug " #{color(name, CYAN, true)} [ #{details} ]" end def logger self.class.logger end end end Instrumentation::LogSubscriber.attach_to InstrumentationNamespace end flippercloud-flipper-532c39a/lib/flipper/instrumentation/statsd.rb000066400000000000000000000003721440444367400255110ustar00rootroot00000000000000require 'securerandom' require 'active_support/notifications' require 'flipper/instrumentation/statsd_subscriber' ActiveSupport::Notifications.subscribe /\.flipper$/, Flipper::Instrumentation::StatsdSubscriber flippercloud-flipper-532c39a/lib/flipper/instrumentation/statsd_subscriber.rb000066400000000000000000000013621440444367400277340ustar00rootroot00000000000000# Note: You should never need to require this file directly if you are using # ActiveSupport::Notifications. Instead, you should require the statsd file # that lives in the same directory as this file. The benefit is that it # subscribes to the correct events and does everything for your. require 'flipper/instrumentation/subscriber' module Flipper module Instrumentation class StatsdSubscriber < Subscriber class << self attr_accessor :client end def update_timer(metric) if self.class.client self.class.client.timing metric, (@duration * 1_000).round end end def update_counter(metric) self.class.client.increment metric if self.class.client end end end end flippercloud-flipper-532c39a/lib/flipper/instrumentation/subscriber.rb000066400000000000000000000044111440444367400263500ustar00rootroot00000000000000module Flipper module Instrumentation class Subscriber # Public: Use this as the subscribed block. def self.call(name, start, ending, transaction_id, payload) new(name, start, ending, transaction_id, payload).update end # Private: Initializes a new event processing instance. def initialize(name, start, ending, transaction_id, payload) @name = name @start = start @ending = ending @payload = payload @duration = ending - start @transaction_id = transaction_id end # Internal: Override in subclass. def update_timer(_metric) raise 'not implemented' end # Internal: Override in subclass. def update_counter(_metric) raise 'not implemented' end # Private def update operation_type = @name.split('.').first method_name = "update_#{operation_type}_metrics" if respond_to?(method_name) send(method_name) else puts "Could not update #{operation_type} metrics as #{self.class} " \ "did not respond to `#{method_name}`" end end # Private def update_feature_operation_metrics feature_name = @payload[:feature_name] gate_name = @payload[:gate_name] operation = strip_trailing_question_mark(@payload[:operation]) result = @payload[:result] thing = @payload[:thing] update_timer "flipper.feature_operation.#{operation}" if @payload[:operation] == :enabled? metric_name = if result "flipper.feature.#{feature_name}.enabled" else "flipper.feature.#{feature_name}.disabled" end update_counter metric_name end end # Private def update_adapter_operation_metrics adapter_name = @payload[:adapter_name] operation = @payload[:operation] result = @payload[:result] value = @payload[:value] key = @payload[:key] update_timer "flipper.adapter.#{adapter_name}.#{operation}" end QUESTION_MARK = '?'.freeze # Private def strip_trailing_question_mark(operation) operation.to_s.chomp(QUESTION_MARK) end end end end flippercloud-flipper-532c39a/lib/flipper/instrumenters/000077500000000000000000000000001440444367400233375ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/instrumenters/memory.rb000066400000000000000000000020661440444367400252000ustar00rootroot00000000000000module Flipper module Instrumenters # Instrumentor that is useful for tests as it stores each of the events that # are instrumented. class Memory Event = Struct.new(:name, :payload, :result) attr_reader :events def initialize @events = [] end def instrument(name, payload = {}) # Copy the payload to guard against later modifications to it, and to # ensure that all instrumentation code uses the payload passed to the # block rather than the one passed to #instrument. payload = payload.dup result = yield payload if block_given? rescue Exception => e payload[:exception] = [e.class.name, e.message] payload[:exception_object] = e raise e ensure @events << Event.new(name, payload, result) end def events_by_name(name) @events.select { |event| event.name == name } end def event_by_name(name) events_by_name(name).first end def reset @events = [] end end end end flippercloud-flipper-532c39a/lib/flipper/instrumenters/noop.rb000066400000000000000000000002461440444367400246410ustar00rootroot00000000000000module Flipper module Instrumenters class Noop def self.instrument(_name, payload = {}) yield payload if block_given? end end end end flippercloud-flipper-532c39a/lib/flipper/metadata.rb000066400000000000000000000002041440444367400225160ustar00rootroot00000000000000module Flipper METADATA = { 'changelog_uri' => 'https://github.com/jnunemaker/flipper/blob/main/Changelog.md', }.freeze end flippercloud-flipper-532c39a/lib/flipper/middleware/000077500000000000000000000000001440444367400225325ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/middleware/memoizer.rb000066400000000000000000000044171440444367400247140ustar00rootroot00000000000000module Flipper module Middleware class Memoizer # Public: Initializes an instance of the Memoizer middleware. Flipper must # be configured with a default instance or the flipper instance must be # setup in the env of the request. You can do this by using the # Flipper::Middleware::SetupEnv middleware. # # app - The app this middleware is included in. # opts - The Hash of options. # :preload - Boolean to preload all features or Array of Symbol feature names to preload. # # Examples # # use Flipper::Middleware::Memoizer # # # using with preload_all features # use Flipper::Middleware::Memoizer, preload: true # # # using with preload specific features # use Flipper::Middleware::Memoizer, preload: [:stats, :search, :some_feature] # def initialize(app, opts = {}) if opts.is_a?(Flipper::DSL) || opts.is_a?(Proc) raise 'Flipper::Middleware::Memoizer no longer initializes with a flipper instance or block. Read more at: https://git.io/vSo31.' end @app = app @opts = opts @env_key = opts.fetch(:env_key, 'flipper') end def call(env) request = Rack::Request.new(env) if memoize?(request) memoized_call(env) else @app.call(env) end end private def memoize?(request) if @opts[:if] @opts[:if].call(request) elsif @opts[:unless] !@opts[:unless].call(request) else true end end def memoized_call(env) reset_on_body_close = false flipper = env.fetch(@env_key) { Flipper } # Already memoizing. This instance does not need to do anything. if flipper.memoizing? warn "Flipper::Middleware::Memoizer appears to be running twice. Read how to resolve this at https://github.com/jnunemaker/flipper/pull/523" return @app.call(env) end flipper.memoize = true case @opts[:preload] when true then flipper.preload_all when Array then flipper.preload(@opts[:preload]) end @app.call(env) ensure flipper.memoize = false if flipper end end end end flippercloud-flipper-532c39a/lib/flipper/middleware/setup_env.rb000066400000000000000000000030761440444367400250750ustar00rootroot00000000000000module Flipper module Middleware class SetupEnv # Public: Initializes an instance of the SetupEnv middleware. Allows for # lazy initialization of the flipper instance being set in the env by # providing a block. # # app - The app this middleware is included in. # flipper_or_block - The Flipper::DSL instance or a block that yields a # Flipper::DSL instance to use for all operations # (optional, default: Flipper). # # Examples # # flipper = Flipper.new(...) # # # using with a normal flipper instance # use Flipper::Middleware::SetupEnv, flipper # # # using with a block that yields a flipper instance # use Flipper::Middleware::SetupEnv, lambda { Flipper.new(...) } # # # using default configured Flipper instance # Flipper.configure do |config| # config.default { Flipper.new(...) } # end # use Flipper::Middleware::SetupEnv def initialize(app, flipper_or_block = nil, options = {}) @app = app @env_key = options.fetch(:env_key, 'flipper') if flipper_or_block.respond_to?(:call) @flipper_block = flipper_or_block else @flipper = flipper_or_block || Flipper end end def call(env) dup.call!(env) end def call!(env) env[@env_key] ||= flipper @app.call(env) end private def flipper @flipper ||= @flipper_block.call end end end end flippercloud-flipper-532c39a/lib/flipper/poller.rb000066400000000000000000000055031440444367400222420ustar00rootroot00000000000000require 'logger' require 'concurrent/utility/monotonic_time' require 'concurrent/map' require 'concurrent/atomic/atomic_fixnum' module Flipper class Poller attr_reader :adapter, :thread, :pid, :mutex, :interval, :last_synced_at def self.instances @instances ||= Concurrent::Map.new end private_class_method :instances def self.get(key, options = {}) instances.compute_if_absent(key) { new(options) } end def self.reset instances.each {|_,poller| poller.stop }.clear end def initialize(options = {}) @thread = nil @pid = Process.pid @mutex = Mutex.new @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop) @remote_adapter = options.fetch(:remote_adapter) @interval = options.fetch(:interval, 10).to_f @last_synced_at = Concurrent::AtomicFixnum.new(0) @adapter = Adapters::Memory.new if @interval < 1 warn "Flipper::Cloud poll interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1." @interval = 1 end @start_automatically = options.fetch(:start_automatically, true) if options.fetch(:shutdown_automatically, true) at_exit { stop } end end def start reset if forked? ensure_worker_running end def stop @instrumenter.instrument("poller.#{InstrumentationNamespace}", { operation: :stop, }) @thread&.kill end def run loop do sleep jitter start = Concurrent.monotonic_time begin sync rescue => exception # you can instrument these using poller.flipper end sleep_interval = interval - (Concurrent.monotonic_time - start) sleep sleep_interval if sleep_interval.positive? end end def sync @instrumenter.instrument("poller.#{InstrumentationNamespace}", operation: :poll) do @adapter.import @remote_adapter @last_synced_at.update { |time| Concurrent.monotonic_time } end end private def jitter rand end def forked? pid != Process.pid end def ensure_worker_running # Return early if thread is alive and avoid the mutex lock and unlock. return if thread_alive? # If another thread is starting worker thread, then return early so this # thread can enqueue and move on with life. return unless mutex.try_lock begin return if thread_alive? @thread = Thread.new { run } @instrumenter.instrument("poller.#{InstrumentationNamespace}", { operation: :thread_start, }) ensure mutex.unlock end end def thread_alive? @thread && @thread.alive? end def reset @pid = Process.pid mutex.unlock if mutex.locked? end end end flippercloud-flipper-532c39a/lib/flipper/railtie.rb000066400000000000000000000031071440444367400223740ustar00rootroot00000000000000module Flipper class Railtie < Rails::Railtie config.before_configuration do config.flipper = ActiveSupport::OrderedOptions.new.update( env_key: ENV.fetch('FLIPPER_ENV_KEY', 'flipper'), memoize: ENV.fetch('FLIPPER_MEMOIZE', 'true').casecmp('true').zero?, preload: ENV.fetch('FLIPPER_PRELOAD', 'true').casecmp('true').zero?, instrumenter: ENV.fetch('FLIPPER_INSTRUMENTER', 'ActiveSupport::Notifications').constantize, log: ENV.fetch('FLIPPER_LOG', 'true').casecmp('true').zero? ) end initializer "flipper.identifier" do ActiveSupport.on_load(:active_record) do ActiveRecord::Base.include Flipper::Identifier end end initializer "flipper.default", before: :load_config_initializers do |app| Flipper.configure do |config| config.default do Flipper.new(config.adapter, instrumenter: app.config.flipper.instrumenter) end end end initializer "flipper.log", after: :load_config_initializers do |app| flipper = app.config.flipper if flipper.log && flipper.instrumenter == ActiveSupport::Notifications require "flipper/instrumentation/log_subscriber" end end initializer "flipper.memoizer", after: :load_config_initializers do |app| flipper = app.config.flipper if flipper.memoize app.middleware.use Flipper::Middleware::Memoizer, { env_key: flipper.env_key, preload: flipper.preload, if: flipper.memoize.respond_to?(:call) ? flipper.memoize : nil } end end end end flippercloud-flipper-532c39a/lib/flipper/registry.rb000066400000000000000000000023411440444367400226120ustar00rootroot00000000000000require 'thread' module Flipper # Internal: Used to store registry of groups by name. class Registry include Enumerable class Error < StandardError; end class DuplicateKey < Error; end class KeyNotFound < Error # Public: The key that was not found attr_reader :key def initialize(key) @key = key super("Key #{key.inspect} not found") end end def initialize(source = {}) @mutex = Mutex.new @source = source end def keys @mutex.synchronize { @source.keys } end def values @mutex.synchronize { @source.values } end def add(key, value) key = key.to_sym @mutex.synchronize do if @source[key] raise DuplicateKey, "#{key} is already registered" else @source[key] = value end end end def get(key) key = key.to_sym @mutex.synchronize do @source[key] end end def key?(key) key = key.to_sym @mutex.synchronize do @source.key?(key) end end def each(&block) @mutex.synchronize { @source.dup }.each(&block) end def clear @mutex.synchronize { @source.clear } end end end flippercloud-flipper-532c39a/lib/flipper/spec/000077500000000000000000000000001440444367400213475ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/spec/shared_adapter_specs.rb000066400000000000000000000273561440444367400260540ustar00rootroot00000000000000# Requires the following methods: # * subject - The instance of the adapter RSpec.shared_examples_for 'a flipper adapter' do let(:flipper) { Flipper.new(subject) } let(:feature) { flipper[:stats] } let(:boolean_gate) { feature.gate(:boolean) } let(:group_gate) { feature.gate(:group) } let(:actor_gate) { feature.gate(:actor) } let(:actors_gate) { feature.gate(:percentage_of_actors) } let(:time_gate) { feature.gate(:percentage_of_time) } before do Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end Flipper.register(:early_access) do |actor| actor.respond_to?(:early_access?) && actor.early_access? end end after do Flipper.unregister_groups end it 'has name that is a symbol' do expect(subject.name).not_to be_nil expect(subject.name).to be_instance_of(Symbol) end it 'has included the flipper adapter module' do expect(subject.class.ancestors).to include(Flipper::Adapter) end it 'returns correct default values for the gates if none are enabled' do expect(subject.get(feature)).to eq(subject.default_config) end it 'can enable, disable and get value for boolean gate' do expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) result = subject.get(feature) expect(result[:boolean]).to eq('true') expect(subject.disable(feature, boolean_gate, flipper.boolean(false))).to eq(true) result = subject.get(feature) expect(result[:boolean]).to eq(nil) end it 'fully disables all enabled things when boolean gate disabled' do actor22 = Flipper::Actor.new('22') expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.enable(feature, actor_gate, flipper.actor(actor22))).to eq(true) expect(subject.enable(feature, actors_gate, flipper.actors(25))).to eq(true) expect(subject.enable(feature, time_gate, flipper.time(45))).to eq(true) expect(subject.disable(feature, boolean_gate, flipper.boolean(false))).to eq(true) expect(subject.get(feature)).to eq(subject.default_config) end it 'can enable, disable and get value for group gate' do expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.enable(feature, group_gate, flipper.group(:early_access))).to eq(true) result = subject.get(feature) expect(result[:groups]).to eq(Set['admins', 'early_access']) expect(subject.disable(feature, group_gate, flipper.group(:early_access))).to eq(true) result = subject.get(feature) expect(result[:groups]).to eq(Set['admins']) expect(subject.disable(feature, group_gate, flipper.group(:admins))).to eq(true) result = subject.get(feature) expect(result[:groups]).to eq(Set.new) end it 'can enable, disable and get value for actor gate' do actor22 = Flipper::Actor.new('22') actor_asdf = Flipper::Actor.new('asdf') expect(subject.enable(feature, actor_gate, flipper.actor(actor22))).to eq(true) expect(subject.enable(feature, actor_gate, flipper.actor(actor_asdf))).to eq(true) result = subject.get(feature) expect(result[:actors]).to eq(Set['22', 'asdf']) expect(subject.disable(feature, actor_gate, flipper.actor(actor22))).to eq(true) result = subject.get(feature) expect(result[:actors]).to eq(Set['asdf']) expect(subject.disable(feature, actor_gate, flipper.actor(actor_asdf))).to eq(true) result = subject.get(feature) expect(result[:actors]).to eq(Set.new) end it 'can enable, disable and get value for percentage of actors gate' do expect(subject.enable(feature, actors_gate, flipper.actors(15))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_actors]).to eq('15') expect(subject.disable(feature, actors_gate, flipper.actors(0))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_actors]).to eq('0') end it 'can enable percentage of actors gate many times and consistently return values' do (1..100).each do |percentage| expect(subject.enable(feature, actors_gate, flipper.actors(percentage))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_actors]).to eq(percentage.to_s) end end it 'can disable percentage of actors gate many times and consistently return values' do (1..100).each do |percentage| expect(subject.disable(feature, actors_gate, flipper.actors(percentage))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_actors]).to eq(percentage.to_s) end end it 'can enable, disable and get value for percentage of time gate' do expect(subject.enable(feature, time_gate, flipper.time(10))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_time]).to eq('10') expect(subject.disable(feature, time_gate, flipper.time(0))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_time]).to eq('0') end it 'can enable percentage of time gate many times and consistently return values' do (1..100).each do |percentage| expect(subject.enable(feature, time_gate, flipper.time(percentage))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_time]).to eq(percentage.to_s) end end it 'can disable percentage of time gate many times and consistently return values' do (1..100).each do |percentage| expect(subject.disable(feature, time_gate, flipper.time(percentage))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_time]).to eq(percentage.to_s) end end it 'converts boolean value to a string' do expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) result = subject.get(feature) expect(result[:boolean]).to eq('true') end it 'converts the actor value to a string' do expect(subject.enable(feature, actor_gate, flipper.actor(Flipper::Actor.new(22)))).to eq(true) result = subject.get(feature) expect(result[:actors]).to eq(Set['22']) end it 'converts group value to a string' do expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) result = subject.get(feature) expect(result[:groups]).to eq(Set['admins']) end it 'converts percentage of time integer value to a string' do expect(subject.enable(feature, time_gate, flipper.time(10))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_time]).to eq('10') end it 'converts percentage of actors integer value to a string' do expect(subject.enable(feature, actors_gate, flipper.actors(10))).to eq(true) result = subject.get(feature) expect(result[:percentage_of_actors]).to eq('10') end it 'can add, remove and list known features' do expect(subject.features).to eq(Set.new) expect(subject.add(flipper[:stats])).to eq(true) expect(subject.features).to eq(Set['stats']) expect(subject.add(flipper[:search])).to eq(true) expect(subject.features).to eq(Set['stats', 'search']) expect(subject.remove(flipper[:stats])).to eq(true) expect(subject.features).to eq(Set['search']) expect(subject.remove(flipper[:search])).to eq(true) expect(subject.features).to eq(Set.new) end it 'clears all the gate values for the feature on remove' do actor22 = Flipper::Actor.new('22') expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.enable(feature, actor_gate, flipper.actor(actor22))).to eq(true) expect(subject.enable(feature, actors_gate, flipper.actors(25))).to eq(true) expect(subject.enable(feature, time_gate, flipper.time(45))).to eq(true) expect(subject.remove(feature)).to eq(true) expect(subject.get(feature)).to eq(subject.default_config) end it 'can clear all the gate values for a feature' do actor22 = Flipper::Actor.new('22') subject.add(feature) expect(subject.features).to include(feature.key) expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.enable(feature, actor_gate, flipper.actor(actor22))).to eq(true) expect(subject.enable(feature, actors_gate, flipper.actors(25))).to eq(true) expect(subject.enable(feature, time_gate, flipper.time(45))).to eq(true) expect(subject.clear(feature)).to eq(true) expect(subject.features).to include(feature.key) expect(subject.get(feature)).to eq(subject.default_config) end it 'does not complain clearing a feature that does not exist in adapter' do expect(subject.clear(flipper[:stats])).to eq(true) end it 'can get multiple features' do expect(subject.add(flipper[:stats])).to eq(true) expect(subject.enable(flipper[:stats], boolean_gate, flipper.boolean)).to eq(true) expect(subject.add(flipper[:search])).to eq(true) result = subject.get_multi([flipper[:stats], flipper[:search], flipper[:other]]) expect(result).to be_instance_of(Hash) stats = result["stats"] search = result["search"] other = result["other"] expect(stats).to eq(subject.default_config.merge(boolean: 'true')) expect(search).to eq(subject.default_config) expect(other).to eq(subject.default_config) end it 'can get all features' do expect(subject.add(flipper[:stats])).to eq(true) expect(subject.enable(flipper[:stats], boolean_gate, flipper.boolean)).to eq(true) expect(subject.add(flipper[:search])).to eq(true) result = subject.get_all expect(result).to be_instance_of(Hash) stats = result["stats"] search = result["search"] expect(stats).to eq(subject.default_config.merge(boolean: 'true')) expect(search).to eq(subject.default_config) end it 'includes explicitly disabled features when getting all features' do flipper.enable(:stats) flipper.enable(:search) flipper.disable(:search) result = subject.get_all expect(result.keys.sort).to eq(%w(search stats)) end it 'can double enable an actor without error' do actor = Flipper::Actor.new('Flipper::Actor;22') expect(subject.enable(feature, actor_gate, flipper.actor(actor))).to eq(true) expect(subject.enable(feature, actor_gate, flipper.actor(actor))).to eq(true) expect(subject.get(feature).fetch(:actors)).to eq(Set['Flipper::Actor;22']) end it 'can double enable a group without error' do expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.enable(feature, group_gate, flipper.group(:admins))).to eq(true) expect(subject.get(feature).fetch(:groups)).to eq(Set['admins']) end it 'can double enable percentage without error' do expect(subject.enable(feature, actors_gate, flipper.actors(25))).to eq(true) expect(subject.enable(feature, actors_gate, flipper.actors(25))).to eq(true) end it 'can double enable without error' do expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true) end it 'can get_all features when there are none' do expect(subject.features).to eq(Set.new) expect(subject.get_all).to eq({}) end it 'clears other gate values on enable' do actor = Flipper::Actor.new('Flipper::Actor;22') subject.enable(feature, actors_gate, flipper.actors(25)) subject.enable(feature, time_gate, flipper.time(25)) subject.enable(feature, group_gate, flipper.group(:admins)) subject.enable(feature, actor_gate, flipper.actor(actor)) subject.enable(feature, boolean_gate, flipper.boolean(true)) expect(subject.get(feature)).to eq(subject.default_config.merge(boolean: "true")) end end flippercloud-flipper-532c39a/lib/flipper/test/000077500000000000000000000000001440444367400213745ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/test/shared_adapter_test.rb000066400000000000000000000315141440444367400257320ustar00rootroot00000000000000module Flipper module Test module SharedAdapterTests def setup super @flipper = Flipper.new(@adapter) @feature = @flipper[:stats] @boolean_gate = @feature.gate(:boolean) @group_gate = @feature.gate(:group) @actor_gate = @feature.gate(:actor) @actors_gate = @feature.gate(:percentage_of_actors) @time_gate = @feature.gate(:percentage_of_time) Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end Flipper.register(:early_access) do |actor| actor.respond_to?(:early_access?) && actor.early_access? end end def teardown super Flipper.unregister_groups end def test_has_name_that_is_a_symbol refute_empty @adapter.name assert_kind_of Symbol, @adapter.name end def test_has_included_the_flipper_adapter_module assert_includes @adapter.class.ancestors, Flipper::Adapter end def test_returns_correct_default_values_for_gates_if_none_are_enabled assert_equal @adapter.default_config, @adapter.get(@feature) end def test_can_enable_disable_and_get_value_for_boolean_gate assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) assert_equal 'true', @adapter.get(@feature)[:boolean] assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false)) assert_nil @adapter.get(@feature)[:boolean] end def test_fully_disables_all_enabled_things_when_boolean_gate_disabled actor22 = Flipper::Actor.new('22') assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor22)) assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45)) assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false)) assert_equal @adapter.default_config, @adapter.get(@feature) end def test_can_enable_disable_get_value_for_group_gate assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:early_access)) result = @adapter.get(@feature) assert_equal Set['admins', 'early_access'], result[:groups] assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:early_access)) result = @adapter.get(@feature) assert_equal Set['admins'], result[:groups] assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:admins)) result = @adapter.get(@feature) assert_equal Set.new, result[:groups] end def test_can_enable_disable_and_get_value_for_an_actor_gate actor22 = Flipper::Actor.new('22') actor_asdf = Flipper::Actor.new('asdf') assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor22)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_asdf)) result = @adapter.get(@feature) assert_equal Set['22', 'asdf'], result[:actors] assert true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor22)) result = @adapter.get(@feature) assert_equal Set['asdf'], result[:actors] assert_equal true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor_asdf)) result = @adapter.get(@feature) assert_equal Set.new, result[:actors] end def test_can_enable_disable_get_value_for_percentage_of_actors_gate assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(15)) result = @adapter.get(@feature) assert_equal '15', result[:percentage_of_actors] assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(0)) result = @adapter.get(@feature) assert_equal '0', result[:percentage_of_actors] end def test_can_enable_percentage_of_actors_gate_many_times_and_consistently_return_values (1..100).each do |percentage| assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(percentage)) result = @adapter.get(@feature) assert_equal percentage.to_s, result[:percentage_of_actors] end end def test_can_disable_percentage_of_actors_gate_many_times_and_consistently_return_values (1..100).each do |percentage| assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(percentage)) result = @adapter.get(@feature) assert_equal percentage.to_s, result[:percentage_of_actors] end end def test_can_enable_disable_and_get_value_for_percentage_of_time_gate assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10)) result = @adapter.get(@feature) assert_equal '10', result[:percentage_of_time] assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(0)) result = @adapter.get(@feature) assert_equal '0', result[:percentage_of_time] end def test_can_enable_percentage_of_time_gate_many_times_and_consistently_return_values (1..100).each do |percentage| assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(percentage)) result = @adapter.get(@feature) assert_equal percentage.to_s, result[:percentage_of_time] end end def test_can_disable_percentage_of_time_gate_many_times_and_consistently_return_values (1..100).each do |percentage| assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(percentage)) result = @adapter.get(@feature) assert_equal percentage.to_s, result[:percentage_of_time] end end def test_converts_boolean_value_to_a_string assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) result = @adapter.get(@feature) assert_equal 'true', result[:boolean] end def test_converts_the_actor_value_to_a_string assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(Flipper::Actor.new(22))) result = @adapter.get(@feature) assert_equal Set['22'], result[:actors] end def test_converts_group_value_to_a_string assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) result = @adapter.get(@feature) assert_equal Set['admins'], result[:groups] end def test_converts_percentage_of_time_integer_value_to_a_string assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10)) result = @adapter.get(@feature) assert_equal '10', result[:percentage_of_time] end def test_converts_percentage_of_actors_integer_value_to_a_string assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(10)) result = @adapter.get(@feature) assert_equal '10', result[:percentage_of_actors] end def test_can_add_remove_and_list_known_features assert_equal Set.new, @adapter.features assert_equal true, @adapter.add(@flipper[:stats]) assert_equal Set['stats'], @adapter.features assert_equal true, @adapter.add(@flipper[:search]) assert_equal Set['stats', 'search'], @adapter.features assert_equal true, @adapter.remove(@flipper[:stats]) assert_equal Set['search'], @adapter.features assert_equal true, @adapter.remove(@flipper[:search]) assert_equal Set.new, @adapter.features end def test_clears_all_the_gate_values_for_the_feature_on_remove actor22 = Flipper::Actor.new('22') assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor22)) assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45)) assert_equal true, @adapter.remove(@feature) assert_equal @adapter.default_config, @adapter.get(@feature) end def test_can_clear_all_the_gate_values_for_a_feature actor22 = Flipper::Actor.new('22') @adapter.add(@feature) assert_includes @adapter.features, @feature.key assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor22)) assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45)) assert_equal true, @adapter.clear(@feature) assert_includes @adapter.features, @feature.key assert_equal @adapter.default_config, @adapter.get(@feature) end def test_does_not_complain_clearing_a_feature_that_does_not_exist_in_adapter assert_equal true, @adapter.clear(@flipper[:stats]) end def test_can_get_multiple_features assert @adapter.add(@flipper[:stats]) assert @adapter.enable(@flipper[:stats], @boolean_gate, @flipper.boolean) assert @adapter.add(@flipper[:search]) result = @adapter.get_multi([@flipper[:stats], @flipper[:search], @flipper[:other]]) assert_instance_of Hash, result stats = result["stats"] search = result["search"] other = result["other"] assert_equal @adapter.default_config.merge(boolean: 'true'), stats assert_equal @adapter.default_config, search assert_equal @adapter.default_config, other end def test_can_get_all_features assert @adapter.add(@flipper[:stats]) assert @adapter.enable(@flipper[:stats], @boolean_gate, @flipper.boolean) assert @adapter.add(@flipper[:search]) result = @adapter.get_all assert_instance_of Hash, result stats = result["stats"] search = result["search"] assert_equal @adapter.default_config.merge(boolean: 'true'), stats assert_equal @adapter.default_config, search end def test_includes_explicitly_disabled_features_when_getting_all_features @flipper.enable(:stats) @flipper.enable(:search) @flipper.disable(:search) result = @adapter.get_all assert_equal %w(search stats), result.keys.sort end def test_can_double_enable_an_actor_without_error actor = Flipper::Actor.new('Flipper::Actor;22') assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor)) assert_equal Set['Flipper::Actor;22'], @adapter.get(@feature).fetch(:actors) end def test_can_double_enable_a_group_without_error assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal Set['admins'], @adapter.get(@feature).fetch(:groups) end def test_can_double_enable_percentage_without_error assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) end def test_can_double_enable_without_error assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean) end def test_can_get_all_features_when_there_are_none expected = {} assert_equal Set.new, @adapter.features assert_equal expected, @adapter.get_all end def test_clears_other_gate_values_on_enable actor = Flipper::Actor.new('Flipper::Actor;22') assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25)) assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(25)) assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins)) assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor)) assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean(true)) assert_equal @adapter.default_config.merge(boolean: "true"), @adapter.get(@feature) end end end end flippercloud-flipper-532c39a/lib/flipper/type.rb000066400000000000000000000006171440444367400217270ustar00rootroot00000000000000module Flipper # Internal: Root class for all flipper types. You should never need to use this. class Type def self.wrap(value_or_instance) return value_or_instance if value_or_instance.is_a?(self) new(value_or_instance) end attr_reader :value def eql?(other) self.class.eql?(other.class) && value == other.value end alias_method :==, :eql? end end flippercloud-flipper-532c39a/lib/flipper/typecast.rb000066400000000000000000000034711440444367400226030ustar00rootroot00000000000000require 'set' module Flipper module Typecast TruthMap = { true => true, 1 => true, 'true' => true, '1' => true, }.freeze # Internal: Convert value to a boolean. # # Returns true or false. def self.to_boolean(value) !!TruthMap[value] end # Internal: Convert value to an integer. # # Returns an Integer representation of the value. # Raises ArgumentError if conversion is not possible. def self.to_integer(value) value.to_i rescue NoMethodError raise ArgumentError, "#{value.inspect} cannot be converted to an integer" end # Internal: Convert value to a float. # # Returns a Float representation of the value. # Raises ArgumentError if conversion is not possible. def self.to_float(value) value.to_f rescue NoMethodError raise ArgumentError, "#{value.inspect} cannot be converted to a float" end # Internal: Convert value to a percentage. # # Returns a Integer or Float representation of the value. # Raises ArgumentError if conversion is not possible. def self.to_percentage(value) result_to_f = value.to_f result_to_i = result_to_f.to_i result_to_f == result_to_i ? result_to_i : result_to_f rescue NoMethodError raise ArgumentError, "#{value.inspect} cannot be converted to a percentage" end # Internal: Convert value to a set. # # Returns a Set representation of the value. # Raises ArgumentError if conversion is not possible. def self.to_set(value) return value if value.is_a?(Set) return Set.new if value.nil? || value.empty? if value.respond_to?(:to_set) value.to_set else raise ArgumentError, "#{value.inspect} cannot be converted to a set" end end end end flippercloud-flipper-532c39a/lib/flipper/types/000077500000000000000000000000001440444367400215615ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/types/actor.rb000066400000000000000000000015731440444367400232240ustar00rootroot00000000000000module Flipper module Types class Actor < Type def self.wrappable?(thing) return false if thing.nil? thing.respond_to?(:flipper_id) end attr_reader :thing def initialize(thing) raise ArgumentError, 'thing cannot be nil' if thing.nil? unless thing.respond_to?(:flipper_id) raise ArgumentError, "#{thing.inspect} must respond to flipper_id, but does not" end @thing = thing @value = thing.flipper_id.to_s end def respond_to?(*args) super || @thing.respond_to?(*args) end if RUBY_VERSION >= '3.0' def method_missing(name, *args, **kwargs, &block) @thing.send name, *args, **kwargs, &block end else def method_missing(name, *args, &block) @thing.send name, *args, &block end end end end end flippercloud-flipper-532c39a/lib/flipper/types/boolean.rb000066400000000000000000000003211440444367400235210ustar00rootroot00000000000000require 'flipper/typecast' module Flipper module Types class Boolean < Type def initialize(value = nil) @value = value.nil? ? true : Typecast.to_boolean(value) end end end end flippercloud-flipper-532c39a/lib/flipper/types/group.rb000066400000000000000000000016111440444367400232410ustar00rootroot00000000000000module Flipper module Types class Group < Type def self.wrap(group_or_name) return group_or_name if group_or_name.is_a?(self) Flipper.group(group_or_name) end attr_reader :name def initialize(name, &block) @name = name.to_sym @value = @name if block_given? @block = block @single_argument = call_with_no_context?(@block) else @block = ->(_thing, _context) { false } @single_argument = false end end def match?(thing, context) if @single_argument @block.call(thing) else @block.call(thing, context) end end NO_PARAMS_IN_RUBY_3 = [[:req], [:rest]] def call_with_no_context?(block) return true if block.parameters == NO_PARAMS_IN_RUBY_3 block.arity.abs == 1 end end end end flippercloud-flipper-532c39a/lib/flipper/types/percentage.rb000066400000000000000000000006021440444367400242210ustar00rootroot00000000000000require 'flipper/typecast' module Flipper module Types class Percentage < Type def initialize(value) value = Typecast.to_percentage(value) if value < 0 || value > 100 raise ArgumentError, "value must be a positive number less than or equal to 100, but was #{value}" end @value = value end end end end flippercloud-flipper-532c39a/lib/flipper/types/percentage_of_actors.rb000066400000000000000000000001321440444367400262560ustar00rootroot00000000000000module Flipper module Types class PercentageOfActors < Percentage end end end flippercloud-flipper-532c39a/lib/flipper/types/percentage_of_time.rb000066400000000000000000000001301440444367400257170ustar00rootroot00000000000000module Flipper module Types class PercentageOfTime < Percentage end end end flippercloud-flipper-532c39a/lib/flipper/ui.rb000066400000000000000000000025141440444367400213610ustar00rootroot00000000000000require 'pathname' require 'rack' begin # Rack 2 require 'rack/method_override' rescue LoadError require 'rack/methodoverride' end require 'rack/protection' require 'flipper' require 'flipper/ui/middleware' require 'flipper/ui/configuration' module Flipper module UI def self.root @root ||= Pathname(__FILE__).dirname.expand_path.join('ui') end def self.app(flipper = nil, options = {}) env_key = options.fetch(:env_key, 'flipper') rack_protection_options = options.fetch(:rack_protection, use: :authenticity_token) app = ->(_) { [200, { 'Content-Type' => 'text/html' }, ['']] } builder = Rack::Builder.new yield builder if block_given? builder.use Rack::Protection, rack_protection_options builder.use Rack::MethodOverride builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key builder.use Flipper::UI::Middleware, flipper: flipper, env_key: env_key builder.run app klass = self app = builder.to_app app.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output app end # Public: yields configuration instance for customizing UI text def self.configure yield(configuration) end def self.configuration @configuration ||= ::Flipper::UI::Configuration.new end end end flippercloud-flipper-532c39a/lib/flipper/ui/000077500000000000000000000000001440444367400210325ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/action.rb000066400000000000000000000216341440444367400226420ustar00rootroot00000000000000require 'forwardable' require 'flipper/ui/configuration' require 'flipper/ui/error' require 'erubi' require 'json' require 'sanitize' module Flipper module UI class Action module FeatureNameFromRoute def feature_name @feature_name ||= begin match = request.path_info.match(self.class.route_regex) match ? Rack::Utils.unescape(match[:feature_name]) : nil end end private :feature_name end extend Forwardable VALID_REQUEST_METHOD_NAMES = Set.new([ 'get'.freeze, 'post'.freeze, 'put'.freeze, 'delete'.freeze, ]).freeze SOURCES = { bootstrap_css: { src: 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css'.freeze, hash: 'sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l'.freeze }.freeze, jquery_js: { src: 'https://code.jquery.com/jquery-3.6.0.slim.js'.freeze, hash: 'sha256-HwWONEZrpuoh951cQD1ov2HUK5zA5DwJ1DNUXaM6FsY='.freeze }.freeze, popper_js: { src: 'https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js'.freeze, hash: 'sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q'.freeze }.freeze, bootstrap_js: { src: 'https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js'.freeze, hash: 'sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF'.freeze }.freeze }.freeze SCRIPT_SRCS = SOURCES.values_at(:jquery_js, :popper_js, :bootstrap_js).map { |s| s[:src] } STYLE_SRCS = SOURCES.values_at(:bootstrap_css).map { |s| s[:src] } CONTENT_SECURITY_POLICY = <<-CSP.delete("\n") default-src 'none'; img-src 'self'; font-src 'self'; script-src 'report-sample' 'self' #{SCRIPT_SRCS.join(' ')}; style-src 'self' 'unsafe-inline' #{STYLE_SRCS.join(' ')}; style-src-attr 'unsafe-inline' ; style-src-elem 'self' #{STYLE_SRCS.join(' ')}; CSP # Public: Call this in subclasses so the action knows its route. # # regex - The Regexp that this action should run for. # # Returns nothing. def self.route(regex) @route_regex = regex end # Internal: Does this action's route match the path. def self.route_match?(path) path.match(route_regex) end # Internal: The regex that matches which routes this action will work for. def self.route_regex @route_regex || raise("#{name}.route is not set") end # Internal: Initializes and runs an action for a given request. # # flipper - The Flipper::DSL instance. # request - The Rack::Request that was sent. # # Returns result of Action#run. def self.run(flipper, request) new(flipper, request).run end # Private: The path to the views folder. def self.views_path @views_path ||= Flipper::UI.root.join('views') end # Private: The path to the public folder. def self.public_path @public_path ||= Flipper::UI.root.join('public') end # Public: The instance of the Flipper::DSL the middleware was # initialized with. attr_reader :flipper # Public: The Rack::Request to provide a response for. attr_reader :request # Public: The params for the request. def_delegator :@request, :params def initialize(flipper, request) @flipper = flipper @request = request @code = 200 @headers = { 'Content-Type' => 'text/plain' } @breadcrumbs = if Flipper::UI.configuration.application_breadcrumb_href [Breadcrumb.new('App', Flipper::UI.configuration.application_breadcrumb_href)] else [] end end # Public: Runs the request method for the provided request. # # Returns whatever the request method returns in the action. def run if valid_request_method? && respond_to?(request_method_name) catch(:halt) { send(request_method_name) } else raise UI::RequestMethodNotSupported, "#{self.class} does not support request method #{request_method_name.inspect}" end end # Public: Runs another action from within the request method of a # different action. # # action_class - The class of the other action to run. # # Examples # # run_other_action Home # # => result of running Home action # # Returns result of other action. def run_other_action(action_class) action_class.new(flipper, request).run end # Public: Call this with a response to immediately stop the current action # and respond however you want. # # response - The response you would like to return. def halt(response) throw :halt, response end # Public: Compiles a view and returns rack response with that as the body. # # name - The Symbol name of the view. # # Returns a response. def view_response(name) header 'Content-Type', 'text/html' header 'Content-Security-Policy', CONTENT_SECURITY_POLICY body = view_with_layout { view_without_layout name } halt [@code, @headers, [body]] end # Public: Dumps an object as json and returns rack response with that as # the body. Automatically sets Content-Type to "application/json". # # object - The Object that should be dumped as json. # # Returns a response. def json_response(object) header 'Content-Type', 'application/json' body = JSON.dump(object) halt [@code, @headers, [body]] end # Public: Redirect to a new location. # # location - The String location to set the Location header to. def redirect_to(location) status 302 header 'Location', "#{script_name}#{Rack::Utils.escape_path(location)}" halt [@code, @headers, ['']] end # Public: Set the status code for the response. # # code - The Integer code you would like the response to return. def status(code) @code = code.to_i end # Public: Set a header. # # name - The String name of the header. # value - The value of the header. def header(name, value) @headers[name] = value end class Breadcrumb attr_reader :text, :href def initialize(text, href = nil) @text = text @href = href end def active? @href.nil? end end # Public: Add a breadcrumb to the trail. # # text - The String text for the breadcrumb. # href - The String href for the anchor tag (optional). If nil, breadcrumb # is assumed to be the end of the trail. def breadcrumb(text, href = nil) breadcrumb_href = href.nil? ? href : "#{script_name}#{href}" @breadcrumbs << Breadcrumb.new(text, breadcrumb_href) end # Private def view_with_layout(&block) view :layout, &block end # Private def view_without_layout(name) view name end # Private def view(name) path = views_path.join("#{name}.erb") raise "Template does not exist: #{path}" unless path.exist? eval(Erubi::Engine.new(path.read, escape: true).src) end # Internal: The path the app is mounted at. def script_name request.env['SCRIPT_NAME'] end # Private def views_path self.class.views_path end # Private def public_path self.class.public_path end # Private: Returns the request method converted to an action method. def request_method_name @request_method_name ||= @request.request_method.downcase end def csrf_input_tag %() end def valid_request_method? VALID_REQUEST_METHOD_NAMES.include?(request_method_name) end # Internal: Method to call when the UI is in read only mode and you want # to inform people of that fact. def read_only status 403 breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb 'Noooooope' halt view_response(:read_only) end def bootstrap_css SOURCES[:bootstrap_css] end def bootstrap_js SOURCES[:bootstrap_js] end def popper_js SOURCES[:popper_js] end def jquery_js SOURCES[:jquery_js] end end end end flippercloud-flipper-532c39a/lib/flipper/ui/action_collection.rb000066400000000000000000000007111440444367400250460ustar00rootroot00000000000000module Flipper module UI # Internal: Used to detect the action that should be used in the middleware. class ActionCollection def initialize @action_classes = [] end def add(action_class) @action_classes << action_class end def action_for_request(request) @action_classes.detect do |action_class| action_class.route_match?(request.path_info) end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/000077500000000000000000000000001440444367400224725ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/actions/actors_gate.rb000066400000000000000000000026131440444367400253140ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' require 'flipper/ui/util' module Flipper module UI module Actions class ActorsGate < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/actors/?\Z} def get feature = flipper[feature_name] @feature = Decorators::Feature.new(feature) breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb @feature.key, "/features/#{@feature.key}" breadcrumb 'Add Actor' view_response :add_actor end def post read_only if Flipper::UI.configuration.read_only feature = flipper[feature_name] value = params['value'].to_s.strip values = value.split(UI.configuration.actors_separator).map(&:strip).uniq if values.empty? error = "#{value.inspect} is not a valid actor value." redirect_to("/features/#{feature.key}/actors?error=#{error}") end values.each do |value| actor = Flipper::Actor.new(value) case params['operation'] when 'enable' feature.enable_actor actor when 'disable' feature.disable_actor actor end end redirect_to("/features/#{feature.key}") end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/add_feature.rb000066400000000000000000000013351440444367400252640ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class AddFeature < UI::Action route %r{\A/features/new/?\Z} def get read_only if Flipper::UI.configuration.read_only unless Flipper::UI.configuration.feature_creation_enabled status 403 breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb 'Noooooope' halt view_response(:feature_creation_disabled) end breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb 'Add' view_response :add_feature end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/boolean_gate.rb000066400000000000000000000012041440444367400254330ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class BooleanGate < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/boolean/?\Z} def post read_only if Flipper::UI.configuration.read_only feature = flipper[feature_name] @feature = Decorators::Feature.new(feature) if params['action'] == 'Enable' feature.enable else feature.disable end redirect_to "/features/#{@feature.key}" end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/feature.rb000066400000000000000000000023531440444367400244550ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class Feature < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)\Z} def get flipper_feature = flipper[feature_name] @feature = Decorators::Feature.new(flipper_feature) descriptions = Flipper::UI.configuration.descriptions_source.call([flipper_feature.key]) @feature.description = descriptions[@feature.key] @page_title = "#{@feature.key} // Features" @percentages = [0, 1, 5, 10, 25, 50, 100] breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb @feature.key view_response :feature end def delete read_only if Flipper::UI.configuration.read_only unless Flipper::UI.configuration.feature_removal_enabled status 403 breadcrumb 'Home', '/' breadcrumb 'Features', '/features' halt view_response(:feature_removal_disabled) end feature = flipper[feature_name] feature.remove redirect_to '/features' end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/features.rb000066400000000000000000000032741440444367400246430ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' require 'flipper/ui/util' module Flipper module UI module Actions class Features < UI::Action route %r{\A/features/?\Z} def get @page_title = 'Features' keys = flipper.features.map(&:key) descriptions = if Flipper::UI.configuration.show_feature_description_in_list? Flipper::UI.configuration.descriptions_source.call(keys) else {} end @features = flipper.features.map do |feature| decorated_feature = Decorators::Feature.new(feature) if Flipper::UI.configuration.show_feature_description_in_list? decorated_feature.description = descriptions[feature.key] end decorated_feature end.sort @show_blank_slate = @features.empty? breadcrumb 'Home', '/' breadcrumb 'Features' view_response :features end def post read_only if Flipper::UI.configuration.read_only unless Flipper::UI.configuration.feature_creation_enabled status 403 breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb 'Noooooope' halt view_response(:feature_creation_disabled) end value = params['value'].to_s.strip if Util.blank?(value) error = "#{value.inspect} is not a valid feature name." redirect_to("/features/new?error=#{error}") end feature = flipper[value] feature.add redirect_to "/features/#{value}" end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/file.rb000066400000000000000000000004251440444367400237370ustar00rootroot00000000000000require 'rack/file' require 'flipper/ui/action' module Flipper module UI module Actions class File < UI::Action route %r{(images|css|js)/.*\Z} def get Rack::File.new(public_path).call(request.env) end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/groups_gate.rb000066400000000000000000000023531440444367400253410ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class GroupsGate < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/groups/?\Z} def get feature = flipper[feature_name] @feature = Decorators::Feature.new(feature) breadcrumb 'Home', '/' breadcrumb 'Features', '/features' breadcrumb @feature.key, "/features/#{@feature.key}" breadcrumb 'Add Group' view_response :add_group end def post read_only if Flipper::UI.configuration.read_only feature = flipper[feature_name] value = params['value'].to_s.strip if Flipper.group_exists?(value) case params['operation'] when 'enable' feature.enable_group value when 'disable' feature.disable_group value end redirect_to("/features/#{feature.key}") else error = "The group named #{value.inspect} has not been registered." redirect_to("/features/#{feature.key}/groups?error=#{error}") end end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/home.rb000066400000000000000000000004051440444367400237460ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class Home < UI::Action route %r{\A/?\Z} def get redirect_to '/features' end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/percentage_of_actors_gate.rb000066400000000000000000000014731440444367400302000ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class PercentageOfActorsGate < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/percentage_of_actors/?\Z} def post read_only if Flipper::UI.configuration.read_only feature = flipper[feature_name] @feature = Decorators::Feature.new(feature) begin feature.enable_percentage_of_actors params['value'] rescue ArgumentError => exception error = "Invalid percentage of actors value: #{exception.message}" redirect_to("/features/#{@feature.key}?error=#{error}") end redirect_to "/features/#{@feature.key}" end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/actions/percentage_of_time_gate.rb000066400000000000000000000014631440444367400276420ustar00rootroot00000000000000require 'flipper/ui/action' require 'flipper/ui/decorators/feature' module Flipper module UI module Actions class PercentageOfTimeGate < UI::Action include FeatureNameFromRoute route %r{\A/features/(?.*)/percentage_of_time/?\Z} def post read_only if Flipper::UI.configuration.read_only feature = flipper[feature_name] @feature = Decorators::Feature.new(feature) begin feature.enable_percentage_of_time params['value'] rescue ArgumentError => exception error = "Invalid percentage of time value: #{exception.message}" redirect_to("/features/#{@feature.key}?error=#{error}") end redirect_to "/features/#{@feature.key}" end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/configuration.rb000066400000000000000000000076001440444367400242310ustar00rootroot00000000000000require 'flipper/ui/configuration/option' module Flipper module UI class Configuration attr_reader :delete attr_accessor :banner_text, :banner_class # Public: Is the UI in read only mode or not. Default is false. This # supersedes all other write-related options such as # (feature_creation_enabled and feature_removal_enabled). attr_accessor :read_only # Public: If you set this, the UI will always have a first breadcrumb that # says "App" which points to this href. The href can be a path (ie: "/") # or full url ("https://app.example.com/"). attr_accessor :application_breadcrumb_href # Public: Is feature creation allowed from the UI? Defaults to true. If # set to false, users of the UI cannot create features. All feature # creation will need to be done through the configured flipper instance. attr_accessor :feature_creation_enabled # Public: Is feature deletion allowed from the UI? Defaults to true. If # set to false, users won't be able to delete features from the UI. attr_accessor :feature_removal_enabled # Public: Are you feeling lucky? Defaults to true. If set to false, users # won't see a videoclip of Taylor Swift when there aren't any features attr_accessor :fun # Public: Tired of seeing the awesome message about Cloud? Set this to # false and it will go away. Defaults to true. attr_accessor :cloud_recommendation # Public: What should show up in the form to add actors. This can be # different per application since flipper_id's can be whatever an # application needs. Defaults to "a flipper id". attr_accessor :add_actor_placeholder # Public: If you set this, Flipper::UI will fetch descriptions # from your external source. Descriptions for `features` will be shown on `feature` # page, and optionally the `features` pages. Defaults to empty block. attr_accessor :descriptions_source # Public: Should feature descriptions be show on the `features` list page. # Default false. Only works when using descriptions. attr_accessor :show_feature_description_in_list # Public: What should be used to denote you are trying to add multiple # actors at once (instead of just a single actor). # Default is comma ",". attr_accessor :actors_separator # Public: if you want to get a confirm pop up box while fully enabling a feature # Default is false. attr_accessor :confirm_fully_enable VALID_BANNER_CLASS_VALUES = %w( danger dark info light primary secondary success warning ).freeze DEFAULT_DESCRIPTIONS_SOURCE = ->(_keys) { {} } def initialize @delete = Option.new("Danger Zone", "Deleting a feature removes it from the list of features and disables it for everyone.") @banner_text = nil @banner_class = 'danger' @feature_creation_enabled = true @feature_removal_enabled = true @fun = true @cloud_recommendation = true @add_actor_placeholder = "a flipper id" @descriptions_source = DEFAULT_DESCRIPTIONS_SOURCE @show_feature_description_in_list = false @actors_separator = ',' @confirm_fully_enable = false @read_only = false end def using_descriptions? @descriptions_source != DEFAULT_DESCRIPTIONS_SOURCE end def show_feature_description_in_list? using_descriptions? && @show_feature_description_in_list end def banner_class=(value) unless VALID_BANNER_CLASS_VALUES.include?(value) raise InvalidConfigurationValue, "The banner_class provided '#{value}' is " \ "not one of: #{VALID_BANNER_CLASS_VALUES.join(', ')}" end @banner_class = value end end end end flippercloud-flipper-532c39a/lib/flipper/ui/configuration/000077500000000000000000000000001440444367400237015ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/configuration/option.rb000066400000000000000000000003251440444367400255360ustar00rootroot00000000000000module Flipper module UI class Option attr_accessor :title, :description def initialize(title, description) @title = title @description = description end end end end flippercloud-flipper-532c39a/lib/flipper/ui/decorators/000077500000000000000000000000001440444367400231775ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/decorators/feature.rb000066400000000000000000000046231440444367400251640ustar00rootroot00000000000000require 'delegate' require 'flipper/ui/decorators/gate' require 'flipper/ui/util' module Flipper module UI module Decorators class Feature < SimpleDelegator include Comparable # Public: The feature being decorated. alias_method :feature, :__getobj__ # Internal: Used to preload description if descriptions_source is # configured for Flipper::UI. attr_accessor :description # Public: Returns name titleized. def pretty_name @pretty_name ||= Util.titleize(name) end def color_class case feature.state when :on 'bg-success' when :off 'bg-danger' when :conditional 'bg-warning' end end def gates_in_words return "Fully Enabled" if feature.boolean_value statuses = [] if feature.actors_value.count > 0 statuses << %Q() + Util.pluralize(feature.actors_value.count, 'actor', 'actors') + "" end if feature.groups_value.count > 0 statuses << %Q() + Util.pluralize(feature.groups_value.count, 'group', 'groups') + "" end if feature.percentage_of_actors_value > 0 statuses << "#{feature.percentage_of_actors_value}% of actors" end if feature.percentage_of_time_value > 0 statuses << "#{feature.percentage_of_time_value}% of time" end Util.to_sentence(statuses) end def gate_state_title case feature.state when :on "Fully enabled" when :conditional "Conditionally enabled" else "Disabled" end end def pretty_enabled_gate_names enabled_gates.map { |gate| Util.titleize(gate.key) }.sort.join(', ') end StateSortMap = { on: 1, conditional: 2, off: 3, }.freeze def <=>(other) if state == other.state key <=> other.key else StateSortMap[state] <=> StateSortMap[other.state] end end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/decorators/gate.rb000066400000000000000000000014741440444367400244520ustar00rootroot00000000000000require 'delegate' module Flipper module UI module Decorators class Gate < SimpleDelegator # Public: The gate being decorated. alias_method :gate, :__getobj__ # Public: The value for the gate from the adapter. attr_reader :value def initialize(gate, value = nil) super gate @value = value end # Public: Returns instance as hash that is ready to be json dumped. def as_json value_as_json = case data_type when :set value.to_a # json doesn't like sets else value end { 'key' => gate.key.to_s, 'name' => gate.name.to_s, 'value' => value_as_json, } end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/error.rb000066400000000000000000000004431440444367400225110ustar00rootroot00000000000000module Flipper module UI # All flipper ui errors inherit from this. Error = Class.new(StandardError) # Raised when a request method (get, post, etc.) is called for an action # that does not know how to handle it. RequestMethodNotSupported = Class.new(Error) end end flippercloud-flipper-532c39a/lib/flipper/ui/middleware.rb000066400000000000000000000027571440444367400235070ustar00rootroot00000000000000require 'rack' require 'flipper/ui/action_collection' # Require all actions automatically. Pathname(__FILE__).dirname.join('actions').each_child(false) do |name| require "flipper/ui/actions/#{name}" end module Flipper module UI class Middleware def initialize(app, options = {}) @app = app @env_key = options.fetch(:env_key, 'flipper') @flipper = options.fetch(:flipper) { Flipper } @action_collection = ActionCollection.new # UI @action_collection.add UI::Actions::AddFeature @action_collection.add UI::Actions::ActorsGate @action_collection.add UI::Actions::GroupsGate @action_collection.add UI::Actions::BooleanGate @action_collection.add UI::Actions::PercentageOfTimeGate @action_collection.add UI::Actions::PercentageOfActorsGate @action_collection.add UI::Actions::Feature @action_collection.add UI::Actions::Features # Static Assets/Files @action_collection.add UI::Actions::File # Catch all redirect to features @action_collection.add UI::Actions::Home end def call(env) dup.call!(env) end def call!(env) request = Rack::Request.new(env) action_class = @action_collection.action_for_request(request) if action_class.nil? @app.call(env) else flipper = env.fetch(@env_key) { Flipper } action_class.run(flipper, request) end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/public/000077500000000000000000000000001440444367400223105ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/public/css/000077500000000000000000000000001440444367400231005ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/public/css/application.css000066400000000000000000000006741440444367400261240ustar00rootroot00000000000000html { font-size: 0.8rem; } .flash { margin-bottom: 15px; } .mw-600 { max-width: 600px; } .bg-lightest { background: rgba(0, 0, 0, 0.01); } .toggle-block-when-on { display: none; } .toggle-block-when-off { display: block; } .toggle-on .toggle-block-when-on { display: block; } .toggle-on .toggle-block-when-off { display: none; } .status { height: 10px; width: 10px; border-radius: 50%; display: inline-block; } flippercloud-flipper-532c39a/lib/flipper/ui/public/images/000077500000000000000000000000001440444367400235555ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/public/images/logo.png000066400000000000000000005141551440444367400252360ustar00rootroot00000000000000‰PNG  IHDRÌûóþ@˜4IDATxìÕ%€¨E†á÷;3ÿú.îîîn‘½¢½Ð ½' Öpׂ»;wÿg·ï½=\ÛyÊè›îm†a†a†a†aÃ0lºûá×^Xôy%8MUÇ)½”ºnr ³‚½œfIá…ˆRJ„Œƒ´zÏ(¥î!¡’.47ψ?üÖ›ÍÌŸ¢Ä§¶^Šï1 »=ñ» ÞëaÓç›…S£èxÛG ï‹X_(uI¨Î9Ki,&JE`hó€#µVzïØ  ø¯c$a -“(Ãü∯IÞíÒmÀ Ã.L=÷ð§˜ÿåìåÅ•«zúâNÄy%VÝ{XÆ62 aŒ,Pê Ø$‰{F)1EÅ2s› ƒ%$¨eaã4™Ðf „e0”RA ‹tÒ³!DH$ ƒ!{úsÌ sãà†a¢Ÿ~ƒÝÍ0Ä´tRå÷«3¹¬DžµŒ´VXF€ã„Þ&Á€ 0B…R+¶‘EÒé½!¶ ™Z',“­‘YDÕJXX`'Ù“$Àv=2B”2!€ vÇŒA „1AQ0ž[ó›oîe'=õì[ ~BYŒÝóJˆ£B^Ïì )‚ѳ¦D…À‰,€`¤@þ¯wÇd„@¢”§Ù~@!¢µ†€(g’™¤LUÁÎ B5#zoHŠØÜƒDf‚ ?·¹?ÝñMÀìàGŸ{‹]É0,Ä|±=] \‘½¡EE‘$œ9ƒEÔ…°Mï0µL(clc@€ys‡eŠ™¦Ï )± „PB ÀÛ2æ>Pc™ìô_gèî`Pù“}s—ÝÙ–ø¯VgöÞçÿlÛ¶mÛ¶mÛ¶mÛ¶mÛö{gOÒ«Þý&ëëäÓµoês¢éžI²ª«ª»Ñ$1çLáº^26 ¬±Þ—a}I1ú/ìžùw³8ð<qà€¾ïg²Ö7­½²“7&óù“þ„‚ŠÅzX>€Ó£g)ÛËIIÒ{ ûüƒ ZÐhX†t‰h¤²®:Ö–Ÿ€6X,ý<ˆ @Ñ«_A`A ¢µaW%‰,ºdˆÓi#º\ÃùTB–rB£}<Î,ÒÒ¿%|<ð‰8ð` Ñ ëÀƒ™·/=Åéݰž{:éq°šË6r&ML'Àˆ¨.Lnù€‚´qvƒ´i…8ËR2fŸP#h1j c Ï3¦ÓiE:WÁ`6" qj§"°™4”t¹´MÏŽDµ!B1®DôœõxYcãúî$¬q~Ê„!a1Ecéþ¯ó<ð!sFà ‰W#ÂE>i¯ý)»,ˆ ÑìÕ„{b™0@Vsb¯ ‚65À¸†{‚ ZƒºÞNº;2à ÊzóÚnÊ4‚¾^æ¿Î ï| < ïý‰_ã¡W77OËù^¾«='øižPŠÇ4yÇéH-ªxøÕvÒ—Ž¦–€=±fÿüƒÔþ´Ïóïœ{ÿùùËÿþ1à.`<ù“?ùÍ2ýûûÑâõ”ýiÀ!šÙû¶N"4îì3FDL€ÉÝl¨ñ§D´•x2ìK!×ûŸË‚dÙZÓt…źŸˆPHà@ŠÊÀöN(ˆ¾ÌØ Z)Bdæåó‘X1ZÚµa! Xû]¤Bz»>Ô€êCöŽa\ß4mÄdèÙI'˜®ÿ…Ü­ßù÷ÿ»yyào8pà}ãüM¸¾ö£Üáü†äòŠj<Éã§òš¡@!²'2¨Àe ˆˆ!÷‡ßœKv#;öî}3!<Ï}¶âŸ$~ åwßêQ¾ø/ÜGÜ'$n?$B¯DæAÆEÔ:Y "ÀÈ"T³’l£hàÜÕ÷ØÂc :aSb¯TÈšVkUølÈ" lj‹ a¶ s{ž ´@Ø90TëGªß;%±¶u½/Ç÷¶äËõÁfkõâJgýfƒ-¤¸´¯R*Y €èîC55MD H/ó’_¼=<ÐV¢ÿôC>™{ÅSÓÛ'ýùI?–q ÚûÄ`h§VÞp®Û- Ä2/ƒ€E¢Mع¾˜°Y2T Nƒ Ï é^mMSbÿ7ÄŸ¦ã§ºôYÀq€{–þTýæêãƒ|qäÇj­ CÏ¥îϸ~a÷Ý@<†íÑ@¢÷¹îa ‚—M›®Ph¯ê3À›¨0{,ËŒá¢@6™½Š³ˆhÕ&؉jVÕ–O‹˜&4¬'˜—s)•ê÷²Œö%Õs&l³ÌuoÏ5bjj1”Cºïžoc J‘IBN³!Wû[_Ù“¥úߦ‰P b]zÿ·óÜ_øE@8 ïÿɇÌ,,™·Uó;†õô™Ëuk‹=ƃD¨‘f¬/H½€&//¨TÞ°…ðÒIy?k¨ÿlƒ´‘!] Ùë1ç¸^KñoRüdæÍÇ¿Ä#^þÅžé^™Óo}0äÛ?±e55™{…A\6Œa7e¶a4$‘}a“$µeÌ(àN#¨›H©5ª@V¶Ò°¡/gØÖs,Ëyø>Ž1/6Míc=of?¹ŠÝ˜ö*‘ÙIU1V H ÓN²Y²CÖ¶uù…Æ ,EìHÜD;!;Éô6BW Èk¡Öp;+;À Ý¬®ÌKûÁš;Åà-ÌÇ5ò, r,«œE”òGHÎÎÜÄJ„ë÷÷Ö†EW8,LRm­{qúI=‡D›êûÏÆ`HÐZY‰Ù—÷ÍóÜù_í™_øþŸ`ràÈ@~Ç<(ñ”ÿ÷w÷üóÝøŒ)ô:€«°SE]ëήàC†) ›©÷$`-eX²JQ¬ÖDﵿÎëE4 !ư uI†"±¸|®1-¶PuÇ(Pû"-Ú/Ä·ñ?ÿûîÀ¿òp€öØ÷hý¿ÿã“ã¤× ù1l)IîÈP °~¥D‡û y Oã½¾zô'¿ÿý«BýD4;1CªÙ-  ¦ÊJhE"YÅ£¼dU!¸'Ó©Š*¹*‰YÏQ§ËŠd Ù“\fhëg(ób1`€úÃÞF´NœÉ6PuÙ& rí+ÕÖ<Ïc4zº¾6Ä?_ür/òœ ˜‡b|÷ÿácßsú¿/´ür­µ{TöL¯õC :i1PSë1jT/qšˆòܵ†lº;ZHd78/…3Ó„„«‚˜Æó``g-©¡"JQJ^³€4„š†òLSí'ØÀVäUÙ„Ùú¼ÜË…yîX}!«…„ÆtÛí7JTYŒ"KiKÕÞÊ0£ýýšÞ—]¦ { ÌJ|ÂõœšR7—ó©LÈ™´Ò¹ ˆ ÁH¬ðî,Vâ5œ®®ò<ß~ìý²øðÀýÈÿ³÷P’dKv.ü™Ì¢î¾8mßg¥L‚Óvœ eTz°ž¶¢yF?0Cd¹nFˆÑD»©µ&ª–>¿|ud‘ÛH2óÈTAÝUG Ià¶W©åÒ™l±p”­+ Ñ™aÕq¿¡ûSÚU”¦ÃÔÎdŸ‹óÝ¢Ö$F±ŸÇþºK¯%×Uj‚Üyσ<2œÔý¥ õè"¨½æjðüÀþÄ|Çß w‹áÒ×ZEˆW’}ò  ‡Udw‡éþ6 éŒY͸‹üàó¹÷ãÏŸ|ßžq^|þ©ö/™#pIƒÎú‹ê~ÿ}÷Ü‘ÇCÂí³ÁZfôšÐd¯° «2r21`{ð¢ŠUzà ˆ¤ÖòCfÍã‹™&`nre€Ñuòfþà}æv @A´°ß<> ‘À`E°åè(Qï‡qsâtóàÞÞœ~ÎÇ¢ æùÝþOóï¤øài;uZqï]•Aiö¤\*d›z C"€Î¥ÛY›R S“s™GCÄí2‚rw]­©ÿ;zv»ö11u à÷ T>üKYÂÚ—2ãn¢p‡²–}ÎËDqž“Ì@¥žhÌ&F°ERa>« TºŒÎ6ú~Í`íç&µ l%\ôƒ»®S]а,RÂZÙ²¤ìlQåÏ‹ðgªä˜C<œud@µ¨99ny~w÷+€ŸÌý¸Î äÛÉYµ¾t¬õ#ó´¡»Áf²\ËÞ÷;Ô>Éñw—f¡#.ÀÔ`õÏ[ƒNÝZK5_»G—JUuF> 4O„y$ öy0‰Õå£ÏSd¦Òùèî­µ–ºqÔÜ"zÕœÕWq“ëQÚ( kì!pµÁƒ‡Ö¢~ÑOø?øç|Gíÿûoâæ}ò7þ‡ã4þÒ„Çfä„¶cÔ5S1­?”£¯£'Ù«­º15”šÕP‰ª.û–ƒmk?ñ¢Ûféíe¸ø©€í§°®Þþ±~¨Ýuwww¬*ˆ$ª8w·Võ¤ßX×Át—h£Êi…‚óùŒîk‘&9ìh‡º¯.qöe^…,k —¸Üõ§Ò]õ5qô¤Ï?éýÝÜ&Üû«Lsi®F`1ËM“mKÂÑ™NŽQÏîžßc"÷ã;‰YofÄ#¹¿;’ÊŽ–EE™çq¾#,aØÀ1 V?˜z¤JnAäPÊPÄ(òÙ®i;\±³`34Ø]sR¸îÕëõjut¶èјAàÒI!¬·?ã:×õ7\:Ém+<|=oýõÀÿÁ·süÅŸ÷^ßô_ŽÜÿòUu£¨Xc±29ˆ”GÁò1WUO4ú›ÛoÏçghq¥E¼ Ü>mcK²É˜šlÔ½h‚Ö¤bÅÊí°µð¢²O]’bÖTÿ:h1T×¥5ºMª–t_U=1)óuûl^&¿*ðDÄÛ1ŒmÄ>•Ô HÔ~|œ×Jµë6÷%ÀIq«¯«ã<Ñû:JT…eäýX„ºk RæÒÌxM 3>¿¯5¾€:ßÍø§ùŽ÷ã^ +bÎd$±@µq= \ÊÒÒÒ"ëH¹»´¡®¦UÇÙÁÓ:0 ‹Ì±åÆ:žøL ªºK+¨ˆÃÏ9Õ-À'‹É:/HM<öiØÏg¥".ŸÔêò×vVÚ&KYÔQs&Øçsü%O&‹¨~ vÓƒ¾TO|$«1“ÛÛÛ:Ý<üjÌŸ |„oègÛ?Ô?@­G»Ú{ë6Ï.Ñ%Ô”j%0‹íöFåŠrØÊôÙŸ{tamŒ N—n©ÁUwœˆ†&øI³1²p~£z^ðˆ3«³›U­¨K,EàKHóO–îw°ö>%5w¨Æ@¢ÙíÍÃÿGÇ«óåêŸÆŽû¨'ZŪ©ödˆP;7Û‰¥.¨:¤W:ãöù_žZ“«¤ £ LØÒ¹›Ù@}ÄpwÕØäg #¶îfÜ…%IUËÔÏÉê gDPдî+ðŸq5îÇýòÕß·u(<Ê-$Ìó{Xž @×Ñ¡eº”> h ô´Xë¼·b*ê"À`%Íöíþû4«ÖÁ ÇL÷m$ìû‚Z݉#a¸íwt¤FT–ßö®_$‘¦KsU—’Ø¢Äîõ]²ãµ®t#ÉZ%f¶2ø‘¯ÿñæÜ‰+«Ê5óìXHÌåŽr-·fg$MÎËn«¬}Q6Î!¶$#ßæ'91.™Çbo´}BÜõȪcò1  ÙÏçÞ}‰ñë™ ^ ‘ï¡(‰ò‰ýÒ%‚û“q<\€Ýä5ÈpÔÉ‘!Í}©‹‹9÷ý¯züäÉ<Œ›Ïþ4<¯ñbÛÿWâüa‹HXœ¤2¦\Šd¯ ›¡è³™Þ« i¦ãúyõº=%§“9«'ëm$î¶Ø-E“ P{Ÿ“5Ÿ»P 'I|Øi"Â%ÂÒß…ai_Ÿ6QU˜2e¢Ñ“€¤ijuÆÚaq 10>Îtr¬• -.ñJÆFŠÐ(nM„eê ÌõÃ>'4ªƒ,LAhýžðÕ\$Qíé>Ïd%% *!’° %„uÊÝ1ƒ|탯Ë—ŸÇý¸@¾¸!øö,û¾þT[Ž`áP ¨E¢Û¥$šcáõ„t{#+ª=pt§/xÍuK%|C²Ö”[s£#-¢%º;c™³ èßч:r'«M~B¾×~Q,íîÄéìÌ~TÁœa½'ÀºðI>ò‘?õ½ß¸{úžeüÔ øÃÏr~1ãüMUóJyOOâ`ºµuaPV™,ìÑÍoÀ_d̹Èòçnnoxüè·7Í/ É_ç®S©Üx)G½ùìo¼ù”gÏŸ3•Áz^Uj¨+a‰swÉ•#{”I\_·Ø*Ïè¬xÃTB¿ÏÇí]2y:–Î,ôš¨°ò5s7€>ÏXš=o*µü=á( :›rqÌXadM®Î¬úûƒ.›l«õòXº“.…ÅtY·ÖêŒ%9mñÃÖ:ÿ3/î—û兯÷ŸHXðbß?•à ~UØïa¤ÀRKG¨íœZT]QýÐYò’VUe«Ò,k_fD×Ñ=³jšlkìî[îÁŸ5A¥³§Ó "/í ­,ÌÂ~!,¶W> éYJrå‘“*?D†¬K±Z*ÎÔŽýiþ+¯½oN7¿!j}vn§@&unL`$G-Üzó2F¤zÿm¸TQ8’6Ë[iYžc$§8˜jçæõb}Ý>Ý–}týøÒ\ ‰ÍʼµœAD¹dˆ[´±É¸£ôÓv£öp‹öq ãY:ŸÝÜ×ßJQÞzèzì;ÕÛ×uññmM »5Õž%”';†³2 Þ{ÂDø¡»ÎôŸ,kbq`ˆyi¿®BÚs­ò s-c`¦R± »éŒFÓ’eô¬öÛ<”˜ÏžóyÀ¯ç==îǶÖ¾½cÆÝÏÛ2¾"*RÚ<½kÙ~ÐŽÄXAu‹£t~$ÚÇêÈ_Í5'Óq»jøÌ8@Û ¨;èšÁÑZdl°ªSzõÓ˼¼z+ÅR6AeA,²MeWªOgéah¬`íæ£° ¶A™½}ÎuéhzøÊ#¶12X?TøçßbJsLUœÞ¦°&”ù ë'©‘@“‡!Š‚ ×?&ÑÇŠ-í‰ÞY“Jîþ?Gyêîù~è2剕ò¨T—wÁ¹PFÄ"m²‡.x?·EWg€zœFhâc5Oׄ"á;b‰Óø×’øg*Y”"ã³—$ÉR .df–;Rvdä.“~U°:³©PfÀçE†°°ÿtz$Ä jJ¦t“ WÆ´Ou”»¦*‹ O~U+ [RQ©bTœ™á÷º+G5¿Âû^{777ÐëC12!CGž”èõý0,²-~S‘¤ëþåvbܘp{:ñäÉ#>¼QG”1aFtöFO€o¼ù&o>}zájÌZè*Àꮼ¥ãªv/yˆÓI…îejŽ“’nË”ýÕÃØ™ô–iéWIg Æ©ˆžü³Ï>HýÐú‘"­ÚodÕ±Ìe<(Ýq‡: )ݯ€®_P–ÉG'Æ:<ÜîLšÌ¤…uýýc&d„–Æ XH•±¹ðÇåŒÄ ìØâü7ÖŸRsðÞ\î—–¶¿…ïÈ1×›ïí6þUVÐ2–¥#æ´AüŒX‹hùðìçÑ<ƒUÓ fó,2¤E¤(Ë:\&!>@÷åë˳(bÉ÷a‘ü( êK=«#ÇLˆ8˜½8͇YMx”žúú[&^%µ–—_«È‚Ê#"g•êôþÄíǪiÑfn¶au` ‘á(X­.\S bôùïvi«¾Úã <úáãÑ5÷c©H´ëã%|¾ïÌswRQl9Ìy;Οù%ó©%BŠRKw‹Te÷>H=¿ô¾c;d²¦ºà¤Õ˜ˆ ¯”AD«,Mž-ÖX¬šWתö׊Èßô14Ù‹Xê,atˆoB’D CRudÄE E¦‚ ˆ”v…÷Ãûß΂â,9c¦UÄ«Òú¬ZL§ Ïs:óø—þ&8žÂ#Pó·Ëi‹o>›÷ä¸9âß‘ËÍÿöÝ~þ™ãõ¨–Ì‘„´­–0‡#;Yaž„¢ÒÜT/’$Š+£!RXªˆWwZýu¨õTz wóL€Â]+˜5y°u3ÕÁ2e ±T·\†“ÊR¢’$»ŸT‹Çºòµ¾9ݼÈ8ÞÏíÃG$fcÇ8¶?ˆžP–"N¨p‰[CG’Ÿÿd‹$sëî4ùq$Œ Æñ€··8l6F¢ìÇB|ÅÓgw¼õô¹"‰ F¥²B€­®-j°XÔ*™6]y©PÁB×.¥Ÿ((e‘DëŠR”/ßXÎ^ˆ`Y…p‘’Ž—…Ãä:dŒ&c1ù~D¸DäNÁp·öo·ŸŒÌÌpi±s1É({p ¹&suŽì^›Â݃ ¶Òö“e0'I’…¿f×G¤[„u…Ð}VŸ¹×ü…/Þ{Ëý¿òË¿s)nà}óÍ?ýõ[nßµÜmãZ~¤=Ê;H=LX*i]øš„*Úƒz­‹’m Lg1°H*ât ÕÛœç ‰L¨X½°d;Ž}T$=6uư´Ÿú2ImwÉ;[€–®¶ØX5.§€ìn)üÐ`u{ñÃK¹èQèýœÆ ýFgÃYøD”¤î/ëUXuvŒ¡‡-‹CjH}v$·©TüÙ‹"TaR¬ânîœ÷)pš :Ó+ kÊIkbEÀ,J„É%‚ÚÑ×\rª;­3Ö&¥R¸M5->ëZ–=ÌàŽÎT˜ ©˜IFÇ¥)G¦2ÏéÌT<„ ø!ÛìŽüóŠ9S„pˆî;2Öh€ÜIœi@Ý <îÐsöaûà1Zºeúú I¶¬)M,Ok© ËæOË©èz ©)UíþnÄÅÄøð>Þsã~äͶñ±ðüõ?3¶ñÝfñ¥QEƒ¼)YuXVö®Lݾºô%])ù ²":{QwSg+¡2™k^1uéÔ=pC—ÚT£ŠæI”£Mõ9"OFÂ,­/¿’c_;Zöþûá·´~$Ôa–ôøµWŽrI‰u'»3"ˇ±yò,‡ín“) ˈ\x^,‘`¶ÇÂÜŒý¼xã­g<{våó=©Qy3Œ=dg Ùø‹2:­¿¿ ×à+ÐTÜT*jî}± ¼Ž§úPÜ#š§UsíÝb’£Îs]²BYF_Ã2¶®Öð0Ã|Œp7–ÀM2Ê&ÀYF-³ÊVôâÉDŸé©NÇOyg0,–›âñ°ò4î””c¨‘'—85™f˜'R]NëÀðµ}Ö?ÿbá½µÜ//x _Ïwöˆà¿,qåØww¾;Ì”0ëŸîr#¥F¶Õj[¹®%67[œ@äMÂ7Ÿ/Âÿ³þ±Q¡ÆŠd??‡èrfÑt ÑFá+¾¦ÒfÖ¥‰gÍn=öä¡ûW~*‹9-ÍC„Î?Õ™[6~&™xÊ¥·c› ÚϤOŽà2jrl¿ ·á Âá¥+oÛê¥ ǽ´5ûZÙ 4Fg{UG74²V«ˆ÷`r?ò4à;{Ù’¿~VþTÍŲÆ]ÒŠÖVÏ"Qô½\y/LÜKHEY Ö’'G*Bu§Ž2™ÂY°ú= •‡DÎâÚ”ˆEiÛ‹ I[ ¶÷Å«x_—»Aõrº½áÕ×ÞÇÖmÇvIô±‚kÍz È<ˆðñŠ:ÍýгC¬eEû§ÓàÉ£[N·²Æ|€o¿8Ï££êùy2±)‘2ÝLL[,úÜj÷Æ-ݸc.EâÆœÌšÎ„5 2ýÓ2û:¹~¸—(a`¸Äˆ$Jcꀽ>Ф9#à(ڶ爲)—Ä(Êe±Z#XO :‚f§ì¼·jŽõWk“³¶—M¼ÎùCsã‹×šQÄÉrÙ‰A°àZb<’µwæÓ=©jæ5b·éÏ=4±‚*iþ„c™ÁNiÂpd– ˜3²æ™ˆÑ“M°¯óÑRýY Klož<~ÂíímëŠGµ7“¸?¿n«3 4Èr;lA“1•Áy×iëضäñƒ#ÓQä\”Jo2vb]äÓwö5¯÷84ÅÈjÜÁ‚†̵x¡þÔZ˜µÄáfœÈ–jñ8Vcíã'*Vsb¼½@Üd+>›Î}ð'VgJ›° ض5Nt7Þ]+=YÇC{Õ<"r¬-Öåêž “i\*5Q¹9€Äí¶RcVCƒð"ázÒa«€‘›1·¨k¬£ZC¬ùE«ýT:s°¯‡†ËxDY8" ¥l}Ï­Î(lUøu(’t—Xp|N(s‘¢6̵ÿ!àSyOŒû‘ƒùr¿n^ûœãuÊ€xtÊ/ÉŽ¡Ò(¾‰NŸMŠ+& Y–»T²=2zt+Ó%í(Ü ø•tGÏpÓø=i8J;¯=y•*³!áD™¹>Ú›®u¢#L¢=œPÙÄÒE›žxøàÆ­ŸttYV)^s]:ªÞzöŒ»µ;±.#[ŸÿÞRï£3‚c)4D°³ç66¢Ð0[|‰×ÑD¹¡‡$Qn=VÆ'yñÕJË2=²ü{Žö{×jiÇÀí¿—m)bg©ô£‰Mc±b]eŠ¸Í™wV…öAۺ˜4v‹p„ä\,$I)ë1 îLÓy¶úгWj[OcV…¿[–4YV.t•|­a”Ê*•ïôq†² ŒC¡Œÿ“™7?àÅÂË¿Ü/ñk¾ô[øxJ¾Ë6Î߸v^»¨©ZÓèˆÒnJ<º”"•T „ìî—…úâArï3“ê÷©f®–Ëè¨pV¡RÀilfœ·ÐbȺ·÷MÚX"Ëm7'žç‚7ŸÞB‡  IÅLþ4 -ÞDÿbÂZ W“þÉÎ#0Ž«ü´nö¹ºf›GEã,Áß@¯Qjÿ9–Ù׳#ó’ম/GéV—µä¼Ú”gÕ5N"À½ÁnÓZ4a˜g#tDúYê§]aï´o€¸0¨ÖÃB­±‘º'BXb{ô±èD˜ažžhËÒòdƒô#‚ _?Öbu«q™™oß^,néûQ“ÎpŒeI¶æÇf ^þå~‰/þêoæã=ò.?yßî¾…UïŸíüf‡¹bÔR4%Ö2_@}õzæÍ½î~ ­.seS¨äDâ`žS‹}±\ëM€øðç¶aV¶¾½ žnnH‚9gï3&§<ÁyÝõCjÓCΰjı?Ø;\|uÇGGÚ©ÿRB‡]¾AùѬKgÕÜ…ð(ç°ƒêÒqÔ‹>#7kz1˜Xû‰l?’ àÁÍ“|l‡"ê¾3çÞ¥ˆUŸ‰8kª¦#·6»#©vÖ<·;¢kö§mðèv#…«¥ø²/ƒã’ñ¼ñæ[­ùµ`NsŸ—ý™óLÍyH°dZuÖOe2MhÛû#és n*p)k­ý˜È÷ÝÑxTB¿^k’ãØî²/G¿>ÙçÎÚÏo¡¯ÓêýcàY%D{v$9¾Ð\³ÇoÀÜëîóL‰[$y‘ûª}5iê||þi#rôý6!mŽEBJ;jŸ _˜à8Ÿ}ïAåÙý|ùìêóP¸†í XAuتК\Öó1)»Ú¾kbº¶‡·InØÔ*¡÷s˺s÷5Yk9S©œÙJê%âJ„sÞ®¿êÅÂË»Ü/ùNÜ©ó³õ¥Äé¯ÚrLEgQ@ÈÀJ²!Ö‘‚U¬µ\'ŠT}LƪB%’åh¹K4!¬Â~âÑ ðÊ+¯qz`›ÙH{Jȧ#3q7•:e^;m˜[!Í.iXÝÞlܶ¥í œÝ¨æÁÝyòæÓgN3à¼BB|fѯÀÑtZÊXkº;K]i(úí,'kHå×Õ.eWŽ®¨·t]ë6yûYÔÛº|샞×þàËo“{?»-Ö¦xûݭ̲ÒY£ø(¤ÎÙÒÒßä'c9ֺ͕²w†³‡½NZAY3Ê\°w*g)ß ×—ŸMŸ_¿£M97ëm>¤] uO®¢–=åe‚„(qæm6`E‹…²¹5«'—)Ûòsƒä%^îa ïıVý=#êß<ß=ÏK÷S‚ ñÉRÆe{ØÖºÖØ‚¹ï’{Nb_ï(×ûQùª ÚOƒ®}gòꫯqÚ¶#r¬ê,¢_ïš±ÛT*Å%.=Àeakp_~ ðèá-¹mîÞÑþk2Zų H¾¸ðhÂ}§-¢×ûв»‹K{“‘'"5áv-<ƒ-7ï†&µnínµf’Û~«€ö3©Æ‚ZÒBŠ…÷kî€ø6"NpìÊu=Ö2øQ„ÎÛ•¤½±‰ºjƒ•|Éj¾Gg`Fvó|@e± ’V…†J[1ÓÛÏÞ¾»ÆêÊŸc!ß™ÃS|ÿàëdnGVµöö]9©±Â]ˆx~<þßÞüÑËÑ’þª¦¶ŸL«8¸ICšeLÝ{ß äÊhÿò¾ëÐëì”%&;:7óÇþðÏ>Åýx)GþØ/ø Þ©ËûáŸùïLÖÿ˜C2 eO‡]¶²‹î$±ªì”ŽU€e-úÿÅ’å©Ê;ýp9†˜îÉãÇ‘e*…K¶Ê%~‡¨¹áüÿ*Šei©ëÞœ¶Ëäa£'[Tkò8ßí¼õôs-Ê”?[Àf"@¾®¬€¥n ’9IÀÚDx¡··æ”ö[ ~€÷ùwv£+¡Z{ÉAÏÓ½…#š¡W¯:¾¤A¦§y¤}ñitß(S»U.í0®üé•YÎ(#Ã{ξ”ŒmøZ$öÁlerMê9<Ö‘úBv¦PøË[”µÀ´ÿö#É2·¨ýdìE‚š¡¬ÅÂŽæšt6~🖳« ¶lgé*7_ö•¿ñ¯x±ðr.÷ËöâÞá㯺ã3¶±}ö9¦52Y™”|<ö ÕLã•ýmAg(‚¶]Ãu—QBÐãxmî‹1Ož<&r`¹‘¢:ãéÒˆÕmÇtù£mq+¶.G,?05Úëäæf0òˆ@‰…†‹yiÍ}Þ ¸ÉjávXK©×Þ˜NËÐÛoˆ”ÏC[ SyÙŽtÂȺ…V—OÆiñCäÏAèœ7;zGz][ Ï,¨ªpÖ×çÿØíÿ¡ æÄÊ ¼Î¿®míûq<¹€²ÊZ¬ïŸÌ‘†‰}a‹€vä;Ö/ÂMª}0|þ\’Ó9O’YJ tMDÆV(KžH•w–åFÈ¢½øÎ w&uDüµ 8†k}þVÍÆTœñ ‘cã¤)bµZHôRYù„ üžÔt€gAlIlÉúB™rr:¯¿øï¹/åÈÓ¼Ó—?õo>oÜÜüñ-“=èõp¥@š‘Ýßn!ÄfòZ›¨[|%‚<ºYý÷¥ðëð å ¨þ®Î%KYLNe6VÉ­ìm„]#$mÛxüèÆºNá¬BÙÒù¼óÆÓ·XSÙKgPýù–›è¾(G›V÷슜M˜¥A`¬I~ ¸Kª³ ktEZçjÁ1™€6!ÞͱPæ¹Da ªüì]ö?Ëð¨a"ƒS“c{pÈ‘Ñ~Ò*³Z—Häý@Œ€÷(ƒÈ ï—µ¡ˆåëã.4e n#W Jj«×±#¥>¿¼ \.Cm´¤wïØW{ÿ¯6‘R  ¶içÅ:¦Õ³ñÅd­ºv¾6SKòCöéŒ63?pdðr.÷KüÚ¯øFÞ ã|wþ^77ãß|ó[Š‘- ´ºmŽC0Ð|±t5 º‡jÊ`ÛÑNâЃzòäHó­•í'i'¿´Ê-]ã73¸UصïŠWuéèº}p…I_Vkeqw·8ÏiƳÈfž÷—yáìÊ ýrB¬¹ˆ vù§lƒ‰ØÆç9 ìVóÒš\R›=ŸÏÇz⣰üpÁÀ¸œë¢'G Kåë… cSØ££ucC–y`ÛûÖ¾,š µþ Mzò^q™1J uöO+2KøráŒF˜FX—¢#[5Ø÷A žI`òˆd?߇èã”ÎÔì 0PV;´ý9)a Ú~XŸìêa¾h E•^ùÙ Æ6°ÖÙj~¸¨%œpÂZÄØ ïGÒáû]߿՘H ªÖÀ+Ü—räÉ»ayððöwÜÍõWŽí4©rf®O#m«ºv‚“è›è®ñ ‚Ö4jæyÀÍÍ-?:cÑÀ/‰ÊªEA4¹ËB~„ÎsñDV bñèÁ 7’ÜE"õ7ßzƾ/uÈÈ¥Yø‰\梭IµØ×ºÁy¯Ÿ*—ˆW ’Yw6¥ÖKtŽ[ÆCÅŠERÆh¢³šeÿõIz›ò)wÞ«¥¥ó!hd¯G…÷?û3Ó^û2Ò¢`…£xj³«Ýa%ý.±ºµo•%¡Ò3 Yäbk‹½ïmÉkØ~˜ï!’ä*ðù“§­ýhÎÖôšëظ¡ÀZRî"Ј+GBcVê2Û¶«Öå:Y• ÆéÔ£°µ}Ï+ÌK]…б¶ÜX5»„µš xt0VK™%Ÿ3éû|ç™Z`ÁRS‰«„˜ô96w½µÃá9âó¯á~¼t#ßÚïx7-œnþ¡1n¾¤™çfZ aT…Y×K¶›b|‹I àÿÌäætÃÃG«¬h+ó&PõçZ”¯Örg–eIµwü·mÉ£·D4AQëØõ2q<~F”†0ì²EÄñPèõíõ`ÏsmŸÕ“ß wm ?1+]­¹2r´[X&¼â CŠìLM,u¤õDw꘸¨®¶‘~¸z[@,ɯ#ƒý(ã á#ÇBy]û‰ s–8c\”Œ¢\ g©ÛØHu9™‹a÷BOýô5—‡F…ÏG˜["z³^ŸÂYcX©8­R f¾üZœý•‰°Ê p7s1*{}±ßµýP¦s™`”*X)²¥Î™87j™¶§ÿš“Õ¿æ¾~\î‹—o¹_¶Üï¶ñã~ôùq¿ê‹¿öÞ=öÁ*wæýåèyíT¦µ‰¤rº€X1Ä'¯¾Šž<šxäzËeÙ…vîÓ»àŽá#6ˆÖ„ªàáƒ§í¤‡Q¼í¸×]1OïžË5QŽ‚]ž‚ÉΈG¯Öв­*Œ´6"«™Až†»Ö8:̪Æeÿ2¢U_×Q#_ÇUÍ|ŽÕŸÑ]XëÀr|>™{Ñ'ʾ.Ý=fÅÅëÜŧpî(*óYÔ%º=™å‰¤„9`ÿ MözÐÏ5‰Ä6Ø*ŽãÑ~+Kl•ÜU‹qTe®ÅWËŠ0À¾ÙëÛ2YNƒ[êúO¨Îäȃ—¤Ò"-©IÊʆ°êpìöÜÈãþZûbÖn{ß²Ÿ‰U•7¢µÂöæ×Œî²ª‚HI¤èt6A.!zÎooõ½Ï³ÿæÆR+õçp?^Ê‘c;ñn[¾è«¿en?#2wEM†NË8@ÞЄàz½ÀóNå/2ì?n ;I1Š)Ì’ì÷`(c¡W"‡YI×¥ÇEA÷´üp´òmã+Å[ÏŸSË„·bõÿÃG8—MÍ«¬Aá¨np!®õ™ªm“”Œq‡””åàzxhƒ ²Æ?å³^Vrmþ‹¢þ´7 n¡ÆµE2MÔ1/(¥E-g.ú,!3…qÄ5Û;c5á ªúu ñdJˆJÔ=&~ KZV:ç gÌÓHœ&Ünññëâ!gGñ0jÙóÃûoŸukU ü–æ«&Û†µÎ0±ßïðÐqè¡{ø è_UßsUñò-÷˶ªxWŽÓï3þã9ßü;ã·5®½³‡žI:•o'ÄZ´ÞNƒ'Ož¸í—Ó®ý$:ã±7ô¢¨Ö¶2ñ ÔGŸƒ'¯< 4™WN‰wwçf'þ²'#Å7ÙéU:Ëp“€@kwe%â›ôŽw­;p>)¹–kÙ)OÇïL#]ù'óJúʲßFÙŠöª[ŽkÊ8šH¹åù¹'Ì ŸE¨LRÕn G|…Ú‰µýHg)êAªXöÃHE«ŽèKUp[«Öµ;„N™Ž®jŸóMì[Q(£1fâí«²ÊÛW:©òáÖj ²`•t®²$ó'J“âëÚ:œ¾wÀ*ÎŒoœHÛ)‘ÄD†[Ñ“¦Ô?íõÑ­Ëb¾+c‰¶0nåÖèRZ–3AÆj g«á‰¶›#ª»åÅ”v›ßEè:õ>8p ™ð©ý\„ ÚzŸŽaÞÎ\‹EÉv¹ÏÝz@-^¾å~٨Ży¼UõSí›öóù­$«( Ù|Zº»e¨‹UGÙêÑQ¶êÈ|bÑÅ Ñ<‰²#_Ø“Ú-©S5õe}«Û›nÞ6ƒ|Wu›Aylâ­AÜFA¤6Þ‘*L[{v’ªÉ˜d†Á׃Qí”…ÿZˆq˜ö71xmÙƒÜ:þþÝëC! Éq*Ø×Nè|Âu‹1a›à• »Œ³ èw) ú¼{RëpYÐäBù\ÈÇÝå/Ê8—ð&÷]¯ÃÑÒÆâ…ìè>‹Èã¼H¡¸»âª¯_TL‚#ã[ëP.Ðu£BûÕŒ~ˆ KŒƒ3îS¾ús{½lçK€ÄÇ¥ûíØŸZÈ7gÍå ¤jö! ßj@~_Dp8:V›GÖ›ÉEg\ÑŽû~¦å‡Év\dÔ-÷ã¥É(Þ͡Þÿä·î«þ›V·_®*wP‚},p7ÓÃ\ub³Ç{•,4ÂM¿Wr!¸·¾Wɪ¸}xÃÍ͆ٹXát?Ïï°ð¬ZOÝÐê*¹@Ì–ûÖsÜÝA#‡º‰­J6D“ˆ'û=ÈG‰æ5ù×3:¸æm? ñršíŽ :–c˜ño‘XOÆÂ“¼kƒÏþ¬ÏæS>åS;£ìí[ö_YX{«!‘½¿MÂ\@†K‹Å%ó"éìFçD<)aŽ-|ƉûñÒ-2xÆÕ7¬_ÿë¿ì¯øßý}3ì'u¡¦—*<~téŠ*ŠÚ;²5BÑf±äÈ–GÆ ÒA„„þ’Åi ^hu¹]Ê}‹e½.­Læ*M(‹Xî£Gíº8ÚW&áQ{`¯eà×ÝE;´tz‡ëÿnã,‹2ŽîT3¶BGî…|×µÁQÜ 2íÑ]Ó%´u1>eTd6»?)p6BÏù¿!ÐÐtrå`¸;¢±ÿ?MÅ *ùœzt´n>Œ’Šëígbi+û‹}‚Ô’$  û,g–+t5yþü9_øÅ_Ä—|é—\J¡®ñÇÿÄŸ¸¼ïÅÂg~Ægñ“~üOäƒ~ÿV䳮暌ÛfRôÍ"ºµÛÃ"¥ nE{ØÏ2@«ZÅýx)G|ÉW#/ËøÑŸÿ™ù¿ý?¿öÿØFýìjѽÓiƒˆËÏÛcë.•;Ä – `æÁ<6Dm¹ƒ1‚ÈÆ1´fÆÜÜ ‘ݵR5QèÙÝ.±BÕø»VÉb§-T8Ý»¯ÖãŠÃ9ŽàÉ4#ÝYÑÕúî® k/e—wö»;·ÊÎyd,Á‘f ×Ĺ/‰Xjýð1c[~d;!¶LLaÞL¬#Û`-ÙçR…ù!9¨Þ®ø'–‡‘ÿGcPzýÚ…åã‡ÎnF“õjN*,¹ï»™â@Ʊþœ“âýøüùº¨ mI›J“P\ûÁQÂt?채 ?Wyÿ{«ìŤðŸÿWÿå‹Éáóÿq\TþÒ_ð ùŒð(3ß&úê¶ë²F@Šˆ(­µlͱƘ”½*’‘¤æ eöÏÜ—ndÄàeY¾ôk¾eýóÿä?ý×WäÓ¢ì^ô­Ü>;ÈõšéÞy\gÑÛÖd5Àº¨^ÀÍ6®&Î ºö}ñìîì‡X`§8¯„‘V)ì„¥ Ü1%ïtñ ôЍˆ³ xRypJˆðúÑ`”¶s|¬´¯4¡‰Ï¤0‰²Û›)”³ú§ÄøäýŽ3@+ËÚåÐ?ÓÝR&7^w…>HÛIDZˆÂ¾N»À¸º¢Ìµ¨òúÆmÜ2¬¿°/)øø}ÿ‡’ºþÎÐ*ÜÙ4Ñ–Á¿ïüþíïßý6L"ªÞñßþÿ=_ñ5_CÅ2®äVè£| *l ec–=O®²?×Jw¼ «'gMbq¿¼|ËF,^¦ñMßôå¯?~÷+>¼ýYr™»¹½elÓŒaß$nOó-©ªáâˆw1ÅÁÛm(m—…§Êpž;Ïïvs¹b¸Y£Ë:J&̆ï§8å&M*V,—¦äÇ=’Vg@KÇÓ]]ò´>mÂÚH½óG×ýl¿ô¨c¿öšÞ–@{BŸ/'’"cé@Ûuó¹XÔ­4þ‘1”ÇY$q$zàêáíóGóW ~Ý`Í7éno£K„j—Íè.«k˨ܪV£-ccûï² 2e Ba\@óDéŒÏ­³ Æ_G6çš+êÀ«â#¯¿ÎùßüW<}ö”oïøñ/âÃüßç{}oŠÙ‡‚­EvÙÅ~4ãè0“¦YŒÎ0×êL0–üC'J7¦lϸ/åÈZÉË´|É ¬_úËÿ¿sĸ#áÑ+L°Eâ2Q³ˆéÂE .›8G”oOlICQw/Àù<¹»Û­Ì5c:\AV÷׊ºz8Z-6Çp‹e‰%±@ ¼à.¡ý`å˜X2¬\A±Úü(Hk„µkbväÍ‹é½w'™Þß ¾ù1D’Hø°IsáÆµÞö±Ôµï»É„Ê  Ÿ?uj™ü82s£Îµ µ²šY ×çOÙÅÈNôû¾ð{ÍxÆ·R€:ºì§¢)éšÜèã7ßgÕ•Š'sOÒÕ“ßÿ?ÿ—Iä;b¬µøþçÿ™7ßzJQ"ªsL÷+°È²‡Œ/ ñý G<½( ¡Þ\÷ËË·äËxPÿãñ_üÑ7ÞzúåŸ<9HÍ$ßwu Yzc-¬ÚJËžË-Ð*¤²åу=yàÈ}d[~^JVp3Ç+!à’Kf^ðŽÙNo™y%Œ¸æÁÂΑÝÊÛÑ*@ùá"” «+›Q$®Ž!zTۚΞ*;z<¼ßç¾3çd.e¸ã ?"¹¯µäº(,öy¾t3ÍZžc³ :¨ÅÜ'kN朗÷îû÷‡~®¥øY—L0Û¿>tÿsíöÜŸ$¤E;tŠ©×hPð~îÇK9’€—i™çÛï½Æÿ0Óž"vEêkAkÿlŠ"—T´%UÝ#àÑí ¹ ;¡U ªxúìywÓ¦$%pdiBE½sM1Ñ]ã__ É.?„%¯žÆ¨ÓžÉy¬+õVñGFº3*¨k&{ÚqJ™iÕÕcÝÑ‚$¥yUʶ}#5éRNÃÚ[¤ÏHi·qžLOP½ŠE)ŸSÄš^>šI@Y™É6’Ó&Ûºb¿Wy}YËG¼ìuaœ œÉLìºc,‰TôžZÓ,XC ãIú˜Ñå±²>¿ù·~çÚMÿæßò[[®ÆÊ¿YØ…g—Q©óÅZEfZ´“…ÞjvßÛ£â{üØÏÿìx±p¿¼\Ëöâ^–ñ¢T|ûûþø7|ùÚëá0Ï­Ù4]˜qO¿$)¬*2…G@󣛫ãö°*º(µ®?ù¯þæÿ øK¹/ÕØ^\X^–‘̯ɪO¨($ùnÉ ÚÜ(ÄnŸt;¨‰¼Èi’êhX#-ÀÇ[Ïž¹9cÁÑ÷ÑZº`•éÙá¨Zxh¦²& 죾JѬÕn‹qLn½ÿd‘g KQ¯Ue-m¾YnBeŸ./¹L½ ޼»Á8KçCçÌ*¾PxøÁèœm¥‡¶ËT% ÞX(?Š3;F>dt&#o‹ZÕ>÷ùèÏž=GyE:{ ¦>7 ˆŸÙî”ö—E¹´Fˆ³s´¹0E†ÌãÜf˜÷²–¦—Â`¼þúGøÿ:>ñ>Ìßñ7þµ|ögü >ðþ÷ñ{ÿà ¿ô+øoþ§ÿõm“É·¾þºð8²'É””ŠÄ± f-ÆJÌ‚9¿ _㹤ƬžSeZæ/\‘ßó­óú;€¯å~¼c{ëü.ý˜ù ËLm‚쌡-| z“ݲ«Ž œ;éœõUGW’æo7 ”!l¸',åsÑúcG×–‡'^ÿÝ~öÊ58;÷é̆ãoÝ‚[jMóñ’»ï‘öü»üíûW‹4.é{­ED6~¦õôÏ2ØÒñ²·°‚)[Ê?5Á–Žõ£Ÿù¿?ÿÖ¿øÏòøñ#4¾ûwý.üõåwáG~Áçñ7ÿ½ÿàe¤°|¸¦-f“E}@ÆÍ||€:ÜÆq.fgâDJùXä\²kÈÉþÞlù5•œ·çêrâœe9޲ò¾tJéÙH5‰Z±zùÔŽ‘W²;£­{³bq¦êù¾æŸ\ký®ml¿nÛ·ÿøÃ|›ÆýØ^ã†wû¸ûßwÚò®Žìâª$` —< Îv%Ä=ìDŠº‘Ž“¯-Ìá§O Éq›¥"r°(Ëh¯E¬¶M%¨ÄM·½þ–müxTQ±H1¤ Y=¢BÌbÕyœÅPö«pEß$7IˆàLÅqxèÍhÇ.t~€»ÛÑLVKëF„Öñ¾…}çñáÞV„ñL½=‘¬«÷{"ÒqÉÞööæti³°n<'õ™WxGè!f(³ÏSWPÄEÿèMd¾¥¥¬$ÊÙò!çTÛ¼úêGí{™ÿÉèïóäq=^¿çßô×ñ¯ÿ{ÿ1O^}†äô'ÝEç‰ãÞ€RHX°5Gi_Æ¢HM¬ûuW5a剌”üßQEtŒ“Ô_l¹»P µ3NØçrv[¾”Ê–Ó±GO`6ýJê“*ø³Î?}æþÏdð‘Ø¶¯}þôé?|÷ã£ùæúVÞÍ˺}ö#濲ª’f\¯µÛþS|†ŽBk•ù’ºµ¿6O.­ºãÚ—Èø OïfbÀ85dßÙ箇 kŸÌæŒíÄ89Ü–ƒ-·®k(ï –ç'Ì]ƒŸä–Gßý¶1ƉͽøŒæJdn\S!»WßÌ}9G¯?.ë{qn"­ªðú>Þ}ß/Ë\‹$„Y°æák1r¸­Ù²ã½ýÁšÇú@¯?;›ðˆžÖšÍÙÚ^6EÖÃÆVŽÏãØÎœ»²$Ã{ݲ;¶æIôu\kg¥óBÉßcÛȡŭÇÙ¼–Bmäc°.ü—#{|ÿû>úæ¥Ïû¡?˜Oû”OæÏ7~úOú æ0ï{í5æ\ìû’£¤øKjCwƒVbsPUÑ߇>@ Jí×D¤ðD¼T&ìQÇù<~Îe‡Äéfêj?417»?\ÝDÁÌJÉ>~RƸ,GjRïÛÏ矰mÛWŒíôG'ão þÂËý’ïæÿ^Ÿú¹·5ùųæ s¬‘¤ò ìÿC,ë2åeòxÀ؆ÚuÉ+3 âÍgÏüàs Nv4-Épu{ 1óÙõó* ;òU°f÷9œ%©~ß™~Àq­¥Î" âÎ.ëxV²—ÙÝÊ& Ì3 籂¨¸b‰G•b4IÓÝF îF úg• )Úoe>¾6b§¿­5WÝp=ü°NtΔý(Ê6&† ˆpö)U•®ÄÒ¿î^+ÀÍ$Ib¨.õ½Ž»Õ–¹+|Ÿïó½ùhÇ÷ûÞß‹¿Ð8N|ÏïþÝÐø^ßë{ÂÖ Ä+×ÏBÙ:Hg)‰­y»w†1&´š_¥X×¾h‰ÚÚ­80Û3ÇeM­5¹µ:„ˆ¦™¡u®.ÆuÝ-¦ìJeâ6eþ„›QÿÑíØ~‹Ÿúçwä»_6ïÚñûÿä7}I­ú`ÄYØk¢Õ'‡[TéÉa¬XÝeR<|p‰‰z¡½Ë•¹P¬®Ÿ¶ ;½e6;»µÄ!’jGĬýZÊ“в·Ÿ¨»e5i¹l¢:‹±ÒìœÓm¢ÅWX{ L€DŽyDûqxb˜í!+: •¼] s H•Ì\ß•n„}À²ÓÔ:"aá¥Ì¢3+hKéwŸ¯`±VHP.…Â-Xj¥- ±I°Ûß^¼ˆµ¦}¿þ[a#ô³ÜO¶ùÏÉ ÉóÝ{0Õ º´Á¬åÅ*ð]?í»ðÚ«¯ñ‘¿0˜~ñšùhƳg~ßgý Ô^çÆ„8œ…ƒþ)u™b¥î›`ß'È+Ý7sßmnÕå[êðN7ÇGÝgÙ¶ªqP*+ b͉7ªv„ˆTxò8F5XævÎf¤Éfa?ýæfü¢Èü–Å[¿ø-¼mÜ<=¸ãݸ<¾=ý [ð¹a •ŠÕÔsQi#”kAe“ºtðdö#»¸2V"â .xV¨Ÿ"º.Ü£ò¤œ-E‚8 f/£¦¢XuÖ^ÖX1Õzºå.²·ýM›tüퟩx7 ¤ýdoqu“&½–R©en†0Š’$ øa+?ÂÊ·‘h¶¶º/aËW—·Â¡´ƒò*×XŽ&95U{}gy¥½wOûäl ±Ó™/­éý#½3β LÇfÁú")._ñoø‘?ü/úè8¿õ·óo¼ùæ¥+ à{¿È>>ù?g Î; *9ÕûÄ;JMÂXq8ŠHUÈnW÷4Æþùd¹aÃ÷ºéÔag‡HëQ6Fr­G†‡™ý„|t|}=Ž}VkeŸ±ñèëƒÓ_œ¸^î—|7îô)^ýácðDvÍ~䡽“cR{C%E¡Z¶°1˜¥ÐC—Y%Ïü¶_Ê/ú%¿„?ßæô³ÚOæs>óñ‰þ0¿ãwýn¾ð˾‚¯ýõ¿ñÛ¿ï÷ã¯û«þª&Z¢íjŸ.™QE]²ˆÑ¸@íh”«¡IF÷xÂ,¦L‚l_²岬!ð{I§‹ªÃYTôù)Ð÷­ËÃ}`ýwóaïú«@ï+íÚRcAšÌ϶±1áë€Ïå~¿ê+ß=Èiñ/WÔ?R¨n#¶ŽüÛºK IQŒ¶‚­}rº—  ¤(PyHsiµß^¹”cëÕ`l-,¨Î‘ý Ä¥ÍS²ä439F!½”®jýà/‚j½KüLX,–§ÔF|¢v³¥>(¶›[¢šC]» rº¹¹¨ÏÏŸ;b§®jôþ€CÒ¤ºm÷8Þq¬'Ž…Ë[šØ I–8®GdŸÅÜWàëùí}Ôø!{d…Ùå‡oŽë“?°ÜÄ0÷y¹® )R ÇÄÚû1 V§9Vç]KÇR}ÝœilcƒL¯ßN–s-̤_²$¾êP*wŸµ!=©P[³ðå_õü¢_ü‹Ïü6Œò9?˜Ÿûs.§½F]ìk3ˆÑÍ¥¼HÀQuðpïL\€³2²]-ïݬu´†À‹J„íòèl!(ös³çÔ¾ŽïU„ôÂl<65a°J™G\&lB»”.5¨RòÛǕƛӳö©ïŒdlt®þ+à¯ç=>â×|Å»cyx?èù¾~}7E(r$Õ–¨¨ÿÒÀR¼…ÛÛ› éªÔ]Óê³t×Ôóç;wêೃå ( —pžÓö¹žç§e¶5l‘?•Õ0q™åœ ô–6ý!¾ŽŽy‚wäýðö†š÷ÿ-iU]²ŽóÝ.æº[碿k0Õ~[–ñÎál—¨žàJj´ãÊÿZê´&ÕiT§7fc–Aüå.W# =Ê¿áJÛp&UýïÝémî]²M,Í›YÚ kfÒ@"[R" œs²]õh‰–(EÜÄ6®H|Ço¢U€CLyÑóöí ÒòïækPk¢ÛÛ–‚ÉÍåðY Õ™ ¾n¹£#ó&¬Y“˾fg½96ƒðÉQ3Ý×÷‰°‚%2«$xÙ¥—’gÿ jðŸûù|Áç}>¯¿ñ¿÷wýžÞ!Šo}ó­‹ Ï«¯¾Æ§|Ê'ñIŸøI”J¤­·¶Ï„ …y0Tg§Á0QæòþêrmÖd"R Ô‡­â;'“%ã/ÿVYvã…ïËèOŒQä6€Ö®sÚ© ½ÿ¢àez pµ›ª0YÕ1¶]V›zˆ'cR  ¯Qœ¶U¿ø0ïѱm³x'qÊ>"ÿª½o Õ‘’Eö¬*‰ïµLN"xx{댵\=êhìîÙúbHeûVCRa@Ñ­G‹ÊÄQ™ë&Ú™ßYÝTÙ +G ©‡“‰,bÄSX@’ÔYÀuêÄìÛž8+Íå˜VÁ-Ë[`o ¢ñ‡!M*uþýd(m+ÊìLÜAÕ–ºÝáÕÿ?fBU Iâ˜<–S{œ'TX††Ô¤g '’ÊÛ·ÆXù>êݦ÷?\]TÇþæõKòüRþõùµaP¥2KR,stt7â(^ë)ÃÍ“çòg‡{Äxß‹ÉâýŸýY¬}º¥•E @‡]+- iñΩ۟m#(•ž®›6j9ó“tH‰XÒÓ²§Ëµóâs3|Ïh;¥m™ø)ÛäPÕ«¯(#Zôd'Æ»ÆSÓ g>מ$â£ÑíØUÜç‡bŒø'ß›HïÔñ~ÄçÄ—}í7ý_±¥RÖàˆOµcéWÉá æQ‰’#e|DUPk§8À½7ßzJž¶~0ì€3 Y³[‘ OG¢‹8ü4,gôˆwƒÒíqXÔQÚªc;Aîê‰ÔC™-„Ái·,— yåIX;†Ï²¶ÌÇMjÝ<"ìv&¬„¹Î~[ˆÈ"”Y fÀ&ŸÖu"´ C¨(ÖÝ¥"l·Þ^ÎËɘQ€·¯™†MEÖ(ÃtV¢Î0M¦k.ûº3¯dM®yDP:9bB#kÜþ}u¦é,³áZh„ü7À%5q„Ÿ£³üx,ó´}ÉâÔåó F:8D{—Ï*hlNZcº.n9^ 2iÏúþ¼>_eÀƒÀÝnžhÃå¥lÜàþ% ‚ÍçÇÊZnz°Sh7? O|QÖ‚+Ë“,a.}x“º³škžäb œÙTµC§¬­Õ… |B'k…ËËYü=?îG~Î?LÞc#_<¤y§._øUßü®Å'Ô\Ø /Dê“å¸Ý½æ,‹n8pk»ÎeZŠãͧO]†R2ÖºIìõÑ’ î>©bC­ïèkMó6ÀŒW‰Å%?ʱ¬†Ï=¤ÛÒ6´dˆD‡Õ¹¬[µæ:Ö_¸„fE7‚ßѨý(ÀÞÑÜT–arXgeÎu.3lÓýÿÅ­VÕê¬Ë‹„ù!ÅZH]»m–0q£–λI¢Õ“oa®ôü·ª3}ˆ&ê%9XÖr9C#ÊG[öÍÐÖûÚƒ]iå]Ýk–ö§1ì-ÒŸ¯ÈÝÒïæm§” £ZÅ7 e•D†—òç*û+ª÷o5Ù®üšÎº O…éšZ´IÌ/•”Qö,;“ù„gÍZ¶•–ZCC?Æj]k ØúË»Egzö´¯+ˆ‰Ž“žµ49×™Oˆ1o>‘eÖl½°Ö“_óåßðŸ½Xx¯-ñ«¿ìÙ…5¶øUëÿŽª,p:ìrŒT‘ôŹÇs\¤¾·“º}œQ¨;êéÓ;ŠP»^¯ß¤¥D’Ûp[©ÕÛ§¡ˆ÷ül¼ô«CHó‡Â#"Äê&(.ĸp·ŠÅørˆïÒÇÓÖ¶YK\ç ¡~ý`îw.—Xì‹ÜŽ)¡ÂçUŒì¢°º‚JÔaQG†™ÈãÁrÛÇõQ‹µò‚½±Jýù”&çYj¡…1€¶œu ÐæLå ¤À˜ƒ$ûªì»@d—ëÀŒ©tæ%­±îöëUoßÒîÄ8²Õ½› F\¿ÒêÚ§x V{‹ ÆH=Œ6…¡Xs̓éÀfçyGßÞ6©6n|饼••¢,âIU“T¸1„²¿Ç¾v"ŽýBûä I,~f7’ÈÔm‹æ'ñÈÕÛ™E„Úì-NÉ4©R¡.ã²XºÝ=5w?ú¼RK—„<ì—%Z u3ÿ ™•±Ñm©.D)‹MO ;Ê|¾íùðï¡‘Ûþ§y§-?îG|Öð_S•ËrëTÿ\Liï¸å¿‘ÜœN—ì#°¢­þÛîtªÑ¢ÉƒÈ°.ËeÊvœíŒýosÞÓjf:EôO•|ÂÚKþé¢|G^òhÀ^ìX‡Š•Â+üÅžöGlp’ìåϰŒúuÔ.;µpTI‘hR+0›áxBêã1£Þ% fOtR¬m°HkŒE5À¼ïWÙ2:2ï¿HmW~Ü™­ ‹1˜C}@Žè¡ñƒô(‘J@*kDF'måhëÞ£AzVïÛµÚ˜­¡]w0ñÏ:u˜ŒÛÆ_i\¨')ᔪ2„ÝL mÙ}Ÿõ_U¡KΜëvÞ|à_x±ð^Z¶ÿðN_øÕßô?%ë™ l@iQÚh—ûíYýÊ<¼=]afbôæØxþìŽ}oFxÌKE Xo™†°7ø–€ÝΛ=Õ*¸‘0#ÿ}WÇ™¿®ÆèÌyÖD4º$( eKŒíÕ²%7ÕŽ˜q &dc«½D¢1Ãvê¤íÛ’aS†%ΣsnQÍÿ1Ÿ ÌåЈ0)6ÊÊÖ7SiWËŒ& ÎX!‹ƒSÈl ùQÀr¥c±½6Š\ÂJ`­ù󀿇÷Ðȵ&ï¤eŸçkþ™û„L†Ò€áèIj»ÒŠzøà•!Ìg\Zu÷})ru¤×²'ÂJH¿åèëäDYøp4·":âUqU‘͵; ÈNb/“´ÔN;Ò‰|©‡Øñ’Xr'ìHQYRtÔm¿Fg tD¬ÿ»™+U65»(šœ–žÐPtÖcL pç¸&Iÿ>oÙmÇ)¯qëV 7¨:ö¥AÓê6ê……œP´éZƒŸ`ÒÊv±Ýc@ê’[…3Üè Æ×€…Ë+5Õ(¡Ž@?ðM~dIÅÝGÖ…ZE 4 RÙ+å¦}®9åIX£üoKºçU§˜‰¯êK4&cO¯£sÝám±h1Å¢V œ’”“÷f­Ou?Ù%²·z½¯ Æ„é8}O÷èÀaõçu€¦ò¨IS {ÖŠÏØ®Gs•:ì{hùÉcä§Ž‘¼W–mŒä4’ñsë‡à1±ÌɈA¹9”‘&˜e€Ç^¢\JlçÉê‡â¬âÙùŽàPÓ]«Hã&„&&ªëù €Ó™ÈT;nw‰\Çøßh3œµDÜÒ핃ƒ½eVÒ„­>V$Ÿ¡î, ã>öO¯úFfƒÁb> bÁd²Ý$µ,£‘c#ñ*ØN›åE`A°5ámõ¤`YÍÃè:~?°²»ÏT#/н•ƒÇ%I¢èH;’1™G&´4ÑàId-iM•àf¢›uÈúoš„/rœXká,BÆRfO{Hª‰‘‹Cm±ÌGÚ’¨¾󸹝 ý9þ¾/EÒAJá:Ó‘¶ÎhÆnŠL­6 ê®9´Èî;_Ò®b*²ßRÄ?Š#ƒ‰~¿¼î*Ò.,Ö´ôHrä4Ò«(Æj9ró«”YAø¡6§º$©1©0eŸSÒ¾¢$â­ ¶º}¸HõÌø·Ö›Zµ˜Õ¥&°¦×lâ,wkʤÄJÇÇ.;ºMq`D>?y`]½îZÇ~0F9 ¼¸Å73aMlÜxdŠ‹¾0­8€»Ü¬C<•8î#Tò?xÆÕe”Ê€ém8Ö•y&üêI¦ú°/•b#©JÖÒþMÊ©¤ãÉö&ɪ7v±¤µÖæèRØàd6·°"5P-–€ý9YÂê*ÐÐ)úÛD:“ ±Æ¹¤‚=uÞ€+‹Å$V²3¡5¢’ZͰÏá³(¬Eâ-‘®Lã2êuðx=<‘͵ˆÙ˜ëêÌ£&i/™ïÊ{hd¼–?ü'ÿÄ/™5²°¬jâ}ñßÖ^-ôVó"á°B Ümq¨ë®¥(ÇNv‹þ}Éb½, Ìô×s)3Øýyþ¬*ÖÛ”dûW1wMô¾¢.?øg—ð–ÞÇ$)*LªÂ^ ¸ TŠPfTÁ‚\*5Ö±V³–U2ZÂH{º;³ð¹q-==!¯2™M"”FŒÏ¿œýyz½ÚOÄ~ö¡:6ÂêÚQ®× ©_¿°¢ÔäT‰·Ch2ª1“(w½± t­tÍSí. i¨n/^2€l̯¢±®ÊJü’åL4bàõEõÆ%ÖR‡•#|sˆ:kï’ ËY¶AylK‹ôçúX”9˜<ˆ²HK»dBgÕßS†øKL}Fæ!fIykPio[jQ Ñ$H©Ë.šâ}, |[9Îò)Ä,W4ì|èï„93Q¯Fï•e‹(>Þ£jû…3î>WÍŒÎæõ°qç•®— S\ÔueI»®¯:wÏ»³FN;õVõàG‹'Ve‹ÛÑ#˜« ÄI ):² jY0‘.Éqž×Þßs?«œ%­IhHsGV°"ñ™äåã0i{“ w¥Ó9ÈÎV,˜Ó´ 3 1ÆÅÐ\×Þíýežk]y‡gËÚ‡~G¥Š¢¢zŒ}l 8Ö¯Anš Z++Ð~¨«Mž0Ÿæ*óJaQZϸ”ºæY$GÔšDgÑ\d\¹Ÿ˜VZž¤Tº¸Ù†0¨8´¢<%˜G±ùü°Š©‰zIt ³ëãZeËÏôÃ_]Õqq8Ð(4ô½TÊŠ¸U‘»OùÁª Âû¡L/b§ƒ¹¾jó ÑìlºÜGh›ŒÎfc[)lRÖ,"¤¾AMó(piTn4€OÆtT:jžW’¬u¦–3R«yª•ú¸ÖûùÎ÷E–&7Ù¯²DN—S•eÇÐg5‹)3ô㘬 yóà•W?øc¼F>xò ïeõï"Uó»ÕšQÇf ¹ÛEIÓd²Ûk^@ó Ýüé‰gÓ°b,W-¦þõ å?@´Ws*' U¨Ü¡EÆ5' e-ß‘ÇÒ“AæM$=9^.E’“Ð3Åv´äpù¬;Áúx'þûÅSSžµ®JBRÚ…ªåŽ7¹ÓayÜàOI¾#¬­õ¶.©Ÿ÷ÔKÞGŒE(³Epï¿Ï‰0‡R÷5ÝÚa¨·k/vuæ€>ß|™îÔƒt†U®Ûx‰ªc±ñ%ƒÀCzfe¬ ‘]Çѽ‰¦:e¥ã´óá§~âøÔX຦£xc¤ñÒ¿[„ãP€ÔŽq{®þoIis?™úáë· –¼0£kÍ7¢iIâo™h©k¦¯V-·T¯5ý}nœÐÉ·&kcq^3¶ÁRùÜÝW«|„kÉË>øÈGþô÷~±ð^X¶ÿðñOnýëd}6g’ º”6Ûg€ xøè1ÆK™½&Ïî8t{àî…GPxL‹Òæf˜o°p³vÑÁ¦»¦6 gQޤXfÜêKdGíJÿEµ~ÒJV-} Úg#ZnœF×Ö‡ ‘Ðý÷љԖªQ˜Ñ¯ƒö;IK]@ä(9øLf°Öñ8Ôbµ {2ÃÌàÝ¡ýɪ\)[:“,¸çPcÆõ*ŠŒdùþÑþ[S¬µ£²‚eYnÛl×å¸*E^“ã3S¿#m8%P»óÕ¶qÝN[žD=ixÒêý•ˆÂB Â`:Sëý1ÿ]>ùÃ|Ò‡ßߦa¿çü­Ï¬%Ÿ›»©®éwYQ$Í­³fg»“£1‰Å\º>ÎÈBíÇÒöj¥‚%ÆÎÚÚKåL—Ùðd m+yp¬šîгÁštº®¬w«vËÅ̆œ,5.¬ê}²Üбm?7°s¡2²,-Cˆ£;íQœ>÷ÈÈGqâãµ<œž0Öß–E÷lïHY]çnÇ4žEp{k[ÔtÙâøy÷ü̪yeG %†÷5ÎAáѯùAâ_MJ(yuóS«e0‚,3‰¥nèzú Ó¬bBe„òÖ—;a¨ë(Q­«‰f¡,Gç°„=,‚PïºØÊÙ–#KõÉg¦Ùܲ|7Õ“I9ï’š{çSѨ¸3Þ÷µ´­3åk›Ùï]Ç9«hò"8‹Ã\XǹMMn*Ki]¬­1”±èõ*»»•Ò‘¨Ž…*²Áü%]1uBQ.KffwÖ³Éer$Qx=Êy´¼0:ÃEÇ641[–c•• ¢àÓ?õ“øäOø€î‡Éûøn/þ&|1—;­Üˆ×M,4ld¬Lgj=\>svG¡Œ@löö†ÉÆèü Nìø˜é¶qgÎÒó2/…òõåŠÒס­¢¥Ì ìÇ&fËT™Piš¬Ü^îJóÂ1T mf©ÿSì¯î¼–màã5NÖÿ4ç|£×}_T­#²­t±æ•Zí)7N§S×ÂðwwgÖjœ ÁhRŒtÐg³¶ÈL©ýZÙ´ªÓéÖ6B€¹ééŽl{¿÷å´¼±‘þíÇáÔ¿1 á5=¬±´m6¬Z“ÀÚBÂXj{L–ÒüÆÔƬ¨}¶V”Ht™a¬À Þ68e^I¬€bG…ʘ”qíÍè#e`’ꌩÏ_E¹Ü·/fv¦'úCó'ˆb6†!¿‹ê8ÿç9Éž¡ÒDHe5:ÿ¬ÎÀÔº,*Ѓ$,éïb¨Ôi&uk5¶²Yf„Õ"‚†ËHk„Yyv`ÅX½f5cÊáEéêƒhè|ê'±%¿÷÷ÿQÖÕ øK®ü¦”më<µðâʈ攰/$ÇÄÑ—`0‰ŠÞ’Å«,)’ ÄSiþ K“éaùÚ]m3ŽÖk•TLÂëblµ"ÁH©Èv¼Pl'ñsv¨¢2´_ÇÃoïÏK4q1".Çû^yÄóçÏ/™¹pšóù¬ß5‰pÉïþCœµª7§onòauöÁˆ+³§,ƒ*‘ÿÜmáR·€„2ŽÖìÍ 2¬~|`+­"·òªt%qCÚz6²¼=uÑNßO‹}V+Y¤4ÁuýÑ­½>Ψ¯5Å\ŒVÇ^«êôÊû~'½8X>Öã6ÞüÙ§ÌÏž2§¥\¥ÔQ‹d`Ö°¥4$f¾‚‹Hâ’t‡Õ¤ƒãN›k1FwqÀ•¾M•ý$”D¦j·‰Oé9lÀ=p-Wõsï¢*ƒ† ,ùf0Z²Ôý¨²ÊÈÛ=0ò²¤"ºaY—ŽŠŠhÕÛôCY>Îñ æ:âÞ„ ‘IXLï?*Ya¶³[Reÿæõ«Ð‘quýC“Æõù3Ÿ£?×,j‡p.—…c¬èë¸|Ÿiÿ³Aë…3¸ $ICà¨õtŒ¸!$4²3Ú–uÏᎳ í³V·F'Œ¼ø¯O:ݰŸÏ¬YìLÖ^”Õ~ûò)ïü„Oÿ”9PŠ ª»™ðäþÚ«ù¾77üöß÷G8¯;³öÛ’Ù×ÚN€†;úµ V© UÔ†µÞÂŒÖMªÁ© ÆÊËígrEÀp ÐÝ^‹µš¤‹9O5!²—œ‡eo¶Í6¼ºƒRz´=‡K‡ê¶ž.áêÜ´áVtâ¹tH<»™ßú:½8X>Ö#rü;3íÓ”A0ƒl’Š0Oˆyñùˆ>ÔöìéS¦:@@àqë+eóÒ]s9zÈ䃢s1›©Õ‘diÿ–"m2gò§`íÊõ½ßì;±Ï½'µ¥ÖBÈc¿‚b¨¯½ÎÏ¡pZ `"]EÑžâ#ÕÊ˾ïª97À]$ÑŽ8škÐðtÚ(ñÌsi <Ø÷ÆB“¯„ñ¤Ñx‚@ꪮ!çÅ<ŽCwƒÑ¿»l(ÞMæfѾ‹J‹Û•¯«œûÈãó½_ý÷!¿|‚T'XÀÝN{O .$ÕO|D¿ÿµGÚÏÒ/úÀé<ç‹®‡\Þûb¹”`÷}×úWÍÏîvÞxãM¶mp{Rù«4y_Þ¿ïû%Ù¶ÛÛÛ+,äÅryýÁmñý¾û§ðÛ~÷äùÝ  € -“ÄB@mÓÍHŸŸ¡ Êã]Së·KÅnm¦Â¤È“J·-YÕës|—Ãe&`¨tÌjf¼$ðcë9£N:Ð À™±3˜Mžö…Ž[ÛuéÌ-ßÉŠ÷ÐØªø˜ŽÓéöï˘Ÿ¢T1³Úu^-Öu?¹h‚L–¢Îû¼|$Áá_ºT`EϵvFÞk!GWUè„Xf2GK—ç pÐ4ˆXjÉí̤ÄYÀF7}¼+Û\gŠÍ¦¶£¡»¦hyÈ­²®Ú~YÓ¼…2Q·«a1ÛLªÅô²·)²_ásÀ\‹;w-40_£»àÌ‹(н|/ Ô&l1H…á«ö¥ÖC?WˆŸÑUº°Wy¡Ü¡Û-Û´Ö¥$ˆÁ›3y^pŽä´m¼ïÁƧ½zâ•G4©ödzÒƒ™ÊÔßÑ0w¯×ËZž@×r·PúsÞ–9èõήÖ{ôà†›í(qyý«÷¿íw ý_ï9mÉø^ŸÆï|¬¿þ­oj\ ñoÆè²ŒƒQ=Yœ—ýu–öIi ~kkiKBØÒ¸"UӋŚݒ‰á( 6Þ—@u• ݺ`…BçD`:â Å•¥CV€)JÞት*îò4žî“ßÍ{hlDò±ó‘,†Eù`EîÚa#YìS[Þ xãÍ·ú¢Û"¼$UNPë:ò/ªê959€ÉR xF”%GâPµrÜ£ŒMˆq’öS~ºv./í+ߊ1X«v¢ìûŒyÊ(<ä×ÀÑ™U‡³ "4$)ßê§%—Í›\Ir«fö„ií+”™qŠ5®ñ‡5—°„>–Ö@Ý;%?†×ja ýÔRw Ú»\ÚJ‰¤EظÚ/hXþžÿ?y_$G®my®”™UݶgÆžÏÌŒ—™!`™™™™™™™y÷3ÿÇäyÌo ÙnweJÚ¶®N膢éaºiwUfJJU•tñ`N‚C7b?M8³?m'ÖRà‚Ï…·µ¨Áû¾µ4x>„&&AË€×åsÞû|þ8Ë¡e´ãhûçùž´÷ÑçÜ‹Wð™GÎeÀÑ£òÈÉé÷×1Þ(hP§CAu.4Ó‚üû Q²¥Äß9ÔÐ06H±"s=Xl]‹pcT‹“œ=‰ôˆb¹°VæÆu„Û€¨Ú¹?½ŽvÅàB$jÎ1Ôuªóá|þîâ1Mî豿·ÅgnÁ™[O €Í>ú™O>üèùÿŽ—‘är¸çJÜ(0…ùN@ê—ŠùL\8™cÍÌ¢ŠÝ£_Ì%b³™p}·³A†›@ë¢8ð\2ý¸Xz Û™Ô ¼45r¦3ëÆëõI€@ž b2 D»¨é•Ê;A[™é! ™¸ïXCÿ&´¸ãR_8:½·IêâYž—w»Ò§µ’@íÐp¼±OÜ1 j«ÂÙ²õ÷Î!²ê¼ämPÃ$܈ÍP|å‰jÍ%»@ÐR3 -¾˜ïg}>S}ÞšRK×Û.×’Çì¶› ÷ݺÅ}§¶\8óâÌì¤ã,|ßjñö\ky0Ó‰×QxÎÄ$øÞ±–Û¥åÁÃH×òhÇÆ ‘÷´b-¡[Oîá¶SŸó—®â‘Ç.âpžmvÝ8ܤÕ4Ëõu’@k¢5@ QHGËßœÓûcSÜ[ÚLÆ#Äd ‘NÖÌE¬HZä5gk\ô7ͧÓï÷8jrÃf:í4a3yœ¹íÜ~ún¸üœ¸™sŽñÐãOœr»[ÿ^F2Èîž+ù÷,Jà£|<ÄÅ.hš”"³˜–PQ}öÞm^àe‘‚¶Ã˜XÁLêQæ'õ3Ö@l(å±ÄIªa;½FYØBBŸ&_|(üÉs…Ö&ˆþÑËïnI…¹ˆ®hZB±¸„æ¿Æ\¤ySÑàK¥1  ˆžäÿÅr`žIŒrXÚá¦8&AK2„’5%¢ãÏ xùA“1P#,Aü¨à¥ËÔÇÆ†a¨±.rt+²¯rn$‡ƒäp½‡¿á³Ncrrl¬AkØ…ž¯SJ]K€ÒÆéöÛÄ(ÚM¨Û_û~;>ºÀ¼¯Öµuu5bç×åL®;ÏÜz£¼‘=ZË©?Çwïݧó±,K>®\;ĵƒÝÑÿ×°Û-8œ„pãˆ%A¬ŽEj]O2[&É‚¯‰\…óJômg² e#uI ‰9ø½ÝŽY1<¹¿Å©S'ò1º7brB>(vì|YYûÜßñ¾ãÝ<è’¤ÿ âoð^1çð\ˆÄù·G¢ÝšJZV ûZéÉ…X¯ ¥nBÏ3K«@m{¦;K ‚Ý<˜eAc‰Ô>̾ Æ]gÅbIÚZ6y¼‚”¼Ž×UŽì¥0›±..@t¼_±82jl\@™•„¨µêÂYPø|q0‹jýšEôÅp¯£TÀ°$]:>¥ á²Â¼¸’2ï±ú¾Ž™YÌ’âýâJ,ÆL%«†n eET]’)•l­‹`Œc^\È0Løü3{¸÷Ö=»9¨ØhúOE¸xLÓ”5òãš%Бõ˜Hc‰¬Æ*(íõmŒ… 7ÞÏùâ8V-§Î8¶[uZ‹Å¦ \ßå„–ÝsrKXT¢¥<Ç ‡šœ’J ß+ï™l°É¿ùÁi¼Ò9`š†œ9{[}f›½ÖqÓuç³câÆÌöV$_w°[pÿƒ"«i1ü}‰rÀŸéo Kij-¿ë§½âÏ…¸hÁIa³1ÍŠÒ÷lñ³F…N.Äžè½ÌÈ¢Éð’øgrÉ0²ˆâŒ!.Ì…Іƒ]•óP _߯ErI êòý^êâÊ;%Ï"µ ¬À†rã¼ó@,ÛE*VS5+LÖ&¯ÓvS„ËEJÖ›‹zÖnÂŒE$ã6>b6茮ø®¯å}äN¨ ÇʶÊm»ˆÇ0íá+?û$NNVV,‹U ÒÆ@ìkó°1„6æaû³Òjö=KÂÆ,Žw;ž^;½l0Þ߯Uì}´¨Öçýô³Çø<û{l7#(Œ´YhÜLÛôežçø[é®ëóaÇÏg·Ï³úý2u4m¶Ü¾óƒxàáGæY“ rÙÀüçJ>á_x‰§ñ<›ò¿þÌo‘yË»cˆÁòŽç:1|Ì×vÃh±—˜Q*Ó¹Rs®ì…LódÞºSÛ´$h{p¡kÊ)ПjfNYëÈGÎpxF…S¶ˆƒ[e?÷P!*¢jÖìŸõÌ Ñ£Ô…”kèŸFñ6.ÀŸB… À ¶R©Ò¿,Î@0²Þ$‚–ŸG¢ÅW ‰¢ ”MP­ÁF='†÷#1 ­¼î…qŽ–F”ÊÈ,±¥€@úŒ©$¸tqî(Xy'¾ñÞ“ˆæÊ…h­Ž‚š5ϯÄ$zš¼]h²œ Ûoe-&B‹cå¾^öVOƒîe‰±ÿü·]ÄÛçíõ¿¦¡·÷µ›Sk1µYh­¬ÄvÖ>÷ž¥ØöÛ}½*ùÕì½ W®]Çÿ{ëYÄŒ{'ŽŽ=¸×ív~‡À_y »°ð¬Êëßò¾?S¼M QD z¨’Û ŠëÕÔm9²Å@y07Õ' –ÚQg¸¨ÁæÌþ(ðÑEóñRRucd6^]ŠÌy"Ú`aÍüò:”ôÝT˜± ‹bH%ðLJ°RºÖ5¬å`…+7!–­¡btÐ%èô~j”´“±T¬’ÿñb Wœ^+)b­d‡Ð4³®AÃêLöŒ¦Kó­Bì°žƒüףǵ8â¾ÛöñÍgöózu0¶Ÿåtlö”Õªym{]/FÑqðÞ¶.ãXËà8;½Ï¼oï;ö¹ZËÈ^Ë÷y¥±~ÚëWëUÚ~Ø-ƒvœ½ùhÇ·fñðoöÙ¶·fQ¶í·ý¶×ñu;Îv\­EgÇùÝo8‹û?òbL…Úø~3Á•´ß0Ïٛ௿47äðlÊ‚øk‰!&”)THOåf^¹‹ÜhM5ËÈÕ2ry{Ào¹‰ÆO¾ .Љi}ð#‹¥Œ†/0™F°õfAá‚ÈìE/›jUrè (˜U\À4àIrT0"ù/ò#Ä™,2º–|ÉÌJå:nDž±CµÉJ_Ä\¡>#@~KÈæç[?WÚɰƒög¨†ÉÂJcH™gg`d2ÏiÛ‡1açG|ñg߃ÛOª¶Þ¦±RÓãëV£_ÓÜy_ïº^Œ¢uñØúô5Ý^Lƒý¬Õstïk5ø¶µXŠf mÖ×jl¦íÇnìg]ó_>ö¿b!ö-…‹ª3ZÍŽk¿?¼ŽÏÃïÃÃÅK—ñ#ïý ˜±éÇ Ë¹'П–ÿæ§qð·ññl¿ç'¿ê×!…;A4WCàÃGÉÒøÆ‰ ŠJHäE ‘(ÉÐ$aå*l½@š¤²(Bû6鲑´bô…&ÂiK“Ù” ]¨è&`´aOÀFÑ¿A¢)[ci’bm‹óÄã"·¶á9Gªt¶ƒó„À74·äOˆíÅû˜õÄš E¯e‡ŒÃ„<#jÑ9=Ê !Åp38"ºŠö¯ ‹¾Xm —°…Ürßü•_€W}ùçàÞ3'ZMõØ:ˆn`|=›Šçº?Vm}ñÕÒͺjßoc(Ç¡äò8î¾Ë ëϱ×7šy×âhÇË{zóÚÖ·ðhç½×Ïqšÿš°Ý¶¿þçÑ¿¾›í¿=G±–Ü;ß÷þËßž•«ÄúR$½»zaÇZ¹qîo!¹ß„äðR:žU $yùý6æAh2Ž¥ŠfìÈ §´˜ÍÂT]C¤/jy0‹‡ñ 6P¾Œ”5÷¤H€F­“`ýGB‚àHJk]?r\#*êªnbRaÈé͘‰«•²N¤Z$´»ˆíT6FaP_œZ¿*Fè8bVÑÇ6ÉF´ÐbÓh¿ƒG y‚,bA6f „.²:· ÂÎy©FTK’ùúõ6r¤'ˆI0xbç°Oà_tNޮՍùC\ÍŽy’’Û"¦TŒ±ëÃ^³ :±ˆuY¹ŸïßlÝH+OQ³ïY0Ø ùP<7©¶ýÕq¶ã]ÏF{ZYnOéz¹éëcŒ´Lsàüõïû .®uBòGµòáËî:†í†a£Éóüw†qØø'/ž ùí?î•?!Äù œøÊ>'&CÈ{Ó`$e ï5 ònå ÀviÝ0ÉŽº¬íÃ"’ò_ä— V,ªîé! Y_žµ±êzbõ9}ÿ©ôy=¸)KÇpyK*äN@$:/l¶ h|ö%}y„vh+sè<0》¥e¹EDˆT,°Mby!*1Pat±Œß9N(æœ_î¾ãV¼ú®“V íjœ\ä׳Žú¾éöºöý¶vbMíU’󾛬XÍÖj¥W7Ò×M·Û·`ŽwSÁsM¬fµ_^»šÆ÷{ó°{ÉGGÖ®ïf½Ù¹mŸáƒŸx¯÷qöcŸÊ×ëo™›³ÂüÓõ»ìvðÃX¼IÂnþÃ8< à½4*ÑŸ%4E?¹¿c’”ù/là³V‹¦6 Ñz)™#Þ»~À98íl5+[aý@%zbŒRæ Ø ¶W8·YÇM)5é8}¶7°"bR„Mv1© ¼¼b#—xÖb˜`Tn$MùP¾Xax¥ÙHµƒJkâ §¶V+&W¯åü·£Óóä3Q‹ÍWôâ+KB˜öð5÷ÝŠ¯ÝÛXtÏ'Þºn>ëhESo}þ!fS壧i®Y½ k¸Vz±‰gX8¯íOÏBí\ÏŠý‹HÇyîâU|ÇÞ‰×࣠ї´ðñ‹YëbÈ–È´wΈ|ˆñ?ˆßüxo|ñ3ʈgúø}?åµ_į…ƒ%ܧ֛'ñ†*bw™ª[s¢ÅÜF¬P \ÂM ,uÉdYäPç˜vK""˜ØKù›gÆc`ù-ªï?ÁdH¡ùc€žQØplR‚é´˜R"O8Ûd ¿öW²Ëê ±¢Xà—'Üð¦}ÖlÞÜx-¶+ÄU´ˆBŠÜÇK)q ‚éEpq¹¶·Á+¾ê ðí_p¶ƒƒ•6¶À£µ(Zߺ½žáŠ&Í£—íÓóiw³n2¦²vOÃå˜zñ•N{«Ò 41îüñ:{´óÊ>ÚÏ×HÏRáaï;ö¸‹ÐõŸÓöyܸíí/m_Çf‘ñ¾5YÏâêg±Ù{lÿ<Ú¹nž§÷\ݹ¤´íöïñ™Um¬™;NuÏ\Çÿgÿo½±y #P ƒ˜`âÇ‘Atn†8 `²Írx»ƒ«ˆó¬Ô¾!|þa:øŽÃ½xêèÀ‹ñnüóLÊ¥åòŸuI‰^\)âN$™É;²wä‹Z ål54H;ITÖò!£Ù«ÅÀ 99Ï#È…]* É0–"h,1"•ºŒ”-‡ ¹/b(c×À4O’×€š|ƒFk ¨…ñpüjAt0nÀ¬­ÈfçàRD"ç2-8C²ˆ"<Ž@R⦘tžlá_;N08Z Pü8Ô¢M°~EK.`Â7~ñgá›ö'«a·Pç7•í³VyÝåÝàû+'øæ¾•vVÐz×c"k±ŽÕ˜ƒ—u#uÚ[öâ|öõÆòXÍâbF–­+¡EÁŠ÷nv?cöÉçlúå"ÎÏq q`5VÖÂï÷ê[¡Ø ðá'.àÏüëÿ…O=úü0A¸NåoJtn»~ g\–t^Žc,ü%1 9÷õríð¿ÝsŸÿif¼Èd¸ç¶Ϥx7üÂÄÔQƒ½”¨+p† @dšn¬|däÔŸL¾%‰#ª®ÁdªܾºÈØ«6C\'P‹—BË©25 ]908~p,)†.&å÷v^¯1Tº ×;*ׇ6“*EnI¹-)|Ü1[A ˆðË*üµJThȪå“ÈÞ %¯ÓÿÁ§ªP$¤q%·7ß(J´sÑMøú/¼wœºš Ñæxt¬†VÓ_Ïžjû3m¶Ù\½ƒïu-¥^E{+«‡jÛ×Ô)}Ÿ=ûjïYåi°­ºÒ©Ð¦t,=Móbç„çì|˜6×ç¿K+ö3éˆíÓŽ±­;Z§¾ÿø»ÿãûpp¸DÝÞ È…N¥7hŠ/ÕV‚®jm—sj­Ôñf÷8&ÉVˆd×~Âg>µüm¿/2‘ŸþGþž)ùÝ?ÿÇÿlþGÌEi‰äv3!‘P­ó(¼à!FˆÖ!ªòyˆ`ô£R¢.3—u½d•!HXÊFàÖyPB¤Ë«p-»‰‰Á’°[fH‚úJïñ ¡ï§ˆh+íb! p R° (?L?xݰB!= 4}µVpkK<'°>0Æ0¼ ·ùRAF¤n`$rB,ˆã¦§‰!Vaó2Ø^ð[|ãçÝ…“z¾ínŒ?B.îœ#Þw³¾ô¶JÏwßlRv|]½Ñˆ×²ž8Þžï|-¶À빨Qs_´ý¬É Ç:c-œ§Õ~9¯Í8ífÑZ€ù`ÿ­¬}(ü>µ–7™'a?_ O¹tíÿü;~?pöýºYX  —Þ8qC K¸(ºƒÆvÞ—¢bèU¡ëk‚ŸFøaÌV ¼ÜØ@þá‹ LÑá¹þ0Ϭ‰ è·§iQüó¬êfÀù$Â¥4ÝH·’Mv2¤Æ;R‰7D$u“‰gй,–¦šÛpŽÇXÿ&‰>Ïë´v /o E†jaŸÜ Þã†,EAa ã"l‹oÂ'Ï¡kÂYµž¯Œ|Ë\ËøPL„d7y$à¼nb^­¢Clð%Ÿw'î¼et=¬H7ÂÅd ÜV~7ÒµÐÇHâù^Åóq±„Þøú–ÉJŸmÒôµŠõ´R/q3¨Ã½XD÷y)öyÛXC{M¯Þe­„m¶Ù¶Çþ)kŸ;e­¾¥µØî¼üï7¾ÿñ{߈ƒyW¢]Q‚¡Ï¯–HDDAßuðeÝ[”|ËCf- ×@*š%a%†.8$çsËnþÛÓ¸}€×ãE"ò³þèßÇ3!¿÷'ý´Ó3Î=G²1sUK×Ý(uŽYA<_ÖâÂ#Zß0£Þ–J@ Å$x-þ$3Þöï\þ3™)o3Œ‘Q¸Ð­£Ó®g¹µ ÛiƒÝݬ-J{_A³úùÚ…Ü~¯Öjjze”6›ªm£µ˜;wßñ¦³øo¾ç/]ÓäqºV¥¤©·ÃÀþ´œ`^4Š}%‚8ïjF¥ˆqgyr"–vQ¬•qo¿º™EÙÛßà3xÈÀXÞÓ•0^ü q#+¶AYÏuqõ&+Kj=ôdJÔß8ë¤S ‰5†Á¶ ‹èbUг–à€Q-jözR;ÒÛJ=¾:k¹ÌA͈áIG¤ÍE¤À,®X *Ÿ’«è "-&¤¶M™Ø¼f¦qHÜÁ ~5‘µw½Ÿ?HÍa(óÇñ›˜ Kðgü‰[ðŠ/¼‹š[™m¥´ÕX­ÆÞÖ?´ÙL+ÒÁfZEƒíþÝVŠóXBÀƒŸÇCÃÃç/â‘'ôïs—.ãâå+¸xåjvqÄ”ZâZ(Jˉ"©h™ \D݇P%È,2:ºúÆI}ã)a3 Øn”~uÿ6› ›ÁcoóûÓ¨çOl6ØÛÜ YÚb3åóúz£çÞ?µtîèÿað=àf£êϽÉPêY~ݘD[ù}ߥ×6ÛXãîè  ç¿¯^?Ä{ø8ÞðîáßñnÌ!2H^±ïØ2$0+“¢ (²5á‡MÞBÜ•â[Ÿ_ƒ "£¯Nï[v‡6{¥@î¾¾‹ÿýv·ÿZ×ñ—áIJÅ3!)…ŸGˆ’1¨Ìµ›˜Rjà¶ÔEÄH,,¯'Á€» Ý`QƒÁzIä9GŒ&V½ë¢ž,—¹v蜯 ¤ª2¤û\[ç ¬3ÇØFaBd0Û(Sð-ìVØf¾/‰!ý7°,¬Œwô—Ë ÁAdhˆÿõý?$ÍÊI‰Ùej.›þ5««ÀÌ{Aˆ‚k²Å7}áíp"š=³RiÝ.øôI[ ßhžÔW±¦V¥Yˆè_«x‘nÅsŸ|ä1|ô3à#Ÿzðèxûä§ó†ÁÏ1fÝQ³1)›ÎU 4ÖÅ‹ òµùuÇŒQ³à$ºþ)»QxîsA,îÅk×®–º£s—ÕuBÍ•V%§AÊüh‚R>;}Êx ‚“'Næe°·™pêÄ œØß˛ԉ­n6û{[ìþèüÑu7ÞÛÛ]·—¯ß›&x ö¦ïJ[Ç¢¬e³ñöÁïà ×»±”|úÑÇñà¹KøÔcçðŽ~ïýø§³…¡È’7ÐS^ØÅÛCŽD½GœbHp.Ø¢²!(¶¡†¼!ƒ`Ž´BÔ;ârÆœ× eY¾åñ1þC¿/p_®<}¾óŸöê_–åÕÎ˲奬­0ØL`=ø.Úw€ ×L&Å*üÚQµj.¥ðœÝ"®°Ò&]V^< ü‹6/º0èy]¹"€™\øÁ S`…£Õg†ß9Æby^êÆæ½¥³¢YèëCG¤Ä3Z°G³1h¬Gè*Ì÷›¸‘H}~ïÁæÄ4á«?çÎãø1z–BãîúÀÍŽj-›OÞÏ`Û]Ë„é˜÷ècGÇÇñþ>…>ý – ˜l¢ D}Šßçþ°S >lº *¯½Óù]æ]¡ç5h`Ö óšj-õ{©.ÂâÓú-–©ã"¶ðÒë5¡†:ºXËŽ‘°À5-/»×vKÀ¹EkÌEN±Ýi×~(B?kº>u3*ãióæ³¿½±ylÇ1o>'¶›l-½Î›­¥að˜¼Ãè}~= úž<uãå6Y/&&$Ä”­Ã°„B‡l5Î3g\ßíp°;úûà0[‹._Åù£ã†ÅøèÅK˜çXÐ-\UöøÄ…®oÖtԵʬAd…n:ù8ñšl«Üv¨´Ó‹n.~; –©³æ)„¯aÙý²aÚ{Û ^~æ{ú•è¿ë§¾úMaÙ}›¦¶ªuàÇb"ÏÁüà*É-? y†]PÅ{šŒºÔ7®åP‡eþ#g: æS[òÔ ˆb›µxZ/Râ7Ž.#¶Ÿï¡[S3²\îS5~{?†‚¡%jª&nd hg8}Á8LáP/ ^7…¨ãgÇÙoN8“ Z«$Œ~ÊÿÓr`€Þy¯ ûpç™[púÔ~[yÛjˆ=ßu7–a7’•,&ûºë«2Âq~ò‘ÇñÖ÷~ï>Ú4Î~à#xâÂEHu_5E`^cd1”…Øéß~¸YªVªÖ Z!5–T8ä¡‹´Æå¼§öl•"Z0ªá.nòA´˜ÍTë¢úàùrG–1Í;nÄ:C ‹±L̳Ú7¸,;dÕM•×B«§'óy„¹ZAz¯Ë™€L¯g¼'¦ˆe§í Ý@T £‚‘ÖßX¡CNIÇb>¢4ç9Ð͹¬©ÔŽÑÂ#õ4G¥0ê‘(·º½V¸J$iZš†q(JVâØ9F TŸSøÙQPËo)Š„×~ø»žýžŒ“Z-‹~þpÈŸˆã@k>@R¬Ì¨Äê‹ Ò.ªÅ+EùØÍSÚÈ¿?}ê–opõ…9±‡§#çwïüÝ.¤m›™¤udÈ#Ú­fdñGR­ »8S« õ.'š>lÃu®"¥?)Ô¬(L„I R¶Ê ²_ÖYx8IäT§%‰Á5+›&%Ó)IJç$f>Ñüg84žîе*ð¢)€¿Ù kÖÚ1­4»ð1Ó|ò\ì·)¸,šw>Œƒåá¤"ÄP6˜˜ˆ?~Ÿwß·Ç\×óm¯2>UYCi}âÒüèÙ÷áGξï<Ú8¢f¿Qá°óGÇ¢É2Ž•ô°™2`æ h"Æq„° èf‘–f“,±1Ç,·è¦‚ éæàbÙŒv6 O¯s2x@7Àêã[ ~Ì÷'$úÙÍw‹öÉ®á)jÛBØH%9 l¬¡È…ÅVRw˜+5]‘…u|>Æ A‰¤9Û€îJþŠ,I\›à@kOûÖTZ[=$$Æ/Z0Cä·Ëx"ýGÍóÌØÚÂ)x«Ô !†ìÕѦqGÝÄ’n‚¬ÁŠXÔšsÆM Që#g¢îr?ÄÇ]ºÎøê¾K!}é¥+—ÿ€_‰  GƒÃÓ‘î/.p¡¢fXMH!o¸jx~r¦b™ÅRÕ{SËPëõˆ]…E©YM1¨qëÿÅŸ-o `}+ε]ÖfæÝ—W¹Ö‘O‚(öG ÄÿŽ?½¼Ïì§ÀŠ]˘ˆÃå<¹?8fþ`Ìœè9aV³eøƒƒ\²•ðuŽMáeŠnApáÂEœ¹å$¶ší}.méò5t,ÛŽ½o•ƒ×´￈|ËY|ï›ß‰wäc7¨-I·‹H‘÷[%Ål¤š*]Ý.ŵ“ÕÞú>•ù.ÍÞ¿ôÃq“ +!Ri@ € ƒ¶a´¸—¨­Ä¸0ÿžV„èÆ†ìšâ3K4åÅPs1 ­Sí7Ä¢ñ (í|¤R𛂦3{“²Z1ßxW©¥!ˆª±«†NöJ8U P¦c@>ª±ÎÄ|½MÞ)›œsǨ¥Šr©ß-Z…†iÁˆ”MÇ“ï_“3EáIÔ¥µø¹2-øìºžu q>'@¨@‰–¨J„ñÏ¢X:¦#q)îpøDk1.,¨fxîW8'ßàßá&ò³ÿÄSü–Ÿñ“oçˇÍL°N¨˜OÿŸ¹·€rY¶vw¦ä‚¦éé<Ìt™ù13333333333¿÷ó™ùšƒÍU¶”ùú„÷·2–VœË9ËS]eK–d9##bC-®íû ˆ'·” šÝÉI²hH'{……`BÕ —Ê$íÕô¯XL ⦙íh7¿ºqåŠ}×RýÅS4÷šŒb*ro NšèŒ®1D“óW%Àåå@G¿£â{RÄ`U•ͧcúFªZúûœ-< Äøà’\t;¯,E J”!ÃV'¾r¶Óûg!ŒmOcÛ›`ð<f5­ºîÛ1¥·h.ÆÒºþïïÿQýÿ÷o×þÔÏJ%2=ú®®Ç//ïËa&¡Íè=MU/züÝÁ1Ê=¡Âý«Ø[sC>¶‹^ðéÃ!þMã{r¦ÿÇÈv#¶¢ê§Ñ<Ïõõ„AE“[=«#Ì4Ñ£/Ó×c¬ÕÙFÜ÷_k&°,R~íë—T"èûѧ«S\'îïMe¦Zà‰ô|YcèydбH✋ߩ'¿:¾ò}KPÛà!)ØÞôKû²Ff‹=QÂë(NÇÖìÿ°jÞ™ì·,­øÐ˜$pÞ©Nî‰/$Z–'=ƒkÇÔ(°Dv¸;=B¬Ö×»g»Ý7Iú…ßTȬ_þ(û»k¯u®*ZS­·‹,ª˜\²«’J«d3¥-DÈæ¦F›o.i%âŸ]5ÝXmø]г@ϪJ¥¡_%ì8.,¶J~êkFÎPOwÙ£èºW2]){SáŒ(6‰óÄ5Ðbj°ô5¸*›Aÿ£Ò0×ûæZÕãŽ}•û!ê±ÿšë×ê ©6<Ì/÷‹î>¸ÐÍëW2Šj«Nú¦Ìqþž_û^áï˜Aü³áµÿÝÿ÷»ô¿ýÖï×ýû÷U§AÈ"AåþêS‘z Æ}LŒX…×YêîCL“WÀ©k4[iGöi2früIlÓAê¬j¬Ì-e«~£ìÈ4æppþê Yô`¦˜Äâ5LH¹ßC馷 T_¼&² TzÞÁ‰¬¡‡gœc~ž,oÿ©®Xⵦ5IʧƒJ©äŸ|O©ô>ð¢¯&Gi¨Ô±]5e¢ eÊ}œVÖ\Ï'¤FT"ÃjÜdd|^\Tÿ½¸ÔÝXÄó5âϤÞKx.§à5Ö5Ž;†âì­r=·«f¸«íƒÚÚ‡”Ñ´HêdY7-ëøž]ýÞßL¢‹óí'ýrG-úcV3~Iû¨5BßO’!Ô’Fðèc¥Þ—EZ´Wü¬ôä†uLlè# §ŽZhl2I¢_h‘Þ 7¡1šÕøˆŠ×Ôµ°ŠŒ‚’Šj¨Þµ_MHÂã(™ð·z‚+*”<‹:†S!g:©ÎÂ5áÊ/ê<Åqó·r؊ʱ'Þ9ˆL«ƒÝñøù’Ó$%©éËúÊwõü3OKÒÛyƒÃ»Øö:¶K¼þ« ÷>Òÿù»~Dÿõÿõ[õ3Ÿþüô·&S‰Ä ËŸ"¸0’3èµÌÂ>Ðkèžñkðs¡F¹†TMo‹Õš)5l05’WC¤'Ê2jÝ ü^Šâ3Œ †U}î–«ßÞœ]¿Ouræ<ãéímqNÒ÷˱Üp`bÛ A/šMù±@Îõ1vMô^v^üÀ‘ˆ@¨$îSVÔH€¨7Øi®×YŒNP©% Jµ/ê’_["ûQk®(Œ¾V<¨B-KÊè"s‹k÷P­‘ÍiB¹zÛäuÉÂÖvÜÑ“£^•sÓ¤Âù¦ÞTGlsÂŽU£ƒË"&Fœ­”-½`з¿ôùÃß-éoÑo’Qþ°¿ãŸÓ/güÍàïóþ‹rñ }m5ˆrña6#ŒÜ\îŠÔm*Tï3Ç!{i7-‡U¥ú‹-VäÝh…¼uÉR"c—Ãr£ü².Qx%>»ÆÈäj©±Ð`nWºâ±Ý.øKH7Öá™Ó¿õ÷H—uì­ X•WpjÊŒ|Ή/­W„Cüq=´xÿÂIz“eT½ß¯{Uú!ìÇåäÂÖåóYõîçoë¹gnRBzÓ’Sì‡fúVù4KdôÔöù/½úšþËÿý·ê¿ø_ÿo=xôH¥æ2ÛIœc¬‘H`”;Ê’3”¨¼ýXÑŽ¦z=ÙiâšQ5Åß²±,¿³¸ôPš›@[UzìÀ[:&Ê_E åF_ 2m_3ÓYžLè£ô膃È(%U‚ÒÈŽ ÍÇ”¡ª“Õcã\»%ÕùìÒ6A„£ÉKYƒ}m5 –؇Q˜ˆ€ÖJ/Ê™å¬2Ž-À [hQF‹kkžÊ1àð–” A×Kq­Jqy5núX(‘iп&ïßÝ)$…bâϨ4úD}Á×M~šåcÁÓÒ( ™GöÌðB1 ì&ì¯mµC¾Ä)–yþNI¿û7EBÄÿjÇA‡¿M­U|2ƪ©Ó@–j |¨Qf5ÉÅŽ¦›†À!«9çÖšÕ™ðD³R†B×mYm_¥gQŒz¨ÕV<ÐÇÄ76*¼Ã#Þƒæ¼ûFêº4c±—z4U»LílÏ{¹‰éª ç•z¥£¡U9þ€FÆVˆ¶MN‘—eU-‚0IÃ>ׯ oµVõ˜8 I~éå×uãÚ¹JùêÐQÛŒeëO±e¶ççáåWôüWÿ«þ§ÿï·F‹f£ëÓa6¹Y<ü›ˆ&žË¤˜PI~Ôgú"þY+zf#x06 {àÈ9ÕéSWµ»v®Óëñó¨Çv~ÏÕi8†Ef”¶_ã{î>ÐáÑãŸ÷êâ7â\XK¤ßù[‰¢Is”X <(¦°Òÿßsò-¿JYó /–_®›úïÈ©ÔþJ-í¸°ÜÔ¥Å*Ý„7Ãk1kšðòrì 3©*Þé$>*[ÙŒí!)²UÈ+(ôšíM3oík’ éáD@ÅD³„>Hœ¸¥Q²É ~~I—tµp ¬4ý{ôdjlcÖ(M+ß5¬z½‹àcÓÎçXòîzmêÛpžKkÑ{*ó8×¥¯zõ{A0|’2ëö÷·òzØ:õmÕ\öõ×ô_ÿÔOêüÿÝûâ+®¹O£t’™ìFKáŽÈuö2êë»Û™ `bó[U'wA˜In™ÃD5øe®FÝÓÇ—¿sP¬à×5Mr¬´§hú{bzE%ž›æãvm‰ …4lˆM¶X¤'œF9ÉŒ(Çô–PJùÄ(OeÈ©xÆç©jR¬J&ýEà‘Ÿ/r¦SŽ þž{gF7…Ôœy¹"û…¥ß nÉÇÊœ0l¡WO|GcåY]ÁÒ:¥ÏÑ{ª“däÚÙ훺òÂm]{ææãßÐéÍkÚ]9ãÔd²g4¸]ò*,@ë¤ÝùYìçpq¡züÝAäÒÛOq­—‡{=|õõÇ;zøÊëºÿÒ+zôÊN12@iQáÔcar©’TºµÖ¯{é³û¿EÒßý_ÂúÛ¾z¦üßðÇ|ϳ‡‹þEµ^µÁqãA^Ä Z"nb×Þã 9šëôQ=Da{ÿ, >…æ=«îŸÁ®}6Šî9‚:®EpB¡CTÒ.šž©áãidfìFé®ttµ¶Õ oÞŸíù_ß–…6Ä$V‘^ÍnJwc=m5b®Û4a†j©)ÙBŘë\3T1H7D0¦4™Õ'>ü>œìÄx3Æù[ùMlÑ[4Óþî}ý·?ýú‘—^Š/ÛOýÿI“ b‚ô˜óHßAä¿4k¹l›ê슦4!t†zÆß÷Ø6WbudW^¡Z%ལ ,š±dÐKR†®(-Dé&&I—Iê#^·\ªËDº¾ár06HGÃLQ¹¦LY‘ÏŸÕ;eœ æyÿðâVv3÷aÇâƒ×ºŠ:Üù†tOŒmùpr_®š@Ù—)¸‹×š9¹¼´—£ÄVͬËSR]8רôz,êÈ)nƒ‡ÅÍ¿GO½ÿÝxß :½zÅ{åZÆx’ü~¾?äû²^îãµÓi@•#€dõŒ“““˜Çè!–R‚¥þðÕ»ºûÙ/?~|Q÷>÷²ÚþàÆ¶ í ýý¾^¹þ-’~ì7Öä—ÃZ.§¿Jm©Ñø)éf¦$’L¿©+J+1A±EsF2õm™¸â#2ØÈIƒCr(’`»VÊð4ÀÐ[7Kª>–Ò„Ññ©¸~Öë@‡]²@Ûí_pÿöW¯<ÃJAíUrs¿úâ$x/"p¨äÉ”W–|—´1£¢¡_šäªtF³Äûô‚ßy&nêkì?VÀËÒôòkwôžŸ}K-ªíØ:ãm5©~ä• ýG?ñ³úâ?¬¡ÏU÷>û²Ñl£þŒ*msï] f8kIe&ŒîòÝÉIþ;ìgC€… jºˆ™ ­öO~…G“~w-^%öhµrøB*G,¢PìÝǵ™å’ è©\·Ÿœõ*kë$Ùª.Ý6“Y»ép«üA7vYvŽí v7ÅuUÚìÒWÙr{&ëÚUöKj ¹ò½×YU£‹7Epêû}ˆ8{,¢äES›^ÒÈÄ]5HeʨUL˜Ä¿Ç¦f&Ϟݺ©g>ùA=óÑ÷ëÚ»žQU`mìÚíâÁ½GvµENó ÔZâåËÅ%¸ŒØ%EQZöµjîµ\}þi]÷³úàïómq½î|ú%½ñ Ÿ×k?ûY=zõ Ìc꥞´‹ÿƵo~ßw1þFŒùʧnë«Uåïq1ªUt‚mE`5ãÂz™SÖâɨ;³ïØÎu×’¢}ðæªÖ™Ô»Ë$¤-3лÔô”Œ Gzëò bfÇçR„ùï_Å{¨¹lÇ—$&ÔÚ$‚O 0+¥6~Tæ{ÈQ¬n¢—QFÃ0Dãºy(Šçêf7UõâÀ¡H¥#`NSÕÔ(([ œ«3×Âko%#XÖ¯½~Wï~áY怷VÏ}›ç?ÿ ¿û·|^ÿß/þ’¾õö5œuÔ_z‰^Î‘í¬¸öqU³:¯AÌÑâÔàX‡ˆýÁƒ Ò«oOÈÈH˜àµzqCŸ­¦È>¼zO8P9=<ñG==G[VÓd ˜$ÍqêìÜu+wVÙ(’T\Éä£åÒ8çmPZ_Ñc² tw~õÄ_úáàŠP‹ -0š,eÍ”†Öô–K„`#ÓåÚF°œ}ý¦ KW4­”SîR­VÖ£÷¾¶æÞh2+£´j–÷¼£$=ó±蹯ý°®<(ÃðßHcô ohP¬Ë@‡¥íóv–È^ª#4ÓSi7ÛgRë~Y_˜““¸gP¾úâm]{PÞó}ߨÃûH^þ‰_Ôå÷Ý×)jkû¶‡?ü…¿ZÒ?¦ß 1}ðc߫×î¿ãÇŸòŸ3]{úKÿDQŸ{צyW4yÒÅ\“Ag @óèœäÏ…4=+¿Æp­ÕäBÈ[ô l-Ù‡7C¡EBc½ÅQNôPÜ|^MVJkvjçKO!‹w!W»´ìé±Ï2J|µÒ†Èen ¹ 7â4Åþ×ÖSO½.ŠYÒJ¿‰£C'«TóE ÐÄTI³À^AŒ‘½ëîçgf oéÛfzî‰Ð÷x´tý³?øŠþ‚ÿêGõ3/¿¦o»ýeMU†o]Þ} /ÿèÏ©ÐÛpè^Å&w’õÇó=Ž{B /¶ç…R–¢ökae«¶ a4Öh¡l-ë©I\Ãóçèúá“?^W€¤Ce1Ðpî׸'B:DÐäXú/fÊ«'€‡baäï ÝŠäx’ɰÒÌv0ó¡aµàTI2"-à¤nò÷Ž, ç:怪ÂhYñ½Dpp ”€º³b]Ð=eQÕM=‡ÈÕ½½x5eY‘B™¯œê¹oú˜>ø|Çãàñݼú°ƒ›ò¬#Ëð“ó åL °Qz4û)qñšîsÛ$4)?P¢Ïå^Íîô4~_ö{%ñþóÙ™vׯêú{_Ћßú)=ûÉi>™õðµ»R|öåû¦Ýüש¾öø¡_ïÇ<íf}5ãö{èÏ)ª§‡u¬|XhW&þNÄ.ÇU©νWÖerñJ<^×Ú¶æŸjŒ¢§•SS`ºî=À wÍS4¦K¬zfÛÜ"-^Cîy5Ì…ÄÎÍОZhk*rF3ëÀd7„¶fóh&…T‹EW£°V{”9T:Ò‘qtšðª ¿ŒÖ‚†Ú0¥/Œ©ã)Ÿkoaéµ;wuãêéµ­è‰l ½ÿö3ú»ÿ·ŸÒï]Ä›|û‹w´«-®MÔÐÛ¤7~ñ%8B|ÒôœFæyH¨Ö#SD ­+µf¤P^ÅŒ} 3¶Ëk@„™YÕ)ŠžÕll7Î…Ï7—\Å@^£ã¬™–&p&èI0Xõ2±3¨ùÓßâ ×”8J”¼ª‰z˜¹ÇÖ´'‹È~ÿÃk¿Ž€·ê` ȯZ 2œEšN6ÖÅ&¬RÖ+ÆØ†‰˜²nöÚ@ù»[(KEÆ-¤bŠy"SQÑœæC¦!Œºlù®oÿ”ž<º3 ŽƒÂbÄ`DPòqœ ¼±O¦ÿ0J¶™e+S¼¯ï£´’õ=“ãïTæL¨‹52¡hô¿ç™§ôâw|Jw~ñ‹úòüÜù_úü?#éÑoÀ˜i"¾ÓQUÿäÖ:Ä=¸ª%Ð )¼³Âðȸ#ýC i,ñAy€Ÿ^mr¯-¸ŽÒQïˆACØâ†•LÖ®‹/GüÄg¤àg,{¹“–ʬ¨‹¾ mσBŸñØ4@Ù‡V&§¥¸™À´îY= «õ ¶|ïdM«æZ‡œ¼Ø%½©çV>c3ÂGðï¨ÀÎàî?¼Ôµ+‘·÷ÿé;‹þúÿósúÝŸ{â–ÞóRÏ^¹Œ÷Èĸ»Ÿù‚Ôº]€<ÀÙÖ49è­—Úv¾¾¼¾ÆŠɯ2Í™Ô8‚Ñ"÷U&qÔ|>‰gT€¢\Ò]š¤O†\ºhôݸ¦,H3!’Áá¢L^ñÇK»;ø—¼v á€ÛÇœ`+… =ïiÇIæKÐð¶ÌýL0µÚ„Žþ;+}2#¯ÑÊŠ¿=÷×óßúI;¢vMgg’!Ò—ô¹1p¢ñ„,ihÏg§Çcs¦L¾=û˜OÛ-Á$#ÝøYk¼ÖÌûx}'ØM?Õ½Xxöë>¢ç¾ácºû‹Ÿÿƒ¿øý?õ‡Iúï~ÝÈWR¢¯j´å›×.= —2Z¨Óè¬@]IY¥iô42w„‘U_dà£K­.íp3P::Þ¼kÁüȳWæ*èU d(°B* ô+ýá¾_9®ÆJ7Î^á(—š{ÃGd¶×§L¬–ûÆÎ7þ™™5IÔÓu­‡cydð‚ˆ\nƒ‰ñÞâx¢€pcËCNÈ(€€ ~ß@éõ7î§2z[‡ÁµLúgäuý3¿íÓ¾Îar «ÓA_ûÂ¥æz†ÞS¬€÷/ôèµ£?G+Ê4à¯YQÖäÎU¥œ¨ÎEÍõy ¥‚×Q\*“—ã}?@4Ï®–äÛ¢qßÈ•h;нòOÍ?Ī;Üo›•(ƒ =gÚ]žè:Y`±‡‹‚ÑYtNÞ%‹ KGÖAÄ£T}_²8˜á4f‘þY›׊Ú3pó¹ÄýÀ¤Iùz°³›Ñ_ÝÍÑðöÄî`@äoîuZž¥%™—¾Äq†¿Ž:.- Êqú.[_yî–Þÿ{~“®¾ë¹8ÖÃÅ£8î§u¿â ï-÷:ËAS­X¶2=#c*CšžÞ¢ËLÝË%”٠Ǿ8o|RüZ‚{c®ÎÇÂw‡¿q/0Î_¸¥þÁßõO´[Ïü/’ö¿®äýê®w:¾ýÿø¿Oëí©üUM‡êf%I£ÙÎøí®ñ¹~]ÓÊD9ùÏ~Ë^õŽÕ¸ðWU3yª«¨™„ÆJ›zec2Ï‘1ðãšr¹‰zfüY}0Ö]{fâå=’d'3ÂÔÙxAgŸ£cý^AkÚ7+/€.!€Ô47rïik©Ôôí9ÐüýæKÐR¿¨èÁÅ#]\.ºr~²QËh«ßõò^ýÿü3úùרÐu£üëž½£%£å0÷ƒ/½6í.õy¸¶JÉÚUíA¤ñM\ÃrÕ Í $3º‹­+Ím®yÑdï% ž¼·=`èßMø]ðüV$ûhž+G¤9&—knÆmŒ6,yÔXA…e÷Gú©\Fs¶¤†;52 òûO.E½Æb.Àq¹¶N3ý à*U‚óØ$ ØÕãõ£Lµqáçl™Ù/ìÃf 7MH É¥¬i4ó‚"/4Þõ_«[ŸxŸvÎÆÞê~ÏûâG½32YÊ^\«â¹ˆl<ЊIÎ\¸Nƒt}‹xÍÅEü$ó`n€ðJ¦ÉX÷£ì˜ÆU°Ù6é˜}ôäÞ땤ä×5€<~S½Óqv~ògâ—vø«*ƒ)¦Ë XÄN• ìeˆB”—Ä¿™ \R`Ú”>5ä³ý»ðDçïnÎ,kÍN­ZÖ ÷0§Ð0©W"òÈí»ôEeu£ÒÁo‘Q¸nL@óðqºøÃp,\›÷0u³M]wõ®æhôJ‹eæ©…®FmhZµâí®Ö×±½y1‡eI¾êã‹Êu­S5›¶%4XÕ½zúæõxŸìi}çb¯ðw¿®÷‡>ò%¾\,Þõžn\j=ŒÀÀs÷¥WIÅ5Frg¬&ÐEÙ oi^–`Ú•¦êœ¾ ,£© Ÿû *«j|¬{£©íkV-ÿºH¸†—CÃqXµÒhvnL†ÔÖÌLþ±ÚÜ7k¨ 1Ì| 5di(UÙA™‘ø[²Hê 4ƒ#Pøž·Umkbr³œÜJŸaLt|÷AOn+6îÔ3|`ùæÐbÓ ‘Ì'ÉãÃãëÙ1qêü™›úàï÷­:¿}Óhó7ü|¾Ž”¢–øìN£TÕ.Íg1bÛ‡ª¥¦k^ã¹oûÈ\—HîËæÏ*A}] 0[î6óu¸lÃ.¿f]ÚßVOÏÿ]I_øõãL§_…xbùNIÔ4³—¸JS’ÁGÿGe}ŽÎö<Æ 0ßT5ٛ ½d„,áé ÉÆW¯nLQæøU¬t %ôw»Fù¤gÔ’Wb¬sCð‡~K5§4QÂïf÷NŽ]UH4˜É˵ Ç9P9ü)Ë“m¥¡Ê[ɔѵJ7§Ÿ¬¾‹EçVEæ6©èþ£‹í*H?õú^Ñ÷SúÅ»{•ùDšg¸ ñû•Sé“Ï>ÈŸG¤©‹WÞP½)‚ǨÃ/ÖMšŽ÷NX%2éb–lMKœÅL\Ì §`‚ûANÆ—Ù¬}yrH+\ä1”¼Úi¿^ë]:ܦ(÷ }U eX—©b›=e5Žÿ6+w|3àQmšôÖ9ÛuVé¶ËeáVâ¼éé¤É­…>ÛÇÂý’&/ Û¤N’Þ'fLQÂ2ñè Åu8NŽgˡؔø†~Ù°†ˆH¿]‰ëÙÇ\Žwï×Çûï8ص0mÈ£­gGFÎGîGd°È³BuD¿o,‚Œ[‡rBdAP©w./U­i–L³¶+úë²DÆRje{³Û«¦ÓSÎQѤ¾^G××Ëÿ¤?ó×-€.ß™Sâßùô³åÿX—÷«o'½ ÃF^“Wu¬€‰Îc h'¬@Ÿ¨ƒ›INJ<Պܶöb/îM$EÛb8Oë‹¿¤±:Ôt” È¥4[KL¨NãÍf®%ö §ƒºBrqhØxr©Oô çF˜Lâ*ex·¯Ô^«Uz; ²–¨¨é:fêæ°Ö”ÆqR> ¦¼ðhzjé)è¡$7ô«¯ßÑ3·nÆ3ÿÁÏÜÓßùül°Ïcâí4uuûŠÍ­{Úí&­û%+»Æâ_üü‘ÛГ;Ývv¬£ ÝëÉ1JY² Ií!QÇçТ´«pòÆîkÄ@ç‚>MèšE³‹"XðL¦>«2ç[Ÿî¸Ö™w½Dþ¶¾$ô*[uàñ¢õÂÙ²96ÚøÄð>58‹ºýý™˜eÑ`ÿ’¸·[°Rs°º\;²ç+½B‰Ç>Yuåò™,}Aœ÷h~× ‡Ù£}@¡ë´•É!kÒ{¾çkuûSÔìÒAo|È|6ܘŒûrˆßwçç¡]µ\\Ò §ò$ˆ®¥©ŠNÎÏspÊÛðw2¥Üñ\^F€àØcäRZ¶È3•+é‰,©ïvÚ¹dïw8üé½·QÒoÿõñá@ßfü?¿ý'þðÖû)D98=ÏٽĹG×Ôˆ²^íz%‡ÃØÒJæd  R]’f§=›å[CAžÕ¨¦št±Œ¦A´ºµZžŒy®VÊRè¹ Ñ;ûÆçe‡•3õö1h,ørCnª*”úbÒ.Çk4A3¬&¿Ö‰IxœÓKÁa12ˆhzÇö!˜Œûø©œ*§ì®åÔÐǚίè¯ý?>óX†äåÀšvqýâ1ü5Žó…ë={z_½MC9Vƒa~ïó/ÇëÐQB¿ >OÀÊ£F€· {?øÓXq·ó$"­m¬ÆU/’Ѱ•µÏµ'Íä%¯Ã:ø(ˆ¾Ðµ†°Ç’Ø×IT¢ì² .yëÊ$bhmÇçÜ%§>zÀŒáTL(#aá>üƒIÂöy­&R«¦ÒÃÈò8‰q½ãó´RðaôšY‡¢­ê YŠs·Š²TÉ0â'ŸÕ4%xkUJ‹‰T6QB5—ïHJ2ýè]œê£Ä÷êÊóO«$Ž@Bífû¦ÇÊ|p8–X¤qYñ‚kTBe#2™è©L»]—ÖZ AIUÊb¬‹åâ¨úºÆ9ÓTϲú¹Q>Û>Í´ÛÅÂ+{ÎçUS°éŽ6íÐþ)Ißùëã]šÞÉèëþOL}ÃSÔíXLŽÎqE³ŠÆ‘'U6£Ðåf•¸ fª&œ*ÉðJT9C0ÐìX7ÛaUq‰£ôJÏ_ŽÀ—U3ÁiVG•ë½%&K©¡æ‹ üð©†1¶èU ±È¾1{@ÉÔÏ[ý·(è¬t\òîøB¯Ï¬âS-Þ®–ôwû±#¦žêkÁE]FÇ…(×!O>Ã.5>ïG—úËÿ»ŸÔÿü™¼Ö¢u0Çýé‰>uû5uÎáñï*É;bYtÿ ¯l3Ž!ƒJ@™Õ¢Í§Jô–¼?Ÿsœ·3©Ò«ìkârt&kН¬°ÄJÜÇdB]6zhQÃÉZM«\®é-8N{Ȃ޺ã ~º¬ûª`Ø]qô2(!af"Þ -×ãe½+Ș~”xùð4 Gƃ²3“YØø?©;su`šéµøù5ÎF:© \ bŒÆ½¾6PhÐÎ\QiX‘݉§çó3}üþ=Cä•ûâ€S× iÓÁĘ3ؾå1 …Ü ‹MFˆÿ†cæ²âAkk‘U€îêfä·a=Jl{¦óóxýzy¯ Øn:j‚™‡¶>ñ–GŒÝ×èQ¤ýŽZË!é¿ù5 ¥êUý;Š’I” )p2ÙkRXÁåì¶ ¢Çd™ˆX|¯FÓÚ”±z-;Im¨öê‰^0¨™„c4{‡+&êcÚs µõÉêŠ7±›ÉE«'‡ì48¼Ç#8ŽÀ©2<µ äAV÷({Ò'ñ£¬Î"(…‘QqÓôq]Æ*;ó>b¿ƒÝVR#™\S$Ð5~V²=1ay2.&­f?ý¾/žèúô=•2ù‹r«9ËÇ®¿®3]?&#…à´\>¸ÐB]F²`ML5v°cpÿSÓ˜ÈcrØ!`”Bpf…8jÛPëSô rƒU}Û^5jÛäÛÍMú)•Õd䥚ÁqÏÁàžÍnoÙG{O†>®ýÅ>äZ>î z+Õ ÕËðJ=…4i“ ƒMÚï3TŽGoŠIÎ÷Lt“L"]Çr )Фêâ9_£¡FÁ=:2ëiç¦õ!&ø¯ý“þܼšQ€Ü[)ÆdžÏ3êË‹˜˜(%yÂÒ< *ÐV™oÁgϹv>ï7MÊ„DöQNNQv;(Ieû:£·ÿéúŸNý]~¯„Œ@1;µÜÓpÚlQÅîÈÐ ­Ë<<»³|ƒKs—WôÍ¥”5݈2”™è™F|‚ —?8V)cõ jŒu×ìÞÁÀ…r”ò šfuš¼LN)ƒèEy•Þ%*„ãÜ`d2va”US_zªGWñþÝÇÔR#²(‰bÞEw&Sýˆu\—ùä(bW}­vó.ö÷ɧŽB§Ž¢r(¨îtu¾Ôû¯ÜQOp\š–”/¼rç81øXàò¬……¨¹³eà{¢ÕÛõšï•!qÃJm†kXš㲸gA®@.­ÞâÉZ¸¹óŽÃD(Ê4ÙñnñÂ9Êlâ³g…^‡<ʼcÁ5šËoáAÆšáì0ˆåZÖ'fÅÁƒ12am›ÖÃ[Çö²xŠs?â(šy0£ì;è¨Ý¥Zk\+çxx1ÂohiÑŸ pØûÁ'ø÷5Âï§óÛO1™áâ5ÝÁØ^wG"gŠRTìçÑ™¼zÛÉž ¸mzgiŸÈ(máº> ÄãQqß"‚}ʨüÄ>9÷XQì=Ê[.åÑõõÿk/ÿ ’þã_ÓòøMôvã;Ÿÿð§ÞN€Ùex¡A½>µ|uêÌig«UÁê廜Òz"*ÑlU~Ï¡qÅ¡°¢kT-¦¡ÑÃ*¾“n1‰¹Axœ¬JœGØ÷ÉÛ"¸}ür„ÌÂZ™¥6—à"®½ŠÒP/ÔzmÙ ©P+ ¬l²‹8±Æy:ÛŠMjR-Î-zN>V…•ßW•k‘ÐpYÂ#®ÍiYõ½ÿ†þç—.¢⥢3´¦Oܼ£Ýù)û¤ “.ÄÀ‡_zÍ˃£¨ïK·CÎq¦èXzÃÍîR˜@â4ÎWA”‘ÙæÞG¶ª1±Ñg«bÈ(Y•0“²îlÎR«¯©ëõ‰iŽÍpî®"¥•*ë›î׎‘çfý†Œ;H‰o>(kEéo.ÆécåAppƒ›·ãL…U.Ÿ[\{F*æÕ‰cdž—æy†ºÔÕôÉ?æ÷×Õw=k‘C’=ƒ¢G€4I)5DìóòÒtôìu?Vï þ½ìqsšØ³TÜ 4­è°ø"åü\EŠ·JÝrv"¬G'Oqð @|PjbæL;4n3ž¦òwâÖ3ÿÙ¯¥Zïü±[Ͼ²Èÿ2‹Õ©q¬ÚŒþÉ«ýI‰ʪ°¸%7~+Æ~feîÇ`Þ±”Et±,U¿÷^a ÇPºpS¼Å´ªz73_žˆ”D9µ>„ ý®:y’lŒõF ã*™_ŸÆPLRx&&zYÍ_Î$¶a÷ ŠëyÿýOŸ} ¥Æp_öº}v©®íiô®»rÅ4ê·a¢£búfm'û`·¾˜dwâ@-ÉY'fLuÚ‘¦ ‘–mY™†Ð^r«Cª&FõçDàT}“ØEd»:GsVûC\Ç GA?“XV>æ}'ûÁGí=¯r}½ÇϘ¸SéjL¼–½AÒœ3Ë»SÐ@¨õò<Ù#“ð€ìJ Æïãûéý)*y{)khaA;˜¸÷ácăs`Èq"%Ã(óĵˆkû¡?ð;tã½/ıv­Z7}m# ·ê,tp0ŽiP‡KÒüÓd6=«þ4yk°Õ+I¹>oîÊKd(@›ƒýîÀòD¯‘'0Ø7#Þ¿NS_Ç‹e°á?þóþtIÿ–~F}üz»Géõ›XÅŠÅ‚=(XpóBYÃw懙 Ôb’ \<ÄÄ™¥M¨¹<«óù¬ºbçH»_z¸´Å1ºLµâ-°Ú—‡ÑNÔ/½ÝEN%X¡ÿ¡¬+ØáÄîž"1¯‹ÊÊ9‘³Æ¤!®ßøN:ˆÆ¾ÉV(iÑWê¿ÑHçõMm:×ã5ùЕ¢¾\64àÌË>¿yGcÑžÑj=¾¸ûûšaÞ'@jÓ«+öq 8Ø”iô]”'l~Ûô2$0û¬þ‘wAÄ{¶¸ü,÷±Òè …æ ·œãQ”„™8ê±9<ï‚¥N'`¿LþlÏë¦Ó3Œ•¼ð¶±FðÄdûWR°„H ×èÂv5ÊkUy ¤gQè´6IšœÒõxt<È÷Q§_ÍÝÉîšdKXádÎûUÉJ s¼Ï‹ßô ÝþÄÔÖàD˜a_¸ÞÃÝ_rDUÛQf¤ÖÍêÝ•+ñ“=e=Þ·3Ïøõ³ƒDp0ÎÏ#ëÀ9V_kF,:LŒ ÛÇþâoÛ‘ƒýÛ Î‡ÅXW$PG)™>ï1ó‰ÇzyùwÞ:»º{üЯÅcþÊÿÞn,:¼[m…1&üa1š`²¶3î4“«–ìIa1¦ʇê}Vöíò«Hk‡W8[g;ûC›ñ]ì-¾vì/‡T9uhb°Ãl]ÕZ #Œ>(‡EzZYk7ŸV%<ÏùS|CP _+8ìf³Ú{„ão½™Q-ò”YŒ^ Fÿ0Eª:ÞĔኩ9fvWdoÍÛ©u_ŸEZºË|]Ùžøtªúú[gú‘×.E=þ=7=u²WÑ©JÖ÷™ûîKL€Q¾r˜3¯£ªPóv)¬¯pRP‚íƒðÖ±ËX©·ToŠ’ÔÛ¬†Ü¸ÝͰ×f? "¢À /fš!Ìbƒ¼iL¶˜L¬,žt™]“ÍîØÀe†Á>4„¹”Á¬üÌ«ÿ\O#¾k(Ýåúd›ƒ­äIÞžŽŒâ¢aO“žýáF÷r)a™{÷¨3‘‚,‚mùû/¿þêŸ'é_Ô¯Á¨w®·z|Ãþħں\i© WÉ<Úp‘‹ÀiÅ+؃Ôr°¬Xjö³ˆ€@T4’brfeïÏÅbW±ÙS£YÙtmœqÒ)J*Ûu˜¿†?v4P·2ßô)2ñ)žJ½ö-_‹x„AYÁ‚€Å@>ž@[èmM!ïÇ2”œŸrc>Ž…‰lu/ªø3e2è–n!ÅiMäGžâ[œ>uÇ«ê}Ô‘clSt]¼~/\±_çT ¢°²$[5»J4Oc¶U¼gÔ³ãþH}…B®éŸœªÌì¿‘5ÆŠ?,eç"ñeD°Á ÿƒÞM†©ùž³žUè_UCˆ‹}³±”¥¾v[Èx¬ëqÿÎr€ú¶Þ=‘.d\ƒÄŒ¿cœ«s&uXà5—´,Qr*}›ïCoï²S²Ù*-OÖfR ø}&h¨Ñ§Ñ6&(z•qíÈë÷8Òïñ¾ó$ﳃóáàlŸ@@ÉËïùî¯×•[O x¯!Ù.Ç£‹¬ÿ„Œû/LÛ¬…•Û@Éj·~^›ÉñØ”¢†¯~k”.ó‚l&®Uã¹È˜n• X1dCÎ#^ÿc8ó[| ðšÞóÆáâO’ôïþª7Ñïø­¬õï©fKwKÓ8è‚¥m. ŒÌ¤4k=qÂݨÔ 7m$’S*=“zGÝÏ—$—næøðnf8!öâ ˜t{ʸ ßÄÄ[TÝh! â/2ê䃫àáC‚÷‘¼Õé“Ô2Ä'áÓx?dg±½F Ë4ô±ËÜ ¹4åKô¼È S ½Ùs%›¡xJ‘~ô;¸‡Åq™9ß’,%¯ ã}È8޽I ±ð ¯ÑHˆ/Y\ŒXñ·‘Á ÁÁi,ÊzˆqT qû€ÜÞ#^å #l|9ʶêr£y猪©Gé… ýEInj“Ž\v¡ÝŽ¥MßUWž»©›$I4ºk¿Û!˜ä<Ƶ`—E?yŽF7D?‚½ƒ9éTow0`Û?Úd%]Ðr™¤­,ɶÂDÏ«€?çÞ s80dB¥Öm†›=22˾¶¿î×$€>Ò[Þõ ‚Eo)•†à›idAÖµÀ{“=Vqc†Ç¹9˜öS–i ƒ>þÂÁ°ó7/Ó %­ ç…Až`}ÑÌ(1Ý7a?7^—XE ï¼Z]”óÒHùl ´Ò щóçåô8F?§Nqa#ó"CÃ_‚Õpg/ÖÎ\»ý@VµÕÍ´)}Ýê¾6þÂŽê lÕMý¯ãó]õÇ}ìiýÏŸý¢Î'¯ÜA›%m®RwÊâºE=&vª3ãQ|nM™(½„訊 Í¢¤”çI죇³ª×9&]Jy4¢U Ò!°¨Ý°_Ô5!pÈyXºv §¯ï’•ŒÒ©ÎZJŒ×AoU¼/“bd&Y…Ä:_Z'µîæ¹€w [.:¨€Ö‹'x_΃×'›åàd`ö&4²k¶aRÆ“œ’ZûÒFÀK ž$QZWàwÇë[ßÄ‹œþ>ß’z›=7+VñðQ=Š †Ø!È#¸F ž¹éL¦Ñ=Rwc½.K”¿Þ Åà9`½@k)áEÉœR¢'ýÔ³b°`A,‡ÄÁI±üIj®sc$Q82(ž«~¼š„FûúRë éùÕUãU›Ò_`&X¤ “WrÌPCžï£Ùž¶g)ÔÇE yt°À£‘gÂGO:W¨Ð‚ôtyš]Œ¾ÈVè£$¿íž©ñx¨‹Þe-X‰ø½­s›O”•ª Äý|R×Ä7™é0SýÚ¥[<’÷2³ÙHKõXkžX¹.²s A"åp8³ ‡ ô²ÚP¨xÂíBŽƒÐú]Ï_Ñϼ~ç¤Íëø2 W+áô¥nåõ!_² ¾xô(9eó%júcõˆ&[ÀK…™ 6ª3å¨ÈJz"²Ñ$ ñäÎh¿‹j¸ríEز®¹g¢B™‹LÈ¥Ég{‰‰! è'Ô«™àÉV† o”òf@ (Îz¥»rM8Þ„”šÈÒ(p­3º.“0##Ëz\B†Ï;e.­‡AA+ÞÔ ©óÕ²|ÈÂê9¶XR×øÃ$ž¿ý©ëÊó·"ó(ôX…g‰É±Ê¯Ã )ˤw2üÍájx?\Ïm‰‹Uü~cø4¹aíçé?{Ó Ìd4°QÕEO$7¢ñ!sÙŒÌArŸŒl4ÙT¸·¢_¾¸à\ÿº_ýÒ»Þl|ßÏé{—u9-ÕÚW¾øx”ˆ{æ6ÑÃÀd&æ}¹½ý«]3ÇU5VØ^-šÑ 8Mx ø êBªLð^ÕRÓmêþb æqs#½¢,O ’\KÉB‘,­Ó—eȺ‹ÆkÏÆzš8nJj²ç6h²Ä™lmÚ âþb“Ù±=rõæËHËáX£ÄÉ^µ–¡9×£º+"k¯FÎVŸñxñ\º~ÅÙB—êI2/¢9|8©«N'zøðµh39Å!¯áq=àÊjE._ÂãÙðV„KOÖÓñK×WµiÊÒ/‰+JͱäïŒrqF:å•¡{/¡–ìÞD&Pµv;&£÷»Z’dÂNÖÜ'Ô}U›‘H±ä`!ƒsæèay‚`¥YKYÑ|ÞÞ‡Ëø íäkÑsÃe°ð­'è䉇²g<×|½´Ûùþ´¶h¸ðõèñSdÚšu¯r;0Þ£g7ÛÒ‹€pŒF¸I…ô€­Ê½æ*L ¨˜x\dyuFßȬ,×ò¡K•Ùø ¬’MžÈÊú §æïo$U% sàµG±–šZÍ™ÄFúžžOì'E TÀ†:hXîÔ~•ù$ýð¯Z‘&½ÙhkÿÃ$°¬N4jæÝÀ[®Išt™à[/hAe˜ã(÷d˜«×ÌSIoÛÚa¥¢÷Â*¿d&£slèž=¤ cßg£™úª^,íQ¤ƒ›ñM™T'Þß“Å{x1˜½ëš&ÂÞaÆg ³Í½àåÓ«à‡Ð o}‚_â€èÕiõ¤ãF¹ûf wLŠÂz­'”° ÀK¥veG€f"V(ìF¹œÙ…9UQß‘| «Ì3±§;ˆ'Øð4½qÖ$;Y#Ü €0bŠ÷‰…9n›ˆk¾9 öaµ=lq«³{ ÇjɆTdH…à3ï¹¶˜hãûX‘U))û™†R5}›¤%<|>Ârv?´Èžìë}öÔ5IÂ7<^C=’ØÛ4¥åÌ‚ÒT¨ð·Û3rPËÌx(k†¹Ñ—%L¡¸Ûåwædñ3 ¶Ûì?g@¶Ƈ?ü?âÜÐB¹˜ÎÈ–ÓyÐd'xæ@")2 ÚZìW}ùë$ýé¿z¤/zÓ±¾=V®4mZaõà¹{¥AŽ2*ªúF4ƒ;P&øy LVòêhæROÉ…^†‰ÄX°É-Éÿ: „@]e„è‡0£xØŸ‰x¤FD ³Ü’›ÿæKS>ü'pÂã”ll”%MðY5IZ(ØÚxšÉ%¨ùöBã=®ì/]MŽj~~Y›'e#³ìNØÄö^÷UMGÆ}+†Éö¨;kìöv©Bs«&ÂÞ'®ßÔo½åí€ö¢ŠZÝhM— c±€w›Ö"pxrcÒ’•a‘ÃA,í0©@àŠººz†窧¡Èi:¥T#f&úÉj)™ÁQ±£6“ÊÓ]Ý¥•#äu].ÉJ"{È¥%JVÑ<ž¦˜"8®A8[q4ÍÚ³“!ìóñ,»A4³§Mf£ÇßRÿ¡bûxä,xÇÑïp„×ÔœýLÊDT¹'L—ŒÔ*Û’‘ôžïøš(AÁ§ˆÉt,ò}VеFY&®á|r2ÜG¶ò¦º./}†ÝŽ I—`ò6H¢'éjC4§]:£ìTÆçA_†í¹>&ܘÜó ÚB±Cjm3û}·ÛÅó‘UR*MÆU”¸&—:ÿÄóg‹¤Ïþª«O¿9‘°«½¿–iLºe@Ac0ƒh‚q®†’è˜xK6 ‡á4öCs.µ°½o …¤ü£›Þ‘¥€m¾H­Þ¾ðémÞ¿”¼<ýMyK\u¯Ìx}âT ƒëýµbÝ`ˆ"ékØF㞥ýðCQ1Ù±©úi®UÇ×x©õ°L„riïÈåÛB”Oø|ŒXò»Z*D»)²§Mœ“˜ƒk“tOá½W®¨[>‚?O³Ë+¾V(Ç^Þ{ ¶4óöœ%$Tyò T ê«fM¨ÁèÑ ·¸!þù[¹ßcÝÐñ±yJQ4@]ŒÞˆÑgÑ,g2jÀVf¢Ðló ÿaÆt‰ÉF¿ï)±ƒÈÞE \C&ƒÁç¨Å*¾R_]¦õi9{Sd nŸù'*Âc1À‡ËûibÁÕsöC•Þ8O•³Ë¢=O¶¾¶‰Pç~ÏæsT×­½_'7®Æd‹TˆE)çÅ$M°½Z«NKÑCˆwibÍŽˆO¨‰@°ßÓˆfâ& „m'³ÒiPç¾Lu[6K’# c@{9NŽâi0ñþá2ø”€RæD/ˆ t4ùÍÆ÷ñî./Û_&éoüU wöæO–éÙ. |žûÛøÉòý]Ei UZg+]ˆ]*%ÅrV`æQý¼hž’RS÷¡¹ò2íìø‡ŸBO@¼ÒasìwÀ ýÇ!‘бû¯Es™EÀšÊÄj•!*¡µªÈ´¨ ÷,hF«(= V­”\\Ú! £dâ÷í±ÁÞ$šÏ 6}eþòÕZA ÄðÔîÿø2à‡}²SOfBË£KÕÙb€ÎtŠSË܆ :LkU59,Ž6»½5ˆ%M¸©êdÓ(1æí‹èéär hžJâúÇ„æ²QÍþÞ£\wtdT­”` Sši‘8„y>N¾=½ 0‰„ 'Kß#/Ù2äræÄ£#aÝÓ3Šïo6”Â׿³“K†ûG0EVf/ð‚í=¾³d¿ã@˜ƒ0zkÓv¼ðÍŸ€‰m˜\ŠønµÑËÜÊoG2C¯*mF—‡3›ŠÜº}@@Q¹ÑçLO•ï™ÕÖhêñö!µR’™ƒ3ž„æjnöG€I™ÛSÒÊžô|F‡ÿ¬n=û·ÅºûW8æÛ§OÎ@¾÷å—¯½:Õ«­u|Î3Í;_h&3§´5•‡Žy\4†’ªí(ýt&½ŒÃýF°¿oœþZ§ùÞQ™µÝ)¯q˜®WZ1g'¼ʸ–iÙøµw ôÐ#àeÂOÍìØ³M‰r&ÒÝ _“Šîäº*4¼é#0¬Ý}&™f›]ÊXñ3ëJñ‘y¥mIôšÜ ýóBXPÖPHVÔxÑþ\D?z/ò$Ñ4íÆÕ;<]3qÜãÆ.¾1›(B°|†–ôÐJI%'õ©Ñ8ÏK·¯ƒm_*NyöÛXMn˱¬­õ%@±còà<'¨xÄ2T›'‚Äβ%” c˜1?†²·Q$Àð,VJ_Í9i‹ûZqÒj¥æF4eF๱ßj¹ŒÃºuéó§!^Äš­ŽÒ‚3ß¼ è–Ë'êÉðäúU=õ¾âý(ãt¯Ü'ó2zŸ7 7^RÝŸräVF>­JsûËüîà»Ï*ºî?*m‰+«ç"U’ÝT ÛŽG¼§$Î?08ÜlÏ~îœ3½ ”Ñ Š5I±¿êLÍý¢ç^½ç”ôŸýŠÈãéIãK?ûòï[ç^áSDMI_‰ Cž|‰‚¬ˆK–DO¶häDc7Ê5t ÐG₳ª7Ÿ‚Ú¿0²qmß(ª@Í Ñ° ÇÂI³zus?¶ãÆ ×h²Ò;)xŒ }¥VŒ}*Áh7«øzDu£õx¯I“ZN;nðkÞø˜ Nb`ÚZÏO'“"s/,6—j“”¸A‘qã(_¢%V³÷gbÅeá@²L§#3b¿ÇF¶K•U3÷ɬ'&o7Êר6ô¥"ÓÂkÏõX1¯FŠßuÉ.¾oF:Byn¦¢½tä €ù—ÖÓ¼¸b·Ã âž‹{;‘Y01Å=©œUâG¿Hvq°*9p,‡1±žì›‰>O‰íŸýÚ©yÕ®ùV©öj¤ÐKx ­»i±ÿ p)… òdߎ1L†’˜÷Áµd¨° #!Ÿ8²>¯f0ÄØÙQ”—V ?°rS>g1À²Ÿ4¶ïÃ(ÈÀÙí4MS—»þü_RÑ€Ù>ʼ~WUW'ÇfÒ–›ÛEŒÝþñ×<ø²SCn2ÛÝ+Äì*¡GBòmˆíé;4ÌüÚX=q$Ì¿öa÷BzØwŠÆyõkðÊnbuØÂÏÃ+¼•ã§å±ÓYò·ÈÝxŽÙH–؆cµñÊöÀÕh~å˜N=>~„Wëq-ëŽÑ¼TUÁJ©*N‚ÏÅÛ;`q\Ef¾z¾î6ªZ[ìâƒ7nIi—û(%1FCi÷°!) ws$Ú*&*Á™™mÕ ÃQ½ža³ÑØŽl 2é¹~̾$ˆ¢Å›™œIÉ‚¶xâ÷ç“Ê] °Ê+vdJÒù-ˆÝE6BC½<þ¹"Ûï€8´½× ÕÜìNžUo0V™¢\è`˜š­ñбÁä áÂtKª7—`)³ŠÐÇAP¥àëbçQ ]«Ãžï´]#‡çç§äP Ÿçé¿[ç@ƒâsa›ãûÅ9\wöÅ14š¾•ŠíÉ•+q_ ¯¿-YKµ£ ¶³Š-¯NOãÜÙ.gWÝÙž‰×ÇcÃáˆÏ’,EÃY¸3Á=¬˜øé›¼Ýx*†­¬ †OlSÊï·«»<~èWò˜wu÷&GÕ¾6&°ÃðƒÈľDé“DBVî6ÿÏò”,f5AjÂóÜ¥3IÕí58fUéöí€Rg²q\ô<Mù¨ë²’"e”0h²^K9Ý¥ðìëH™Ä€¥7½ ÃH®ô®8bJgOõb­-—ì¸N D@æP:ZbX¿ÆsΘ€ 7ÅæŒ*ª¾ž@QºíÇßí{Rie¥(5´Êªèµ¨‹#ŒÀqûä4®™+°È:‚O³hݨK ÒjVNÈ~CØSþãÇŒJÝMn€&S®°¸Ug"ëà÷Zâ°R¾µ¤TÕœ’ð9}.—£0³Ûû!¤(ó˜ fLJõ„ër¬Õ3²ò\³‡SÄäè+(³Ø_<¶$4ûi‘}ǵK寂þØXÑ»„]í†w@o»\ kð$‚¸ÙZ<Áɧ}OöqyÌÕ-Ýtë}zãªÎoßÈÍö±:Ïû‹¦yÕ|<®Øv8„]ì|~«øŒ ¢„ƒ&UŽ0áÎçðå÷ìPØè¸$­ð!6£Ž¡6²Â oŽ˜oÔ$¼Èê;5­‡ŸoÖ-QeÛ¨a9+ñkà-ôžØoÍy¢4Óß&zÃÅ* <™ÀÇ{åKµp^Û³Ë_ø`Aw,OY™½§€Ò[*11©w#Ǻ&7¨é?0jöTa¢l¸œNÉýngÅø§‰ÌÜÏûú­]7NN5ŸD-<¢™èÆs.ËQLËM’%ͪ²Û²¸Vî/>Û"wÝ;û8ö~ÒJ>à¦Ô¥W|'‘à(—ÐÜ8CºW‘ ª˜H@µFП闤{¨±;V’Ôšs`xBßP\³¥Äj8—–ð@a›aúd[\wÊ(ÍÚ`LL%«ôö}¼<,Œ™{[ 3Ž•>h£!•äh¸ÇvÑ·)óððWw ìÂíÖGÞ·>Ä{ʵ¦¼Z¿2Ï$ÌŒ¬CÆ$a×x0@MU¾£XM"‡Alòù“©$Žc»¨"‡’X¹¼e\§(1 ÅHŒ¢BuØ,cê^µ{Õ8ÂÌPʸ>3½z%^EÕcœ]}] u\¢Ö‹f£¢˜DAmÅëVqe†}d(ÿ]±Ç×à_’n׺_ ÏÅøª“[®÷qºš ^d¡.sÕãÊ"æòLê¥ùüÆê4û–ÑØ®|¨ÑÊ4Ñûd×É óÞÚ0’ªˆüQ£BHfÐFVF6•V¥‘O–¡wÝœóï…ˆ:8lÙLðÖ•ÆÐߪ'|&¹D¹cr¾‘ön‹Ø"zGÝã˜YÉÒ¯iQkæš„›¥áÉ:{±ä})zúCïǶ•ÿ×Ö[cmê”ÀrÀ½¸ˆ`û¸D¿p&d_$xð-8ÿ8Öõ´d¨0Ïg.çŽÖZJLô q_×w××ëûÿåÿ\þ¿ëqiî½j 5Ë]ü?Ƶ4x@d4Fz[唸(}r<Ì[ l›xOô~8G¶÷§×Ë?DÒ«_æ¨w íCÿé:5­7:YEöîáo…I'~æôÂ’6*"³èÊãã §‘w™œ”¸!½J-‚‡ï$iô/h|3¤†/Lt‚“3 2öÏ.Î!2 ž4 zT¶P*.®o;Ó¡—ÀþãgÁäI+í2ÈŽäÑ=™ÌžôÝXf¿îI°˜Ãc²%ˆ’Í­-~rýqx$CŒIWÝ+ÖŽv™3 ±Â†WÜ÷DUÙìgŽF]M†Y›Ñ¨¥—R€žŽäÒSLÔ¬9¾N™‘`ߛﱄä+|1[6á‹É{šOséÇÇê÷ˆm<2h¢ÖÜÐ,ÙÛGA“åÀ—ÔåÚ,CQ­Ðê/?} ¯Ü)5ÃD`1`º)#êV ¤VÜ7áÁkêRJ£üØ Ö%ùÈ QNõrìÉ3Û»ÆÃ‹-öÏû~‹ƒ íuû†råÙ›b0ó˜ìe(®ÿxäÈ&Ȉ±mâÄ##­R Òû Ô“ ãÁï’ þ1cå•~ë‡~ïo×ý‡ÿ£¼ô 0nžÏƒãˆ©¬,Eƒ‰£¡,<‚ÛB|ósÝ¥º:9‰ç›Ö^ïÒ/÷1?©ÿróÙwݲfuQ=ˆ`kѰ­Ô³t†±ù¥bÁšjÜIvBUë®ÛU_¡…#¼h=Äöñüð‹vO¥—€X–°G ’³~Òèâ‹Æóü™òÕÇ#=/6º™ZžXhµðÚ`â÷=G’ƒòø²ÉÂu^Î>$¢.MÉ%$ÈcåúÛ#~Lêê`3Ô‡g¿Wd$¥kçÌ#ࣲ¤ˆŠá×M‡FàŠ°«ôÕÎs Úÿžz,äAoª¨§–2óÓtB¦š$iXíÁÇ(V!¡±‚V¡ôà2±3ÖÕ=.2ÿ~+Ôš¥¬êøw¼d†(aV¦mŠòÇ–‘¸Õå†Þå ßxU{’X5wª1ÁeĶ ’ù)|Γ¹s9…“ÿÍŸÒ/üo¿U?ôïþ·ú–?ïÕéÓ×¶©q|H¬oáÇ›ÁóœËÐ~9 ƒ©éY:éè/zr-œ¤¡'F½Š4Ç”aÏÉqøž#‹!7· Û{ž´}þÓ¢‘ØU²¢òêFrU¡74Xð¹¥Õ[lÿÜnÖzy!V{=£„ØÈæQÑœw3±ÂMYö.³+-°¦a•{(|Öê=NÆuî°úË"Dz*]¹¤£”L ÿ†]<¼{ÓË%ƒc% iZÝǶ¹ÆfÛ<áû±à"’¢À‚'7Æ9—mÏGÅ7m_výÁ½?êñC¿œÇü•ÿmÇTv[e/‡Ã*šÕ×µÉMSfPPˆöáÙ0JWd,ƒld‚72VWJ–ã[8îH€Ù:øf)}ô:ØÉpþ3V¿EæRwxV;S˜&=|ð@4OA†µå u13ÿr¯û÷îkwõª®tµØKÁ—Q^ †jl?VØ@­Iò77ó%ìR ·s\;ëý«$ó( F(0ñ°‡Z1ç«|<Ô£L»$…ãÇaV0ß)!‘A ìÛó™~®Üßi†+—|˜À”4™2é­ÃLc¥”‡«ý3(=šÃÀ¹HÊ ¿¹±|œ¼È€“ß…êh6gn…Xig$¤:q¡b&ƒP:¤çÒ#{vÖFÈ£÷·ô›àø3œ·ÑrïÃ…ì8õÐW8Ôé3ikÍûtuúy¢#xÀß›E‹èŸÓw)rvŒ¿ÉHDÎçìé«Ôó7йŒ SŽ ãdªzäÕwLämc"•Ç~³]R±™§DóýM É€Ö¤üü7|\?÷ßÿ¿zðåWõÓÿÍÿ£oü³þH±à`;W0¹¡O¯Ÿ°û£µßo=ÝÙž¬†÷1¶õwÏQD €Áò'Jú7õË5šr›ÇÚÛ‡JqÓ²O¢‡æ´ g˜hö¾ ¶oµ¤8 wrVÏÕuõâÕKœçÚǤ üá%±Ä,á„࢙Ùâ¿÷d¥Ë äVR®…u»_öƒ#AoFü’5в.Ÿ|Œ¾øðûžÕS×ÎU§È@xÝxÿÛ+""L]?P½oÏáW ©ÔLkãuÌ“äOžídyâïÎÖŒKÂŒÅ%µ¼o¶âü®ŸŸkÚÍnBWê• Wd5]¥XT µ"3Á#Ýõÿƒ Wòï½S&ôŠ{ÍàpOjËFÆaä" â ¢\ŽÜ—w@ŽÍFwMËÈœ*+ðØ×(…ìfÁêÞŽ\Nax¢ŒIr›åôe‰ãAqÆkñ(Sl7Ï'C¾~!кœ¹0ï¾òˆ~Ÿ+DTlKF 8#b~MÜ+«ycëâ@Ýüù.ý¾¾×¬æpåö —p¥œ­ÂýÑhþùŽÉ™ ?mú! ~Ïþœwû:¾{ÚfÛ,u þÆöô_âÑ{Óí0¶}å§~AŸþ¿¿?®Ïd”™zgåº^pk@‘ñ ÄT­%¶^^F6D)Œ¿WïcrÅýÎ=†ÜJ³ð#‹}(½ÿ¾¥Ì·?ôÕ>æRæ']¤÷ý¦'ÄF"¯ÄUztLÎkCBb¨á6ÉÛ›± cƒ†3xm ¨x¨ÿ_ê‘Ø%¥í!½!H‰jæ ®?U“ŽŽ€C¿µZ™©Î Ó¾G”ê<ù ½|>º÷sYO¢qz¹H7¯ŸëÙ[7tyy©;wêÞ£Kš»!£^X©‚'+¼ÑÔ—âx-š8¸5I¦¬mÈês°»ãºö$=k,vOšh¤yêÏkÀ„-õÂÞûX+³–Äè=¯:IGôÓ“àÕâlé í˜iÊĪլRiû±«³³ÕP葉?§ì7²ªh¢2*z ®@÷¤fª*÷L Âš mEx¦Ì–Eq”V’Õ«ÿQ> ”z,¡~FÓÝWÏå ¡ÖKà 7÷#ùv ð8oOða¥i†= Š˜Fõ5þð#Ÿsì¿‘±¦²<6¶<À·Â‹´a·j=¤í³ÇÎräo\¹}+|FŠ=K‡NDzø•D?äØ7¨eÈ~äÛ%îEF>ÅçÁþw.Wµ7+1¥L–ë¸E0uË£WgÜ·?ö>½ú8xHUŸþ?—žþð{ôÔû^´&Ü#ép˾Ý*Eo´¦½KMY°1g-kUÐìc;D–)g_i2T¥­~pŠ7û‘XAÊY༬—´¤M_å˜Ã”f;Z®×’ô’²0Y'dâ`7 -ú  2·óáÄìc,|Ë™@Á™KÃ÷Á<(Ú÷âI ›WJmˆï)7Ôqªóö»<=ûaô®ËËG*Þ+Ïì«{ôwþà 7EJž¥®_;Óµ«§zðh¯;.ÕWJ!Ä&_ÄøAß ù"”n[if™×x]xV¬q]½'~€ZÄqTúæ9$ g!4¨‹û\.ãÑg¢ïDƒW}ÌÿfÁŸyE¾;?“¢×XÚEÇžÇV± eª¬d‹E±BC몵äõ|é•ÓÎA¨3ÕMS˜RÖ³”¤,û]5¹còDèb[¯bc+‰æ7_h2cêëq}¯ÿƳ¦i°WÈ‚À7D”½5|¾‘PÇ“]››£@}u̪¼]Öéx'߀ôÒ˜wp3~ ¿×áÒtÀîMó«Ê2%¨à‚>ªnl3ÂË¿ Ò㉊¸o5PÁ¥´Q`¥M–°ÑTl ’1iÐ;ãœBa¸J —´þ?}ßûGáƒÃuåƒÞu[Ò+û½.Ášý>ŽÍ%IY,dT«¶}†™ðôOâø°|.ƒKÂO2’?ñ—@(‰0Ìݸ¹,ËËDm°»\‡D³’Ím7ôÑј…|Ë÷µt6æYDé¬ „)&F¡Ç_Ì)–“°ŽS[Hëí¼×ð•ÀùpR­”aì'z&]hg]^ì*ªà 8TrÇ=ÇýàPô­ÏÝ’G ›°Ãd?×®œÆãþËÈRV«/m¥×a¾Îo.ë8Øìì×±®LHx4€‚shM<Ef×™Ìrz!!.¨!ûÄù·¸&•R%°ñ…Ñ:à’®éC4´ßz/„ …×wY59ëæ…+®€–0—³µ=’þHµÇÏœtAFûÚ½*gI8z}5Ð „#qªró‚V»Ð¡Ðǯé±jg4Ê_)ûimõ5úPkw¿e¦~îÏÖ.…‚-_µà’({È»Ïnc_^Dœ\=SñZêþ¨ïŽÉ=ô¯¦>s»ÓÍ,ô1˜Pc5nÞÆO«KL!.x~ïwéÌ!®§Ë[Û`²uD\ã'À,ýŽ”ËÉS×5_;×áÑñow_ú¢¾øý?®gg1ØîÞåeèÝ>=Ó½"]dt_6¬âsw–Y2¼˜ýâVðûâãÏA\½ÿÞºZŸ•ôòW@¦›§ÚŽUëõágîF.ßùhà éôâ>F~Σ³=^ä8ò9P ²=A¨kÙK;e&MÍA„’SìÍ%/µnQÃUE3ÚNޏñü€pNUûýQµh®Ç;úýGxÇ•×G~šÞR“æÆµ+ñó{ôèòx(ƒ¯Á5Zûªš%/$µêã^Kz«6A¹Å#ÜßðqwÉÃ`†Ä)s×ÜK¤Ô,*éÛô´Ï*ÑýúÏÚ°õQs )#‰ÚB¡#Á§ÈzWÃ@kèKµZÈò3GiÔäSüoÔ_!€Öšø4µÄïÅ%еŽÈ®%N ymKM At¬ÚPÒÁ»ÒD_Ad‘^ol&ê妸rr¢³ZýˆC'EºÜ)2Áo&ud#¤Ü/ÙBnßF ~3‹ÿ¼öÂózí>#àØ?ÿ¿þV=ó©…ÍŸ'H¸ûó¬«';=­¢{½éÄóñP–·ß8æ5n"dF©L­eËÞÑg¥WüC¦õõ‹?FÒ¿üUÇ)?íÎ3g¿¤zÞ+f©&¹ŒüüfF-/`ª'Z‚KGc'ó }Í:C”:ìgà È®êR§hœ7“c•æÿ##Çæ)^G/'ÁƒÂ÷¢Á\ªl"]Õûr‰í÷‡¦oúÈÍèwì¬t¹mìeÝ-1/œéáÅ^_~í®€Ãvû‘ï…] I;3âô=Vx1eiªdqÀ* >4&{Îÿrép8E6&¿ÂÕUWf³Ìs7ÃF÷µNâ†å û|YÌŽŒª'ººð ¥å©ÎŠGü¡³Zéã’h,®iw±3lpÊì÷ÞG¼O`Ä”íekŸ6LñÕeBbÚ5Õ½µ…¢,˱$iÈkE¾Ø›ÞH©.Ìñ C!ú ­«âÓ"Ëuû Æ>æ]ŸžÔ“¸Öi ïµ½¿ésׇð98)βên¨!´›r€,VœUîkXÏêRÒõ©Æ¿+H,u]Ò—¢O–ìi V`F&=ÓmDÀ ‡À±0ØvvÿGĉþͰ” ¤%FժÃGúÂïø1}àøîm¿%JY»tÖ–P^$íýþ¼'?ßjP£±jõb%_ö¼J:ˆ,*ùG}µ¤F —?ó‹?ñ `: üÈ‚ê^µSI½²ÇøOy~•ô•H],pï$Ñþhs•Ö6é}£Cï#öïàaëTÅV‰ïŒ'Xe?iqPé–@g_Ô^ñçzér7;©'> ÂÈõƳ“Yïyœðe¦h \90î+õj¼¨K p5³Ú)-Néš&Ђ*·ø©–Ä}­b’ãJ”äB§lãëkÆç×kÃä$c†kÞ.̨ 4Œ•'®“áÉ*¬ªéPfôÀ|Ÿ˜Åb €¼½äZ<™í²è©D6ÏFèììý´ÆvÇ>VŠ·È•u xi;=+ß+žèc=sˆ œPŽC™˜°y#-­ª§(‘MFS 6xµÇÈ€J¼nÆûü «é míß'ŽcšrÐèo2‰ì´ ¦ÿ$¸–KpN Rb˜5Þ/#¤vólS©&UÜCa8&]j&%Mø°Æ $p(#˜8¯¤ª‹¸a<¼‰‹‹(E&}8D“<ÈxËÛ0I“qo÷Gÿáô©"Ã@ë볿㇣¤Ø“å-R%÷{;ÇÅ¥ntiÁ#ŽeË"«Ñ]â"Óªîåä}¤Ï˜}‚Úú=_¼zãìñCïô1åyLóå{×µ²C˜ 3Ÿi‚gkqIÔÙiÆ6Œ‰ÊzèØ”T¡7ÁˆßÇÆ²ŠÚv”T¢—Á±Èçô1`°‡:Ö½CY”ÆL÷®“›"“h=5õ(E€S×”-$»ôñ÷?7|DøÖbû7“.X­EC¦ÒZ $Ëí§®èòüL_|õu•jµ^¯È©å6{ºK«"¬ê;ÅuÕY*°ãç:E™$&`’¹yçóvO"œ—Îy|j8­ Kolê÷ø€“k\æÁ˜ 6O³êéY|9j‹ÏÃ÷Þ¬»Dd¯½·ãjå=l6d6Vå›2™aM¥áõmÏg'&ôÅ]5Dþ0²ãe<ŸËH YFcý²yÔÑ»êôa´GÏæQ™GµŽ~Ü7mÕ‚ÏF[ù Ýe5Êr—ûK‚™Ö»ÄÍNåFÐXƒYÞé© KçPáí=‚QŒÃc—­ ”ú’Òoü­i:;;–gü}a=ÃKSCY¶Í;E 2ï&ø¹Ô6zV\7¤Úå¿SB[á­Õª\Îb¢Ÿí"™ÅDÿƒþãÉ•3ñ¹Oý˜I.ûÇYÈêùoýdn¯ã0Mº(UçµÇsO5é²2òÑ÷ ~œý‹¬±•³‘,O©ïØ\/Ù#Ç߃ZÏ_].OIÿ³Þá¨7P~<> MdÎ E³uh#52ÿ;ùy? Ýá(Kps0I¸owåÌAÞ¼î7e{·‡ k [”:rð,ó"¢éß¹×"ÐgÔå ú1†96~÷Ê?ú(2›KQff7ugøgH  òžû.“d>ÍÒåQžŠÌG~½šK†óÎYÄÎ×Å¢ ö¢É¯‹}§ÿò >#QI,¹Ò¶ñœÙö±O£$6U#çÖŸûd¹›Ò,§-&VúK1±·Ë¯ü\ÉÀ"ØÌ'gÇýzŸÊ&c¢³gE/%JD[?‹>ãÄóTÕØÙŽ­¤{ÊGßÎÍö­™z\ŒÜw@Õa=[¬ôñ2§ø‚/?PêÏþöÊ æƒÈÍSðšâžÝïuëXŠßs•Ì·€£6üD¤£Yîï0™V*·Ž{ 4én’ÞécÞ®oJ[žïê|Ùø+«×ü¬ïŸO’¤&¨¹²:˜©Øc¸$j™ë¶ŒGï|I@…Р£ôãÆ}å­ùª¥—L*ZüÅ®qê“Z÷ äú¹»y¬–Àé~3¥¼r/ÿeRÎápˆŒd²ƒ¿;‰ÕÌd¥èôdÒs·¯êÕ;rqJsݩט„XýÇç±sŸÉ?”±ÞÍZŒ†+ô«ðqgꬺ\–Œ &VìÎ@eOôÇÜB¾«g:×iðb"ÌÞã¼¹xõNüÏ’³R¿Ž¿‹†ä”ćÄÔÈBUºÜ‘Ô!ÀxÑrÜ™÷Å=“¶.ñšÒbÅŠ1ꪅ±R¼ÎûV$‚h9\:ûêýúX˜´"{™û»$TL)K.kD/éØá ǶUÜeX6Bbwϧw ½ªZgóVÕä颪 MÎ*»ðŸF è,JZÒ7óÊ=R†ò†bî!JE@d7dÊ7mT_¨ë)zZ§GbÜykzÐìhHÓ{[R´þj´Í½’K¬ÙdƒÀrvÂDïEñð¶’²–q\_yC¾üº®½÷)ÝPÞÙïwRª®NÒÝý>Î7_“šÙóµ è—nçY+g. fGSÿXµùƒ¾º&úfõµ–ò¬§ôQJP†‹pMéäáÁy0£W2ùÆã£Ô´úÕpÓ”¸Yϰ;š®fK´˜ì,·R3EIÍÏûxá•·¸–ÃbÚ@–Ñ©¢Ù>z2>˜¼ëöÖ3g#çÎï|ðü$Åäéñó·®="´÷—/2­*U;Ú5&åh£­½gõ5]%Ð^ôGZr2 ß>‹çOšâCõ¸ú<¿† m³Ç5~ß}ߥ¤X€ØãÉ•óá’>od1Tb¢¶ T§I¢ŠÉÌ ñ*¹y gÉq>'îÜ q0\£Æß“Lù^R ¬ìUrðdVùˆzš™.îÛ2 §Lè8®ûQžÙ*¿fŽ ªØfÙ ø¬’࡜‰Æ#Ü7Éh,ñ÷t-òw<ƒ8>̽J¾Ÿñˆ/Åj½RE9€Ï3o‡œzÖˆ£løðX)ÙÇ(ÿ˜q w uü‹R´Kb³ÅSõ¸§Â˜¬Ó7¶ºÁøgôÑ÷½È˜ ‹ê‡ƒn$…ëeÒ£³3-îÃäãã5Ùñ°9ƒ£T×m ’åbz/x°à²×ÇOêî’~é],Ê£¬åöT'<›c"p-Þ äÆd”¡žñ:ÝÌByÒ•ú¸Á¸ XiÉpÓZF7Òöž<Ò¹Y‘3_ÔU̵g·½&FuÆe˜²ŽRPÙ¶ØŸÃ|Îa¬?[Ôt!G]ôI/Ü8‹¬%& &ÝÈ8NNNâ÷-¾œ½iH™¿ïqpúÅÏ})¶ïJ¸xM;IÇó¶æ’dvRdêVã•y;G¡Hч7ßc*qÌöGˆ¯k- Ú[îÉèØˆŽr’âgÃb—«'Ý¢„³Úþ “×â÷ø ñ—÷eÞDjO> ÁÄ™¢jcísU#XìA™"z"&Qvî;ß{,BÐiJù/sí>,{ %ŽÍ¨¢ÌH‡Ó‘J häPNè±' O¾lܱ"L&T"Óuå¬áà^PPË )î‰Õ÷Ìf `Ûg‡F•n‡1á®î€êË£ŒGw/ ó0q½§(Aã•×´îÌ¥hÇÉnM„¹¾!¶‘õ‹Isšô°]?,)K”®ë^[8³[r!“}óÄßz/Ó´Õ§bг‹Éö [8GÑï+µ& £ª/ÿèÏê#Ðwcň÷?P}ˆMZd·7ç]Èäò~Y¢¬¹Ù¯Ö5äécŽÑÛC_þ IÿÒ;s$ìËæDôT¬|-™Í ºŠ~Còø¦g  “ vÀ&&tUÞÂOðe1ùÄ5¾aЈ£¨#ãÑ_" ™4&£ôÒ'Z—’Xåfñ^“å¢þ«˜h+å ²Vb)?2¥Åè«,Z¶EaI¢'ò–AÊ»ž½©—¾|WO´Q?‡)ž\ •=FÌ ¡&Ž´H/"”ÃQmu¨®%ou†!Ùvœ‹mc5Rñî^[‘sAZQ¬v´ºôØC Þ³W4Mès ×xzHªoú“í ÷ÐÁb² Lj­')¯.VƒÕ ý1yFêû©wõz jóä|ÆÎ úúd”R ×6JF[‚¼#¥rG/Êm[ÔÜà±yŸp¢PJ`dIúœ ÉŸMF])óœp2ô¯qBK®h ²4(L2±ø~MM¨Dìï=ÐÉS×$Of¨Î¦ãˆï%aö!éÊÆõÊ4éá<áŸB@bO”Sç»›'|Œâ>&€8ó™GÆ—¯ß[Blñc[Õ¸æwïëáËoèÚ»ŸKäßÙ%}éлv˪õrRõg'»¸N‡-\Eas´UêH׎~ ÇSSù4ð4ÞyÑvôv³´ÕfòUÖ£ŠGk uª è+FŽÕ+àvœlŒ²Y–5Wª"ã‘WÓÔ c»Ö¡hÇ릉‰¯ˆŒ‡V MàÆä’-“¨=¾{ %&Bj·´U`å.F}†&X”¯&3\Ýëˆl!£°æIÜ ¿“yð¥e¯ó3=¼óP×wÓ±y¾45&?‹Žâ%£œ‚ `4ÍÎŒóÀ«—ŽR°Å.~®XÍ·DO¤”æ€8dWXù7 Ñ îË1S<ì÷fóO¡V¼bï*Õƒ{KR9žè*:&_ŠsI§¼±¿ªÉ+ïUÒà`lDÏÍŠÏû w¾îxÆù[‚8l± fS۲عÆG$>NÙ4QÎK¥ÒÅYƒËZÖFË\’¡ÛµU¼Íä@>#À|µ–a'ÌqælLì_ðK(MÆë.ï8€`ÓšKBo&6é߬M׫þv¾œÛªýºnàf°Lú”[<#¸phJçc¤lº³';Ÿw_}#ÎU™¼‡_κèÕŸý´®¿û9ÞƒL"öû ¥”¯ç•Ö"€În¬ã2Ù@ ¡DKÝú‰lJ}Ì[}µA÷é÷ylu;IZßÞÒöég6ä³×ÔsÓuh±¢ˆ2aЫZ øm×’Šod¬iºânGy>3Ø;5~VHÔÅÝ'é°9ÅáéÝX©K|É ”1.q.SG*õ=ºí4}臵èÃÏ?\ A`ëA°í…lÑñ·-R‹øŸûÁWôýŸþ’þ¹ßÿÃ:µ7z­Å Új7¹ž½>hÜ%gºÆgèÆhUE|p7yúÊJvñq—sá5}¤•ýhÛ~‘NfÉÌæâæ/óïîj BÆu˜j }„Rì0ßÔ¾C¨1¶U”’('úu†éÑGPöªŸ&² ¾jˆª(÷#4L“bÁS»ß‡A¯¢ÏÑ#ëIƯðÕ%%ûæXì]»wØÖ?cB/Ç´±`‡j†w7é*|ȧ]ll9#„ö°Pík ….¦Í–PU§§§Ú¢­F]ÅsðFž„Æâ¦øÌýUÿÍÏ~9 ‘ÿÐïü¼þ–o~VónV¡>i>’é7´Žjè~MÔºýÿÅõÌ9-q¢k1Bξè…Uéºb6×z‰Œ¤»/íÕÕ¢'ÃõÏè­(æ‚ìn\Q Ú!YRbU–Uk«{*N”ì§òÈëó$DcdvszaSÎXò=ÛŽ“ ÙÁäòÏíÀ³¦(÷9@À3[P&›¢’<Ò3¬²-k˜¤›§gqm–ýA§uÒ‰ ºË“#UˆÜd§d“zÍRÈ¡39çÀRŠ*ÁrTõ~0è»~éÕc@Éê~²H÷¿ä¦hÄ»·Ò%=R×™³Fdù¯”¢î×0r‰,—Z•‚]–9wÙ:’7±ÜíZ¾ûÚµ(?4µHð”ˤ9Ãæi¨Óò&°Å“"nƒÛÐEsÏû¤,‚çE’FPÖ¬ACk6W„¦Z§Y•ÙñÃ#y•]Žt±ßj¾‰T‹µöã¡#®w]¹v¦œ9Ô "ÿŸoÅNÏÃ<þùü’ðúømŸ¿§ÿþó*C^Ú€†JË<#¨bçOü•Z)¯ò•,FjðGúû3R¦(O2 ù‹Á—Lº0×Áƒa(%‹\ú½NŸºNßËd·Åuâ]–LΨ;Y9»ˆíÚºðn}±O8ò{{µI“ÛLøÉŒ\— ›ÈBã}Êd,¸~‘uxrcR…e+ë¸MbBbdËÕä‘þfRâÑ?X-× Áïäì\øv”ÈÂgÂÁ"1¹/#üT†„E–u§Ä¥Ö#ç÷)žÌ<¼ˆ –a‡SBS+€4÷c›U\wÕª×?ýE Ï ŸŽ<Ë‹·e‰^Ä´ÛE&^äË"‚õƒÖ´xÈüñÔð2}‘Õó}‹÷÷äÏ5n£Jï —©–û˜ÍÁh ä “$kÊ%ëËWï wåK ñ‘;k|ôÊÚ?x$1g8c špA?w×&ÊÉ×çL7'Ï+bŸ3#P_îo^·uBÜßð=z»Çü•ÿåQ5O]‹–¾ö¹gQÖfg½>Hh©O.Þ44VòÈ(ÀÏ Z‚夅x¾$Ÿ‹•ý§ ISÇA8“¡D,7ÚIRã­ÙU4•À'å%n$÷Øö#ÏÝŒò™Lt2‘ÌÿHõTn4¶cU£þú‹o\è¿úá_ h^Ñ¿òs—úä­s}ôZô‘âš6OÖUäïç˜AX…¿H&ø<~ (Öb–Aª&q¸¼ïŽSc—0‚ÄBÆÂzX¬6¯Þº¡åÁ…ÅWÊjPˆþ ôH&ª¢¼(¡Ò‚›²S½úO@ê¢PÌ›¥¸Ë44§@¸Ñ„’²ç}Õ<ŸId}¥|džËd¥aK‰Œ … †Ï%Òæð9`Âã 8t¡vÈ—0ùz­yB)ޱÅ1»D §Dr´l?×ÚDÁè!V÷-ÌoÇ}d"{¡Ø$ÎÅ}Œèå”$JiFz›‚p8‚ú<£§•HÄþ«¨=¸°âoÉú(,!«ÎIyuüÚÅ#Ý®Uìÿ\%|ù›ƒ?åd›7Q£G’%› Àï<¢'Ëû3V2\5ð½Å¾ÞøÌçSioSNL|¶‡_~EW¬ä î$Ïe?f!Xaïj†õbˆ.÷X’sßÊÓð7´°b»jOøPØBÌÙW‹~ëwëŒÚ¶ìÂÞæRRVQø"Ò`utÓ¨w+qw@ÈЀGö¡–qÂñ@Ǫ/sOžëôC2Ò$ÿ]™ä’‹ÐFB×­§UxË«=x»jf…Ô8ngÑusUb­ÚkÖ3×ÎÆ9lx9á‘oz‚Ûm3^÷/ÿÐËCZýô\õüZ •þ¦¼¯;ûÂRRówP 3äZ÷Fà+úJ™laÅJÀÙY@ƒ»ŠXÅØwí6žrÏA é -ñ¥”î€Éá×^yæi¼´-Þ¸“XÉs¼ª–31¡ö©ÉaRÌaâaád¹‡(ÓhˆÌÑb— 0îZ쓲 C6;Mý˜àñß`RB¯*£ZFS²+eÇÃf×µçÓL‚èœ$mÞó‡Ë(;~3p*¬,ÈŠ‚$;y’uï$ÈŽ9óR[Uí_?ÐjG^C|fB&n×em/„bâ]»dC¯¸¶‡qÌw>óÅáä熰¤È $ñ;J*)Ú³gYtwYø^…fÕ9™Ö@àÅ£l9WÞŽÅÆ;|§³ @îg"¦øêÏ|({\ÿ‚\Oñ„8OñüÅì#®žðŒ‹àf\ƽFæ|Õåß²á›mx¹Ç}Acžãß[Ox‚E5¼ÙÍyKï»þôÕw?~è­óãÿm*µ •IC?5d&˜Ü¦Jƒ&V¬¬˜{(!‡â†ì¨-2ÙÛ[{«ÕÆðÇàF¦±MIJҧJ§Þm§C™GÂ*ˆU,ƒxœ7ÅËûÁ¨².”GgV‘ê”–¾ã#ãxb&Bƒ=#^¶=‘mÏ„ò™{‹þ‹ŸøRøXô:©œ_gé£Ë>éøÉGú‡¾ñЦbV½Zd#·Ì¡­ñsª‰eêÞO|Iãs©Ñ‰ŒÅ+ò¬/Çêq}¬ü9ÌVùýÜE4‡=šTµ/î§Áz½úÜÓÈ” /˜blKzZ†/—É>"­Œòc&æ•#(+õ>ðE™&KÔ÷ì]•,¡uâGFäÄ£¤`‡ rÑ£`F]{ŒÌÂß Ûð¶Pë%«x3ôÿx‡—¼×/9Æ!¿åâÞÄ€'p¥nQ›å±)=0ÆYûßu²1€;vÑ srï©Jô2¬â¿üC?«›z—ÔíS¯#&4¸=™»e®Öt±ÛÅ ýüˆ HïśÆ(Õ\½§BͶ±¼øäÕ{ôj‚é}¹×ë¿ð9)g7˜Ûec»©èòÞ=Quh)û"ƒxÔ»®®&’îŽYÌÕÓS=zBÏ‚2ffÎg'KT¸vyô-.-h÷{}¤ÿä-›è["gW›¼Øj®ä @ÖºurÖ¤”Ú£é‡UHv*3*áXƒ 0`Ï2ãE³HÆþ–ðJ«-Y,—Ò ‡Šä^ƒªf>I ›À¹/껓ŒŸ“óó|c¼å¿™TòãIlÖ­†Ö¿ùc¯¨5Ož'çÆ7[©wýø“þÏ/¯ú^ÜI7›–@ eÖ¾] ã|š®ù¼ëdqv&‹’ŽÊ–¶!œXŸ•{k?’ùÌ’¿ÛHª$§^„Œ{1LøÊ Ï:#!#¤1¿7  @|Š\í°ÆãµØ*CP“E÷ÜéˆÕJ+H?ÙB AÂÒB=>MÌÃù/ÊUžÐoeô$˜D<%M¾”Õ`ÉsïGó|I®î.ËE5jJVäØyž ìȂ̞æËþ¬75¤à§R7Yyú±ØèÖšJ=Áò–…W΢®¹·ØŽ/%;Ž]ê_üœ–‡šÎOõV¤Ò†9o­»^'¥¶Æ I_6¤5z9(!Õat^ìß×eãwØ#¶oH€¤Áç¤Ô÷åÿY+hȃ¨¨S$úpå$2yNˆ>Ín'åø™œj>–Òlx6)¼.™“,ɺi°“‰pŒÝÀí|DĨSFÃ]~÷ÛÜÄ2 ¦DT^É4üÓrqÓ‚Îò(âuÔÖ!wÑd$»àu‘y Ó$¥—†°aI¤¾J Ne«™H7J&Öj*öÃ`eݽâŶ”›rDÂAéžÈÞuëÚW#ô9òØ2ÔiêmQZ¹grw¿ê?ûáO«ƒi眂ùÝ„šæ?ñ8 ù¦§gÝ:­*²Þ?^(AΊŸèn+p>2kÂç¤L:¬tP¸°HÀt;–ªM»$ðEÑÈÊâqân¢ÆíN8ÑÅ¢aÕT-/ŽTHñ t|)cϪ\. ÏœI´gR¨^Û˜h"[ pt²Ò}Ʊ«(&\„ñÖ¶ò:IÖ©ZóçÌä=)“ýâs*œeIŒµ–Eduvo¤ív'¬ºXmÇë,Z?EæA#5éa‹ÝíxMÎjro!@þƼýmfÔ‡Á›ÑlTíhèwå ’ •îÀóúÏ|N/|Û'y‚bVäeÌ?–dÌ"ï_Éf$ÝÚít>Í:‘âñ¨5ÍFyEI†^ ΦîÕpÜ[)Þ«ûÞ:ÿ1i³Âçù/üÀOZ-ᘦ3¿PÈö`g¶ú>Øè}À ”Å¥/º¯õ@–C|¶ao[¤6ÏQúKã'Í›1~SïQ6Ûr@27¥-ë÷èmF:È#¾aI]~0Á³¢Ír&<_ÅfÅÀ…?krìêþîX$ÕxXÁ»2¢z‚k®ÕâïÍÌ«KCC±Jm£néGœ«=¿Žæs¥-$µ¨éVßåÓÿØö@òsoÅ<ߢ¸þ£ŸxE«ì‚7J´ ßßðûKýýßÿº:YUüCЬÛ!s%eímÄÊFY©$Åâ6$>bròʉëíkvß“\=9Õ|z¦ÝÉiLüÛ@;ò%¢‘Þ­p€/+Lü&—.æÝ} wnsfzÌpwÇQF¦5£…(·WFîÃ~`§*®i—|íñˆƒË‘¹DƒqÃõã ”5ét À„ Ë5}¼¶óu#»@>˧d-$Ðdƒ4éF1=V¥cQª-÷nR0éoRfë1ùñ;%ËJ//OP‘ýЃ«ñðà kùüoÿ1¼7„¸2d5)Ç‚œ¢‡òÊÿÏÜ_€I’d׺è2sˆÌ,îîj†é™éa4‚ë€t@ïÀe8ð.3ó½\fffæ{11 h¸™»¸*3#ÂÝÍ^iûþ?Û/2kf$F²þ¼3+2ÜÃ#ÂÌ6®µŽuäßÃIc@ ÏÁ÷ƹiöÑeçŸ-h£*L¶©s.cñ½({+4$©±Xëuï´%“§i”:½1¬U%#LÔÂúÖ[äÚ,y?g¹¬7?ö,×?ƒÄ†V͈QV$ZØf잺_ÎÒJ´[ˆN@m»$ ¹0ùz§±Äï§ê‹[ ¯`¸ 5AZM¬öÕ™á”b­â°óŸÿ‘Ÿ×…§¦SCqCB¤ÇwG4<I›E¯3êuIÒÍí–Ï{×PÛbÍZ:'~15„±Ç í V½~UW>óï,´À"vŸ7=ÙFOÁ{3 ZÙÞ•Åžh9&SDçÞ"g:ªNh¼óxO8•68æ ³ósJü¢dZ*Žqš²dž`xAÅE Ølüœ…Û‚ôªº¤æJò¢Æ(».­£ô>KA$Ⱦ¸,GØÄ\$ Ù=pCb»”…‰>y»y K¾áy1šži÷°Sž3Âûûûÿqªæ95Ž4<¥&²+0eÍ÷±7䔲ʋ•Š¿‡Q8.¢_ª:}E‘ô?>{¬¿éé¥w•€<¯^Ã’+.[är=oÐ5–^7Vgº”íQÊõ)|ƒgÑ OÐñÎV›à0+­ÃkÃàßåK00½!Ï3‘5TDMï°MgXyY»í×й3OO¥æ¥l©ˆìòuÒÈC7"N­…×i@â§D]Sv`|Œ¡!wQXÅÚsY{šA±ñROî ùü⃠ßiÑî¹nm#-ž¾9-Ùܸ£×ñ³zôßÙdc7м™n6Úh]eÐÄùwR›úÝůÃõì±@¾«#ž½eØÎdîôÜ÷ÿŒ¥[§šæˆÝ®S—ò=t{Q°:•¦·s< êò¤4Îíùýj©e×iðÚPèölUˆàN¬a0ƒU="‹Ô,¤qkÊ”ô¿ÜÛ€LåãJxD1”GCˆ—‹IÔj·¨01ð¾ª|Pt$ÿKQ¢¡‘Ot<©E9¶(©§½¢|UÝòÛ‡È}qÏKûïhp“£sŒ‹-¦RuÿÙ&]ŒD"®ãÔ®,þÎÈ!ïMôñúѤ{ö­yò/Tsn?µ˜±£[Ø‘½“ô§^Þê{ŸXéaw4]. \M¨EEº²Ð ¯ akUâ¼äëEµÐÔµí³¦åRK"¯Ñòîaqµ"2#ï­´wéœ!qåÅÿ*6ÂlG FlÔ6 So×µTÀnnžŽèmZ$C"P6‘ž-ŽN‘’O¬ÕSÈ;ö«§èsXCCÏ¿#y!ÝP6·z«kÑ©…x3&]0ÅÓ©é¨2a4Xì­ØÎyãÔê4jÇ´#“; *ô. Áñôë½&èQ×Î{…ðÇV›‡!d´@Û‘4¦^ýÙ_ÕïyJýÙ½]Ãàt(S,ß³µ¶£nÑKü›ë†µ‡µöV7'¤BMŠuÏ´{ó~Uw^}Óõl§Œ±ïØu@¼  Dž·¤ÕÙƒ“,¸¼.z¥˜¡`ÿÅëSÒ&œKÔÉ@4¶?gÖi¬w…ßë8Ò©µó=Ô苌ÌÖÁ‘sš’³çNbPäÌÛS»"ÀÞ~Ÿ¦b^É8Žünç‚dŽyYûâdAã06ín^/GÂMƒ‰ŠwoÈÕN£{ÿ›«hrî$òû„ìDQè [žÓÛ(kª(ùÙ{IªzæÑ‹yäœ-Áˆ8îÃŽÍf'&=î‡=á”àEþ̳·%Zn=Ǭ~é9ô9ßl¹x”çeÉ;}þOyJÁ„¡è8¢¥J{ÿ^F¡€Iþ¹y⊚u5¶_;ؤsöÍÛ¯wÊ <=Ût[@¿PZ,ÐU1¹ÎÅÝãjlµPC ÷Ç)Eh¦H ” |Õ )ŒÆ€´wN®aáD„ΫäM Wÿ;‘ ¸—UN!E@!Yq„(F±ñü¡A±ŸœKWKýº<'uH”G É¿$3,±å={ÇŸ*Œ‡¤Yªv±âùÑ ÚF–èé±yc(8‡ÁŒu0L«ËX‹¯r´˜±k‘ˆ’³Ó&ó’í Ÿ °d„“DIÜõ˜O庺GÚ*F&L‹:NÓùå7õù«‡sn¿ëç{6Ö¶+ûÜ Ï@rKï&¢s,y¡zOÿÁ&ý›ïCr ГT'LU©$Ó@§5v›þ7)Ë꿇:xõ®’3Úûó‡k{}†¡²³‘Úߋ˺’:)Ž 9ÿøƒ¡Ó.2…ç·(Ô€•˜e06qúN+¨[ hØ©:UI6jøá$å¸#÷IoQï°M·ßš] Ì Ò,htÎOlŒlb\+ÈãºÓÓÐç°-DIÒ49n ì„‹Ôõ1MXZ©C64²k·æ2bb®áäÃ;3y/"b­¶ MðQÙ}@•âµ'´Tî/ÚhÿŸüޝu1«j›^7Æã4Û]Ç­sº;b¿[áÇ€ÆóSøæñÏÿÙŸÔá›×Äl¤:û<ù½E±®æ‹ê÷›ä,÷?g‘VÒ,ÞV7kžƒ3v*Èñž: ±IÁç&:$;¼Z»M©†÷Kúi2ú~‡0l&xȪ™ÙÓ+€ ‹Q< -ḭ̂i%Þ¨Fj#J >R KX,lÀ‚­å&XóÀ´+‘&ªˆl€l¤,ø úÉ?¤EðX³ýg­‹‘À%¨ô&?O<§qf1èØú¿>ýz[Ð Ú1¡å WiÞþd+w¼5û÷óÇUŸ]g½g¿¨V0Aã s èZõ„Ž 3Q©t·áMMÊÕ¹-T«•º*ëíÏ}U^Yš0n 懧#’ÎÎd£e]VÃi€—a׃k¦¢FO?ß ´:G~©††/k{ï£GIZ«©aö~STPToi07”E óè_¸Û•Õ¯ú»EõÀ$Ëæ|*Ç\$‰Ñ `øÄ¡öþbò‹ Œ?¨öxÍ/ü…ŸÔ›Ÿ~¶5ÀsÅ.bbÊ çE{÷ŸwÕω{buA“R”ÔÕV¯Hr¹Ó<À0Z•@9™â.ÅûnʽxÄ‚¡)¸ÅâÞd3¬åƒw;Aé-eë»ONaÍ “9äìÄ}ÐmÛs[TÐrM¾1i$O·dHá¶'äµ?DÆÐг¡ €èÈvzìc’&¸KüÍÏŠwN뮢IY‹ š" ‹¸p"mÄï )†þ©Ï¼ 8‹•4¢*©Œ„7²õ\xKÏåERòýßûTÕü ê{-JÒÜ"‹÷” â sËuè¾2[ Z,ªÞ£Ž†ú[pZã]ŠBG]î²åpë8Úëß÷öÇuøÆ5u‘¢:É©Ú+ä~‘ìgú9US ÅEßÜóËM@^X·ym-ÍÁ€Ê;,Uk¬½-Rý=’’TŠ3xÝ4ðÚtNYÔṗ=FÝkœFRMS=lðQHˆš µ u!½jÙ£§(©ëOapM7â½Ø 5lÆQZ…«¿†mBÆÚÝZ[1`YNšé`N¥P»¥I ð_ø‹?¥q=è©ïú…ôˆý Õ5¬ÁP7ÚAØgD—Bý$(!zm,‘ºbF|ãâò:ȳá/镟ÿ¤r‡#ò|m¾_jjI08/tpß{O1zàõã÷“Lv ³ûXT5†ãj&inµA€DãЬš}oMôÍVq¤©–¤D޳uUH¤• åœ*Œ¸î_ú•Qðõ®{^‚4nÁØ4Œî°µõú—æ‹Üþîµ Ô¤þ?ÿèÁÑ –Z¡g'2*xã)„Åge)"Û÷W}ü‚ctqOÔyາƒ‚zŒD~úÕ;º±¥ÅrÖÄ Gß/%t)³'ëÝC yÜžûÚZṳ́÷£{8ªi»(;ŽCY])`@Ì:¥XVLí9 ÅFòºÕÊÆ¸›úåÊ ƒ&«Q´ÖÒˆ|&²@3{œtî©GTòWTJ耊ÅK£/Éó{/m±cá… ­åxtͨf;Š<<'¼µÍ)Azk/‚M,(9Ø/6ëHs# ¼W»–kv°‰Í›”ŒˆR3€½.Q]Sažrm³ŒÒ·DG“¥‘ÀPOÚᙚ4O#†D!>‡Í¨1O`kf´É“=ï±2·Koe=âßa-67æzÅ8Ø=¼ô¿¤õÛzæ{¿Íž“‘#¬Â˜ÈISÑÚÉpŸjM»6dtÔXtç4Ù[m+d'jfà‰:Hݺ!ë<Ýf÷1œUDH¤˜¢˜ƃ F´!Ï1DÎuecLùB=pªA–UaÔ9˜Í]˜ @x]Šõ|V¼®=—ÖçxÐî…D ŠÓ×(¤^}~W¯)`[¤¬«Ÿ~Aw^½ª·ÿêâ¯9êeôôN¬Ëð;#I15‰h…k{Ìêr%ßcTNçðõ«úäÿöµ¾v;.Ëœ€®ÆŽ&œ†P¬Énuþ‘ËZ8²ž?ÆzLÝÏœqbHÏò¬ç5²ZEž¶öüv´÷hÅðPKÚmí©¿8x<}1²‹¿­5•¨ÕÞx—ñèaÌ…!7[O2¨nº¦º4oøV„­5½Ðªú‚7Gª±Yš¯Û'^l­‡R“æÆ‘–9žGwÈ…:c­ÿEQ¯váÑÓ—ÎP³`‚ðû..ä„Ãßãïè‚|ßó×çHÃfŠÓÕ“É‘« οgÿNÔ)(N–QŸ=ìôʶê±ÕÜ‚IQ=Î1+W§Ù#/ˆ/¡7¯–~ŒÀ¹ë©zŠ„"ÜÚ~b¬kÓ=A›Üsµýööâ¼÷ízõç>áÑ¢ƒÕ¸9Ô`;ðî¨äu£Û¨ô¯¶ñ j‰Êɹ®ïl^2?@^ÓÊj£óè1F)»ðUßè}bqX…M–öT#Œs×òr€ACd§U¸l•¨µ°IŒü»x45„ë¸ìsÚašu°o1õÆ` Êh÷kÄ[äãF„Q‹ƒˆÓœ§DÙ­ŽNԚ݉)a§°è$…5?‘†êª:g$¸óÖ}ì¿ùÓzä#ïµ”ÖêÜ›'äôÑ Y¦l0 ©g_—ã2{€w‘﯎%¡ :¯Ë—æ“zù/ýŠÏ7#û®AazAû®Á­KÄ\°wšupù>%Èýï4 Ð…f'uf©'Ÿ8ó©©\Ú߯ýiáïËÖä.‡¯½.ÌÃ0ÚsŽÍ ÷6 '»êÄ&-<ÂÉéÁy9´µ+ žîžX+´w¦l v%lSè¹ï-…B›bo±î’ë‰4LI(±“'·He®G¤ ×Vžw÷®é¡q[kul**û«7ÏVzÛ×´èb!Ý”á¿"²°#†–»: ±;ë 7½píp®ûZ '{\íþÅFä¿Ë‘ÊtÙX>6gý¯/ý#Ït¶ `Ù%ºxüÓ )5{ÎVöi¾~g .9EÉ|aÚíËg²€\ªvbsž#“ãuDÉ;a§tß»ŸÔ+?óq»±»ÈžcÝ4N›‘Z·MPÁ4#Cƒ$«mLäµc½‚tbÍn%ùDá?uqCG€ï÷’^øñ_ÒñÕëÍØS× íØê|m“óÆíº=—ðªªs?iÖ=5ÛûßCä3oò67¶ëµÆ©´úér¥®LQa O‘’À©²Ö`‹ôª>DZvÄóµÓ”–«GÞ¹:XJÚjgôÏœ»¤8²^\u{5¸€šâ¦W×êtÑäTmó!M ¼P'¨‹ÏcDê¢UÅãMÔ!äZÛÔT:;¿Ú?aÎâ'¼ l'åÒÓ êe„Î_#ÏÚ•6Äl\½oŽ»Èó(›É8­V#&ô¾r¬Š†ö°CùØ©‹ÓèœXˆÜ R׵ޭjOÖ_-ú[ß¾ÐÙTfϸÎï7§fz‹§ Cl³È”ªb3sî“ýD7ýóÛcgKí­ó ãûÞA°ã!óødèèµý¾ÿSmnÏdŒ¹yr%™ÃÒXç8—iò<½ÚèìyÞž=©[vfì\<@êiJ0ðâ(´®«ÔEŒFXXQÛÁ#÷¨5ZúÀ±yñq1âÅ— O•¥«üúEìvÐDŽ+pÌ9O‰5†‡8¢ÐÅVi"ú!-Êõ¨£´œyiÀ>_oþù·&’‰ûC¬Ê_Ó¢¶\Z]ÆÖUǎͦآ¾i 6\gˆú¦•Ó[DélÎE/ýÔÇõÆÏZßñ˜‘0^zæ)‰áX¢Üu‘Lê#®IØQGcÚ ºúéõÚ/üªÕ`lÐ-ê×11·”{ò ês7Ë£ðÔ5Y‹ZHYÚó/<ùpT´ûÅPpÏqrý•3i@\ZOa[Ž’»¼Ö®`1çN|^›Mþ‚º'%}^;£ÿ‚[CÆ¥Ü)xÅ¡}–6ÝÌîLÿ¸oýPXèPΧ݄G颚éß›—aoéUj¶é ü·Ñè¼Fóe<7˜½S]þÊóüE]Írµ™× °N*C¢IcMp`µöºaˆŠ‚ÑâŸ(²Ó5ùM3 /IÉæbϵ¨ËÌsµXÚ¤h¨$áBÛQ‡Á-cC<’²‚ž/¾¿ºÔzh6*Ö‡ï5¢ ÝÎ4éâéàŽÆ è­,¼H:kN7ž Âö=뚉iû÷å¼S¯ýügð˜9AG!¤‰&[h[D«v¥ÕYP»ØKÏsÙ,g#ä¨i §0™v}Ga=#(ºõ¾X'U¢€NsbÆÉyªúåꑹ“%¤$í<ê]µð ÛM»®†hŒ‰! Ÿw»GZz܃‚nÓ»ÙB c÷`›Nupi͏÷ëÆÍð>HžÞ3ƒ »Q=}¯kk̯ÖeN=vvï×>ó‚Þú䔽.¼íQÝÿÎ'tþ®gpé$žàˆ‚è’ÉË6òBëm¯ßÑ­WÞԕϾ¨[/¾6צ¢nᯙ½y€½Ã¹Î$ÚrݼeàÂ$•Áš’.½ýq0´àÆMýÄfŸ¼½"UëõŒÁŠ×ažGF^FuîÁÈh º*w:ò¢v¼b[p½óÔ©d»¾£8JJ·ÚÞôŸ¿!ŒÉÜŠ¥ê›yr{_œ XÉc›Á€pKÞ¢ rÝå" L¼Rãú¡¢²ƒî¦ïÈ+#t¶ýË^Ž(#?¡’ÁèA2;lþQ$ÖFâã((EÑ}=UýÒ«7-ZPv¤µ³Û¦eßÒ&8d¿{!Ý[Qsz€Í1‘vIi-ôß½\õ.Kä’ö¤Ú‰¶óù‘ñw5Úµ×ùäx䆶[<$+摦 `>‘–²ç[ú*Ù‡>ôî»ÞÞgPÝHøùì P$F&BãáR³@Û9¤1ŠE8P˜ãÅGêsæ&óV^°Æ+‡#cŸa6ÒNPÝÞ®[4¹“a½"I‘P/ÌZ'¾óý rE*ÄÓSKª Lat 6 ÅäøaaU|Uש.hVo¬ M|ŠçnÐv…WÊšRT–bL /F{ÏÙy·èt›,•Õº¾8_"µhٗΛ|öf£«ŸyînÔ𬸯ýË—töò}Zœ;pvÈÌ))ÖM5Ü9ÒÑÕ›:~놆ã#Jîh÷¶‘4M[8÷œ’‡H¯·û±ÔÉo"ÇJcC™»òÔxκåÊRX]ß“dúû)®Ù÷*5Ïs¬z“ M%¡^åi^;7 Vå¨ãmÑø«°h 4•Së ½š•æo˜ £:Kï,-öX ™Èˆ"›h]›±‹F‘KmFƒë€ß0¾£ÒI5öèË,¸¬½ë‚Ǫ̈J´SQœ"lLÇ’¤":!ÒF’ÃzïœK«Õg$ùZa¼Ü+Ì^ømSÊÉ#q_G¹öäÊã‚o© ð ^O!ê ^ìõrìœ2æêUìT<é̾Ói¤ˆÖ¢mËÌ5Þ+k¤¶ºÞÖsPÂóˆn!ÞOµÌEç·+`RÌí²G™.é /¦Ã›ÁµZ¿Ž®ÜÐúÚŸr óa©GTSi±ôpCŠÓ½&ÿM ¾w…‘M¹óŒ]åõ½n6É‹k†%¥xáÉG(lƒÁ€Æ¤]9Xg·<ò¨€ ý:D_l0_b„a¥)aîtFC—ª9½ ëD{vw%˜*¥Wn€…fÿW®<Ç-pÔ6O¤Fò'±€cÕ¢PG¡Ž†Mo˜ŽÌŽ-˜Ú‹Å‚[àæ:¢fƒœ!f°©‚e)ÁÂã®Oí¿Ž¸º gÄÚÉO½tS¾¥‘ãö{$eA·ÕŠú†GHó†5åf^7&U©ŒÌŠÙ«Öúß_”Þû^ˆøŠ&Ç…ô":€OÑ Ò8p…a¸=B|)M˜r¥K=Äê­>ÃæÇˆÞ×yðϘ©0)ÛK 7á‘úÿ¥µÚBmîJ”, ‚¶uP§;]~4ðYÉ71O½«ER_•ÕÛkÙ&dHÃI -‹Îæˆ4§"“ŒÄpŸ`kMˆJ-Hñ4öâœbô®"ntbá½ò¹ya üp§ ¿•±µÝúðÍ FS.4ãFgœéЃèo÷QN‘tökT‹Äš±CX »ï»P)Ìß_Šc=âúâóYØuЬ‘ÂÉG[Çe­m¾aH©§v©†–Yã©%Ê£ž´8‘²½ %Š»#ð÷Ý÷Ì­6›ÂÈ‘E×Á©6äþ¦›k‚ƒS t6Ü}Mxõh¸ ÓePÐv7CÕK‰ÐÅv‚„2¥ûO@vÉï’Þ²Ö9”PM«†ÀA Ÿkhãñ£×€p#â_—· v žŠ·i¶çûðˆCQíÌå5yeG ÃÅ…§åT«Ô¡GR§@{2ãH¶¨:žD ê‹Õ:»šæ ·ÚÔF<Îð¿Ûã?ùÒ­†ñ@‘M[Ú¡n1܇MàÞn£±2{ÖÔ>ë––ç{ŽZ)°»‹îì~†SyÕ"Š’Í¤y‡ÛL!'~r”™ˆ\$—…,O § ¦9"°ÉÔ€dþ #êÝôé¶|?X@ šoUíñš¼S#¡UCû²ý0Þ݈î«ÝÈÐì¼Ýññ+ÇÚ ^4ul=ü "H)4T÷hj l§ãXcªÖåÆ~±€Zÿë+Ußrvô„ CìÚæ6©F7¡X‡–Öùdöb4ïqÑ‹X€´˜ ĸÞÐHAq4é‡HCnžÏ#ßø~½þKŸ ÂKÔXÿV¤¢v£DÊÁ#È»†m°#¨k|J»²™€C"%è$‹Î¹Ñhw‰šF-ª ºˆ¶Ê½tM¨"†›²vØ´DÎîÏ–²£­) ¤VdGîûf<–Pêòôé4z®€œÆ1Dç<°б˜çÌæ±-5|ŸÙ Ëóͱi4(FÍoïmÑÓ‘ÔÚ¨‹Óù/l³6‰cÒŒÏX3íü^û”à™ƒ£«ídÆË óbn ÈÌ04©57L5ÎïŸéÉVkœ€H…d¾ùÀ~÷¿ë)²'ˆV\‹tlqPî¢_Xªj=HñRKÁÉÁQ¥®1§Â¼ì‹ÝÏþ~䃼4¦K#¢Þ5á ÏuºáÂb¬út´UNbB4¯µ`…sà'ô°×Ú A°{”0khûß“’,µ¸¤b,w•M6­˜Ë²ÔWêtÚ ²Û}u #¯Œ_½ºi٢ܓ™Cñ~i0ãà ¬-¼]ÍTâã€E§€½Y×’µü¼-¨—F]“.¤Ñ¾"0šÉÙ‰cÓ†ZøaÙëµ<‰˜¯·y¶6Ny_¦ì€5n¶Û.ÓJ¯û fš› LêöÜ“éö‹o€*gñy´ài«žT” nkÚÑDN!˜3ƒ%É…¸<òï|Þ,­ˆ¨¦ÈGWRLee’<ÝROäÅ=Ä&õ±='=<ÿ“Õ;Ú&eç9y¥§Ã¦†HÙ^£6Íp"£†~†Á=V7pÞ%&uîì`̱•ÔS§i¾ ÒtšÇšählÒÈþ“õá4ï®xØÇäÀÜͤY+EE8·$ò÷Á' jé:Ugb†N6û}š4Õ‘Ž<°^dœ) s ›Ö¸xقÓ#Fˆ}$¤V;Ô]B{`]¢viïóò{ßѺÝNþ)Ž˜þòŽÕAÒfWx¤Î8q= ‘QÛ‡øŽ1lNU ZšÂxºBÐéo‘q>uÏ]S …í웑µ6y— © Úhí¹Õ‰Ý(¹ÄhpFÁ™ä ãìQȯüÜ5?NB #Ð8¦@Ùbzj ä5í­¢EêmwìÖ@è²ÇÑŽ{E*¿zcyJ EfÕd†„–扉¾ RàŽUl+šƒF—ŠHÙO/õ­g‹y²ê¤É½¶ÊŠÒz£Nçµ€Ï×Au´. X€©Qqÿöz¤ŒV"%«ƒ(%+–Z½j/H–†Ü:×yìÞ¯Oß5 2é\0¨.+œ¬$ÂîJ$â49´w´‰ÂBìˆ<š÷ë9oG{·¹Ò@«£ÜZfiL¹‹Â.'Ñ<‘Ìc,Î;†DüÎrÉÓäß™¦¥ ë}Ûèj§±¼XTþã<ç§RâV:lc6òGgéíÒ*ê¨4BG(ׇ@ó‚§Àœõj6ƒdéŽl¨vûŒzK[1ý¹uÑ‘:·÷äˆ|ãàbS (/XDýxn‘µØÛ·¿OÛš&4´Æ£Tv:ÕV¼ŒÔëi‡…tq挞üÖ¯Ñçÿü“¦ò|½åÎñPcfÊ~‰hp^ƒÔMÄs°™H•Y¿—^’ã’£¡ ]݊奧±µÇŽþ^J­f lã-sÎz¨ðd¹®¹ˆB (hÏžCÁ0yQ¼zË™oåƒè†ƒóíiÊ×§QÌf˜C´VÕ;”ú%DöÙTÁc]œL×Úî­ŒÆ'gk`œøîÑJÁ‰pÄ~/>ã2PÏè•»¥Ô §ì“G\Åë†wÌRŠî¨ …w´ÅöF8%ïÇGî-lÆ“ÅN»Â`¨BšA‰éLp¼ç‹ï|LãzmÑyó´”8Çì¹8f-»^7}¯ˆ ‰ „áÕNÕq|s)êHyîïÏ`Åqœ"@?—ˆö½ôò­[$]Sýݵ;îëûëeÎ'Û˜ÝAHŽåŸRŒÆ)`\EUEÁrh¤Nª©µŽŠ5e…ÅÔú´³ZÈß`ð²½Õ>ZAµÐc#æq©‡°Øýu oÏÝ陎_n@âÆs0ˆ`>wóXkÃõVè®Aº:ºT@ª¤Æu5BÀêÄ>n@žÃ{«’ÕŸ¤veç43ædþOi`)SÒ':/øM2oäyŒc÷I;ç Ö`Ñþå…Íߘêà÷9Ù·õ¯ ¹ËIJiß›{Ðüá…I=¶'‰Â.eòµîc$åUO~ÈXÚ¯ËmÄöýÑ9æ|ð\2K~¿£ŸÏkéY¢Ï2ry» æãå÷¿Ûê†ñ>ÉJø0?®«\[ts}L·#ÐÓ³×3ÃÔ/Fs_ü\è“8%·Y¿B}_3ÚÃC3,=ÎCˆX†~»wRPªßžæI_©¥&— À2þË6az)äž "CÉs ›ƒ7›mó¥3‹qJ$b_]Z§áAâyŸ|óŽM£+é–R£iÅðih—uMjKSa<À†pó#ß] ^$<<ŠÊU?}]z߃“¦˜:P5CRÚ) %%ŸÕ¨aƒàR ±é'‡[a“/HŒ¡ºyˆ¤$Üëá9!Œ7©Ç¿åkõì÷ý”mð¶¡LB=–¢¢) J3Ï>ï°£½²øs݈ÚÜôT UK6 %—ícÁšâ:뮫 Ñ‘ÝÑ9”'' É©H÷ÂT°çM­ËŒiAŸ¯ðf/uÆ/IšW›ÈÃ{vk‘Ã!Xtl±QzßZiuD‹Â™ãŠ>ªÚcp“µzÝbþYÏ)XÒLʼnktK3¨0(Ïð’,¡DÙ¢,7ú·[:}ú46®ÐLÔFé-T<ë”09ö|ˆ!Ÿ¸U[äNŠ·ÝúdyËö@änÞÞ½ë¹!Æ"yÄð3‚yc*J{¾ó鞟1(n¨Jf‘´Ñ—Ü+ ¾€×”\ÊÖS•MZ Î~M*‘ (2ê:Ì?»"^Ý¡BI²¢¶Ë¸ª OùdÍ0“›8”šçœRk#fb´tšl´´‹ècQºq‹ÔÑÉs¬•v`¬ý=cMd7‰ã壪´ÜCd6tejÔÖQÑIžªÓ¸qöTÀI´úÄï3†Äº“zh\Áp ˬæòýW&ýñËIK/P /).ì@£?õY¿¼=Rµ+i®Vê· )©Øf©¸§Àpð™}wµX-$7$ÝÞœv›\Üçá¼G/ÿä/i\o•0–šB«•Ó©[ÊÉZÃÐÚZ¡(·×À›2¡5Øy6g”©óÑíc߯cÔäX;IsŠÇw HéŸ×ä0©dß`ÄO»Aè¬ÎU5VO!úûHÙÏâeà;CRÔ]]µE ·iî݀ъøþ^&'¤„Fï¶O#†Ñ‹´Ñp8åG‚œ00CD r3ÞSu¸Sg©jÚ|M~¹óbzx?ÐÛ8=‰£æk ÒÖ½r?Sï0\D‹HÞ1Ìë8)BÛ­¿NÔ~¡ÉèâÓéÜ#—1Bk=l蛣£Ùëßßoµ`>‡xn¨w‚Ñà±7j…) ^0’$Úµ,‚ÉyŽ`¼#ÑÎÝI3×Ô4 õ”VÕ~Ñ?»Y¯U[âµé\Ûha{c ­-è®’2‰?ÄD¶\¼JÕXG©:”¿§âïÛ9’„žùDèP"]BèÎðÉQ"1 ‹TA¦SÏxˆ”,ÒØn-oMWÕ—âÂ:µFòÒ¡§ê0ɺìE8\µ’¶›¸ KN¥ i xÐÛcŽIV÷¸ªLh™Û~åxO=»&Ma—GÁQIÔª,ül?·ÙbH‘¤Å–àg ©©¬„!À7A:A&R5LN<Ÿf@ìxü[>¬ç~øçU½¦Àà3ò‡!ÑY%6]Þ—·½ZtØ·ÖÕ5IÈÕjÒdžºN´ÜÒC $E¼Lë´1éÚ&J%÷ È\³‘ †ëÆMÝÚ¢cªèS-àÚ&ŒhV°-4oŸÏlù޼ž)g Ü ŒÒ¤¶5ÙÚçÃýëägá©"Q/ÙM­Ñ·®˜æeŸºb‘`Ðè7¥ï?|¯Ðÿð:Ù±³ða°È^ßâ»·¨5ù}»Ž>‘ŽQÊÀê ÿ¬=2Âëâ%ÌBm ¦7bOܦO¡éÎmüs…‘N*"^ D);¦Q(ˆ*}°¯™âoÍåq3>ÂYþò‚}ØÏeŒGãê$ähÔîXå3’•伤¯—k—_Çÿ›èiÄøø’ º"}'<Œ¤€‰ÙFAÍ “Ê®Ø:ˆ|ƒ™»÷És³aò“ûjÊŠ‘'‹å]FãøŸ¦(U{ÿqZ¤q¯HäÅ[[B·›î "“N¦¯Ðí MGi(a"{ú‹|²©FöÒr5à u‘¢ÿê¥NßøÞ^]ž”ÜSª zµ0¹¨h›;}:"nÃ`U„û“á'¬_^©†ii© ¶”ýyµ®U&LeZ‡%=ôµïÒ¿ü9߸%X“­¶Nq[Äboè.-ÑžåË Ea6£ÆÇÔX[Š+Èq,‹¹~ŸÖaA9ð}eÒX½u}l¯¯ àsÃ\p¯xëù.‹¨t̽!Ýç5(Ñ‹&‡ož†bc”¦Öea< hP¦†éNÊ¡5g¿ÚÁÆ šÛ# T£Ï°á†¢Œº…¶÷=¡áà’†3—´YžÕÐh\ž1lé.‡Ð+[Õq£nÜ*Oku›VÓ¡–ë[êï\×êΛZ­¯+êð¥6^cÜxâ80ø»ZÖÃY¢`Å"¦3ßo4óÅ×8sî¤au넲nA¤jöCÁZñVüöºA›?Óå©2Ûc§ò¸ìaFi²[×´Ç7Â1,ÓÉ$’Â1ºe÷Kã‘- R<Æ1#ðB¿aö`R£j'E~( ó¹ç#ŠÚìñ!R`bc ¢$«§³J˜€ Pš-×nà­.Cñä3ZéädÓ2"Ê¿$ã´n­®ÝQõβ£6Ä¡,µ°ÚGZì©níKeãµii°JH>lÍÀLlƒ>û_{Þv-¼µkcÒÜèô{Î-ìv1¥*€“ÒÏתSGH’&µþvÉÐËÙ7LÏ£úÄ]xDꢉ›-Q fƒFû]‚*íš’ôÔw~>ó§~Ä6:K7ˆï\sH}ž;ŽH¡x„Ûš0’Æ!,5œDìâ'ÞZQÛÌp.HùAãÞ"W{OÖþ«RÙþÜyÉ´Ë6úõ–.æý»žF¡÷ž,û=r-YDÝyŸytKVðmØÿÜÕ Z#ÿË“RA«=¤í}—¦&­ŠŽÆXüõ §4D}u¦0,%¢dŸ“m€Žq˜[½1¨ÒæÜƒ:¾ÿIÝ9ÿ¨î׸wÉÚÖ«;^ÆP «Á°±õÂZ*®GRŒÕù¬´/í2bç¤îðŠöo¾~÷xE×^PwçšÝl¿lE}ñØ»aµSõï»Ò†¿h0^ŽøÍ‰|b6œ„ÕqÜS…—/ý^#þH…ñÖºjóEe+„CøGÿysF,ºà3B Ç)ÜS`£eC†hÐkFiÚÚbµÚ–k2Û¹†))ú‡?»¯é™…žZn\û#i£¬ÿãúYÝGâx¤Â˜ˆÞ|h*y¹¥ %_xD7y¿nÞ÷´ÆnOÑ`eT¸Ââ>¢«U¦Ù$Ïó›ÌV gDå¶&œÑ@À¸Ý»¨+Þ¯kO}“GWuî­ÏëÜ‹¿¤Õ­×cÁƒ½„hkccN­Â}Ú´ Ä8R?¶Ÿ$f¢F"Jîö«•&<å5­1à7Øowk&ÝbæÑš¨ã‚^ŸX§f, MŒïØ^Ï^½V7KŸ¬@òÆýFËAêRa aÖäXŒÄû®Š©¡öAtÙq¶ ¸!q¾ÿRœ/…ós ”- 9Ñ[²¨N¬†H/“”éTA‹Š"l‹*Õùœê=ÒvkÑ*k7Úø¢º»ƒZÉí(ôì,Yš©ëæh¡8ÚœN[D[ñ89‘âu1f^/º>™›7oöž²TÀ°ÖP“þÑÏ­ô¡s¾ýbÑ•²§¸µ¯kÇ[}à =¸óœ²cþ:a<¸W ÙÝB7~n<þ5::ÿ¨yñÖ†šü…½–¨Íã@:ÖÙ¨sîøëXš×Y0…èUM˜ \}©lçÇ+›‡ÃÞE]{ÛGíØ{ë :ÿò/ëü«Ÿ4š¡.;ÈüV—5N£N:ÿä#ºï]Oz«3X7èòI'ù°žc;¯EÌAf–î*ô@˜{Öô[€‹G2öÖeœ&«q,V+ëÂb€-‰4'ÜϤ¾hgô“z…þKêºG1Œ(á•:ùÄíš’¨C°¹²Å9ñA¹ç—ÉùQs˜=0Û7]K0²[qœ/ÌsñÅ6ˉןé\jÔÎÃóUj]\%+аá„^înˆÆ"YM#Ô<"ÄŽÝA×Öq ÐM!œk’⊽˜AíÉ{Ûã”û¹ãÊ¿«sLV§{Âi¿%í†øýBÕïÿc·{}ìN2“ó5¯Ò9Q›¡¨goØS¹Vø ¢vz£‡^¯µ0Oi©R÷¸’§€Øv °wË…¢àÛ~÷GtýÙWlA$a„G"ÙP7Èófê´Õé³0}1íÁi[<˜±&~¹øs t'´‡Ï›CíÀu'Í)6~7rÙS|çGkÂøz:¨oø‹èIrÕˆ€.Î-Õ!¤Ü+«· FÓò€m7jL£×f<—^ÇD@®TwûWÙ½UÐ ió•QªÁ‘®>òA]}Û7©,Ï(“É`.w–Þ‰A3Ö¤«v»³Ì(ŒCk£Ÿ†–š œKŒrÖV·RßÕB"’±¦Ç^9ùãñýOëøÁgtõ=¿WŸý)Ý÷ò¯¨×Øœ¸ŠÃ‰&¥§×7ØÏ¼šÙp³ŸS 7ànôcï©EnîÄz$¤D æÇäkÈÎ?>Vœ;8M½Äs˜›06Ð)gi,30Ó¤4oO*Ç:mÔ”?¥\¿)²†FFJú§B;-–ÔÓä~"zðHF‰5ÜòŽÑc‡‹ˆ.Ð8/†EÃÜ£Ígã™E„ÔFÂ{w]Ðâh§/ìEWv¥lOÅ…ppþɤ̅7jŠb>á<§Ý6¨_ØB±ßñrc+k”¾ETzÅô~Õde»¥/¦Vؕ钻æÕÍž§*áæJ·îŘNEãèêÖK¿C· ?öÎìVov{|LÞšVB»65‘ä¡uü—çÎè©ïøZ=÷ƒ?+8¦ =´A¤ëÑ 40ÌO¸¯©ŽÅ’^úY0ÀEجÕÂ}Ru}BªÀ~v=ÅÝö:e kKe6E°7‘LÐÛS“&§±ë3v:- F=½„Ìm“ûµ£ÝƒM)Ü“FËöYÙõ@ “2Ì7¼D´¿ïÉ¢(ÎÝõ¤Y‡×žø½ñØG4­Î(­d•Ñ.«$6÷f,IÅŠH{çy½Xµ€íº¡Ç[q×¢™ŠC»]ÏQGcM•®EŠîH*ÌÏ&²0P´úÝžÞzÇwèړߨû_ý%ÝÿâÏ«®-ê1œKiŸñå÷½]gyÀY§·ª5 D¤?igKÛ‚:)Jœ'S…&¶¦–hÖO©ÃšƒøÝZ j¡É©Þ‡ãc¢èßížMc¤ÖZ Ðb@JÖicRýÙ:ŽZÓpë¥â(ƒ7…5à(< 1œoÞ)£].¨Št l™Ð0J±•n–U#u¢ØÏëx¨íçG™PžŽIß-°HIWî¬un‘Nï®úøÆõ÷àS˨³'e¿—édËnñͨïÅÆTã!§¸ÏÝ|1 Žw‹i¥Ð0Ö!0΀Â77½¦j%t¡—=MÅç|)ä#â¼Qœo6ê Ó,°Åáaw9ŒOââOÍ¥a° ÜȆ·—>|·­÷Í}^‡o];¹ ÙkWÎøl›Ju&Rû÷ fňX Cε–GB.ê7êäútcßìæÈmå’·Öë,°^Äuo²çÊ Oò9"106,ÞÖ¾î”è¥N?¡½ð¿u°Àrnœ/Z}ÑG±çQ¨•ë™»1tÃFª9ñ~¬ÞÑåE³o!ýxëgôúÛ¿E›Å9±iuÕÓ›h°Dc;j‘DÔŒˆÍùÄë”8·mƒ£Þ×ô°=]^ é©Ù ”É Kh©kÏ5"»ŸÒèNÊþ9½õîß{×(~½îÿÜè‹¿ÐX–5GìO~çGØç<%èTC3n¶ö¹CXÈ>DÄ\‘†qÄù‡,”î¤ÖÁÍñÜÓŠêÉäq4/ ,àOpj¢¡2zgßè—ùÒW6ŽÖåû:T½`*¡ÙG—KÃsT(ùÚ¹"‚"]ˆ2ØÄHZCaÖÚæ"ßÁT¥íC—ºH‹€–91>bޱÀ»¢âX“‚èTgO@ïÝþ†ßõ=œ_ö§uWÅAg„ ¾´›â ¤·†®¹r†m— Q@ôÉ>Y£-¤n©T'ÀˆæÕ)µ–]ñÚ!\å±Zl“£¹ öXKµÍáú°§ËËcO ¶Qü<É•N^h«(°å®ÕÁV. òtÚ<拉͔…2ƒÿŽïýV}ü¿ý3§ãnz_¨|PŒ· ÃKÇ)ý…¯ÿå玧qÜ/¢¯‰ˆ¢!ͰCCïTçÈÎgc>¨‰ð·)–%y¯³2çÏ\…ôÏ ŠÁW?H“¡?b†!¹÷•’s)Ñý`¯s½Iä2êæ¶Úß"»îîB„²;nÉèHª‡W—„‡ï¤lNC²d³§ð׊¢ý"ÜmÈÅw‹V¸‡w'¤Û‡Ôlì¶zøìä)R<}+¢†¼”ˆXö6äl×V„Do ôêc}Íå=6 KfCØeëeOVq"&‡ÍÇa¼?™:BW[hJ~§'qϬÌÊiƒÚH€ìÇr¨±|ÊÚ´¡ÆNºº^2³ ×t+(îú€§‡÷¹KØÉ. ‡ãˆža45R’1åÂxü£ԵϾ¨ã·®C|í;Œ»N¶tÝmëfr”Õ¶V{xÀNm>ªwŠwm'UQ4¥]”ºâµ#Rg¯Ck-ó£0#Щ„ôª€°ÖùBqÔti¡ZwÑëÕÚͳ™N£!—Ë)²©Ó„z£ý=[ž~9o²q‡ªrn“±í\›c³Xé•÷üݹïmîÅ6l6f¿wö†7Œ0øû¸¥£ÊeŠmÚy¹²"w]îEC›ìè‚_qC9iŠ‘N-´Æ“Þ²ŸŒX±÷ÝÐæHƈœuó±ës½[÷ß¹­'ÏŸd‘n×á°4ïþžÓxW±Eæ,ŽCÚ¡XuB‹úåÒx´øLÌ‘@K¦Ö(:-Î=»F—Ž‚7éÝÀÚ`HùTNíV/zIUOøfïžÎHJ‚õœ*Ƚwž´>÷¿¸ûr½(<å±pðQ )-Ø}zaÔJ­§¾>’ ½“ÿM¨¨ Xþ•LÕѦpH9°‘ZËÕ£­î5v#º±v5Ôm“Àxx7x¨ÚQÓq‰Ð”#‘:BzÏRÄL’”€|¶|öRÉQÚŠò·‘ŒÇ ±ûÌ ½y8jœ½n»ï49 È¢S ‹6~Ÿ%äxa¿íö–lB'rì,*óöm£ n"xLÒO Côî?ôú•ÿöO«º—¢¦-NÃîÇÎÃŒ!­˜µ´s¬V:koFÕ¯M!Š1w(üš;m©¥4º…§À6vŽ}æð”ÍxOçuÑ£å½Ùû(àõ£«>çªGSÊR܈"ý|™ªÇŠÜ—³±b‘»SFÑývë¾§õÂÓß¡©ß3œ‘yóbœÜ˜q”Ì£/Ó|„$0*‡ND‰†N(¨«JKSÀ´õd¬È]·3' /»µ]ÏŒ÷ŠÁ  ÷Mäaßתw†Ú…½Ž½¾; ÇêõÃ/< w_>Ö×?tU}ŠF?ÍÞ¿§vûÐuˆSÉlT „PQN41×âkP_"*ãqo˶ì|· çÓðL+í4ptZާÔ@–£î5’ê'«ê·¤T³É7<—2¥ 粜óÙÆí]3ž[mHJer|þæ›± +$®80Q’• 6Xÿ<v¶=îÂ\”×Ì(º>º'¾ƒ±Û•µË†::Ï—í<ÎéT“çÙÙP Uç–_ EU+æ-V¾¨A(ïb=JxËåz³ 7µ[Ô¤Öé¬n½Î/íþg"½®˜1™Ö [ñv+Гbow+OE‹ƒý”ÚAïŠí@­¸€.¼í1=ý{?ªçøgU_ã¸#6Yß Z¤lÂ¥ Ô–éþ M£aé m#°'u-ÕÈ wØ)l&“ë£Ô ¨-‚iZ"²Nœ¤¦æ™È-B1jÖqüm›G™7Uóâí3…$Ññ[P›Àm7òÝdbÓߞܡxý±¯Ñk}CKý䮥‡lÃÙ˜¹©V(÷–vÛ„W¼ÇÖjÞXr[w NUlx[¯pÂl,݉ØF¼ˆ3w{D²S4çÞ¨ÅÔÔj>ì}ÑiÎ_0tà¬>wû@onÎê»{C÷íOýµ¼¦`GßGÉm”aØmt°Vx>×Ðbëp;Œ×#£Ën{.M80üôaŒÕ nP7üȦ;±öݦӽÆ0N?›”þÖÈÖŠ` írðPJ³I€ù(öe;Ÿó0 ¶Ÿ´º{oìí6ïq[’á>ƒçê.žÃÓ/]ìr€­—óÝÒ×vÿÅž €b“««öêcpO­sº¯xÆDwH¿ˆ¢PmÃijlÅA6R_”ƒd§©ôi/MÞ­’rðð,Ï:g§("{ß·Šá²×²å9é«ã9[\Ÿ[uñœ‹†F¯‰èÂùú½=À…ö~§£cõ«¥:o;ž‘æŠ&ÊÉ4).FHËðü'¿ýëtçå7õÖ§žUm’Á¤…B q?Óyts *Sìgþå+JA4(E@ooEUÓ7ñ•kö;€¶XÅÍ SÒcÚæî$E…Fgv%n†”u—=º…,Ò€á5Í­‹N¢£Nv-§²çÏžtbo¤Pß°?ïlMšeŽ_yÿÔ͇ާ¤J1ºÞ¿æU§ØâQW{ 49›:íŸ`¡H‹Y­d®Xz‹ªÒÏ SHÁ´-VöÚª¡Ób‹e¼_ÒŒÎU·2bО/Îïéæv¥?óüãú¶'®ê©½ëMÁ0Þ4A&Æ8¦kŒL j­b0‹éOl]VãXWcÐqeϳµ™(vŒ¯…Æ•p^d·¦¶Ô-Ó?¾ö–vFÿ_º¤{œVzèÇ’JÍ‘¶hƃZé6[Û¼qÛâ s‹±k".$Ž~aÝVeZÏú7^ gPç°Ž#e¥•óÇõ¤½ƒ,8aÐÕZ½Ã«Ã/†ÆÞ‹ERe®;Ú3bK¾ÕmìSÍúÑ/ëý—:}ÃC×NhqÀÇèæˆÃ6ü*‹~ÁÛìßÒCŠò)gPé»ÌjQo´öÿލ¥ ®ÅšÂ ¿ö/ݹYµ3ú»êžã›žxã~ùÙ›ÊÓ%ì# •ü;tW‘Kƒ{(p¿ð]Ïv]û? Er˜^ÓðJè:2ýMñõñ¼(º³‡œšVÍÁeFZ/lK£#íI‰­£.)¹«uþ¥I™Û™"ýº ‡Sï…B~ªG«¤2cFñæÝ\gý®'®Ø.ÅÆÜõ΂5Þ#WšŠ4öÏF´ÙÒ€b›;iE¢3ÒM‘úÇ(6®tÞ‰U‘ÇÖJéz]­Öï]\̪ yÑ~´ºÈºd]Ùìë½µò0˜GÓ•8Ó)敚Ä0…{{Ìn]ÃqðÙ˜Qèiö°|áÑçN**zWò£xÛcŸ’òÊ Ôv«ƒ‡/éí¿ç›ôìüŒ#³m!3·âaõ¢¨0ÇžrGç4ä-æbW5jl,±Ë®uª:¢=hÆS)ÈõÑ_;/Ý3žæ±°ÜXjNŒX”ùwBVÅSPÿ6‡EÊ€]­”÷E¹H½Œûgõì{ÿÖ«‹bä2éþn«‹Ël%ºçnˆ5…8†£†¦€ù@¿Ÿ”l#F¤¸¿cÈ-JYí `òû-±yãwRR;Æ(¦¤x.ïdº8'b²x-OÅ´˜]ןë‰hxñj§?»y@ßýÔ­î ,\c=ªzä¡8bT@Q›š°Ëˆ+N½#õ¡µ7¦žlNX§ž®•uoÏDZ,¡ÅÃTSwõTÂýEÆÏ—R>½F"z\@–³{s#´ãb|"«ÎÔxxº7UC ·"OŠßË|»ß¼¢WþSRZ(% Ãxæbc·ƒ¬™t%æ¯=Ö£z©äÙÓ?Ðѹ ip…=«EÄ×íìsG¼y›sèéQÒ î(ÔŠA6¶h(Kˆ>âà܉‚mv ZÛ0¦¦ý͆ˆ¶ˆâ|£`;X÷•ö5¿îwÇÖzï¢HÝ·Lzx?©O“6Ó¤g«¥˜ˆ<ø<¾Q“*òÖüJ!{®Ñ)Iᜈ‘hÆgؘ®‡˜ §Q¡ÄHA2ãc£ÞéÍ*<—Srqĺ¤jmÆF ëb#MÔrìž¹ÿ+‡½þÜ‹é÷=õº.,ßU×ê.±K2‘ˆ·ê÷÷"Á'‚S¬]GÖêŽSÎy„aÿöºF+¦&ñXˆ;R¥Ó À’{T‡?]jùÛ‰$¬{ƒâµý”Z­­E óQE’‚£ø›×K(ÀûnmÏ06–0V•MV‚$±R§sƒÑRO°’ð…´IÖg/Ò§ìV¸VßX¬½7 ù×›Çë/yÜ‹¥—Ïä Ï!¶*l£»^О$¼PÄ*#¨xøqü|Pí;žŠèŽZŠDÉÂ#?4žë:Jž ¡ëÂÛ!ǵm\¯îëC÷_ä…ÃDoúl¼) ’Rb²IÄΚno Å–~’3†ÊÈâ¿CÑ8v¡øy±µW2]{þÓ¿çu|å–n¾ôº È]žº•r&ŒhŽ ݓ܋OÞ™eÑíä2µ>7£ìk¢ð[åä’“Ges$.…ú”Ý ˜Ûs_ª– ,Ìû³ö^ÓþÆÇ‘°£Ž§ìµ’Á!£žîÀD²BÔöòÜÕ÷üûþ6g.+yáú‰}éÒ^'4µŸ¿STú=ûÌhÏ-NA §í ukîÞî¯Òa ã6`É…iW"µd5†XkŒ‘OŒ0E÷›×¹x>Ï ›w»^Há34ækjRNí’ƒ0)¼ä:&|&7ïLú³Ÿ½Oßûž[ºd/ü³²lÇ2Ù﬛ʾxy³mkªïÀ‚]ûëÇõƒÑ˜3§€8ãsq û¾?5…e·óÅŽßûíßøçºÅr›¼¨gzVíç8 §›¤,0}Â.×Ý`“c*#]á¼±Õ.ëA‘=÷ìÛA´1#ô›ùj>Ä€”·k²9yñ~œüÚÎ[Ô mÖ÷Œ(î9ꂘ5½í&¡¹ÞhtJ…¼õ."y‰nýïØß÷WünÜ‘ÏÎ ÄJÔä0R±èφ9 ©8r¼´´\èÙ¯SqÐÝÖÏ‘Kí沟òâ7 )0mîÛ¬# ªÔyÄPZ3 =u¹´÷ëqCùwvÄÁ<¯#zuú¢Þùݺ½w¿mà]ªzǹ¬KgölcTÎzõ¸jSØ`«'aït)A\(£ñÌ GǸw‡Eö1=l¿µQ½s`l,bÙ^½{h÷Èür¯ãŸ½òzMU¯›æB5^æ ADé+aIŠàÒR |€Èð¥¢õDb¡›}.;Õû,‹m)/Ûÿ=gKÁº¾¬¬hXÀ‹Ô¨pX1~èTÑ’XÕ—‘+1òˆÊ…'ž{fÙÏ!pìëï"A¬À•W{6ñ¨˜'fhô܈Éó³É×k\²Ùˆ,öxïöú¤-%²w&,²cu*ê:k;5ãöÊs„Ù­ð=wyØs ¯rðt8˜5€i'<‘ˆ1õöË…r—ýèù²h]íù~bî\özß_ûûÔ¬ÔôÈÐÜØ:òÚÒZñ³£ñB£Û°t“#z­J™”ài³çš1±HÎRŒD-|&‘¤hÛ¨íó•7LŒ…U´ä]¤hR2Þ·Ü:wb*Ê6={}ÑZê)'”Ii¹Ø•³¯²1â¡û¦g¯íøŠâhô×þ®bÞŒkÕÛ–ƒ2EëQ7Ž]Û†Ã5sXH±ÅÍ™ÂøÝ#>1DÕD‘ …Ñt¿ ÂR^÷Z´ÈÀq)f0†ä†Ã¯kk§ºñvxŒ‰qΥȫDŠ j8Ãέ¹£rl`DÞëÑØéû>wQ·Ž“TëµÓ­¤Rí1hl’§å©NãEÎn(N¼VmÍì1²fƹiÅŽH‰BÔa÷1MžiHWïÚ=ú\“¾Ô˜Jýt™Ê“Uŵª ù©‹]¼ÆŒf ÊIoTuöΘþ×Å©\[¡O ] E袓΃jbc‰ƒ9aÃ’›9:xzEÖ]x]–Þ¨#,É Hê§1¾Æ:Ek5”pãçï-D†̺‡ªƒ¶œ’¤ŒU4EêIBñî“sP^î©x>õ…[ûzÏ…«ê– û;DùÆõ—I?0aï5ˆ^Xôà;H{E’Ì´l©®â„rИ‹9æj÷úà_ûûõñÿáÏjŒBÝß¹s²Õmìån’¤¥&¿"í@Ųƒ&wOµ8n£Ñ“xMg£TÅlC¡`Æy$S ‘¤©<ˆwuÉ@œ©1ÕÒªª}oZXÇ–b×*q,‹½Ç©‰fmŒ Ø3hÇÖoÿ6É>ó^­Êl<¦Q¼ÿWïLRL9GÜWŠXÀ}ñ0T3ÇY±P A'R¥XܦÖÁæLÑÜ7ÿ‘ ÝÖœD“±1“šBOÄÓÇ+¤ ð:­8x -zkùfĺ‰ÍE_CÃ$U¿g4|n¯‹þÜç/é½ëºöœAÙ ¥µ~OÃhLÖy‘g@ëÐ$|OÒü§hdQô(JœÉ:S×Y4×mâ\d‰d [qÁ©$½tzƒÐÁ¾¾ÔÑuÝÈÍ@êØ¶9ò°ÆÏÐnþ…Y _*‡–)s®“(ÔúïöXà[¢öÁ¦åç^áAû½ò3nÚt|ÁãçQø@!n´#y«>õÊФ1ÒàüSÇA' Ÿi5d9°Ø@‡[BÛn*"S†X­ÔøÔ¬®AžÕ·€¡†àÑÍ)šr”ˆLèv Ü9W¥;ãR#U^/#Í´ØÛ’¶NŒ™ 4ÍÞ‡ s¤c0”…âÛ­=†ÖŸ#á4â8ÓÆÒ[öX:EòàòE½ï¯ù}V«!<ç») sþ…÷`›:ÀAþNTRÐâ9æ±·ºYßwl´bεºÅÒÞGuή°Ñ-Öd_ñ€ýña­FW+­í“Æacm½SHáÎYä:‘S¬ëæh#;³.²"½Xé¥»íºµ›SGç÷WºÿŒSë8CÄ›ëª!u‚¨SîõSˆ>-3®oQ7G6iÁ•³çåÊj‚1õCÃ#Ôû dlÏ+Ìïjd¨V³pŠx_–[í‡/"݉G$ƒKZŒµÚÕ©}×÷q­{Ûþ¬ÌXS¶äÏ?û€µG]Š”Ë…z‹BÉ(–4þ®ôó‰×ª¶Î¢dí‰ÏŽa–ð_i{÷0#ƒ#Ü÷ðr½Ð²áà—/vÜÞLÿ[ßõµs¦Ó0ZMÂDjª”šªÅêÎÚF»ù§Mà¬É˜eB Š"6uêy,6rèÆÞëHôÜŒp”…´Ÿ¡8ßp þd€Z}¨Dzò(¤ºÎvSe/¾y›šéD%÷—÷¤ê|8ÑX0åC…)…û½ôNƒB*+ŽІ’{Ca µc÷œì~\%übÞµôÜ­s1ümùRZM)â™þ‡!Ò­˜gékLFTHx]K¥–e‡¥\wy0äíò殿;ÂÆH;t;÷Øe½ç¯ø½N|8¶¨'Ž*Šôþ±9êî»|äÜ`3O …ÃåNø.RÔ{ Š"=É\ð¤L¡Yà5§‰t…ÍÁ¶‰8›«¬æÑ«¦ä¤žƒ™.Ûߦb87‚Fmb5Æa»qϸ·Çˆ _¿+ž´Ù»(œ£Çö…Ά½æ”;]™,Åi·môn ù#G…JÓ¯'c§v,Åb%ê+ЃÀÇHÞE•¦­G:cL‘v²4S^…õ¿³m§.Âáë§³è„s6Ôqìß÷´ÚViäó€ÿ®5˜q¾>ìëû>{NQQ§8B‹®E]B]‘”2D“×!MÛ|»5Ç«s©\N—v² ãz|áî¡Ý#óË;>ùuïøBÊýˆ@ç-³ ŠýœBHP‰¢£Å Û‘<‰dÑCaÿöóc‘Ðk)·ë°X·ùÔ1ÑS´ÇAû’ÇVâ|¾¡Vß‰Ú c"ݾsË~?¥bÇÄd9e<~f!ŸÝ+ìº>©œu7¢9¬ÆFW¢.,zCy»ÇV«£‚¡ÀntÙ¥Œ†˜l(¸…6Æ%ŽüÏŸ¶«l7šÐ$X,!ä3¢í^¥µ µµƒâlR/÷÷Õ-=ÍPzŸ»õôCº»çá!ï Ó0ßÙå÷<­þ±ïU×cl£10| ÷ìÅú¢b#ÍMDeOªäY ˆ ˜& n0EJÊPËÏÏ7RI€^9j§Î3F´ÎÃ$@tiuq¶M¢9Z†¢ì´æèYôâF¬iì¼ã³—uåÑ‹ñÀbÒ^‘ÝÅ‘7·ndÙ{QkH“³ÔñHÕ¢ª X‡€t u "€Ðl€óÓá$nHÜpѤB:­¸AªÓh‘O¼–Š¿ÎNf€HȘе駇Ýuï—6¶æà·{íV¯~öl¸:!DæÑPÌ÷v¬‰1øµ[„.¯]¾¾œCË­ãÙ°Xºj®q.ìu| Lïºxñ廇vþ×þ÷åŒIåeš¾½·›²ÉŠÞüDÌ6ÖD’‹ó*ˆÔ¤jÁ{ˆó×hçNÅuŸRkßUrdèÚ-7î›DäsIáµR»SK‡–¶¡‘§[‡T¸¥Àuµ[ùRã±ó+C;ÁBï¼EWÝÒ%?ɱ–1¿……édŠÕÒÂ^›êÉžk¯ï” ´ÜFM‚`Ú,ü¯E‡Û^¯ÞZêáƒã@×îÀO¨0$6ÀÆùãl°‹E¯8À_ÄðØ^wÊ£kHéRÍÁ°pNó’<Í%¹¹Ûßóv½ÿ¯ÿƒúäÿôçí~èZÂ0mI©ãÐ~[#›ð<¿Üä ;VˆQ¤ØæÚiÞÀ:"”1`5Š5@!B›tŒJãðGdok”¨…Ž…öiœ+*l¾y°_w³‚o¶ÙihŠQLæ@½ôä·x×_²ðC}Ë‘ÛuÕÕuQÝ©ÉñÝ$ºÐP…QŒUÎqÒÍušõw”¼V3̸;¤'¥¡¢¶°äZÔÁ8MG=âj¿òvnëh ÏÅë¯çSA£§v½E#©SÔ™ßtófdc¨À¦ÝXŸ½‹ë oöºtpF_ûðœ/‹ÝÐÙý¢`X6[펄Î*°}{¾­Åq¤îAWƇß—aÄ•žÔËŸ;¼9é”Ñßýƒ¾œñѲü³[m¿]òEÊäL˜‰°E LªQÐkŒŽB*U×!·³EâÔýŸ¿qFäë–zì÷ö%›ˆëSÃlj±E•¼>&LÎ6pLsܰ͘gÙO®ÏgA‡çŸ|Xïûë~¿>ýü…HRö\Ò˜µ@MH’sF%y!"OŸj4‡…9 "NUäñ²|´#ÅQùS`F¶ƒ% Ëá”H{:ð}·®n€sIn ‰Ö’ó#âŒÈÓ‰µqeáÜ|à{DÉ)Î/t“5 Lòs»¤ëCV1þ5÷øI¿ÆZ0¥®Ø´©{´Ï48ŽOo®Eµy•Mˆ=og~%¿o6ÉuFÛWgY¨Îd}šÎGrhg*ŒE8(…çÂð°ÚWI6Áx&„Vz/Œ#š•÷Œ‘ØÞÛ/¼zV÷-×zÛýÞ&rÅ6×½¢¸ÞT,ý›˜3Sd83("N'ØòEš!sv”_Ð=FÎúòþ;»·ø/ A7Kæ09áá4•¹]s´šHË厭Ï cƒªàØ[§øy’Ī*mK¥{ŽðÌ<ë³OäÞ5ÕÁYó¼¨Þ?z^~d{ÖÅÐg}î[ú2†ÝzÉ£Ýé=òè6÷ >“u-‡xVqãi¨•×J¼½’Í í7Ý-|Vçm²…Ú/íçËÇçµ.^Øt²÷œØcuú“ê:e¨¥ ‰ž·½=uû{±¸L=ëp3bQhGŒsl—@îÜãêýÍwk±4Ï—4•ÍM{¾Õ¶´¾Æa,21U^»©ž쌜\¢Oïì¤ ,õ° nD¾Œr‚È*Þ7G¸¾zOG:ÍO×QÆÃg³½¿BK¬ ‡adxWßöMʫɻªî[%_¿£ˆ”¯l-¶{°M¢tn:îy·VÀãH÷ZÔás3ÖL澯 Iuùº“ÚùmÅÂø4zo£4¨Wxt°¯’{M¥X´h¯S už;ÿ$IDÁ{sÇn§]˜–æžã¹±€hGr»Ñ|áŒn¬»kšß-Ú^ô ‚t“Qjá¹2Ž­®h÷huÉX«½—ó;ެ);ld½p/ `æ—/uüлÚwË׫ªYnY=£)Ù¢J10²SeÓ0ýù…“ÿõôçØÁsÌ©¥Æîµ1ƒ³²ûšÆðÜ¢xÞvLúÁÏÙ1B:8Cz]#P(íïYÝbq°d¹cOMV çž6¨ì:1¹¥±^ˆNb}è²3æÙ©Îµ„—’sÓ\®²(ìD"r ÏcS4¡©®·MIfP¶AþÔÚ”­›D´bƒÏ8¦Û®ô.ˆßÕýçõ¡¿ñé“ÿãŸÓ­WÞ ëRCÒJž/öOß)É"U{jŒÈífZéë9SC-DÜU£¶'5GŠ¥ÓÌ@å~AŽ:Ÿƒ9¡;2Ó¿Xj,ó¬F" Õy¨tXcç#µæ°o>ü/êÚtî h{x:èÆÑ¤ê´*ö½–Qãví’¯‹HI IÃâTyWZ Á£ßé*ºÇˆµ…VüÆ߉ˆI…ÈÁæ²­¡( ŦN£Œƒ=F H¶÷ˆ®®†¬O÷bö×q9[O¬Eª¾nä‰v®~îÍQßxù °'»±\_'…n,ºXãèHÿN“4 ‘Ùú,.ÇÀ~‡ãp {àguÑ«êËCÖÿž‹þD¾7!7k¿Ïjuzëñ°lÉ’'.J¾ïäzБ„("¥P߈ °(T‘ǪʜŽÃ6WõŽb¯™ ΑŸË ç²»¯£È%N¡ãhg*ú ç}~Ï®™Êä4%ÖŒNqÉMXœbC„ï Ù±D9DS}ãJÁˆ#Ýi#Ò…úÜÓ EÑ‘E`?˾^^Ÿ×K×2÷tc·L´•BšHú¯éÂS«¨ÏM}lWÊÔ˜Ô+Ö˜ÅÓt?¼¸HžšMŠÏ4î4Ž–ã­¾é~à¯ÿýúÌÿû#ºö…W ,ß©ÉÙÆÚØŸ½µ·&ÃQê7šoH«ó]a*àB0(£Ú¨~ØßA°Ç”mѲ÷7¡‰aÆ,‚]¾ØÇ”[ ³7uýL9ÎÒR·/¾Í^0ê9c†™Ø> Ý<ÜšÓUòÂ3øªØhÍ;·NÀ.’BNˆÁ"ÅH‡à©ÑGÔÓ‰D‰€ lÚDÏ©6_µ§ª±a‚töF§LY¶TTžC^€3âHÀ'ÒUŒh äuš²\©8=|¬Íbc­}úÊ=r漞^mTÅzÓÑiaB?¤4:1¯ åÝ$’3NŸ¢ŽXaàÀæ\?qO’RÑ—;_{îºøðSÿÅ4Õeco딹W²Ma´~¬]tòŽˆêd+ô»Ûƹš¥¼sãD Þ¤²À4¿®åy+a­ÆÍsƒˆ‡:ŒÝ·/ú¦sb‹v¥¢Mít#‘Ój }ÄñØgµRF·}òÇ:‡<Õfƒv_&¤ÜÀåÞŽ¥‘JûãÇ ñ•çb8bQ bYDP<¤4JißòÈ0vSÛ°>õÖž8wlÿnÑ€§Ê¬8<ÉtAöö,rà=a(bú/xOÖâÛ[‹îž §1jC‡ÚPÏæ2ôíhÆÝúäc›è  ê;½û/ÿ=zíg?¡—òãbÉQPVP¹œØ 0¼iR ÆH*¶Ë_‡Ìðžna0?•-š›îXâìó°{¨C‹Ž0ºòà:Ú½wþ=Õ–Ê SŒlñî7ÖÃí‡ß'yê†Mö좓¦!¬·£q^™Š!`»êX6äÝ ‘Im€¸†ñÈö\"ÝXÜ=É·Ft N`àõú]ã"Ã0H>yn t®D÷ޤùNǕꄑ²¿‘Ò1Š’~éïèãdÔ#—'?×–Kl;¶µHÞñ0ªúÉÎèòÞ‘öò†t0´í8[ógMzîÄ0N:_kÉÁ®}ä¶ó¬ÇÈiز²9¸ð)ÝcäÍÁE}¹Ç¯þUÕ”R÷|¦¸‰†9…gû:ˆ™ÂUûIäâ <èq°à¼éªPO ,[D$tΆ×8²ˆ,2fI¸àH²ÿLJü¢™7F&g›ŠÝç³èé¹7ìµb$²{DyIj!žõšF¶‚5©«P¿h=¦›’‡ªö·Xkàû­“²Й]VݒφÀªõé;Ú6;†Àº³h95ãc¯Ÿ›šŒ×·{½uÓ„i,ÕR<¿OÇ9]ÒqR"‹j(tãßYÛsN¸˜Âf”áÔ¢ÞÁc°ÁÚ}ñÄâ0Ý`ƒ§„`G¸Ÿ$=þ-ÖûÿÚïÖboÁd,ØX{CsÃ6‚Ýh‰6I¢¢šL‹£â‘Ùé5rû@…÷¦Ѧ<õ$ÍíTÅy1%ÒÑ$Xê(ÊÉ„±ªp8¸‚]­º~ÿÛ1¶‘í[–Î6A÷¢{–¬®Õ˜Sqd?#ZœM˜Çw Ý‘¼“”Z P$c‡?gÁ®èL"æD¼êù(r|.‘ýŽgOÁû¨ÃI° kݤ„)þ×¢8ÀÂDìU|~Jšõ/Œ¨k§8Q¤§wmMn¦…~ò¥‹§b¢ 8 ætMÃ`©ªÈ"£8x7\+b@"‡Ç"{wV?ßß¾±¾{èÔã×þ÷ëµ–+¥¾‹:åæ3Åâ‘Ô‰0Õ;£`•èçw\ÇÄtHrU€p~dû7Ê„¾mЭó16¾ÚbŠŠˆ<ôF¸'/hC½{ÇPš%vsÃI¼u톺î1[P_nÄ®ûÔ÷Žl3ñ¢¹G¶‰/¢Aôd¬6`œVÍ­ø ˆËù* ˆìw@#EÒQÂã‘§!¥FcÞe{V$îS·зî½ê­¼[ßæup|Ĉõ 7›P·œßw6Ž*׋>^¡¡eý½F°\,‚˜²Â˜L3Eˆ-,' õ¥§ÓÿäÔ§þŸÕћ׃ðP&õã)¬J« èáü¥LJÉžgµŠ®góåþñ€ý=Y3–&Ip%3ìs®{« @1ó4¯£xD<9hÔ/«a˜Ø<ü¹“GÕÅZw}¶¿í'xá²ë¼Tʃc7ˆ8ë”vXZcí^#甤–ºoÑy\8¬021SGìöú±Î7l湿ì +Ùé˜*_5‰c©ÈP,E«uP$*ªÔOl4¡,Ç&1ÐdùVÑçoœÓ;/Þ&ͺ«hxL˜³­Ãùïö½'þL«.çP¤;…¨¢N;é«SG®’~]Ç”ÿMË™Jt9#®k•S´ÆÓ§†‘ÕÙsrJ<×ó–u^4`5j §²ßígPLñM»¾9aŠGC)jˆ’k{…ºIæüЛo×µ·ËÏ~{L÷ÔH#Žä&/ôU®`Òu¸{}*'Oi‘nq¶^[®.§ ÏY¬ãê׎aÞxEؾ±En©)ZíüÑ ^­ˆ¥”À2Piƒôkß=æß_º}V‡:c“ø´M„4Í›aáovŸïžïІ3ðRãˆiBUnG]Pö vË4Çϼ dÆi¹o½øæôçîF"¿W|Ãûìs9ž» ˜CŸ'¯ÏõæåRßHäí|õé÷êþ9÷˜gaµ²÷Çá”,-<¥¾a,ÂØñð]äȽy¿GrüqÜC}І­ã€mê”ÝVÝ5 […¼’d]\§Ö/Hu…•8H†ÎNŽø¾í}—“²§O¹_%C”£ZZЋuZôÌwgÓÔÛ:›ßo¢É'ç°6ª¤Ÿ;#l\˜a™¶[‹dl½xãŠsæ}âî¡{™_¾Üãç¾îéOç¼x=ì|ÎxTNìL«tº Í¡Éµ€ýMÕ8C{|jõ÷0ªãM*Øüü.(R^jFĽÐZªo°~~Ó3÷G`¥%ïYU’ÓeÓ…cäŒe8芮¶ƒë  b?¿.äCІÙJCúªu(™Y7,¨O@0C3õµG%^tÞ¬ñ¨(’6Ž-¼4&ZNö²3¡½íòWÎEA".ªý *ê$Ë™C!n^T¥4G!¯–¦ºæ¬»ÆEºlÚÑ9­OžÚäžÎÉé u–rì:=þmÒ{ÿºß£Å™}ÉÙ‰é‹ÇÃT6L‡§Þ:;š'ÝRèr£Î'w€x±:‹(¥]>7EÂFo¹¥}ؘñ¬I»qužVÏk”óA棋‹Ho|¯s Tã4Z÷¡Åêsg—׉´ N#ÙSÂ@0g–$Ž?µQ3¡]˜»[€GkÄŒƒc·k_G%n¨¨]»rn‘Í K:¹"µ;ég;—:Njõ³zù‰ÔW¸ßÖiiÃiæ“ËúFz¡í”õóo> $º5„xë;§½&†Vw3(‹EodŸ³ýL5’ÎÂá‚úÄҾ̀蓩J÷:2¿üzŽœ»I5ÅHžcØãäq¯Ï<¨`‚ˆ9¿¦ú¸Llª˜7nÑ×g1Ñ羿oGçÄrðÿôžbdœ{ôA}ýßõW롯}7-¶QùÑ©Ñû¹Ñb6æÖ]†h“q£ ‰ƒÑídQfgˆf¨Û{õŽ/1£×Nš Â-æôŸéÌô,¾;EŒfRb]Œ£Ž÷Οˆä–u´´rWçüpíÎN¨$OÚt/lÒm³5…<ê"lrm#&*‰kŒ›m(F+t#VO†á”6¤³ö-K ÷š1"¼n«‹ðz¤¾xÃÑîq¹çx­~Nsnîx.ÃGcçŸ)õ¾ä`:ûò…7;½zg…z&lÌqÑÙcãñZÓz°6¶åú=Ô&Î&ÍZ žÖ¢ÈOsÿN>‘:é^Gæ—_ÏQTþ]¬é*X²éÂ08=ô%ˆ£(èxè ÏË){¾P„þ†²_ß7t&káº{ÞíPàךÙM-:×ë­¥srr»É;ez÷Ef F,N¢vãÆ_"ÖÞ\Þo7ùdÃsa‘z$RÓLM¢å¾«vt†ÀPËäŽÞE-1ßÔ5ìy5¤àú•*R¹mïï ¡…kÒǯ]R6"«ä•n¿Èð ÛQÔœÂv˜9}úÞä9§ÞŒFL¯mQËöèȈÝÃzÍÆ ”«ø-NIcT=ó½ß®ü±? ýû.ØfÞ.x§ ׃qaœßé9Ï¹ÇÆ]ÐïeCºÁXXƒ Vu*rßhÌPF…A:Á|͘ÁA3ŒA3>æ 9(#6î_â3ä§µª[3ˆoúë±*Î4Íê„tÀáÒ@À`ó´ƒÈÃç¸Âw¨ÚÙôý0ÒbDÆîhyÑ<Òƒ¨_’ÂâqŒ–=^R§Ú:8[Ñ|K:«Øu…“Å<” žûF•­«{Ò„é…HSå|"-FѼ:Ø‘h%S‘Ú¿xA›ãÍ ¶„¸Œ]Á(ÿ€ÊȺrâQÙ±OŠ@dÒÌ:þæ³÷}öî¡{™_~=ÇòÛ>ò39å+äTUé‚Jê䆾ž“E©PôÍ&µó)^§ ­ÜHežƒ´jã· èÛš„SnMÔk—üúök‰5UÕH]nÌ›ö>âÍk1¬ÙÈ(6}Q6^<ì÷_¢]ÒQ¶Ô@BѼò<¥Na±òˆ¥MD6AÛ<–ûRçÂU‹…´Ú£ ‡çE¸ìqFiG™œF=°¨òt{Ï_Ûסœõ3tªu øéô ¶ëµu`™ÔmîìˆðÖ8á)-p¶©z e6ÊÅÚ|5M3uürÉÇc§hn^” ›±›ù‚ê8R|f1q¾ñh}ÍßòGôÔw|r‡Ø~ )-B«Š§Å‘>çØYv2Zcñ«(cpL}ö"Í!š .¹¡èét#ê‰Eÿ9nÝ\pÇ9[ïÆ°Ü’†½ ªäæCGULÛm&ÅÊÛÙL)G ^#Ö%ìÞÐJ‡¢¤­•„#ŦŒ: óÉ®±+ïJñ»úœ¦ëŠÔ©!¡ëQ§9méôG5ÈíÚæzui\«g4ccz •Ì!ñÚë0"½}N}¥™´Ô^£Ø~˜àù ]iûöÜÛ›…>þæ9m®?«uî=Ö Í9 ÔìÞ¥—Zm²ëˆÊíüêµ”äóÇÉbþÇo\ïº×ÑÿÚÿ~#㣪?•”ÿE%QŠãïˆn ×=÷–\‡cužR;Ÿ‘{PØ¢°ë 5/"q;Uoþ¨n…¡oŒ¿&.4zmÞG,J­Ó*‰R ™‹8 Z•´ÌE_¸rGï{ìþ/‹•—öÞ'Îô:··´ô싚T·äþBë,ƒöLAÐ"8†“§§‘Ô˜µéÒ÷ާÂ]eö‡GżKóà"eµ•êÝ.) úüO\½¨ïxêf£ì}±ð™ˆ­E’IŸÛ<oÝ;÷cQ&õE½—yó‡ò&¦ª2’žëµ=n¡º¾]Är¡KÅUøÎ´þæœíü:Mwk#Öƒx‡>óg~\׿ð‚mžPWñ”—¿Nß)ÕÙ€÷ã9†F¯ç[¢&\Fn@“‘Ïy-Î 5ÂÞÒŽäÙ÷%z¡ô†zÞþ†‘Eq`ó‡83lLŒõtäg²?hì×»™BÚS ž‚-AM°v 6³†òN‰ú‘Š#ÇZ$SbKaصHr€†aÓvVé\‹ŠS™ª‚1u ühLÁ¦–Eû$ê“tÐÙ7ÜÙ„s¹©Ed-õê÷VÅû¨ª6ïã÷QE§ÙÇߨ׻ï?ÒÁ²b¢ü3Ñpüέà¾Xf«›ð:´ú&Î D‹wa ÇfìJ_bäi½ÖoäòÄ ù6»"+ä‘r:"Í£ñà7óRsP8,xÎÇeÆ$ O+@1$ÑÀ=U¡ö  ¢ì)öL“2`‘;+/ÀŤϼôæ®2aLœöoûýý»ˆ!p Ô2×ô8¬x3 7:…HÂHñHQ ³ÛA‘JìVHï؉Qyé ÿ1D½ès'RzîÚRoÝɾ™—É„óf@zÔ —K-Kûi$îísœÈdëB¶u±oÈY‹BøXiípË)Ó;uì൬û¤ëZ¤²uR¼˜’#¢é:‹º3«»J‡¿Wþÿì§Â·ÛîA¥m¾lêÓ4ùUhà«I`Hœ„ÔY€ÛÒDîœ$$M1Ælè轫ò#–%ång^f¼ð 3ðɱUˆRbÞÑŽJ4À¼‰ÝXÎ6Q¸¶#‹Å©º.éXæ¥oÂD2 ë©GëÄ>>X4‚½QĘÃaväR4§Í—öö:ju>«€ G³£b™Ü©·‘j¾mò¡ÆHÝ‘ÏËêmö™lÄ !@eÒX{ýÊÕû¬Í½÷z¢5™¸®ù´uºj{Ì"ïfbÁˆ(ôi&CõT?MÀs¯£¯U¿¡ñÏ<ô}ßù…·n”R.ª&& §¥Q^Z$¢Ñ–Ûüœœ¡-çyMÿ<ÐV«i£{è]ÔYdâèɩޡ‹Ø•AÏl-")F'’B^g‹¹î žé Ær c ¥Äæøˆ¨cWüÿ&GoʇØÓO½|‹/VWÇ;,ÌS©.£YAÚ; ÜO䘹Ƥ$ŒO^$tGh¦ñ Ô@¶Rv/P¹_ò9°Àý5[Ž”.¿ÿÜ«çõ{͋ڶì»iZ•SÄoÂb¢°‡¦şGSÆ`üÑn¢§Ø®3MD(Q„ÇŶßÍF ¹¢ˆH៳q\xÇúÈ{ß¡×éÓzáG~Aë·Td¯Oýbî|ŠÀDOGšsP¬(kllmC°EŽÆº¯›s ÷}†7ìLÔŽñð¶áœz»6ùy{Éì\ëœ[ýœ6Øà¦ÞnÃF:µèMíÑ´c¶lxsFwh6¬3j#<è>Yª˜âyÛ@w°ТԖ†ô ÃØ9œ&0çÓ Í ÿZ‰¿o:Ëà`IŠ]OupÜ ­Þpe‘âî{§IaMÍÆ²ðžØKúì´¾å3×÷ôÞûnéÒ3Q@ isÝè‚x_í=é‹~oåmàkŒîÈæŒo·Nºú’HŸû•~£cJú9ÕúݦkdÃ… „7 뤮vóB@‰,Åó Šlx÷T4)!žâX’TZ˜ñŒ%y°óý‹O•Є´–Z*¬$8Á¯ý®èÆÑ ûή"ÿU¬y4Ï:üýý÷­Ä€]ªÀ² eÚ&Ð+¾2ƒÓ檞¦24ºókÙ–D^›½&ÛPœØÉ7 ÕißÅ;ÙøÛ•ñ‚^9º¥·-‡yØڷ–¦)"‰è&Ûœ]· ë‡îæ‘­Ö×D»y›× EˆH0fÔ³E4û^ç4b9;À²0üð»ôÀûß®7~ñÓzé/}LÛ[‡íõsQ-¨çåø³VÉæå(j¶¨hézßXЧ&]L-°ô6µOyÊ×^Ø?ó©Íi i!ÖÆ@€Wû}+6îa,€IkžÔÃ)NÞÈF̼³y¼Gáºmºýª½Mœœº0÷Á–àí{4!¦Sâ)XuJ! ©P“$cŒžé["hƒLí"n¶þ9’úšR©‡x4-αE£ˆ×dj‡aÚ#ãÖ ÜjŽ'K.^#ŒÉ.Ö† ÂϽ| ßýä[q¾ÎX­6Ÿù®#KRŒHÐ!Õ MŠ=6‹¹MÏOÃñë_Ò€Ü}’~Ãcšþ©Öï6°L©ÛSuÆÝÎ'¹é>Ùߪ³÷RØve”;UzáaùÝÕ 4&ˆYå'RÇ9BÈÅÒ^Rµë«ZN’¢û‰ †bÿX|÷™¤Þ#œqœf8e°pp64¿øÂ[úÞ¯{G¬Äš‘ …jûÛû.-d#ra™qœð¢l37÷íÚåFÕ¢¼ üÏ÷Ý#6ˆ2œÞÊK ¢[6ž-ÏWÓeBžö(áS à~éêƒzäày-ò8·êJVfxq·}îµÂ35ë¤du XFSà²BÜ¨Ç »›‚UÜ;F’t-´H½îÇÙÏåÁ-"3PÌðù”ÚhÞûè‡ôè7}Po|ì³zå§>®Û¯¼ï”×ÿŒÙÀYhg´9,Ñ¥Œ|O^W©ðaym„ˆÁë~ Ú ÀE·V{îdΆq]Ùïµ³ˆ Q”©Õ÷¼­µPýñzÆz-— {¯ã4³o²ü;–IKÁº0ԬʢvjºzÔàÑŽö³/s:ÇÓF‘Æ„Z ?«×+H‘ÖËè¨O#ÞfpÄŒñÚÖ@M°4à¢m„èÔÒofDÔ$«­ö3l¸—PqÖÛÈ.¼Ó`s¢ÝŸóý{ÁãUÄziióÞXèú#{ºµUm2¶Æq°5äóÏ `iyä¡4ÓÿWÃ@ÕØºÛÞõøÓú2†÷ËþÆŽÇå?—•®Æ"SçpiÍsÞŠê zܪN­nç˜!±‰ŸZÔ"cU•óiv†Üä¡¿äSµÀ-<°VŸ z vüÁ˜G—Ë¥ö÷VêÝëç]ÙæSt©èèÆu&Æ=k­nÇ»..tß¹}o¿]0¹#i›Š”æ@¨[’ÊŒVùa'™³‰P«ÂÑ7òÑ¡ËX¡—ÝÃôd)³cÁ”8÷”i)ª·ß^'}úúE§š!-6x™tL-KCpûgc“\Ù¡Tð³Ýræçi£ Ÿ} zjŠë]ßÛ9(Zž˜ ‹ö^ÿ7Ô¤ íÞbdâí–o><Ôptd$‡÷½óñ»ò¹ßs·õ÷êÒÓO(e—YUec}ú¤a;Xß~¡–C:@’—<6 Ýv~þhÏ3£äàÒ¬¢ìŒ h¨šfŽÂÚ9fX4Góýæ/Ê;ŽŽµZ=cô¨”Ô X:ñ¶ã`ÆSÎR`Ÿ®ÔàâK}.ÖÆ¢pÁ(Žl$b‰ƒ­¾Q©/ õ¹é¨ Á(opàp‡·ýæÙ¤û*¢ï»Åø,˜øñ½a`ƒDÓUI9v¯8Ý­;ÙwÁý}üúe#]¸³¹íµ¨2ùÜõ׎rÑλÝs4ûØœ®š~J5éK½ýò›]þó©v¢©_Cƒƒ3Ì‘höäŠìñÀ8 >ÄHäPÓœ:*6©œ¨ïÔUÙµ:—mTds©*5kkl,5M{ÀÝ©ŽvsÛõ ÕÞbÖ_¨]0Z£k³÷.’5i¿nôÆÍ;º|îàÔ‘ ‘ãÛŸ¼¤?õ…›íËe¢Ð2 ª½zhðhÁ[pGÓ>G˜§µçÞ7 lÏÅ{Ô°vJñÑ£¼¾«ù÷/Òù¹üºÑø0`󥻆/ó×Îëíïè|lÈ).…ÍÞΉ¨4&ãvÍ©VãÄb#ÇàD} ’èÎ Ãè·RÅ(Á`Ü©Ca¬â½s+íï³ÀPâ‹ ©Èhxˆ^ìµ/¾ýqÝ÷ÌSZ_¹®×?ö¹»µ’Ïh{ûÏ#hÏH»‘³Æ%“ ç‘;_ü“d_ò6û~’dQòVYÉ¢" ëxrÔþÔµÏÉj‘´Æûz[ßÔzuAÑÚoG×_®4kÈ HNQ·Õ^ü=‘À@§®Ð!dWxí hÓÛœA³c ‘wÁ³8]¤u‰ŒA²›“¸Ñ â[Ñ£ÏÆ}ä9¶xRÈ# ¿íöwV´Ò¹nÓ™!ºñÇätAŒÓùðZD\F÷…[®¯º´ÜØÚŠ×6š¦õd +ýþ~À$íÑ=ÚÊ+Ñ9$ë?ùemÿßùþƒRÖoø¸ÿù«¿²\õO­5ÛĆŒ—"¤çjvâ{I™a?™ÞD)%U%jÞÆiÃù« *6?=»(ÒY`óáçò†^  Š‹Üi,%ä´=„‚Ftj½¶NzÇCbPP”4µƒßÜLú¾g¯û&Üùdê–ÎúþcñÛs®ýü·XØuüÝ%!%e=æÐ¾ãYpÝDï½×_N¨¤ÕÿÛê©Á9Öí鸮ôôódð$Ùø›yדּÞFÈóÀ`Ю<9G¡E‡Vº].œª$…Å™=}æEkp"µ‹èF<÷#Fè¬kW*—A½!%ý”ŒåâÓéñoþ°Î=zÙÚ(77ïPôF×ß~Ÿe~ $–ÖÁòùæÑD$då2w´HG:‚z2}ïÑÎ¥E}}þa}hWßAç²×:\ouk3êƒMÝ_s‡e–Æ@~ß½2PjëÊ‚)nÛ<„Rýt^­¨ÑÑàlÌPÞ3H —Ñ»Ãj@³×£ƒ‹FüÍ;*‰Ø«ì~)G=ŸŸÀ8GÍX6lQ_Cçۼ؎Uo»oÛ" @µ8LžfšKëAxê݇јÔy.W]íyâÎç.Ô»‡¾ØÑw<©ßÌøø÷>ùì·üØÏ}aªå]ªmÁÁ2Uaáí³Â9¢È-„]Vw…À.õ*©X²:+nMÎþh^×l ÊPL3¦S¤*ž2Iî+Öþ—½˜ ×UµˆÇ7#—ô\­,´' ý»Ý_’À³Þ¼i眆ÁìÖH>z§dt–гƒZ„ß«.2íPúe¢“  ×1w ^ßœ6)ÞÅ•rÖ } :/}ÚEñðÒ)¼SÂðâÈkçe¯—oŸÑKwõøA‘vÐæÄ˜QŠn#Cz¯–Ö†µBÔªBs„_ƒiVË›¦ªz|¬ì©«ŠŠDzŒ±$}?ƒSíï“ÖjžÙŽj‹qLÜ?‹¸ïuééâÝ×pçHW?óüÝã]ý5<‰“'v©w¢Òâ¼l®#âÅc(óûERbü s¿¿;p4D×òzºÙ¤HV·ß’Nö;ãÖ°ÓîR)Œ`ˆ½ž±T’oölpÔŒèUŽ{ªýbžÿ¤’vøÑéT&Òµ¹{Ü¿…Æ'° owSkt3±±Þ«PÝÒ¡ɔ횆–]va+vI¤Ôšá“¤vaûýîQ¶³³"/êË£ >7RnÜ?m¹Ï]ßÓ7<šµÚyF¾™VÆY³»EO:´µz—ŠÝ KÓ”—ž/ú2F?¾ô¼~³£æþ¿¯þy€^‘…vòâ[Ÿ—5žø2KdÏ­ó¾Xh¿õô×ä%7ÕPêþ¤"M*–ºšrrn«"3Q~]â™DW“Ü{©Ö™2¡íå–"[.æÕrµÐÚ „9è²7Íw®¼*ǺrëXç÷ºXQ7$Ž'Îvzââ^:öަíäª~¡ˆmìMŸ‚èÂFW#å:™ôÿö¨¯„ÖÕ`4H8J´vºnî¥.Í `dáxg“¢:¢I¡&%7.?ÿòy=ü̱‹VlüWxÒ ÍEäÁµyªÔ¬¬¸Ü/†áàoæ-íT‘¦á9V7᫃´^«4)|£ˆèXâàµòj%4ÝOÕSg%ôÙ[ÄþÞ·é÷=m_ûõÏ<§·>ý‚n½ô†.Ò`½–’èÌêÌiÁÉRÀ¨øæT”\#B'µ—ò cÀIòïpÿæKæàÄZÏX¥ã±j¿§®ÐÎå3FY€X7[Îá6ÁLýæ^šIm"]UF¥nEVƒ Ý^Ûæã䊡޹Ƹ—Г­éYÒvUÆÊ¨àC„•“Ü['£(Û£ä¯7¹sÕCki *–vÄ[ú÷ã¯,ôMo[)×Vû…¢‘%Ò…µ´µÈ:´¶õemM~NÎú~}™£û=ÿä?fØŠßÌñá£wýÔzûÊ?ª¤>ê¡gÝ‹²™âªÝèŠ8“.È3E÷ìG׌H Åðê¹ÝÜ“³÷⹪ºÔYqç ¢={Ž¥Q”g6`dU«¬€žrrúv{j¾Š”©z›¯ôÂí­ÞùÐ¥̰1ï¾›sÿÂaÖǯn<esÔÃjHë¨)þ¬ˆt…h'|@í¤[§Øj,n ²{¥àq`Pe²+‡Ž12m¹1ñî·Í¶h¨;·Þ1ßoÝírQ7IO݈”{ÞÁˆ"CK9±IÛæêÜ„t¼»ÄÀ}^aÞÉ :@AÐë*Fezåa)%rŠ-±»%hNõŠƒbe`ögOg}PÝm~ô›> ‹O=¢½ógmÂmnß±‚¨¿ç6ϘÏ:ª\Óf€ÙמCm¦Ëö>ù¼)ÚzÚ,L‡Í¡î<ùµšòB6Äóû+mÒB·¶D!§©aj+ž:K<5ÖP!Ÿ1ØíPâïtäiãË:áZöº6—Ù¸1!”xUœïtA½ž½°] sß#p– WJºì¤®üsáž“ã·X“I´?OLœ9ÍE-2'®øÃ¬áAÞ]xkØÓ^«ï»Æmå*¬£+u2ªw¸R‡R­Ü/sÜÖP>Xü=yÙߺ{èK}wvO¿ÙñÞwmø¦_Xþ\6ß®Ûne§m‰äù‚ð ÞRš¬Yl„v>¬¼£G köT;™D‡UöX®¹MXÒ!©ÚyÕ8kÒóû,":©9‰ßöØŠ|D 6ì’.½k]/wnë‹ 6¬h`¾åÁ…þÇOO³qL½¤Ñ5Ä·:çÔ’MD€škÁE÷ƒúAÝñ|Õy$Ó£ Лvˆ"Òõ±ÚéÒBÅ(à‡i‘T·´"»»&a^5÷ªûÔK=ya¥ûó 'A\Yúo}ò ÛÛ‘ÄÈžf*ZÅ ¹zlíѤ±à|‚HÓŸgõ;ll­½uÎý: Ð;šì< K%lNèŠØ=ÍÑ¥¥ªxœÏË Fh–È‹HÌ Tõ\ÛX€»¬³Ü¯G?úûÌ߸¦ÃׯèÖ+oÞ=ÞÒp´VJ¢^Ò2­^TIG¤…j|`^[™k'“ÏÄþ•/hóøG¤ öwuSõP)âô“Bê<ÇZz#&*Ë–ÆYÚ=Y ȵޫñ\—=z$`¡M/¨Õ¶1Uì¾æûý«C½y”f§¬‚TïUi4¡½¼Nö¹x#0\Org0§It›áte àk½Id¤ŽF¤6§ RU3:›´Òç¯ïë=¬Y/¶µ&$ŸÛ±¸®1©[xt½Þ(÷æôDóÓÚÖ—ôeŽ^Ûª¯Ä(uÿߪuóíH§*³Œ¥ªÑƒôY¥Õ†­Ö` ã'Â(è‡ë D”ÁÆÂ‡R<=ãìÊhUïþ £h'dÿîj¯ µD=K•-º*ô¿Ó*ªN))Ü"­ê Ï¾qKïzè¼NÔB|X·Ê7]îüä†+xæA!ÈŒKͽ€Q‰Žï·0FÃà—c5<,wOÑR iDÊ­AJûd7`Y¢˜Ž‘Û**Ï%˜dó\cªÞz\7“~â…súCïwí¬æ©u6†n/Ò¸;0õNëqú~}Ù*“¯ÀøÙ>øÿ|ïÜy³–ò`bA¡õ—Cß®ªý›\sö­8UŠ9 ¾cÇŒŸž¡pÏ®ºDÞ‘ùq2'—Ë’ZgÒ|!¨âAòf§¤G ±KÉ `Ö°u ŠrI"œ$jùìK¯ëÝ_P,02"žòÁƒ…ÞûÀ}úæÈfŸëÞO§Å÷­~p¹€’ªmüIuô¢¼-tI=ù|ϳÆ4›çc­ƒg±Š\E±•±ñbõ  ’¶Sy)8ŽÇQ¿xõA}ó£WízÔ#PV]‡i$‰F•Þî¥[zápm†on[%…U`ĵïÍ»ñüH ²DŒ-»\ 0Ò£ÈÇœ°haضEÁ‡–ˆn®®SÎ0PìtÇDå·Ýkgog&J}Ò…§¶#“uwÞ=nk{çXÃáZëÃ㻿Ý}ì–†»¿O›AP d\dpѹöœº£»¥¢×~eêt~i‘g̽ãýr“ᢡ™ *˜÷!4)·èÒíI]Œ†…ÔE˜ÈñŸÄTÔ{(…ÊcÑÆûàÁ‘”/±yŸh©…E©Õ v[d]“ѹÀøxÔa{„å|Ý2£åé(ÚÉ{¾Àʸqyãp¥›ë¬ {ežo‹¥Š;_³G†99ü\|lT×wß÷ë2 Óf­¯ÔèòòÜnþÁÿÿÍ“"bUõÀ/2ñ.Œµ”šA{~pšãÍqœR—5ŽÅ¿?r÷®}­ùù¢Øæ“‹ÌP3VÕ6  œÐ(S²úÎ#Íäµ³ªëœPñ¥¸ÜÍ9ÒåRàAvŠyàB~×Û.êÓ¿¡”û§’‡Ö=yφñH%´æö®YüÁHÛ¯·9LÚñ‹OJ3ºÞùuFd;c…aË·Å÷TiILéû^_¸±ÐgôØ™CZTí'à¥q³Vî{kÉ¥ ]û^òB_§%4bx-Û_NjŸ½ Šê¡aT i+çßJ´è†%c°‚Œëu­A½Å¢ˆä,¾ÔHH­)vÔal¨‹4Ô=z÷¶9B£„‰€C:ûØe]¸‹?©¡6‘K)/å²´…,²ÏÅÒ_·Žô©›ËØ¡4#žomµ×uZc|OŠ5Ì›é4ºÈR/"£cG7šõT w´òÚi¬Ìh5˜^¼vŒŽb1½Š>2ü,‹Îtƒ]{ŽnrdöŘ`bCÀé‚m| ÝwPô k #U½µ^°e«ãúP¸`Ü0òv¯Ð¨|æ­=}ãGs-£ï5Z×ï`߯E{Dç&ò(„pl‹¥|7ÝrÿGôëYy©¯ÔñÎ'>üOå¤u!n] ìãxß¾ ü÷R…µq œîAsKZN1…oG]вs`FÚØta{M;¬DÈ.¶4šDÚÃÓªhµ7æ¯"rÎU‹Nú¡_}É^kW'„c•þÿyjžDa2ò·TÆè½øbrMˆÏ”ì¨s„á ¨ìzz¾`÷œvºW\?’Ñ!°Ãgeõ Ÿ€h,àqš2[·ð`°u™Œ5Ù{þK/×£úG”@f“ÙÖ‰ÉNj‹ntÆÐ¶±6E¤D1™>{Kq GÇÐL„ß´7 d‡é‘ GñûÀ Å(†‚& ¸ÃCﵯ§Fª‘Ô.þƒgwîÙÑ/)æ{$:ȦqrŒÌdG^tZž;ÐÞ¥sÚ¿|Qû÷×êîïï{dõÄ]di­í8éxk›9×éuÈz OÉ€k1:ˆéD泩ûõ k 0b@ÛälÆAmp#Ùö˜?ÿ^(z˜ƒŸ>wKÛâ§Gÿ•2¸׫˦8Âzâ¹»(z[c›£á„¯Ñ-ædÈìŠz¥E3½>we×4W±þã[5ÄŠ¶6¦Áè ¾<>ºs÷З{d~ùJÿe~a­ÜýˆÕ:ŒþÛ \ª’£Ë#š.-K]tÙΉÜ>£)¼‰íÜ› ^!›‡2óáìšÞI2M•=  E“ ÿ@ÄÖ©Ÿ½.»ôæ§ì¸5EİØxš·®ÝPÔHÂÁ}3¾þÁ}=vnq’¡6ùB:z7o–çSP߬•"«.§–!æ’ñ]¤‹ˆõ—‰ #ŒÐ¹KÛBÚÁkç9ZY?{õñ¸ù]ð…»Ùä¶N*òµl„Ã*„ßvˆž÷•úÂ׿iÛßWù#AÁÝ$ Ü ymÄÁ9%¢„=Ø~7ÄÃC; 9ÙÑæ5x¼}§ÞVÙ©Þ㻕"]½½ŽË“Ú{Ç qxØð+˜¦³ËIO_8â5b”Ú ×µ€ :IiÃüè—ôìÀÛ>1äµ®@ÃYÀ£Ž›°÷j5"ŸÀm'ÜŒ‚˜ÏU{olì¶æ— =½EGÓR•k•ñ¥D:¹z6q¸àì``Xâc4­Ðµ†s¶»¶»$åi8!ºE-#Ï:3µKϰn’Þ¼Ó+ÌéØÛ³ù¥ú!Á€§úÞ=ôë92¿|¥Ž½åÞ?JƒAF#½¶œÂQt5t˽…”Qú½8ŠB<\¯5*!¢p¹JùYýÔ„ …/¹qÈÊ„R Us{1aUý?ÞÍfu­¿ô¦½NÜl9b-J?òŽ‹ŠƒHw‡ö¦-êtQ„êo›Ü‘Ðñ°QÌë!ʽÅIOß½-dtI T¤;SVm…=Îu•ýì oíZ¯®ÏëWo>°³À]‡#¤jðÔãf7m¶Î [çÙ®o ‹s¼î¤æq×f(º½•á6,=f…tºg&Ƀ5fÆãc;âó±RÒ½ ÇüÉ];j­¤î¸v¼Îiž~Žn·¦[ÌÙh v~"ók Ððð¾«Þõ˜‘“E oÞ€ mï— #WÖhö»Ñ‰ åJtÈÏ.@1;K±‚u¶Ÿ¶é¦åj­P/Ú;¯£]C)xrÔ'=µº¢s«¢ÛÇdÛkwDj°V.¢/tà“GP»N&0 ¯îÑlÃ{;qþ®´¯JñÛïÏ^[Æ”Tt8™£—†ƒs>.Sÿ§îúõ™_¾RÇ>yß'úeÿéÊw˜séüézk)4Ÿg¡SPEÍN bÝ8¶áЃ÷‡–ùÔjl…–ÆâzÕþüö¥˜¬*lÁh™ à 3z^?(Þud÷}ѧŸ{HƒH„Ãþͽ‘ü‘§Ïì†ß¤‚x¬m..‘ #Åâh-v-Ó”_Øg`s5ɇ·LÓtq¹ÑÓgo6ÍqæF¸ç¨¥ÑêlÌ8*SûzíATªŽDÇMñÒ˜ø]Ù½ËS4Hîì˜Asdž æîUQo£küøÀÅ+¶GÜz6óHŒÈ`ã&‚Aîj Zàgkì䓽ç䣥| F×,Q²iüŒÃ¼5£ÌkFx Óó7Nv› £Š9a;šàÙ{ôoËpíî¡_ÏÑoË ¯ôHêÿµªñ¿¡€íÙéÛéŸb›È"ž–Ë’BdÒ ¥9Fü%Šñª˜aౌÜÖTüJÅ‹®ÓÔŒ[ È‚„Y”ÓE¨àížö‡c®7:—ó©íƒ»ãk/ïé‰KzùvÓ ¯ifqÁ¡ì¨\‹úHuM›!ÝX­âèX·àV!Çm? ͦ±-.Jñ`ƒÒ|O¹Œ>ï<ÕXÌ­¡-òGŸ¿ ?øô«ÚË#)ÅXÛbãmø…Pd…5/ÆBj.äŠ&-Û6:ŠõÔXbŠÎ” sVuC í ˆy®$ WkýœøÐ€Þ#–$ íÍÛ´xÚˆÅrê@-¢’’¿ß¾SïhüRêÉÃU3dŠ‹&Â俜Àk|ø¾7õÂõ'4–bß#ó1í1ùÆ#wø‘Æ–Z¥èÔ6GûÞø’ÁÅÀšèWvGl•M]XL«ÃZ[)úÛhÆ¥ÕLx…H›.Ù¦þî‹×u~1X-úÖvEdBZÊï}²×fÍØØQ¯4ƒ ¹i×7¶ in:1Ã]EÍV’¼SîUêfFÜÖŠ¢]X®E_CváÔKw¦¤7®=p6Ö?9ø!wÔ’qÊ­|_®ÿ§~#¯öô•>~ú}þ·’®ØFì{©-\RJ¡_ƒ2G$]60–KË:†"u ‹dz¯øžîê(¸!@åi" öN¼ç¯\\?Á {¢€sÆÉ ãS–ÆvÊäŸSnmxš«KêÉsþè'_¢îñEk Œ?ôki¬Xó€¯hÚ¶ú…/¸ –ƒVFÒƒlÉ%>ž’‚èü†¼×ÿn©TäH',šO( Z=¥71°¢¤Ó‡õ«Óñâé&<ÑãtF?ñꃰ‡¶>õ¾åÃ;3{¼Ÿ l¬,žÑø¸õ2ŒÔ6ì»Uî/ê’³EH3l£>:²Ï#zjS8?´Ú: k=× îž'É …E›¡vĵ0pq„Â=<\¶,uæï†û䀥òót²ÇhÐb-m7";Óúš‡nµºÏ˜“ÍKtB‘ Ú˜~¶jPpX.Ÿã|jõ6ÜSŒ÷ R¼½^dZfS Ïg@Q#"{­fìöóV8gÚ-fonM²9t?Q°&²9Ø‹éª[K»i1æj¼Gœ3ø®‚lm4D&Dp¼YÞ§¥º0šÔJâ{éöÒRÃÙSÔ»B¿ZÆ.½E—ƒ½sÿ÷ÝC¿Þ#óËWúPîÿç¤@X‰¨=šé¡ZaÏëR£.ϘÔy6\bšqíi’ð‹=dšÈ¾h8v[€aÍ ã¤ä¿S‘ï%ž(o•tU¹;7¿¨NÈî¦ø½¯ 1 Çm‹1òñ0¡h-Ç®¦äT¶ÊeâÚޤ¬‡þ ;È{kG³ºØg#ËëŸ6 ¤a£±E‰¶tÎzcsN¿rã!6¯†ºyþœbçÑŒßéRÂÓßM«`\ŠªO‰yùèšõ”b³„Ö¾¼>1øF~×@ØõvëX±Ÿ>·8¨—Bdžæ~S²³£–bçÑ]K æhìˆÄY+4 0?H©Y:ƒÏ~0EÇIÝ¢¥éÞwÿMÝï¬yŵ¶÷Ú/csGL]R'h[lØ!-QÔàgð½F^&•Qˆ< f/ðãèp-îÅ ›G®ÔdHM}Óå×ÕMkK¥ÞØ,5–Žt°‹Ha ÛFR(]ÜëÛûrdû´Êæ"ÌùõÝNžº;¥ˆÎûÀé#‚s¶Ñè\F­t~&1Þ8>ÇgIÛ1s²¥ak%}?§¶ŽŽâæµ+oÞ=ôë=2¿|¥÷<ñ‘¬[ôǶcZ‰P_q«¨×9е”U ã£2’Vñb»|Óç*CŠÊ=à.-¬Þ•X½qØwb-Ã>pê*ÕµI\liyd6²¿ é‹1$]féG?ó5ûyZM„.­\ÌzòLTȫʱÓúˆWBp ÚaÌß 9dŠƒ‰–Ý8yÛD¬äZíßh$X8/™hÕ0*xLäœ#=º«ÝÑŠ Kž×'ß<гwÎ{Ñ![•Ã(M¥m¢^ˆ´7,:Zø7ã¢ÖžÚžHùÕF{’¢ààñ¨ ‚§™ 3v“Š ¥Ës#UE«päÌóBDl©V£ax ÆéÞG3$]×èM,B7 ÆÜ65‘ éx÷Øre‘Óro¥ßõÎCõ}R¥žÅû 1›[œq vEDÂy"¢¦°ÝjZ¾)Ù¼qáÍ;ùÿ8j±çw~ðš»Ï5™wߤGön f„«ÃÙv­:ÙurÛ\â»rp1­+­Œ¤ùÅ~%BñzÇrެ-›¢ÝÁy¬IŠì¨F7X–õaˆh‚•ôæz_cµ9jˆtœ;ZÛ9û\dÿÛoTT0óËWúø/Ósë®ös^Í…žV5c‘Œz„ˆ@‘ÄТˆ`t}\ âUˆBñúηCCÕ†ZûRØ”¢ú™³v¤ªÁ<ŽVYôäA°W¥‚tn }Öó{8¾qãžXQ—â¼ó¢xŽP#ì{6`hæë{G•-DˆÚúÕ\)6¡BØÞ)-nÙk5´+Ma“ܺW£8¼Ø·×Q×S´ƒ³ÉTg ¨£×U|AGÏëç®>ªkãy6å]C`ø†â:"„Í,§£·Í° ¸ïOxÌ–êâñÐýFW_ôjc.6~Q7„ú ÷ËÑ9–Ä"‘ãc«ÏĹj%Mf‡¨s/—v$j!öÞªzoÏLª76JJ¤Ÿ<+FÕ©#2,3'Ï/}ãã‡BLŠ÷Þ±©úm)ÜðoF  /­ö®yà ?Ñ!8QdŸ7讣}Öž{b…ó·Ø¡ÔÔí'`y¨<ð¦ƒêæ´ÎëñÜjuožø¾|ž/´ÈIçzC±Bä÷h‘|,n·:cr°oÈDø/ö\Šþ8FÙ1°Œ{¥'1>–ѨY¯.ccuE‹Èí0‡É`Ëýýmiñ?ß=ô92¿üVu¨OŸòF+CÞÀª¤¢ &vbö¬Rççã 6,I…–Ý:Ö ’zHïÌK›|AYƒHî‘TªEŽnývÓŠÓÉ^?iÑ÷F ˜“çÐ µÿ÷húv,ǵ~úó¯ÅÈÃ~îþþÇß{Aqa¨[ÚaàARA)µ‚b7t¯ÐŠ‹ç‚¶SàÞU?«NáÞ­ö%V˜ ˆLż>ݤM8%z~/’Î?Ç¢Ì~íÝ<:$å2i-;n·¶9ÖÄ v—Ç'üŽ ^¦ò¦Hûgl¹ÍVèžSföúV·*ÔbâÑŒËié¼ð7>“¸˜Q14ÄúÒk6Ž=ŽìgöˆæÄ¦SYŸãšJ-n(­Öh›ƒÉù.—1br¦V"§aw–V5 Œ¸gÎ_×ÓçnJ¶Ñt³±ËY]â÷z1"aÓæþ1ø¨ñè©Ûy×e&‚méÛ®÷9³¸ì<ýê­¼SÊÈ9ÛkÆ´)àìªèÛîû‚9H±¾÷ÖtÑžKf¤äN“×/Z]g¾îž;ûÉevS–’ê>$¦™Ìã/F‡ÓžQ¿£$,Åz$˜Öò阌®G_WÖ'º±œ)¤³Ž<æ¼Sý)múkwýFŽÌ/¿ÇO~ø±›i±ü3*®jNΡ¦¶ÞT@Uãâ7öQIÉÎG¨§JM–ÖºEǰ$óÁØ©½´¶DUê2NÅì—qÍ’ÁRY£e÷ÙnH¥Šb&ì¾;UÞäÕW^S»Èô¼ýÂJßöè]×6Û(¢CNQ¤œ¹`è6©ÞK6íZÜ0Osñ;³ù¹Þn¸H…Â8 ¼yp»¼=[GвøIíøBµÍÉ=×P($ÕE°½özSôƒ/<¤Í¦m°2Ã?jØl5Zûá±Qž0r‹6Zo{ߟfPBáÝ »lÜQ†¼]¯­Ðsó ˜z­öàï-{-FãÜ6Ì€Ó $¸¥ˆ¬#*üøØŽZB´ç?ÉO7Ì™;®–ÈÀx:>ÆÞKî²m ‰óy~M@ŠhÊ3wBäeŸ7íÁñýÓÃoéò¹bQgɽŠ˜5  »›hÇ"à‰bІ?覣Þñ>¦ÑA­… ”hОÓ-Üiâ³LNŒ©#{œÔ×^ÚêÛ>®~{hyÿa½¶ãÍÃ…Ö[Ok:… (úˆ%¡Ð½ßg»öÙeV†£jÁÈĵyz]dW}±õš”YœC»pd…ðÏÖ‡Æ{Ê ¯Ž[{o.¨wØÑ4l–æ˜Ä= S÷_wúÿ—;ýÖþwK‡WîÒ6g óÔmt'Àº †¨F+Û ÃŸÂ)Ç6vrÿP ä~§`*Ï!Cã&ô!¼Ì õ–럦B‘Ý­·‹ÕŸòêºÞŽÜÍ“ì@£~ìS/Ï©—q´ïЕeÇÈä¿û÷éá‘v|‚Ž@ðâðºáy £_¶°<á µk2`]\àžšÚ‹™*ÛqóÞaõ…× €¹¥/b´âEG ™xM×ïLú¡ç/hs¼Ñv½Ö0lUç–l®§¦Ž‘¶íºB,QC¥Å¼ù“F«ßX8æÃ<÷áxm›s¬Äš5Zªf¤Ì(•íÖt¨Ñ¢î 36-JÍ)ÚyoOè°§¦&g‘”*Š|ždaQŒúÞŽÝÖÜ»çÛÆ.:4[ïöòÈÇÕ;¥ßý¶kº°œ=í±J‘$óâ§ÓwK¦à])PÓ龂{a£µ ´«"Y&Cmçik©µäõ‚Ý ;w¯Ÿô÷}Ngûí‰(ñåã ´ßEš:¶Nº÷5˜c¶ì2¹;ÏŽ¸öÕîØõg¯cŽslgá^‚|‚³ ûã»d-ZÍvu{FýÞžÍAPè|§Dâ½FzíÝ—/ÿÅ»‡~£Gæ—ߪcü–oySµþ`’·ØÚÏØEçTn$ö;yïІëm¨†&”ÈÈ›Z›p-Ñ# õJj‰4Yäì£V"m¶[»“äx”,I`±'‹í%œËý'Qƒ}ëõ×NíZⱈNÿîÇ÷uy¿‹©£˜’±E+ÂéºjÂ[ÙH±6¤§äéÕf<º•¬æS†-×:Y Ë™¶\Â=:µ@ÛŠv°©]5Ùá½q¸¯Ÿxí¡y“Ÿ hˆ [€0c}¢ ;l<‘U7/LÖ:¨0 ùÞ¹.~¤€Ú =Ë!Ïï¥7á1ÒC»LÊY¨È[Ûü ˜ØØ‘‹9ÚníÐHÍÆýú›æ-&M5ÝÍ= ÀVÐO9 ÀŒ(,ÚQCtĈ]R«ªïyûë:ÓmÌ1o6IÞ·"”§x¦Ï9>yfÒQ3s´xQ·&ªœò¼Â÷é)¯HB*üÌrÒïzàó:ŸtÚxéø’­±¡]8::’‚\C«nÚœ³ÖŒ‘:?×_›ô›ý¤‹’¨, ÚûíÐr_[-uýH|fD¢üÛæn^.T6›ÿîc/>;Ý=ô=2¿üVÛáÜß!MC-“:8§Üƒ²7˜:ãâgÊ uÙ•i;ÿ»x­"…¾.ªèÔI°çÙÃþH„ó¹ìL«ŽðuÄy×e»ÒPÊüZE2–Íl~áÕè ƒ‚Àå¸Õ}â%+ä²íóÍXý5ïrhFƒèL­ ÙõÔEȽ˜O·ëFÃݯ©"÷Bø›kœWóþŽïf «­Ã†Ç)ª:™)Ÿ};ìuÐpí•öX0¢¯žÕ/\yÄÓ3k^ƒ˜kK8 ‰ÈÓõ'0à0ìç¸ÝxJ¬``N¢æÝ¸t{+Ûâß Å¾òÇI"׌\TRc‡¶ïq³¥Ã /c¦ì= ë¥ê¶GÇÚÎÜY{‹»8 p§T1 Лø€Ñ—ÖMD±sX1£cf÷ZÏ.«¾çñuqo iGr2¿]ÒD8Ÿbê'R€€ä¶Ÿ¥o¿Ýžâdá]cDìPšÁw¹Wíæ”Y¤¹¼w¤ï~ø9]Ø3†[Ãñ_.èh›Zû8µ›R‚2ƒ™s¶ôÚ¹EŽ}£*(~F"Ê]¢DºÍ(ŒŸòܲµ†të ­tŒ§…·ÑEvaHMíü+·“˜ƒ¯K{†ôk¿ÈÿÍÝC¿™#óËoåñ ï?xQ9ÿ„ËÑ“ÚÅ}X”9oUÅÆoÿŽE¸VÀdj$h*ÓÚ|c´0G|ÿ56rX—½¼UJíX§ °¢ Ó8Ù÷èÒ³vÐò+ïÀ«)[Š,çÎë!‰”§§¹Š{­^'´Ÿ}Oaˆ1^} !4<Y†+ššƒRQX¤öbMľÜKß›a]XÝìé÷µ·úPŒ@ˆ‘¡÷>¹Ïpló´#>Û§/éÁÕô³¯]Ò ·Î*w %žï’P•àE±á†M•4 2%îLôDÐ|ŽåÃ5\–¶]î¯ë^Ñý‹C{:òÉæD)bw,7u¡Ï>@ê‹hÉ"X"ÒÒW¤ÑAÈJš’Õ\ìï1¢f³Ã œU~7´ ‡Í@ùúGS]¤a)Þ“§¤ƒÜ°¥ïûZß9Éÿé‹o½±ÖW`ä»ÒWëøá'ï{­ëÿWršŠ”Ý+ˆH% N ·a†$ ÕƒÅ/t^UÉÎ@SD„À‰T“_DyU®Rµ¦<êµTeL#dfd:¢UËe ¯5HD ‘ÊžI7Þ¼ªíØ"‰}"Û„ÿÎ\””ÌóM.ª6I/z7>žÖÚfÝðîíùÏÉ'8†‚í;´2FêkRWxi‰TKÊ-çܯT3Æ£4ÐX: ª”è]â…™a±k’ÃõûûäõûôËפŽà!£xç<¤ A´)™­–ãFܼxkŒ#5ÑÉä-² óÞ]¢Ø®ËQ¼EמKјÖkëàó"UT›B"ôD0ŒÚ>‹“5N¡@Þ9õH×{ƒ† ±ˆaŠßø¼æÃ³lî #¢]qNz¤ä-ð•a´{Z”¾õÁ×ôûßqEíjQG*ÆI/ÝC7Ã~ ±É‚9´i¿Õ ûëúÝ>§ß}ù93‰´&·}$Çýêác*Ú¹·~iHùHœÈ¹Ë,éC±=gˆ;±#¾`@ÑS·û³îN?‡,ѹG?[RW±qf·£3"ùù®­9cS‚qi÷5ì¥î?º{è+qtàûÇÕ§üU;¾óà™?{õÖ+ÿ€RZ&G‚ÛFY{kmA¯‰d-¼c—œ*iƒÙÅO¡Ðh ÃD±Ü·÷ÖÃ:„x(œv9¤Ñ4ßKšÛJ³æHgåÅU@b§sG‹¬ O¿u„§>ñ:{ÿŸ½v¬w=zßn!ûBõ™ ½þ—ÏÝ™'ªßSdPÍIsþ´]JÏaLmËÚ„ìox)Rõî'§ì q›ì:RuòDÚ[¶yGÁ㌨F$g×"­açyŠ0Õ ˜¬y§Ó)ìü«Ã9m§¤Gçï··UKóxÉ6ÁÍZ šk 'rÃàïM–_ø¢:ÛÈÜD&Ú`whWŠÏ%‹\¤¹†P-ʱkÐ>K‘ŠR¶5tDyKq¿œ9“²£±ëè‘¥Ë>SØOê]9[ÍÅ^ #¨QHïñ¾9dŸeO×Zñv ô•{çó…k0ÎíKÏÜw¨Îl´ªno²ª³_Cà ÐͰ¡µÏ€uÔs&9hoÑMzÇ…£» Ç+zÿ…«†íÀ w kïæ{'ª ¯¬ã¾~îÆ“8F±Ù£Ý+R¹î˜]ÞË:»ðìG¿°¨ûµ;€^ødÉHè·ãB‹ïF'ƒ÷)ÒºÔTìßTtìŒâš)ê’Å·ß?h?Yš×ç•!Ñÿ·±NÿÍÝC_‰£?ž¶újŽÿvÿÕõ7çÕ>Û°$XoÓ¼`L‡–n*ÚÓZ­„n©Jºj¶-6 z'L-VúûŸH§ ±«¾¢Ó׌NçÁ¼Ði2B4føj¹Ôf´EåqN jÌž ÇHuÉþ±7ÜÑóWïèéÎéÔáÏ?³ìõ·¼ÿ’þÕ_¸b“Ë `Egþ´à–&ZÌ,»b‹9d„ûS·°çRжMoÕ»In ! Ο&y»=nzµ—Å|C£Ÿ Fb£ÈÎ%Zhx‘ä×w>¯åžý›6cÆgTIK}ôá·ì¾'÷‚ã&Ï—bຒg½ü.kôN.ÞŸû~—bÃéIìü¨yÎy§hvX7^+lû=T¨êK¡€Ë†nGž%x=BÚº“#ŠÙ~b¸H­UgÓ­>Ø«·Ÿ+°w;bSÈW??*#âÑB× LÊsZsAß> ç’Z´Ž-Ië¡'ug›õü­szñæRW¶g$õ Ê£nF…šÁ»îyàS ÿáÚÌÙhÞûÎ=¾³×\ö½6ëM87j– º¿?™Wø±Ï<¯·ÝÿÝkÐ¥õ'ÞsNÿÁÇ®é09yaÙÚu2‘Zx{q2«[žê""Rr;жÆn½þÂæ0­¸aø|&Âîå²uÞÍ4ÒÌ4cAj½'a$k ¤¿¥ðÝ1ƶÑèO“ކ´ƒ§ÑÏ”qü™¯¨ûÕÿì¯ÔoÊ«Iãá¿ L®x¤a‡GƒÌ¬ì8œ®]|É™¤BÔüc.’goC¤^’ÝøØú¥Ìâmy–Á0Ã4=ü'ІI˾˜Z"»Ãþr¡M¢y£îؤ‰>r yHëVá~ìÓwûÔßó(Ø„S‘ÀWÝÝŽ¬‹ú?yS²–Míê-JqcCœ ªyDŽ™P©„Ë(íˆ6‰{rV`ó®¬>â*pµu—˜>zèþ¨©uuþ%/ªÚkÒÏϘ¦ù¾&¯»yŸGC‹%Þ¢óêÑ}ÿKê»{]+!;©$TülƒÃ! iÉ:÷ò÷V|<Ù¹¦ú>êW8‡V'­×öùÛ¦žcgKÀ€@™ŒÖä‚W½KåV«Á¬ÁEe;¾?ž ‹}O}×ûüMð™˜1‰BQˆb!ñk[jªiyñ¾šF¬ ž&pUh"hƒf«1Á$œýÞÞÒm­¿OžÙêɺ± Ÿi)UÇÇ£½ÃhØšÜØ{gÞf0t Ò8³Ý6㻀›”ß¿ ÂÕiþy<-õK·ž´õ­?ó,ª*Ì}#®RLE™á¹y´UM3-Ë©crcÖ‚Ñý{½Ò=-õè×%rܱhÂŸŽ¯*#|åýîFQͯ'oëo™‘¯dôAãÃoÏ?ûž‡þ­ÅrõR[@²Ã65uRÆx$UOå”=ºÀ“FØiôöh7γT‡ª‰èB>ŶlÙPèªö»™%æPò–ͱ©ÞmÆ1vÃXN~oµï½ã€üZ°dÈ0¨°9úæsxå Mʧj¦Çñ7¼ë@‹ÚÒCS‰`¹°Å¢4©¼¥ëEŸ¦úf(k£ƒ p\°Ås“ëñ€l.¢éçÔ~§;„vVY¿ŒÀ<ÿ +¯AÑñ,^«Ñsûì®gô^xD·†•§a6šœÞCaÅï€:îýßIÚm£ÞÕõ0€ ¥ŠÂ‚N1Ês2?ê`6Üp1š3`ï×ÛkýÙ_;æâÓ8š—½ðàNêñ'K…%O¿výbæ=òÝ”;MÛÁ¢cI€VÝÏmyÿÎ6𼸇ÀU¶T•GNÌ 9Z»Øk ÜÅm:å½0WçÈënËÚüt=}Œ¦½ÿŸzãAm¶Ž0Où$Šžh6 t^J=#€(¡b¤Í½Šës(y’¦Þ#vœåXôƒ–êÙ çô…úУÿûÝC_É£û®ýߔΞûm9žzåú ªã_e%sÛX¬À‚D0z®ÖØö¼Nâzæq £nAMöç'Ò!Î웃—¥\CÇÀÀ¼ü†O1Z’.'ºÀ ¾€ÏÀJV‘UU$ô(Æ)éSoÞÖ{¿|bávž¯œ¦É&ñkÇÒ'¯o%˜qͳ‡iµHÑ€@ ~|­`gREZ~•0‘ÜTçµcËñùlžÜÒ…Fι-ZW‹K™×àýd¥eck¥˜ïN[Ï_Ò¥ÕFçúC_ü¹I¤:(Ûu”ШöÍ S¶ƒ1Qd  ËÉ7H^g—¦=ò;Å4OϦècr.-F›‡ž¥ßïÌ»…×-^Óša~«$ð/­hoÂlF«BcJ¼?û9£ØÜŠÓ¬ô¾y'‰º‘'ø³%2æFÆ …òÁ#"¼c4}¸Ç2ŽÎ²lbpöúV‡tƒÊz‰­Ù–Îòëïb1>sç!={û¢{ Ô¨ŒêšUÛOZ¹ëâmÖñx²Ve|¢û-®1 Þ`8úÒQßógßÐ&-T}r èÏCÞÏTeïdÿJÞÎ;¸àÔ¾{Á£´šÐágycËË.™¬<Õ=réU‘ðÜa!µ…CËfD1—‰*^§vîÞ¢rãν¡ÝweN×TYZ†M—VN ¨ˆÄ‘²é2xÛ1úë”P)™W9™ÀZð½ Ã`sa˜ÒDÎ5ÿÎà^:oM^ O‹ÖЉil“^îïÅtš]¿†šN…¨Ø4wñ#þ;]P|fø¬ÎÑÍ×s¦ëÉiî£éc 9xÏÎË4žV“á'#FGà½0¾^ÿiçýjO¥Ì Œ©ËvßÐ ½¶9¯}åaû7#jÇ–öÈ£µWG½sµÁ)óâwÖ¯Þ²Ï`§öáQ€aaŠ9NÕ7¿I›Ç)5rÑ/6X#A”‹èL“¯±Ž5ãï£Î–eÆv=}ßVßýncU|᱇{Æ€&_ᑹ†üvÇëÿ:+­m’Wê^9÷âoª¡Æ=6ÈL‹Öɇ&¤µíBŒ†ý °Mw”l¿*MÃÝÿKÞ!–ÐØõެ-Lª’Õ³–êS7ŸÕíàZÁCÉrö@Û?ñ¹¢.ˆý¾ÛÖûä¹…þØ»Î+—aî~|Uð¤¼9ªBŠ8îÐ,pÏy!eGºÂÿ3M¶²k8+zØ9ŠI­ð‚ì¹}ò&†i„GÈÒcAã9„åaýE qÓZ}‰fœˆNJI¿ù ~ôµGµÝÊù~FRMG„tˆ1ü®ÌHX¹£™©ælÊ)°×6Ûõ& Ò§vì×4ÙØæ]º qìØ‘Uò•Õ¦ÌPm ?Êbµéž²C’Sœ–Z.©µñþií%ÚÅ Æ=Ü>CI(AzÍÄj+'u/R-éÅ1&“R¸D*ýÞ¾9^q@3®]2Hê7^;á¼®ïœO¬Ñ /ŒÛå@?uõ EãvCH<‡B~—÷3Tôî¬%]9öÏ8`6¶v¬³ÑÓ­ÌÝnÁæ——ÍqÒS'†×z"#0éb"9:Ȭ‹2¢è…½§»‰êô/¿òÚ+ÃÝC_é#óËo×ñ‹|ÇK5wÿù®hNôÚ­RC/·÷—Óf Cï4¾£Ì5,»ôÒÛ¿G ÓÑ_§?NZK¹`”¼8jùWßè{mž±çC²°Rí\^€ˆÉë;g´Õýê+±bÆc³ÙØORZÿ×ܧ•Šjd5 ¡¸ébC3iºäz¶áÏÏw:ˆêŸébß±!Hme+ò™±:©-í†óÿÀwIWoïx´)9“o•Ñ`NŽGG±–ZÈŽdîNz±Õ‹ÀuØmf‹jN»çDùbÚˆc'—å§i³eÃ72dž¸'ÝdGd¦Eò×6m7T©ë¨AIb=]Ú¾SH™J]¿ ½kÆÅ)ô­öaë+’0•¹“†ÆDÀÀ 85?÷`4~¾òVc3acFÆNÑç!àM‰Ô˜E")e3|·Ž‹~øõ'µÙÅ­|¤GF—y~«éž£–h=TÛµ®Ü>BãÃk3¥zcœFžç0¨§#ºi—gQH¯ÃÐÎíc]òä'‹x_Î<ö_ß=ô[rðËoçñ3ßùõ_ß÷¯1%kËIòˆ³®¦Òä-» µU±ù¿²e‡ªë†¡Eö˜J¬[D‰–V+ÒÖ Ûò£Z-da. æt<õç$07>^}C/\¹}‚7Ö:³ÐßòKxñm!ø{"º1Ͼ…´^ îé7oJ5ÖL::7 ¡[$M ßËDdá Ú7ÝÉ\~ûlL¤Éóí»cAÍ#t²™[Aàb”LÅm5Dæçö‹Sù‹nmú‹Ï?¬ÏߺÐOÌýiŒ@Ö!­ï<—`[Ö¢=O Ò©WRÂüW¶Ï}~{÷ÐoÅ‘ùå·û¨«ý¿S)—¤JÁóäÅ{Ó=ÿé›0@N™ 3b-©œ²=Öå9ŸÛÙ$ … ÄлƒÞέc„$T4DëÍVt-1éö­ãÁ­Èmƒ±HÁ®éôÙNÔ÷Ó¿üI"J…Œ¿ã}çuñÌŒsTÍD„¥[ºõƒ¦0ˆb`õ×%,ŽØ‚ž|rè ÅùŒÜ€tvØb2ÏkóÙô,ó¹4ã°±ô ^ ñȇ÷ Ö0è[EE#ǨN?ý1ýØ›k[;uÞ9”½Hlè_ëb³"¦ÿ,’·¼v=-Z$“ª   n› 5K/щGÙN-‰´QPÛŒ†¦W—:Eĸ …|/A47p1B1’Éãµe;¨´Â| Åm@A9à²÷€¹åï‹¶·Ñ䜻…Q¥Së°Öã~oߣ£žç=òû=ë?Ь/½s ú©¨Œa™ÓVf@¦q4s¸ÍúÑkïЭMŽÑÕùXÌdt{ºÎÚv÷:¶ò|ë||skŒ] Ú÷TÆHék,«ZÑFÇÙ™Ó£]g™øSjæ.#j¿I Dù¸WÕ9[йÃÀ:I¡ä¤W~ZÿÅÝC¿UG÷Wý;ŸÎ^Òoûqý‰ågî~øžRÇ'Uå…PjS äCÛ©-ʶõ Ú’jEñÎ%-Àb’[Q>D6¾qñ_J¾ñ(óâ_Zf¡nbõr×Üë@±V” 1nDÿŽué§AŸ¾ºÖ3\Šêks!´ëìXfi¡ªŸ|íXñâ•€¾-NÁçâd‹9UuÄ¿·2Øô ð—==ã¾yQ(Úï j”è ‡¶_û;l½¡X¹G§ˆ-ÂìÅÕx¬…Q³´¢8-¤vGžÛ3o+#ø»tfÒÅ4fv9uÜI>)±a¯ š”C*•šÂgì°F£·s1 »¹}ŒR ÈoŠÒQM fê04Øt3{õê}zï£Ie@¨ÿMï» ÿê3wôÚÑä‘…OÜxÿt™À‹3mláÓ"›15eE¨ú9J}ÚF¥¶‚"*cÆ’00N‹ÂsõßY$i·È::ƒ ËÚ}SŽœgбôƒ/?¦w_¸¡” 'úó}"ºÊÕ›‡zç[ú… ±]’.-’þÜË[ÕœŠ>è-³É’¦""‰á?|-^ÈÎvØdî-ßYk"wná;ÅyÛäkðjY`eÄ+£ÓˆâŠ¿ÈBЇí*Žþvº“NÅÛ¹à‹žÍ˜ˆ…Â"©Š+w²ž½¾¯3ýFg{Á¯Ð¢k÷bq’ ]t¯=›AÀÈà‰SàEè –â dèØÓÑptx° Þ-/š'M£Õ‘ì¼ òVªÕ ”yêÎÿt¡H_»Éúàñ¤÷l‹ž¤wŽÒƒƒôB_1,¾¡¯•4˜ºŽëðµE=Ö ÏwÜH±û´ó_ç¨V ˜ŒÀ\ìxœ$E¦ßÝõÑ“§[‰Z:wÊ8)çd¼Ë§oݯŸ?|§ŠŒÆcžO>·ˆ|„Ú# 4IzüüRV=‘ô|)ëù£¤©ªñ³Ïêm妉¢mÑϵ –Թѿýž<‚’!ê›q`ĸ';¨SJÛçnOŸ¼{è·òHÿ¿ýNÿÙÙ_üü8ïPrƒˆ÷ð ÞwÙ š­ˆžårž–Ž ízJb÷úZ£ªoøÕ‘ã¤PAÄ멲:à*Òvgö÷,ò˜¦"ÄŽ7[Ý9~æ¶¾þ¡«:s°TòÇÀVDRÀâ霅ËS„ÞE„/öö£“5Hè,Ú›S\NÅ>w %1†ÍÚ Æ"ÊÔÖ‚D­o>¤½½žºõÍÇY—²úµtæŽÁ—º¤_9ÓÛcH ÀÈ{£E V{ŽigOSãír«Î È´ÝÂ,£Ý9êÌÈœ{üªŠ!ÃØq@*8/»RÈ:SÎÖŽ|´•~úÍõúæÂÌÂ[‹¦Ôó\hBæ9 †)Öjµyr±¯zê‚á§Ðݰã…ꛣQµ0ˆª-øŒbDŒñ²×e?ÁðÃ>M%͞‡t°ßs4"“%Bwì–?)éÛôU}éú6öÎíÿ‘;7ïü¢¤¥4³súök,‹%{b£¤j&t‘ŠS«³éçÜ{ÍÄCð»}ãÏ æÁÒI©E3 Þ»$m·E‹¾xÆcU÷WK2n#¯Ý€œçó£J©åÑ5ýÄg_Õ7=}Y1‰ã_øÈ9ýÁï»N-€’=;“HÅÅá5)Ö Å0Ñætî@Á:½õˆ¨Eu_¥%6~¯…t 8è0ÐQ· :¡ã‹…hiŒŸGõ¨©–Q {œwÞ.5­¬ÏéõçVúš‡îèÝ÷K"Yu62 »½ÍyŒw1ðì¸ÉàªÛ®[®ZÉ6;6ØR.…<¿_gVËL;ùÿ8àq»ïxÐ×V­ŒìÓS¡%irA¦¤ù»x°Zäd¿[1»ËÁØ2²ºEgÝjÔUxŽÜ8M‘z)܆¿(†tŸOkYÎÆs¦×¢ÿžÆÃ‡§ö:{¬8–Ä(KÜ¿ptQ?íam7“­­äõ¹œŠFÒH)GbDû4:^ÿ™®ê‰3YtK‘6|c]uóh°t®{z8,íZ¹÷((ÌÙ@~h¿#q@”ê=R¦S2w¤sƒÞO¦«±8zÈF„”¦ÿnf&oüGôUÝù?þÏðÆ~Çÿ÷ÑÃoýåûo=0ÃG³ O!w =‘R¹©(ôئoÏG)N.ˆÚkí5$Bq(ºÍxÅtEU±kÊÿ–}3qÑ#«± dc2¬ €9–nˆˆ<Åßlݾ£ƒ uv5Þ–Ë¥ýäxäìRWÖE{ãŽHCÕ9F†H=ŽVtNŠØ G}û²¾œ$ˆ@K·PIs”9EÓpHsoOƒÞ~¶á€Ø¼¯Ž^»cŽÝY´¯…¢yA\ŠTq¤à!z ößkærÞþK+o¹òZØ ‘¨ÉTr—ÿw%ýÛJÒWãHýùªß©ã?_þôç¥ò)kðP°#uð‚ÔU‘²bÑõÆB•R}bC12Nƒ²\ÁMÉþ-Bw.2(UÊy®‘$p…VX‚Íìï«w¯Ô°DÃVÇ›—h žÏÊ©EM‘Ú÷yœúƒý VËÝn¢êƦèÛÿï—uc¨ªy¡ áœ{`К@-¢”ÍS Ô MŸ¡ÖÈÒ ¡!Š¡à¼2¼< £·‘KŒ: aÿN$•vÉ Ieù¢ƒú:¤ÆvàyÁ¸¨ý³*Fã­‘Ez+ðýÔ*Þ&þÄùC}ý#·u&oðr -Njôðë•ñHmnçTÇXÄ÷´ÛÌê°>Vòó“.,µöwMߺÍz $oív¦¨O”ñ,tý©½):÷àØ(áèr\Fh;õÍÑ› \þ¹ºz¢xUÃÀxläŽsÍgWXIRÇß7 Qo‚q|Q@ž-ü¯Ü|XŸ½}IrÅLÛøCc‡Í ¢fôgLûãsg%6úýE§§÷FK…G*ô7†^oÙÓB¥¥¨00±–×õqŽºJhÐí€Çê”Aú‹kò»††è=9)4ÖL"ý;ÊÝrÛ-ï“ô}•Fwñý3JÒïÈã=të×ëõßRËÔµb|c—-Z" úýs 3È–gœåoWå7惧­Z{\ö×!T,Î)Ôyšl"…ÕzâUyM+œ/w8Ù>eQÉhÈ÷j÷”3Þ›hCÞMYØsµè¯ÝÖ»»ß6 ¨'Æq4ž¬…Š.,õ¯ sÿy+i¢3Ùa¿ azUr¾»u×]ç€iùZ(:Ò<9·OJÞIB4çÚçv.Ü]¡GŠP¶,LZ&m„&Äw‚ê« í4ÙZk=/ÝTßì¸yÜé³×t\–º´8Öra.mˆ»·ØcWi…ÞÄžŸ´Ó ì¤Ø  ˆç…bÒYfô|LÒï–º@ä`ÿ1¬–F1ÿ;¥¤/,(”“&¥áÍn…k”!/t±ª‰.GGõD&HÛ¶to­D0flíZ|7>w3¼\cðÊÀe—AÛõóÇ÷éÇ®>­7Ög¤ê›=›ùbOÕ¿Ó̰{v•Ë(Z–dð¹^zÇ™¤\'%ŒVMzqÓëÚ˜ƒr°nñpPÃØU"LHD­æî®b`)ìõõ'sn"‘i3°`¢XÓq޽Ì?ÿ½"ý/EÒWëHüÙªßÉã¿Ùÿ…w˜Ö_ñÚ‹„Ÿ½yÁbÓïíx­H¸´BZ©K*saµØMYS¼%מ„ò¡ ÓHgÏèì¹ûií½’‰ݹs¨b†©Ñ~£í]ª>%­¼›çÇôÝ|B‹ÅÂŽqí@½í¯øá[ú•ë¶HXx; Ÿ)¢|ãBè3Þ;ï «L³”kð¦Ã¢‡f/ˆ¶\×<7Ο©¾C:—Wûf¬@·G "G<{Èäx]Ò@îò1AÕÂkGï×îr²×F¦6bWº.é™ 7ôÁË·ÕMkÛl×ߘ؀­eu±´{¢bwÄý1²û²iu}ç©£ÚHÇV'¸\³¾µ,µì{ÆJI\Ã6õ¨¿^| ’~ð¾=»þp|ìK{þž½ë¯Ã ü“ÙO£€±"ÎÜ]àQÌ€þ †´b[:°0{æ±µ ÞuìJ”?¶;øû[Û3úøáSº:œu‡#‰4yÄ[û%u?‹Æ"M±P}¹ôÈ÷ ¾²©zs]Uú•}.Ì"W¢6ë”4¤Žè€ù‹ó¼V€Ú4lj6]æ2ƒsàËbmFâÆÓ#(‚^;Þ#é–¾Š#½óÏUýNÿþÞÏ~~*ã;p¯*Tf@h þY ›ŠèÔ*¾HðôÉu¾O®ÐÖ¤OIaáÄ ´ªxu½õ1H4B?sFI€Ê’ oÞ¼í×£;HÄè\ßÒ ]îôèÛߦ<ý°]ZŸº9éÿà-MAŒ¤R…t~Cs#êhl­ögÉÕj¤Š);dùð=Ao‚÷G§‹$:B¼w_5²‰Òøà@BI!¶Üñè¶v,8×"ϼþ·Üp(æ&_+fwur&ù¦Ï@|H}/½ë¾C½ëÌU9Û+çÎ6ÁRb' O뜂óà5¶¹:%<êf0vSï;}8õ±]–·ÛÝ':½ s=ÕªIúK—ö 1?m‹ úÅ2lú¬So°Ùš6l7JJܯý­zŠ-_X´í< ŒÕ’–fxa,çœj@à»v'Ý5ëÕí}J‘ šæn甚‹:©…p°¡ÛZ^$éñ}él?¿îÑ0êæÑF×6UcÕ½†©\’:ZöóR§’šq°(‹HßœŠ©¥UEͦê©|¢c”»ÓS·<7¤„t-æE÷×HúßôUÝþŸøg4&ýŽ>þØå›?8oþf)õ)á@á‰ù†é"®´È¦[êÔÒZö”‰b¹ýó)²ñ‹Eî¼s>ÿ¶?¡hPämÄh…ðxök¥¸°˜RŽ©1ÀlU7®]×¥ûï×™U#¦c<¸ßi,Òϼåá0ÁèåPà‹Et6î2F&\O3l)Jë…§€ÆÆÑCþ×ÎÍ´üÒ‚Qh8èäLѰ7ãÄâ‚•Wè·:×JÞ'ÅE:½Òîk×jï½&ïåKÖõÅ…쬊E‰WÆóúÜíKº³I:(‡Z¦Ñ è† Açü#¬- ­«ýŽ1  Œ ß'êk§NïUßRÌ®P'ˆ˜Š¤á²÷*¾Õ'½±ê„Ë^ ®ãäËxhò×FW¤ŒÚ(àbìñêQsv™ß±a—½^1ë~$9Š^¤¬vÆ)MwtF¿tå²~áÆcº=!õ*ܘ2Ü+âjìb"*2FФ£"½µ.zýhÔÕ£A‡CU¡þ†¡f@Û“ôÊ÷䪛|—oÁiÖ§²¡‹ÑRŽe”¼å=Öý’ÏOäœcZ k¨Ü™£±K1÷ý_T©ÿ?öÞL’ìºò?÷EdfU5ÍhH¡™½(­ü—¬å•a¼¼kf{™™™™™™× »k¶%3³hf¤áéîªJˆxïßßûûâ:«Ûli Þ|oº’"#éÝwï¹çœ? ÚôîžvïÿôLÿêÊwüá“Óë²µf™è‡ôDÞ%¥8/¢g)’&dBØÑjøAH­´hB&}ˆCwH6¤Nψœü¶Ë—/jÑw™ùªë'§Þ Ä ÓÈuJ Ö ~жýÞxßÏøËþ!ÚŒU¿ð½óÆ¢ q–pÏØ×!›NÖR/ìðØÁÁè ãl©©ÜZ» . i½ñÆw"Û+Aã¯QzÀWÝ|78YƒI£L-êÄÅfó§\ªhÛ¨ãÇ)%O÷~L¬É«¤ç™XÕÂ-Ð3«qîf{ÁÁ5}àmOéÞ+#ê°s w¿LBNO•UV‚̓c.M1®ô|3”«"‹ftl€Z I¾2Ø)ÿûMuM5Ž» ö:ºÜý”‘¸§;É£ã8áë±TÝí<{ñ3˜4ä" ௲հÝMÜÚUÎÓðò …²0¼ˆ¢7 OìŽôÝ×îÒǧ«S‹xYN*ÎmîÀ µæ*ñµz¦Y3Ʀ†_¹—.3?h}sßÛ´0»ä¿EIÞ %[¸FÒô=OØGÊ:×(Sk÷8ädßœ5笃+ÇŸ^3Ü'¸XýrÝ™}(Àù»{Ø‹)a=Æß?üú¯6Û×ÈÂ-Ê8&é¢"‘𮙆š±‹v¯S—–RÒAej ì#6Ó©¤5Ê‚t¥¦Y³ ¶{_T‹üåKÔÅ›6â«×5òEkhI5ß,Ĩ€MÒnqI÷¿öÕãMï<Ñÿ÷Ùm‚höNðó¿Û>|QÃ>×ÌÝŠüqÊ$?³Ì[>kúŸKG%̪2 ØF|Îþ ‘²v÷ÂS>W¬’eȲNÒšDüð(Iy09›þS*@CCôô—1²®y ˆ^Eëòj«÷½}£÷¹ãT=]K-Ȇš §ÀB>¯› t^Ñ&»ÝéõãJ—JÈÞ´‘Å:hE ï‘6c‚X ;ÁV¦ÿÞo\N‡AçÚ\20ŠLîó¿»ðA1·_®Àéh9Æ|-—DÉD‚³±ûÚ< y`qv<ïÏ;Ž/èû®Ý©‡w·Iàa]X¼ÆbÉ_¥ËXߟï9øKˆÞÎ[+›‡©lº·¡ÈR9gº¦ÀöJ§®:%ÉÑ÷âXÚÆw–ºnù9‡´û:w[ža«ƒy°áÛ/‹Yd5(}fÒ•ô§ôöÂgPùªÒÁwüà×¾£ÖáŽNE -+Í ð8îÈ fF¹¤6^Èh-ô²Œ”,=€d°¾ñ* ÚDô%J”®jò_ô½.^8’’Œ{m¦k×£'¾yÛ®„'pdfHKøÆæèN}Ü«Þ_(‰2ÆqÔŸø†Çô÷¿ãIœËàTLàµä»t†ýJuêáœ1åä§ ãQ^Ê 3\þÎ ¢ I«p.¤C ÂS\ÞcÐvK˜ò€ì³¼E’Š'»iM7Íq7T–Ö2>Ê4îü½ð1L;Yx/½ª™ªìLKfß5½øÊ ÷¹ýšîY‡ïˆ|á‹dïJÒÔÿgË…ǦûNšŽÂ‰Ð0ðåÿL²ÔH $8ïµ nÑS­éË+’ö¹4ãžé¥ËŽŠ›IUø`-¸›ˆ¹úÁ}q’jølwÞÊ ^Gû¼š"Ê¢X ûãÖZꇯߡïâH'»~Êýó›%ÑÜýó‹ V½êÑ‹ÚS!À«ë£·óg Žác_}:¼¡¡*JNRÈñøùD$†#’ÚÉ‹²c !ÂÈ‘Áó,VtQ¢±…š¯¿N•¢Ú-¿÷¶—êÃ%mõö!ßÙôLᡯ~}Qù¢±ÖE³è¼!‰VÃy©µÐ”²(1UÄܰ$“™[X¢#Õ3kDìo=e¯UØshoêûâwÇgD-sÚ¥åj©£Ã¨µqq¨ª{),­Œfgìj“ ¶•çÝ«7þ¼÷ñ/Sæ‡\Ûìô‹¾ð ½íúˆ+  8¢x‡U òâàz‡›¹–’Ñ6YÞúâ_Áص“–w½rúOß<å¤\¢KK-i_e2ÔrÄ¿P.”×6üíþ y·XÉ$Ëlì8Û̓žê~®m€æìþümê®ZxæZ=x %•?RGÝ9Gý ]ÞèÅGOéŽÕÚ¯_„¨ ÌëxŸßÑÛº^¯Ýö*ã(©GþC&¼G,)ÿZ‡ëCদïWÕwM2&^BÛß Zx¤`c‰ëòðP¶óZm°ïÁP¼Ãk&Î ´ËãwQ>n¦·_¿¨~êH^[ù扑mŠÙûâë%Å–U ÈpY¤ox»¥(›ÎÙAÖ¸ÊøKÙ *³]Vž}4‚„ ;Ù,ÁCÙ4á4§<Ùí,ž˜š:‚´»€ E¦öI_®ß8Ï@ÿä›ÿÚn»ýíRè›v}¦¸,‰ÞxËMÊ.‡Œ"¿áqÔ²¢•V`‡ÃÓ—e“±7S‡×ô„SÞ‡‡+-"c`X¯7:>=‘É(-òäànlY"Ûtp÷½zݽH°Ô_÷Љîÿ’Ǽ” LÁ[ŽÍÔ.R3A¯ë³Cra5]Nî€Yve0vû晌a“›™l™0ˆT;:tZ3óœ À/ó#‚LG€‹Jùî¯g¤mʱýº7¥ ïêÂi ™­‘ùLAf»a•õ`ò’Ëk½øÊ©î:X F.ÞQM÷ÕN½Œ¯›::䦶]/ß!:¶šD -É·Ï%ت¦/Y5^Ƭœ›ç‘õ¬x 4oýÑ’‡ŒÝõ^[.{³No/ém'WôökÚ åV r&ß…“ßHÙ&7`¤ï_ó™GnÍž÷X@DEkÙ<ÎèY¥÷h¿m–Þ[ŽqqÌe1É YLî„3EÙ4^sH­ä.ÊåÁß—ô9z{áçz&Žtø ß:Öúá5úàKìNŠ' ˆ+ÒZË¿,ÈÊå'’‘tMV¤¼ëÐðSŸQ´–"ÐÐ¥UÈLj¸”2_< ã"W­:]ouꥎ„dµœK”ÔÌ g8ým¦»_ò^ºï^è O`1^ûßq]ù›»éK²y÷Þ‰Ò€žá|˜Óò ‰9˜ï¾£wLbœÔѪZÇ©]6J0ìÂÀ'ü¼¬æ^®OóüS"© uÖây’ù9ð>s¬|gÀ: -úc(‹YhÏÕ éævaµúƒw_¼ô³$ë=<ú;ú¢gâ(ÛöúÚé‡%]žL“lpÃÜýÎÙ©)Üˬ!¢˜»v˜’ê\ð]·¯k(•&ÁÃ66!å>$2¿~ÔÜRLFAÊ}ýt£+—/D)Cjîb¸ô6ãívÁÃAÔ¹;ŒMž_mº]Eï|Ûë[–+ý¼÷½ÇwËt‹|Îèÿ=p¨7½ëV¹´¿ ³èZ•èjI’Ò^fb‘¶Á¢CiŒzp ø•ÿP±¤Eæ¼,§VËF9¢Ž¦ÅbÍV6ùmwgƒxìÌ}šXÒêÝ>ùù°0>›Ø’̉b>Ù²Pâ‰BÉ!« 3r ³øöD)ýýZoG½å´Ó[¯Þ)™tÇêÔç/8<Ö+l¦ õfYèŠU ¦ydÔ£MYní#XÛb‘èzmŽk°™áü+<`÷:N~Vœl(—câ}žá€è%¤o¶­èááv=¼>Ò»NŽôÄp4¿h)ú÷6õ>蜾›džðƒ²¿F&#î8›EFÜ‘ÐóI÷“ÂÖ…€'Œî:†ÂÒûK©Ö¹´”s¶sv´ùµ/–¸qÎßï,¤Èz ÆFRjTDü8þ˜±V«õ“]ÇzŒ®ûÕX'C}ÆÍÿ1¾ðôþ‹O|w·¿jl­ #Ñ Hµ]D½©®¬Õ¡øñvãr5>,ìx\ê±Ovv‚fÂÈŠ~|µ”Í:¿=1¾Y.Y¿ÔI^ƒ·ùá±=d¦8!/ƒé{+züñǵ]èž+GjÉ·ü¾{Vú?t¢í0"Zoƒ'’êΦ–®Ÿ¥®dü*=Å~ߢRîcZø…}îdu[ºH³©ÔÍ{ï HéÇÉ.΢ë¨Ì·E¾«Â’¦’@ЄG`òãeÉ{}¹œpó…æÜYXZ±›¤41û]Ÿ }X=ÑÇ]S s$s¼ô)A…_´ÀLéªÈŽ5£LóáZõ@Á’ È}a{[¢E7ùè#Yâ—‘21vjÞ U¯z×öŠ~ðøv}ë#·éº¢·ߦGw—tZ|”ƒ!D&ýæâ˜û"›xãg >›[ „>-q›J¿ðÖúü‘ÍN‚d ˜³,f^e–´‡ÀŠ Åû#‹úÀásÏHðjÁéwÁwÜ"[­de漨¿¤®üSuEO‡i÷‚šS|:wBö½æÒQ´O¦P1®÷48 ZGÎ_³aù¡å.²NùñÚTú8l¢ÓÏuŒ6]j¾ bU¡D!eÁŸ¨ â)x÷¥£¢ª¥Vóæû†ºÕ[Ê ýaÖUwƒ+ëÒR›ý8\±_øõ'[éD‡n {u³ÐÕa¥§v7þ^›Z‹b’ãHßJ=”=©Û'¾EÎË6€æ9ØÓ„ÇH*ÙR4ŽÀ:»x“q¹×¼hˆPïä<ÄùÜ›¸Q³ôN…·±§ñãtZðb °6ÔxSCï™Â”-ømý\Á³1™$”µ¿ýÈìå’6z: ¤Lžéão]xó—Ž»Ýj«¡U§Ž¨Ùx<ÚdçΦ°ªp{—ìZ#`‘Omî,Õ¬Õšzœ/ðÈD£u<”2óEšTƒ§R[Õr¹Ð¥ —¢L0¨Å‚rõÚ‰àÐ$ˆŠZx—Iö5äߦ¡}Ї}°>è·å ¢ßÿ¦«ú÷ß-/ªQÃÅÇcáå:ª´Ýœm5´ *.Ê‚0˜KN0ááŸ$]«ý¸ÀÌfb]ˆ Òµ¥q§.í"+@*Á‚l‚¸Þ‰~ßþ>Nº_#†Q¦Üzœ5”¼+M…Ò†(éÅý'1 |nÏd1ýùÇúÍ·½K}gj™—½ÄÓÐæ]2GüŽï©)Àñ&à74ÜþñZZ³ñÁ)1:“¶Û¦më44Ó0š+Ü®‡¢uíuºë´m+ ½·X'¬è0u.ÙS%Ä/ÁhAuì )äÂ…`çs£<„$Ÿ_’¢™É„­ƒÑÎ æÇc "UÝxʳ½æ\"“À9lÞ ~2þZ϶ÙfEg:º.Úɽ¿Ù¿ŸNl,™´8²qÉ™Öþà¶ÜzÏóÐE¹³ey…¤oÑÓhØ ¿¸é™>Þñ‹d_ðÿ¾þ‡Æíø²*Òo¬9±¬u{QÇ(dS A–4 ‹ß»] ê¾W?ªJ|±èÑO?óìµà—r× _v_ãØËEçÝYÙR|¬MÇÇk/gmÈRÛÞ•ƒGtf&»l‘˺À~ Þïù—f6´uú˜Ï§¾óÉâS™°0ûäΆʔe°Ó6‰÷8Rû‰Ü²¹““ô:²Œ›üÀ @’’IÖ3bÇØCÖ‹²¬uDà;Cc¡Ëµê®ëÂ륺j0zg­DÜ%=*¿ù3wyÝ;‡‘~}åñá«~÷=«ïß^]V~=e‹,{O÷Yq% /¡î1Ü×MúÄß'K‹K5q_¬m:W´¤”;~rsA­Hh ˜K~,ªy±ã;ÅNpwñC|GØÍÃßñÏÍì¬@`«ˆ$:i®ÊÈ|Øl°IÛ g}¿_sv¿¼cŽqœŸ¿w °ng€úœ¡iÙ1Þ3YïÓÙç»õÈa<Ï’ôgõ4¥ž¬õLŸ÷þuÛn6ÑwÝÕ.0 Ç+¨=æ¾s«ÓÅjåGI-¾{˜P¡Î›åJT•Ë'Y‚zÞ ¦z5ƒŽ,IyqôyºÛ¸W‹e«‹ðÎfWʱRâ¿lEÞµAß÷½ß­ïxûb¬Šô_·.uÊâ˜ïK±³K‹ÏuËvÆ6wîÄãÆéò˜zÞÝŸ¢Uc¢e„l|^pY ‚¯@ûñ‚ÅœIé$-ÜÛÜÉ3ïþLyzV:¶Iv¼FùM‹©û ì¡s€|˜^‹ÿƒ¤Á#J ½tã—ký®»S×5,Ÿ'òê1…ò1H­>…„ˆZ6%àsò?¯×~^Üê4õÊAPQ~)ŠE¡Œ õË d–,:©<(ËHâ3¢ÃÈCW“7MÔ‘W† «Ì߇è†Â+œwÜÙˆ°s`œÏ!Ä=óð#i,‹[–;µXùl¹%Ø;—ÙÔ ·ÌÜå¯q <½g¶œï ™°?vsÂûy«LY•\®û’Ïú˜ƒ?cêé6 <Óç·üâx{].~­™íjúªZq‘»Tïé0ë÷ÝœïPø;¸,Ëy´\c—÷1oX[8Nø®·9ž¤ (sÛÛÍn—Ú€‹ŸÏ¥‹‡¾˜HÌXDªJ\7úl˜É„cs)’·|ß÷èM?ü( u½ð é¯¿ö.‚ ãYÙ“Öni£«6‘!ØðS,žþe†æÀéÙ:u‡õg¹)W€Ÿ@’!vhµrE0i}Nß‚œùt+µ©ëÕp¶·ÒÎy+~LÊQÍÉ; ”‡ß‡s ò׋ºSýÞç?áÁƒ½ö#µi3ÒÀÝ4Öc§ØÙŠ.-~½ñÀÌLïs£@t¾‘q0š°4ÐÉ -»2gÝC ÆF)¹=œ³£$»éœ)H‹*Álj®´Ó4Q·ÇÅn§ˆÁFÂG¦Ä¢ï3Žñ¼ˆ‡æqös‰Yp”÷ðV‹ÿb5•®ØH ;Êdž} DÊqΘÈ1ö7"f¶CýÆ¿ÿ×ë©§Ûì¾ç•¿[oþí³bþÏí?ð±«'­ÃëfîheÌ`aSæ’•¬í”×÷ÔF×B®¡eÐ!ÿÁ?² ÆÑ|hª’J^$&ÂJúáív;ÇKÀ¸~Ù/ ¯5ù­C´F¿ C©Ô=sýÉÇõÎu§—Ý}Ù¡÷»m©M•ÞôðfÞùYÁü&“¶HÔߣ«dë_¥Ì“ÚRæßº¢fYÏØáùB‘Uv)y(Ž ©(ñÈbiG“{£³fá“Ý÷ÞÀ•Ž?¶Ç¹:LuòZá‹`êDM b‰'¦+£…Í&Ï Ãsß9EoýXc£N•žv~øî{ÐUg§ÅcÅâ é‹N•‰rQ< “¬$ÿ8£/¿©Q#G®½_xéÀëø©]µ”èÄZ,i-öǰ›TW5Æl-“Š܃H§™Ïð¥ýq¿ûyºód¿ÚFŸGK1JXdó÷Ç'(eñR!Z*–Œ¨Iú–ë+ù ã-×á,ø¾Ðõ2— 0{.°óóPénÞª»ßžš2h6H:²H|•„1¤hHvE6ë\#¾³JÐÉ{`4VxÊD>“u3À~œç’cyPéËP@èè#ˆD×^`0`& éAu}â²BN ݰΆ¬wüÿd¼¶üß|ì…¿tcêé< <Ûf·½ãåföv3ÍF91ù™/‚EšêÈÞ¥j—šmF-ºeœt58 ý¦)¬?;í:ƒ—–ñ $ƒŠu>š™Š_–L×OÜtj2U«ÅÒ½EØÁZ™¸+%¤Ñ/B 6»(‘4S¹ú°þÓÿûVmǦ‹‹¢úÏÓE ¯µ¹œA/;µßpÜK13¡! Ï‚WŸ„ìj”VP3&ÌÝ«êÏ>sÙ# º²P§|õçy ïr§Kì@ãu×íšz:JÉSfZp¹ŽN±I»a–ù&(~ô…Çô¡‹Óésë<Æéï6’=Ydñ·‰¬ÏggÆ6"÷ŸƒW’q7, ü8ëjªËÕ ø&Ù¼àr®ª^£§ÕßíÄ…ç‘kú9 0Ñ™Ô/ÏÊ@4›Œè²-0JÂ2–=ÙÂL̓ì'cja&倖€m€{³´¨@~qpž³­Éd¿t\–NµÑEg̹'0¡%¬qÎ`KxŠxÆjH ±Î”)0Ξ oíKÿ‰/ý<µSOçYøãÙ6?»¾ïfuÁ~~Wj·x6[7Éš¨õ³T)ño3âuEEU£ÏýQÐ0RÎ.Z|aZÂê⇒ž?óÀb®k7Œ±ùuNZ¼téHÖMCƒ«XáßPŽ,ÁšZ›»Àë§ôù_ùMzðÉc½äR¯t#ˆt#ª¥½óA·Ó7ÚswË¥?8;ÉÒ©v+?®›S«ng¦Ž}ög(ɯzoår|Ñ*J5ùîGâ [,üMMÊ,gî<—rØ=fÅÈBZHžÔa£ f™ÕKûëúèƒG=·J‘3ÎJ÷Lâ+ΆÁ¶A #“« „?f¬£«6é©Öø†Ãâ sº¼o´NG-þétÿAC…þ˜uE[/ïS†ÈŽÔ¿ƒÇÜ]?gd¶ý’…R+çï·û¢ŸGæõ´–;îÂÏdäõŠaY6$5d|K`We.ãÝLKmŒ€=Ž£O‚‰E÷!eg2Jˆùx…Ϧç;L—%%n[÷‹å¯nžhôtŸ…?žóÓ¯Þ÷p+Ýÿ×uݱ Å€iQ.eݦѥzºµævQ‹ËÔB›@Ö u¬ÑëŽM©Pé2EÖQJ”4F2‚[n?ÔÕëW@èÀ[äÊ¥‹¾Ð¸¹Ï8[ŽÊ"`UÚ]Y¬ùaH‹ºÖ›¿ù;õ­ïxT¯¹÷Hí¾çI8ö«Ðç ÀhgmÁÒO©<.jJ™DëéH¡_förb¿Óê+WÁoX5¥ÙÖ³£¶=!»ñ…Æ7å± %/r|$ ªÕÍ‰Úæ˜çË™Žš¿ŽÚ¸Uà$šícÉH¨¡û¹üö;W_š åƒÖx)ñ}+Sý;e`!Êb Q\f°¡ÖkÖyÖüðŽî¥SµætQÊò…ܳɦLn¢U°€ME\GÇV¼8Ö&ý¥Î£Ccº™¸¿ÿ‹ Ëß1aÉH ÐþÌÈ›@"ƒht2øÌâ61Sö™¿;y´ ’Â~WNutœc¡Q]Ú|hØ!„Þ¾&4É¢C"~ü”qßDzºÏîâ¯ÿ#,˜ÏÊù¿v/xø£ýÆ6¿FjÝXG²ŽÄ½¨*|ùZ€×4iñãFϪé,ˆÛ”Fƒy>wÞuÙI[õë)[¨U4»8²‚ínp¶{—½ÕKñ@²Ý%Ñ?êäªü>¬ãu)˜¶&µQO>þ¸~à‰~õÏy±g_ýp,4tÔ´QÂpŠ"tŽÄ¼±;€&; 3‹’Þ8Pž€®Ö¹¿4€u&ú®>—6|‡ç¥‚:³—1·¢E“Úÿè"zü0){ä燤èÁ‚ËœƒýŽœXAK‰çEÛèÓ¯<¢÷ﯧ]¨Í…ƒî±Ûä°áÅÀ¬ÔÌæ AFæ!Pi.»ƒTŽÿk®èÛ®k.›Z¨‡§„e*ôËöÅ»eꪔ|ŠÀ¼ç! ŠRŒøŠ/à¥Å}hMI—¶ªHªpŸTz##5µi±EâŸÏ1 ¹ÿ>ÆJ²zÈà4¼´©ø^úù£L¼bdκxÿr7¡_A©DÓB•.ëA4nKæTRö?W±ö'5Ûÿ ™Åw–Ïòù™¾ÿõeù›ÕÚObÁmQr¨>’U»Àè©™%¬N úÛýñ­Bê¢$¡`b>ã ]ù‘[À.þÁôÃÄs²Þj·Ä@"åÊ•K0ÞE½ƒ âblm”:þ8]µixìý«/þ}Úûèß{5ïôhëTì6S÷Kçua-%'^Íò!A£µ3ƒ®ŒºCÝ7:WàP€·˜ùã±5®;õñC45x'~œ™…«QÍ µ줽ԖG~NÖGK1Òò–’‹5'¢U+e_î,½úà) »Y’š((€d%V=בŸ.?Æ­¹@Šýqkú¾ÓN&ØÒÎ;àuär öÄ”e<ˆ×Æl£k0ÑIçY5ým×- pRêâýðì€MŽªJ_cñYˆl`»öÖéŽÍÃì ™Ð¿qv0Ñó7£L׬äìÀ'ßkŽÅ€¯¡ô~ñ<òïV"f nˆFp67‰knaP·›h 7J‹XíNßýýr¡ì?>pÿÅ?~cê™4 <Ûç/ûÈ—ÿ£Åâà/˜õ•¶ÛÙ€É «³4…°.Ç0_°K—~èj(ð² âËqxhXÎj½d ,üûdÙÑŸœj³‹ºµªmž—/]ÔbÙá=¢–YêÉH«ŽdGÓñ§Oêó¾ìkõqwëc^¼ ”ÚXî_… “ÜCz]±³E ‘ÖÝ™@ûiñ˜–øøP_Ù­Uܪµzk%Ö}d.MÕ1·°Î˜ŠaDY%X@½VïÙ`,^ë:µuõ9·=,ÿ¼?ìæÖ…Ô#Ke:Å&DcË-ÔZÌy³‚Iñ·Ÿ®dxÐÓJ—lt25H~xrð~Àb§¤žÔª:vÚ·­’ñúsT!r¶?í­,Ä´Q#¯žåÛ5íê}rܼ[» ˆ½‡›ÕŒðù8ÆÄŒ¯?Æë™y.>rvšÄÁ>Õ::ª8þLNµâÓÏ53Í¥7÷÷^üä­Ú©gÒ´}MÓsiüƒÍ7ü£a»ý4Ï¢®ƒR sÊ=Ã8F™f&â‹„­ûŒÌujµIY9UüX"ON‡@[+·=:¶ĹοÈM‹…V«…bD™§ºvÖz³™M‚”l@ðèçŠ;Ï]#—îÖ× ·ë?½k*õª’¦î‘&èÎHbjËxŸGyh‹vïG©(Zƒ»(³ü½ ¿Žœ‘͇oxæ”>–-±8åÛsëg^ P’¥Ôá Y·¸½Ùx¿ÃQèî‡ÃŸEÆjj± )¡à\=d°9ñ9¶Q]‹M‰•xl“ÆoR€íþKq‘ÐÓjúļ3$=Üσ÷ûŒ´8Ò18Ašéí¢¥DU/z«ïž˜žîŸüÀǰ@A¡ÔÁÔ÷‹ø^‰’ &LY:]/¬by<µð¤1²]TvQXHV\ÞsŸ¬#ÊÀÙÕ2kre;Ü3Ö·ÙˆŒ±ÿÝáøÖ-c›Ê¬é¥•—KzHÏÀÑwè¹4>Wúÿ¶o?Ø»G+‹âO¡+ ›F/Vu´ÃªþgÂ80ò‰l+Û Rúq@uÌì¢|–OM?^Tx¹ÑÛNÖ§¾8¬–~¹àáÑ¥íb}mâ¶@[«Õf¯öbê’·D‰pía½º¿¦¼}¥¿óÈe]+G3 9³Š‡ä²ÕýܽS™}fn‹öÌQ–@õ&@ö!•mÎ`1-aM-³œùñCv võV |c»ÎN‹ý¦(¾èå®Hx”L¸-£¿íIþ$ª©ùc%uQIT×:_„qQ0Zº îÞXg‰Ûƒ*¯¸À[««ãÜmEÛ±ÌÐKe›q–¨GfžÌ$,©A–5*˜ûdÏ,ÚØƒ1 ’À_(ùd¯pijX‰¶÷æÁZ~^ìÚóâ Ö•ç3ƒÇÄÂÎóûñÛ‰ï`¼_ÏoxÙ©A:춃Jº>sÖãubÈ9ÀäÖàÔšŽï¾¬;‘6½#x<Gwáþj§çÔüŸ»{þÛG¯ýÐVëß\žÀ³\æòËȹËC}_F*xGÖuïØu[Ò~ÂJ@F§™d´Ìdvƒ_¿XôSƨ_ôêX·®à¢5ÙD–Ãùš—·.ÀH;o^†p™›F±³$5ò–Ä=|—~óíɨ_Ç¢NF‚[›ú^ 2îm·›žŸEiïÚèâ %Ó°Àmc´2zû¦BWL¥Õr»Nʨ®™ç‡YóLb1ÌØ ã ó#©ù“"p<6ÞOºãIµê"Ö‘A¥,µ6Õ…‰p¬Q%J^â²jóïAå¼ý²—Ï•Ïö¡vÒ|âÁ²g œ¬Y ·Ñf ÷‚N,?ïl|Q&b1ÇÊ‚“­eîIVø ?•¢A-ŽŸI‹–HŒ¤DÀŒØÖk±\ú¿F [¬ø®a[ËÎåd6)Ù';c®ÛWæúÚÿÎ\ž— /“Ó  Ü©6ékÓÖ&=“gáçâü´í+?¦ëú/i­¹ì8Œ>WQ¦^rä¶-úÄH-ØßtAÕab˜³Ø›J0PGUvbm& v¶€yžÅýØCtì4¼#(Hª·{gÖµkDZëµÈ–L}_táèÀKUc«î¾o@ðaâ² ù6ªD†„&×.®éÏÝó ^ÜïòAiƒ…=|Ï«¬+îæàè³,r0¦/SƒÄY‰öð]O™A>õ××QÅՈŹd>€ŸKö1‡8ÖZ:véæŽŸìC’ÁÓ:dP^wÙ©^VNÔ48PÌû§:C‹ué9£T’ÅçPÔ !åTk”Ûü³d1‚/bjzË.^#2¨ýëü5Ô 6Áõþ¼M¨0GJ tÍÍ$ºÝv¿Q§&\ÀgÊj›yþ³»ë‘¡;Ì/·„Wøwi¯”UÇ1dÕÙ´é ‹à^é=áy|SÒ-|féKjÂÙò˜&ƒ,Á“ýÑ÷Yô–H•0Íeú-’þ½ž£è9>>cûÊ_¼\-ÿ¯µÀXÊ¥‚Ôp”¥ÞT3©a=ÚŒÇ)»â—•ÏÖtñ ©~¬3ä1K÷çßÝ8èÚõYC\-º|ñÐu´”²+vÆ-c9¬çtAR·ìç¢vú·¿]o¼ãOÓ§2Ò#Û5V¸“T¾[‰61âÀÑÕ”€oOç—³HŸë RcÎm½!s²Ý;dæœü¸óR@Rç”Ú´™zpP\ŽYRöNoúø‹Ò€ O™fj™ÿ£Š_ ªÌd¨d¬q ÌåæÁ¢‰ãJCk3‡„džîÙwtža°cΪÄ0ÀY´•|'̃ñž'45d]'vôf9·Þæ®)˜3Û_¥ÏÁyC@›k"BÚãsÄøÊ"À¥ß€7» ²3Òè°è³îÒóß¼6º³âXœgòÙÏŠr›45°è½Y`±P·:H$JýQ3ý]3éÙ0 <—çgl^õú~uðu½«®FY&ƒœ’ZÒV(³ c²šV™in6·¹Ö:ºîXGŸ,lD—æÕß%¦*q=ä$ÔYUT«Ü·ÖÆNU%Œ±.]8Òj"‚]^ÛE½c.ól`Ýutö;A1Ù®6ý¢ò€þÐ]ëÒj8C,ÐIˆ‘EÆÙöÞË¿%kcñÈÁaÚù•~äq Í,‹Ë³Äü¸¤+ð*öIk,µó,•Œ¿ÙÄ~phdŒ£¦`y]’0<šq*ªLlQά‘õÅ.žQ¥äñM«w”›ê¨3ŠVôàºÀwW K^%æ5e`×Ïi·h¦¥6K€Ìà´Å& Ê6o‚L–ƒg ÛA§Xƒ2m¾‚³Ai(Ê\õÆ$ûµ0€ªYÒ_«g­i± WÒÜ:$XH᳂¼Í,,K¨à ÒM~òû#gX>li¡na¦B¹KúªúSªÒ³dÒ¬}>?{ûÊWw]÷•¥3•¤Ü @\Û8ó(|N\‹Úèˆ 9”Æ]0Íţϟݤ('AÄU1Úo+؈”:rXO-Àð«×®;;}>?:<Ô¥ “´ìç¡M”z4Ÿ3dK|*RÖu¯]ן¼üƒzåê©àŒ,caQ·Ón»Ñ ´Ž`µûâXÏÕw«n ”àÁ@w\$p”.H€“ðÄMo|CTÑ7±»>õ)v“°Û+%)íOF0·ÁÃÿþøç¨ÃT+?q¥,’Ò©sŽ?’5Ô– –II¹¤ï̤Ù5÷cðù »†œ»´ÞùgEé.ûÊÓÌP'þÞ‚W€•x9o±R B#£%³.>êþ"ØA«5«ÿ2¢)"ñƒÒïËg?Ì:hV9&Œà6Ù€IÖ&ÙôÒ*j¹àˆHB>%köëÈ(3ñÚTkn?ŽŽ¶ž`yS"#˺ÝàÕîו6þëwü2ýŽSϦYæ çóW¼á¾X®þe(òË¢Ù¢¼CÑ5ã˜FÔÂk`½Æ¸\ëlüdè±ã m.J2¥ÏËE¯Ë—.x—V.È mÚáZá*‘‰xmæ·3LË6è×uoÑo¹üNu(™FÐñ¿]N$À€ŽRD ¶óvØÛkäSi§úBBù#ôª¶´ŸªnOà7d•]¹ÌÄB¡ t.hïŒEd§3Þ…I ¹Œ\ž1ùq~îÁõ)£ Œ«µÀqªhðÛzŒ•ñ5šôRirPÕèŸk‡­.b ¥¨‹Ìt …ßë5-è¥S·\MÏíÀr”Ž"K£b0n±P'¥¹¥–rÏ*”€ã>f5âÜ •½E(gQ䧤”8„ϳ8LµÀLŠ_&X‘9xæ[K—-§C–hegF0ŒìñîøZº}ìyXô)xR$¨ñ<`gD¶ùß÷¨û”×ÿ_µSϦYò…ó)ýE½â£»nño|Sàf›:¼ªƒ#¢&ZyS‹“0¤¢Ûiª6%;OCÈ"9¯øIrª#³É{v´Cƒ™Ø''kÇÎ;®]ºxA}×Q¿Ÿý¸÷vá4qQ ÊGj¡ø~åIýÙÛH²:9ëë æcþ¢NŽÒ¯¢Kkœm?û©,¢õ¶—TçÝa1Ê2¹ïŸ2TÔÍ'5û‘Å J³šìz2OR ¶ŽAê”_÷‹ŽžÔÊ]ù¬-7Má @*’8?¦VññÏ+ZZ¼š+€‘õeÌ&™,{"b|GüX ],ÐÁ¶NUrysÚªYÐYØür^(‘QîLf^tÛµ&C 3ƒÌ™ïèvèÙ7ƒ‡!“N­yаõišÁÊR`q9ÚéQ~ÕaÊbiÜŒMžW|¿ÜÓ£®£,›$_’HbÆE8^Sû·¯úèƒßp×ë4ܘz¶ÍröÊóù/ßðª_t·µ±:£YQãµnnmSfÁpl£Í-³c³œwÔ\µ7œ×á“Zw‡Ø]z E­®T»%xq¤¹yÔ¨õv««×U‰ ÉëÂÑ¡V«>ºiª±Œ}û,kð$°’=¨}ÖÅwèSn{‚5ÞÖ€Ô‰mÞ¤.¥ÿ˜@ùâÕ¢Kj $²>êϳ^é#IŸ4j…(azÃc$Z‹›B¶þÀÏ…ÅPY–åâ› všJ"*¦<ºªÑ?£ôþ[U µÜ†`!zz"¤ˆ¢`‰@bŽŸÍee‘ù¿m¬b€<:xæÀ®ÝɾuPˆ®2‚CàP©ÅuV»ÅËf˜KX”‰¶^®ÂˆŒ÷)uT¥ÀŽdvSƒ)‚Ö*‹2ãF[õì’ˆè(¯wßn¶Y‡GV-HÜÙ`Åù0‘³É™Ç@‹À·X,²uj/ÿò3?öÂ'|ˆ4Þ˜z6ÎróÎç›Þðòß¼Xþïï¨UD Ï,â!ý. |#-è–uY¯-­tüIJ0®¡«„tŠÏé˜<Þ/%°DºuLÃPuõÚ±ÿK’Üj¹Ôå‹”OõõØmûÁ>×’ï{‰WÏùsºGõgîz@/í×ñ#ÖÌE"šaoê·-èè¡uT L£Õ2“šM*±ÅE¥B;ÊFd,0›m9 %j®UC² Í M¢XÐ#A²ÊÆ ˆÏæ›ù±^z¡èy¶ð›6èJ9 çÀ%á+ƒ­-úU3~¦Ž•ãPåãi¦*n˜•¢ßqÚ–°E.Ÿ€ÀnŸ÷#°ŒÌ° 5;ù\ÓOfc3&R·(𠌂)ÚxsðñIi‘µ¥²d»`Ï'7Ä;lþÜ€æd(dã9؉s¬¬•dG{¶£Ê•3ùÌ“¡Å˜l~2Í{YÕ?ù¬û>ERÕ³xt¯ûã\çãæã¡÷~ñ—½Ï;9ÝÕáõj­°xNsòÃJïJ‡"<>`~KÌbÊSÚ÷€Î%ªt $M.´…ÂfNYH¬ç†$µ|ײòÚ9í¹U-<ŠWÓ×sö‘vÊ2^9Hv°X62꾃kº·õÍ'½ü¡Á³(BxêÔüx ¹nL”(³$>µiÿ;2´Âk/=ª³Sü-‹„¹€mÛMÜ [û»¼€SŠ@’ DðôÉ—Ñ]e#a>ÖMªÆ*h[¡«VèÁ&cïéªççß^c äEå¤^f¸0’q™>ïñ•Þ©£=øneïoÔ J´1×!1ÏSOü¿Ž÷¢ÕLïP" Ó˜®wXÄríZ»ð˜ TEN&©@Xå¸yNŸ'6´ ²^›< ˜à6ùqfhz1²ëåNyˆëƒ•_ÇÄÌþÁƒwñ³^'µSÏæYþ˜¤óyëù‹_ÿŠ¿´\~®µ²ÍŠ»àqf¹ŒA»)r!IŠ$Z|YŒ‹J ©ŸÚÇaQÛëÿ§,Æc’À£—E'‚ÒØ¤§ŽOt|²Öè÷1ŸœöÅ G“çz1ΗÌ$Z“µÞYòZXÕá6T}˜=¬?÷¼·êƒ»cpµèÃoÖ)ëÃÐE» ;lli£¥u $£qØj4SóTs?ýv–àϵúE´e–x}ñ/YVƒ¨xWÄ÷é®rjx™«i¿ö­h÷Î|H1‚+ÂU,P¦HR4òù·eYø¤Ú õ°ËS†¶8LØÂ<ljé‚‚(I7›IžÑõ€Íe\œ~_…Ö'ŠÅ*©¥|¿ë„_‰6J^H™xôˆVÔøLOZ­¹,t¹tÔPHžKB!ÌX2—#ä±VAxm`R™]Yâ>>€ñ ´r»,ƒë÷”m[,≠·øù¼Cºf&,f;Y†Rù S(‘QpÝèíéQ¢ë}江ùäÁ÷ÌÍI¹¤‡Ñ¤Ï–ôGôö‚ÿrMçãÇ?þÆøæ‹ËËý7lwëj’Q…UÞ¨?ˆEÕÅÒ#Á}PmO6U…Zì,ôeÈá`ѲH ƒøhmuªj¨”Nçdz&·È]­V::XªáB‘ÚZ«›Xíηú1Ðþòþv"%Üýù8Åýkî¯èŸ]½[ïÚ… .€ãn.e6´Œ’“Ö*ðôØ®cÑ ñ¬Ð¦ŠÃ^ˆÓ™i­[hFI±p@<Ôß¹óûÕ“…Ù bwœwœŽs€"˲hÑVð:ü}å:C¡S¨1²‘Àc7ÇêƒÏ›Rié;½uÝë÷¿ýy2Í>+­…¿|›m`‘,a§®£Åˆ_Ûß³Fð8¤‹l‘ ï̷‘d9˜äÅ÷¦~þ˜Zý(·eÙ+Å ;›3p|!þ8ç€üJK„A0ÅÜMÈwHRt„õÜwÒ`«ã‰™~¤ÿ¥çà(‹åB?þy>÷᫯ÿÖÝË?xµ<ú@MiVãe4QËn Û¹|!1§[±ÏM@ŸÒñ×$!c ͤF†A'eÈBªf<1ä×ë®]§c&ÿÀ\éÔÝ\Ù·ªÂ™ŽhÀsÊ@Sïç×ÐX(ãôï¯ê÷\ø}ê•GÕ! 8¦ÊäŽG&¥à‹ ztø4v’S=KäšèHÂ,*ý”*(«qŒ´¸™· £4ûšåcêUÂ÷óÝ•x¯S·TsIr´Õ`õD¶I{6ÄRùc3’dy7Nö:gg®…zx˜J:l"h1gÀ ¡uÕïò8öx”ûè¶¢üÓfd%”‡¶Ï¨Ò Ìa/çvá³Ò"ƒ´‹3‹,Ú`Á4¶ë®^?u5ÞêêÛÎÇ…®\¾àvºf×ÿ‹™ÿ[›¿¶Iñ·ÿ·“kE<#²äÂ(]õ©ýê÷^z‹î,µ „sŸKRH‹ :›ÉpªÓnÖ?b*%N¹`žÉäÎ¥Ž÷?ª:4d›åXU+LñÑýdê8½ÎJpÌj«¿ßØgy²5Q ­Óq‹J¼OE [d4]4|ÏæcÅ®`VÆìóT:HÎ^”q–=_Øt L™J^`Yö½9?crȳó9‘¥°`Aìó4f}«$À¼yðË1OäZzI¯ ðž‘»Ò,u‚W&Æ÷.+ÈÖþÕêŽK¯m—/¼ëÆÔs}–ŸúAÎççÚ«¾úRÿâ÷îûƒï †ßÐÌÈHuöÄû0YÎDø;1½å3þI-Ê \üë2 ¥”ñ$69—âè<æúÉ©®Ÿ õ¥"å°ääÃÛ®\rK]Ý i~Å(w§ÒMnK#SÓóÇ'õ—ß­ßñ¼GtW·â\šiãý¥#“è!aÌnˆmTr©ˆÀFÉ âë8æ´ƒ~ÃÅë³#L°Âç÷z‰òÔÌNç 0¼Ñe*×,ukQŒ÷¼Äw„Ÿ7ùÜIå³ê±{Y·!Ëq¶uÕŠ”Ëš,4l“øå¬ZKÀÍ™×y[pR°%¸‘%$+b΋d~X!ó:ÐÅJXLËäþ Î1|ÀEG™_ì\ ‘@N9Ìg¬~^d céÊï,EŸ¸[kí:ŸçSöÂ/h:?}ãï.¾îŸoNO>Á ,ìtSe’ ‹»Ë¸}Œû®—¢Ö QÎ^ì`fÆBåÙƒ¬ùã› óŒgæ€bþ#Rܯ…•¢ûÇl",©…+ô˜~lÚîFœ®55E§—ZÆrTëå·¼d–q1yæÒdz[=Ò¿¿v§’3`Å8~,`5HˆMÂïÃJd&»´$ñ8/ÇL­¬Ôûw›©L$º¿xÇér۪닚™j¼/…·µ`øg,ƒÏ5=_ÿ`Už J®2ïzëÊ”Õ56HyDGBiEo–úC¿À_j¸òúR©‡2Ÿ4k”Ñv›}Ó[âSÀb÷±Wî¢3*û±ÀÁÈ%F|Ô%åcûy‘íí7n솺™ôÄ$çãOþ œCî˜ ð_”ç"ëÆI˜qì*¡L0ìf5aØúÞ‘Ö•ÇÊꯑôåÊã|LïÝùü雟»}Õ'¯–‡ŸQ¬œ¶ÚÔ퉶}%„Yx-I†j$FM¾Ð­¼£ÚÛÄa Ê $Ÿô•èÝöJd¨&72HŒÙóè{]¹xQGGsvQ³ªofÖŸivBnE²|ÞM/nWõ»/½U¿ç¶‡ôâ²9»;]è­pÜÉ ‘¹Ì(y$6óʧÌü1m»™v¤u€­'5êp<Ñ‘¶jÊï[ßC&3ˆ¬!ƒRâ6$¬`¨·dò„<mÁ6›Š%½wî¯:e?n}áž5ÁÌn.X:JYQ‚ Otêù¥„¯G¨°€bý«4²–Á?‘Ñ2–ùEÎõ(Ôg^Nú’œ%ôQ:"«bd\&Ý?—ÅÇæ½Áõ’ï·—aëØK±}òUŸe…¾u¥îç/6ë/¿1u>ä´—ý·SŸþñWßõAÖŸ|énØÜ›ÛwaŠûß‚TV#OiÍÑ…ÝSÕ([Q «Ô¬—Tø;2‰n“ ŒÒìð¬¯"âyçã•ÊVZ.hqe¹’M§ëÖ›-¿ë„ {4JyY `滯لÉp±I”KúÏÇwéF>À눲ÃßÙ+£ôSVº0ªj¥'8xàȺc¹…»Û_~ùº>úÂc²†}±Ô¡1ðþañ†Â•‰‹ãó5©¿2<Ç‘JI¥ÀXðÈ>âØCq¿T5é_>q‡¾|s;^ª2oq☵âÅ-Á ‰ëBL˽CJHÃT¿­‡›‘…ÁðÏšmûdNnË£3=°„.´ÓFëh©ö‰ƒ"Çæ½™27»ÍlkæYëgFéü¹ñè05Ï>Zèa1,Z–K)dÿ“´eÀ_–?MÒ‰n:·Ýû¿šÎÇÏÌøŸ/øÆÅ­¿t;l^Ûj³Öš—h•«€¦™R:„j¢ÕüÇbVÈD´|G¥þÐrdÇ;·9vd9R yº±ª-Â¥£ü€»/Á®97äð`©–4¤ŠJö‚wÝ-wGdGOöBÝ:½NQƒñHJ];ß~º¸¨7ïn×çmïÖØðp>.'#SrDY,ý>Þ0PBôR—g3«ÏCÊ#?p÷czÉâtnë ÉY™¥KðÄ”Á·ºXÄ>OŸ†ì¾ëjÀ¦‚ª¯ Ì(pð‘@æ¯å³ßr§®Û¬Žž)ˆÖZ ·§{>‘ýäɼӨú9VµßH%ËQÖÆ cjƒ¿wÕ¸Hd'ƒŒYv·™m’›9)Ù܉N'y¶T!€Îø¸óÛ\e%†`–ï0G"?Þ̸ªÔÑË—5—ïdkkí÷HúÛú1ÆyùÏçLôŸéñÏ®|×ﹺ=þ“m,:‚M°Û’”€w‚Jï"îoùæÀ2 ~}d F±híå¬yÉ,oÇOvNÞôH ` UƒÁîmÁm”Ð+ɲ‹çVsÆ8¤Ö,G$þ)åizãŽ{´âïO¼Ï›úx_Þ±;Ð|ô…Ñšìevï³ Yð9z5,~çÔñˆêÙEE¤’.6¿­8ß¡iXŸª™ùe¹,æïkE6=$íË,”Ø)1ÊPÑjÁ§ÏÞd‚œ?ÂŒùþÁñqý4î›FÝ_?þnp}(Ëy0ö@“…K‡Í?ìî\ývI?îºþyùš¦óñîûêWÿ³ÍîôãU[ï ¦Á oJ#Ú9Ù‘Ïå£ÊËèèÖI¨G#H`p“©Ï;þxr+­:OÁÏÁkóRÔñ# Åâž² 0oå­µ9˜~t¸r-a)j³FT±Î³¥'ž¼ä8lu§ÀØÁÔE—î±b%À~~èME`HæšP`_:=¢}ë顾üú›ï` òE°O-¥»MðJ4gktZÕo¼ò˜^»zÒ3kþ~#z¤;*(þüìhÃåѼž^`KÓû"J–®Æ4ƒ¯#¸2ñÞó~—æ¯úŠëGúGOÜ!u‹Xt“6ߣìègøÙ[†L<"ÌþÞä¿þÈù¤!ÌÂöÊb oPTYìs‰®@ÞÎÙK:ž¢¥÷l) 'Á× Œ ˆÏ íÚÓñÀmÒmVÊÝrõ’þ‹~‚ã<€|Þ óñî½|Ã/éÚößì†íÓÂPTUçô£Ò•Œ†`æŽc&VøB„ iîˆdñ¯þ#£dä·)€5Ì Y«¾`àŸžÛP°l€ÀMÃXÕª·ýŽûq\¤1œúö°¹¤üµë'R±¬D·ÏÙMmsF‚î–Âí¯ÈT9Ͼ$ñÊ(9H-—¥~h8Ò—]¿ ·ê’A« i5lƒ9]Té—¾Àü‰{ÕÝ%,«€ Ž,ö²bne:x~Ä5«¤xûØý¢MUf ¬(¯Á+©ñº²Ÿd‘IʪþÜCw뻆 ìæQ1#£µ•Ž5:Ÿ\µ×ds¦ÐÀÚ)CéRʬµ©]¸[ÜSéf¼àÝËT•va¿ ß"µ 3ù#°éÙ Yˆ_CÔ°,Wþ]†;ÒüµÕT‚¼õ0³›]÷U¶ì£¤·é'3ÎAôólí=1þó>|õ­_¸Û¾¶5–}¾ä™ïuð|»eUtHY,´¾pÅo2Ô\CÁ¶Ž‘øeM:62ø²â׃$64Œé*´šªwoy@5glÑ 5‚Åbáå­’:®èúZow:9=%°¨Ö¸ ¥)AdJxÉ’8¥EÜI(Bð,Xå§k·e¡둾ét¥7m®è¤x €gàõû:ìè~ÒßyáCê4z÷SiE¥§­ñÖK`ž¤ô±·=“•hû­R‹ 2'0A}3QŠ,ˆ^L;+ú´^¶/cî˜ÍØç$XdÏ‘ßd{]_>2»s»á©ä¢†}”?Ç21¾QøõÀ—»\JGšÛ§Â!„’!íóAOI é¸~Ù?+F2†¢Ì•Á­Äùæá@yiü5÷_úË’Fý¤Æù°_ÝšÎÇ{nü†/ýšß2®×vhãÅØæÑ([ùbSÌ…î ²ê ¿å%ª*©:Piö²¤@!=ä0Bj‚òG”jZE̱©³n¶®-6¥jk5™å.+vÚ×ö‹Î[W7¦(5éÚõc7¶:Xh¥‹ìß3¾BéÆ ‡5ð{2.Yd)¼@ÛØÚB×ÚRK}Ïp¨o9^é©Xc>üh§Ï½íaÉR†äÍMÖ”ŸBh—±' ^ ì&ID–¤Xìe¹Ú^ïï{±¹xœ‚ìCã¡~ÿ#÷&‰¤^6ðRÀ TVŽ”6NK .Ì‘²ø $ÛaÜ4¨¨Í;{ψÊb¥Q%“#¥d7ÌñèÌòà…œI¶»¥´–ºÅðò(½ûèç.-Zj!8Ò–[vC]`¶¾¥6;íÂdÅŸ&éûôSçÃ^ø_×:ïÙñÏ/=påÚðÀÿÞíÖ÷•ìì1Ði­Œ $Xç´‹6 D#R ÃÛà µÜN ƒÆñ8hŒÛ-K–1ª‹ÖØ0pÕ¨ê@½gã,¾BGO¿è=Ð)0Ÿ ‡:>Ùh»ÛXßgݨ̱ˆç gö̪†xdb[Ê@r‰Ë¼‘2¿Ïƒ-t­,µ(nkÇ’"p´­·ÐÀ²$ÁH­cœ÷ôúê0â‘î×W6ÝIêÞÿ‹"í"0ùbGéq~þÏõ‹úgWï’d` (ñÂ/ÊÝOΛ(¥xæ7:~3u`ÉlŸ|çß…>¾W»±ºÌ:Òç…RØ^9÷¾œÍQÎÁÚ†bÞÕ¶¹ 8ke9ÀÀÔ¤Ž.šaðkóBK;ê I¾pBiSÖ¥,—› ‰ž™–F˱Fkœ‡–‹ WDu¶ZÔ¾NqöˆïÄ®>-bÖx]R™»–nb°1Úàohl5dÙMKumâýK¹‚‹Ò>ï=úÈUªñΕ* †>8M¨î¹\•ê”}„",pµÔâuQ–âµ~Ác½Zqn sXÐ.ð]¸pƒ©DYŠFÌšTƒ¿âí®€ÓxÒ{_k?Fe‡<¾S‰Èºd v2¿ÍŽc!o¡’,ÇS»Z¿ŽƒŠ"C¢­ N'@ó´c˜³•>u™‰à!Àü‹úÏ’ôÖ÷þüµÎÇOϰ÷ú¼s äé4þÓ=ß¹xèñÝÞ ë_Qkí†qüFÀ/†¥³xã^¶±×£Åàö®+þXžh‘õ¶Íˆƒg5êÿdD o Xîä4-¹±·üµ‹°›EE6a; ~ô¬øˆ-Ó1óyPf'Bö=¥¾eö"k%Ö¿ê%¶Ú8q2S®)^’ÎÉk-䦯yÄ×Ü1R¿©€aUÊVFv@ 2ܵhN 8 að(l„'‘ËÚë7¿ýn©›RÌžéwJúç:?í£lËž>ó|~Ì#?o÷Ùã«>f迬_ôï2˜É5qõòh-ƒÈ³ oÞ~g÷˜-.5nC`qkÛÑ’iÜ<’ÖÕ²–bÿà¡ÞÿÂþù©óùÓ?íýÿOÓùxzŽï}ìÉWÿ³Óõé¯k]J°g-fVù=+ûnü^@‡d<¿*?6-8Ñ’ ñ/±åéýrÆÔ¼ fyH“ô²$aélnÃíb¡ƒû’2%x0ÆN}6Áj |!‹ìº¸#d‘ Õ  Î²B™M*}™ËpS=J±_¡v,ÕȪÈ XËP#Þ]Äqéf æ{ïí¯•®,¬¶äÙx:^óꢹ é÷>ñ2=ºë|±D6ÄKOÝÒGVÆÍe¥.ÔƒÛâ`ºO˜pyf¼Šžý8O~|ºÓ°Ä‡!‚GÉ8HVžJX©«!É.y&“‰œû:ZóûÜ–ähèc‹laeM_Y/þVIߢóñ3:úã§éxÑç«I¯ùä?3~ùŸºý`ùïO·›Ÿ[¬+ì±3û;—ÈF¸,)@ñà¶!­A—ö³.'7I@‚§À‥«4XŠZ6{TN²ùyûC®K΃"³Q žLžO>ò–Ksê,°žÄX¬µRóÙÀ+X²³ÊcWR“‚â<§Ì zv2çiM-‹ÆiðšÉÞÂ_ÀÄCRC\±hÔ(k&ë¬!O¤F0µ¦®>ùzG=Уë& §$Í=ƒ%T‹½¼Œía;•§Jï× ¸¶‚ƒEòÀ²šƒ>/w؆ºï8/þR?Ðö©q@+Ê[ͳy&×GÖÌñVã:H¨ çÀ’슡nÇêÞnãö÷Hú­nu>Þ-<žã|ü“Kßxÿf3þíív{ïÌ÷(2ÈwììÓ(×bÔ<,Úu›j…ñnR8Èm9©Q£Ì•˜íûзä˜J¾1ÄM¤=hbÑ!–Ë\7”vÄ¿¾À“:Ep°ÄÇ‹(¦’¤]|@mð;$±£)T²?© 1` êLªÔ½,ÉÀT—KÉG ©¬Â»…üIËÇ«52Ÿ<¬ÅÏ%LH‘¤éä{²ZKúKº[þÝ­œ{@ÞÜôÌçãï?ñÕn½Ùü–q/t±07°a`׬b³/·«î¶(ÍÀð®Á<·†š®ZvLtñJ‡»|µ:µät¢*•°¬Q•pð3ÕºçŽ/¸Ð"Ë8ÛAå÷)¸òÑÌÒ&þâX)Àjókæä)-„Í’Úq­êdKGY ü'vô­“¬òx˜‘95Î[–Aö¦ðb4r:õQrƒè.rÕæø¬Šþã—ôy§Ï“Me›(ý Ò˜”q¥X¼;)¬{EÈ% †Í-µ~L„É0*ù˜ m1Øæ»S$\ÎYeÃ(΋ëý¹ê8/Î <#[ [!SÅ1R}èwUÚueÕÌþÍÁÁ…?"é­:ïöQVï’Îç3kþ¶íkþÀ¶\¸÷puø…’ÏFR;¯@'½Ÿ(-Á›§V+>«f%ÞRÌ9C­NHì¬LÌöŽXc¬Õg£L4~l÷EwgÀNÖ•¹¬6Vv­yÄsXâŽØtŒà„¨À¤n2ÛïòâñÑÙZŒçŸþf—ÍÝÈ(ûÛ—e`².LQkà83¶Ñò"ÙÍ™ ]YÀ-])šè/sÄÍǧ¸fÊZfÎÛðÌèCVkÝ®­l ,Âø ëuËðTÖ>q½¹þÖSïþy>íÿõ\Îý™<þÆÑ7¼¢û²wÜZ+,΀٭q9Ô]ë0{˜«ª ”‹Ëä4ø¿ÕôGÎò§ ýóšžã\ ±ÿê_ÖiüÛõö}[«†Œ†€m¿2-.’†ÀRêØÔ9¨RŰa•*;ìÔz뇱IÂÄ4knÅBi!KΘUvº[,~0šÉ±)”T\ìà= "6E©Nd-sݱÇxª¦GG…ÏF›z¢JÜÑ´å/j²=U4³ôÌ,Àú: ÎÃBy õ?ëwó§ÕýúûÃþºÓ懠ØåŒhöïÎ~Z  é`ËöÞ„n.¾¶Á{AV›EnW>îÛEŠ.,Ü܇kMÀD"4¶±v²Öyä}ˆáƒÅpÁÝìç[0–‰Í¶¾ÖÞ´Ù㶆•ŠF®„- $­1”®ùHö¡O©ÝóªôŒ“"€¤:mö#a“€!TÀª8A€0®ò·ñÙÿtû¶QÕ6²š/”hl¿üÇV›s¸(  “lù²ôÁ·J˱º›•#À°Í^ºÞädS˵ž<·™*6GÞ?…8:3’ G&ÈS ¾{Ù-ß<’É‹@ù• Lný|ûÙßÿ¤KLJOY×ý]±©Õ¦(šå{%¡Ò†›™¢‚•-k;p%PIˆ›Œ óR–alØ®°#K×Û5T bC ԇɇº b‚ 4–€D L ‚BÔµ¢àqA rË`WŸÏD¨åŠ$Ù$‰, °Í~B¸Žä@¨ˆBuO«ºöLû1›‘0 Œk¢a÷®#©‘íJññ‡Â§=â:ªš#מûÑ+¶`º„M¹}Ô†æ™#Õ°®= 8•M»sÍk÷û>NHVÈ„'Ûg,¬‡-“½ŸŠÿ‚õWî®øàéL^dÐÝ~É\>L¾ò̃޽fý’ãKÇ/—8ܬB±éJÖ–­"09âm=F*Cß!ÖÁP‹—`Xvˆµn³ƒZ+CåîSAY0ëÎàv•4àî² m6÷&J¨Ÿ !5g©`²Ûº(ÔOu­(h3ž–WbÑhÛg®#ª× kÑ·ÓÉís›Æ­) ú3–²€ ×±BÜ2å{äãÖ3|ú£o?Ö˜Áf±®ÒšÃÊœxd“!¿4´™]$ÈzŒÊD@=œ\Ms›!:\Žº‰cŇKýyZŽ’ºÞù=w»æ6¿ ¬L^äÐN/¬Ë’OYüºûÃúõÇûãW²sIY-3ܬY!…0ʸ“¯Æ2BÀ¸:±®+f¼n¯زλî²åˆ·F‘HÁPê ×\oaIŠS–òÉÖtÀáöB€ígPk㊠äðz‚6kÑðà‚v׋eW0 †'ÕX§–‚µ `¤-#]ƶ†0;·".ÄiØÜ’ƒ~bøÇÃY¾à wWR[stö<жÎ[׿¸{f¸Új¬ôbÌó\OïeAgÏ¡7‚¬c}Öú:yYˆ–»Þ„‚›yã#´[¾ÿL9ûý· ñß´s7—/“/>÷໿˜ßzéø–×ŽJ 0mËÈÀ0W”¢)Ý!mBÑãø´ÝŠcè„6/.™zH dT†qVN£1X„ˆ6{éZcN‰a//4чƀr³mîQ¯`B§…’¸™BÊT'ª‚ Š-B­ñ8ÍZW‚`·,X°n$©=àDƒ7ÝÌö½lßË© û­QêëÓx¸’ozòÆ6ã2ùåvOl&Œ'ÿ+¤Gc=s+ sS£·E4¬IÚ–×0z\°J•ýkÚ_üž×~§óו[3Òv2ùðßüû«nñ#¿ò°?¼ëzXogA@W«3.ªZVE& ìÊv63Âì« úUQ=$†FÀ 8`W!„غG¦±ÆöPš ‘˜t@ÄfsÀpÛ5Õ+a5ÝP+î¹ m8o“2Ê€lveÁ…p%‘5ÉÌ6K¡ý±²­ñF{]12á3V.ê V[J' FÞ(àÆ‹×òO¹º™æ¶NÛTç$Úä'ïiÍÔN•h™â”B"r=Ëb‡ë3Ž"ˆ3çþ@ŠŸÒÂâVÇD/~“™ &ðÍùos’5?+É{åºî(\b´’€\Wl5á×h3¹V¤¶µE׌´!»  ùíê Ŧ7é¯aw«mÝjÚY=‰……ICn¹"mÆc¡-$ŠšÒ]Ñ—ÖuEÃòžˆ©eÔ'B™ñô 0µÍ\ ±p»öƒR‚ 8ÔJ‰`£Hx Õ‡™B§ƒ ‘¨Y‘Ág"ßõ”;ð°§AÖ–x\»>c š±J¼ž¬ÔJA, Þf&ë -7]pÚÒÝü±à§Ën÷SÀßr+gnaýÒ%ƒÉä»–?¾Óq\úšZ÷o»®ëÕ$²é‚& †(°Á$ÙL )´¡´ÆÕͦ6.¥ô¡mÆãj PP“#Ä9p@˜ž&ccž"©ë.Äé-/+‘…¤¾T ƒÄf†hP…ЦD`µ$?¤ÑÀPÀ—~4ED`ôAH°° "¢ÏBMY*uü¬}Ëø¬GÜ'ÔÒæg†O–Ä`(Þ)-ƒºÅÉÊí™+ ,ÐÒ·A{³ÿ3)~¯? ü%§™L;÷ÉT¸ßüa‡¬Ÿp¼ß¿µîhA;A"»pp„*9!=¬9´)²!a{‹4ÛF™8‡E:¨[gD,8Ü®]ã1|鉨dM¢h¾å4–N_œõ] ĈÓ%ÆlÅpr…eQs%,0IYu›ø =H»æ@Œæ¦[éK™u,*l³$„6?.ƒ$ÒI‰ Ú,OöY>៯iÌ=ŒM(ˆÒg#Í ذ©Óù7+:ì÷ØF\Ÿá_þœË’‰îô‹Og2yfù‘+wÕSÿü…®ùN5/–IÙìDl³,Ü„c…ÀêBÄ„q}…‡=F)„ĺVÙî`"6 “4$a,!TDæ¦m³)Ã…ŠØþàìÏÂxX³›óîÈQ! A‚I@ý䔇”M¥¿éOÀÛ÷lñ°N4‡YÖ5lg @ëϨ5!!$µ“ŸÑæ2œ§›[:ùí[nÃ÷<þJˆ“AÊ®'ùQv#nv=t­†ÎÝEЩëöÚ?Ü`qý5g¹ð &³Ük®ñ>›L>íâý^ö¶,ŸX÷o±Ö;`‹"ä®ho³áâkÜåØÈ#‘ÍÝ1ÒsOŸ@°‚¥ HÄ©à#M¥@‰eü‚…~r+OoR­íu·Ï+¶<”Ä5"ŠHuÛêJJQ¿ÊãªÏÎK¬Íý@`Pš”pV$ú?bÈӮȖoÛl_þØ;ð·\9Öx³6ÝÇ9(ËH0܈‚Μ#¤'R÷ɵÞOgãzà¯8Íd‚îrý%&“ç”o9<ô5]ôÙëaÿzk]¯–ÒðŽ ¡¯ÒªD· 7tѡӦE7qTè´>A°y!“)$X–B(ØXëŠ"ÀÔO*íùše 'ÛVêÊwS×ÁHéƒtËþˆá[j6ñ-Ò©tmž´§Ô·±"„¤èv÷’ 0 F/Õ&€§sŽOxêÝpÝV€G˜”Çx=XðW¶Q̹ó~&Æd63™œ^B@Ajge\€mÃü@€@Cw»‚ÓCjù ªL8ú¬Dvwïúõ—%j»Bck\KA¦ÙÞwã–oÒNF¦Ú°m­ÁHD¸çàŠœ6!ýð2L&ÏEtÓnf2yA𔋇»‰Ã‡rðîëþåëZ¯D–]ÉBÀ "@ny"Fˆ5+Õ—1ÈÍ?Ëê ¤1ã¾Ãð?iºáùd¢,Ðôvˆ”Ç–T6*f„–0f@!B £Í\BÛ,"Ô~–5ì-(Kdk€ýû·“â$˜‰8:óõÀ§0™<—Ð ¿õ»L&/ .p÷ÃúŒ^í7^÷Ç/Wk½’0}X€Gc€ÐÐ-H-dødáåØtqºcpûðY}”PÄ¢ÀíêÈ)@„Üs×S&}AY&TZÚ£‡ ¦?Ä‚(h €n6iD˜qQ©Ûì+ù¿S®‹HDR dSÚ_tÜøk&“çúùß¼™Éä…‘+ʹ»?ãÒ-ï‘>~íêz7¯õõøph¸ß¶­«±$+¦fÆØ·ªq*7ÝíêYîÛiv-Zu+ É8 ¸ƒ+FM4XzôoÚÛ ¿?!cøQÆI'¡”AM·‰ºá£úÕž#W¥(#Ù°›V¦i³“åã¹Àd¢ïÿp&“.]zÚ}/åîC¼¬w«kÞ±®õì6\6b SA¢¥ny$R»ÓÐnGàš°Y‡P¡’hò{³yO슉Ö(TWÄHæEw¹MHJÙhnÇb×õ3…z–I¨Æ¸_m $(6w/@Ðy<Ú}ðáL&Ï!úõÍ2yѦTÝ{¿_ßø8/¾j]yyÄ]Xëµk®çAQ$P†ãÖ@dX+A -"mÂèB?Ô$i‰Tl± Ô³Ë2AIõSÃe¬Ë' ¨¹¢eV[c Ô¾ÓÈ®+‘… nJ™M—²5V bu)o <Ád2Èdò·øjgî|·¿qìõÚZÖû$~©<¬×º-æœÉ#¯«Œ†'V¿öa˜6 "(@u¢‘àØ§ ® {wHÄ8I8c¸ë.e‡Ô¶°ÊГ)€³jË0(º›¯=ÀK§I%Nˆn)–R÷Ôǽú]€Ê³Éd¢Ÿþi3™\Ž<æ1œ¹ë]ðJÜfw/É/_à%îâêÛ;ëu5óJ»žDDtO/ÉÝu·,c݆TS¤3ÔGö9¶™ A,ËpÀr«mÛY-—E€E¿JÓ˜y°ýœ Q¶(]sب8Ír´\¼5Ï&“‰nú­y™Lþ'þò^íÌËÞ÷æ×ß_ºôãëºÞÎ  Í˜QÝ•8G¦z2\…-m ü¬ Q´HoÛU= µü¸ƒ(#ê—–K ­ìvðê Ï@®-%qùpàûx6˜Ltýý‡ÿÉd²æá®ëñþ÷÷‡ý$ Ôcqƒ¶ „hÃtö]5{±søWIHedËÇ0QÌ#Š'Á2Ò7{ü¦ÔÀÃê¥+Ó±+‰(Q.è˜{ÿÀ³Èd±Oþ÷š5kÖÑZyþªÝ}–e÷X,è™&€¶»¼=®˜íý R€ºA¤ØR 6N°Mÿ© òÃö̱,fSmŒûw'f$@&Ã9ÌçuV÷ÓYñ¬Ö¬YñL¾qÖ¬YëîqW\s‡{î–£Gõ¨]†äH×0|lïK'™¼óÑ$ ¨­åªqR¥D?q ç`cŒ&@¦©-‡½èﱍ À…@Š®K‘¡YÓ×—~Žg‘É$x¦™L&q¸åI·páG»£GX"²Æ|B#Ÿƒ%ÒFp¸ÇÇޡMQ¨e|¸»ü cgÿìÈÞ… dÂ=B¸-N¨2ÆïˆâCQðÌÖ¬YºéAϪ•Éd29~*ç×åÂïêþn$HÂ$‚ž2( QšÊŠ ÚÜy´(Ü‘I2·Ù‰I8·ìzªb:¡6·9É-)kÅpO\0ªfo€z:âE¿:ð§)ãvvOˆtûYjVÀ`îðZox›ÿ[1kÖÿUñœ=`Ö¬YçϽßÙ³ç¿LKTõYƒñæ–‹[m°0" uK H¤¡2Ýþ„Æ“ 6]#‚ÔO5€Úæ%f|Úó'Ø#[DáWfßÇ.øßjÖ,Ýøàßc2™<ç_Zßëpéâ÷ÙyÎNÜMÄ6–Û Ã@&„  aC€…ÜÿÐf$mbAŒz ÔÈuÐf½"A!…èÍF(†m<¸®Çú àë˜üL/¬›Îs‡Édrépü мÿºîÂðÄ¢”1…5”#Ç$B ¼MSF#3ð‘6¨¸' ° ˆ¥e° ãve#7D%C:1t®eø‡«ýŽÀ¯ÓL&ºþã¹Çd2yúc_£,·}Ð×õð:Q–€ 6*a2ÝsD°‘„%p‚CÚ •8i8SØÆÐ¸š¡=)ˆ"@ètìGìDˆPA…†„ kV°)¸+×@hä —RpÒ2áM) 0®´† °z~ € Ñ’Ý^“¡”èWkm6òèz6ï<ÆdyõÊó¦fÍšuöš³=<ãuïZv˯*TO­ãJ„qz³*MÖ‡;…„Mcä¸[€eÇ«Š¡f(„ZÂLc «Á@‚Û߀@˜\wÖ^ú/_Îÿ[1kaÖ‚¾ëWæ d2y~pû£[ÞGèÛ\ëU*B)WùîÇÜò¾kæëÛz×õº„E€P€ÞfX j&J ²§ZimÜJ÷uß ÅÑ'ßÌä2¢?`^aÝ™LÞò ^eùÅ<ümÏZof¯¯dóRÆ×fÖ3ÝK€EiWXÃÂ${ú¡# !"ÀB„ŒÕaE´yM¼ð³Üê™èWî?…„“ÉåIJãn>Î7ÏÌ×Hù"tgÙ×D‰sN®T'ÅA,Œ«-5ÿ,¤, ˆËhw¢›ôp&“ÉäÍ^ïÕ–_½á·_—¥¼švå”á®!]‡ó|u=#(!P)pV[åR)Ë£©õ'Ïar5ÜÌ3Ãd2™ä¥zí…Zowþç.\äâ;½õëþ=°2¹<±ý¬Ö¬Y³fÍš5È­²fÍš5k6Y³fÍš5ȬY³fÍš dÖ¬Y³fÍúW˜œÜ™YYËJIEND®B`‚flippercloud-flipper-532c39a/lib/flipper/ui/public/js/000077500000000000000000000000001440444367400227245ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/public/js/application.js000066400000000000000000000017551440444367400255750ustar00rootroot00000000000000$(function () { $(document).on("click", ".js-toggle-trigger", function () { var $container = $(this).closest(".js-toggle-container"); return $container.toggleClass("toggle-on"); }); $("#enable_feature__button").on("click", function (e) { const featureName = $(e.target).data("confirmation-text"); const promptMessage = prompt( `Are you sure you want to fully enable this feature for everyone? Please enter the name of the feature to confirm it: ${featureName}` ); if (promptMessage !== featureName) { e.preventDefault(); } }); $("#delete_feature__button").on("click", function (e) { const featureName = $(e.target).data("confirmation-text"); const promptMessage = prompt( `Are you sure you want to remove this feature from the list of features and disable it for everyone? Please enter the name of the feature to confirm it: ${featureName}` ); if (promptMessage !== featureName) { e.preventDefault(); } }); }); flippercloud-flipper-532c39a/lib/flipper/ui/util.rb000066400000000000000000000024161440444367400223370ustar00rootroot00000000000000module Flipper module UI module Util # Private: 0x3000: fullwidth whitespace NON_WHITESPACE_REGEXP = /[^\s#{[0x3000].pack("U")}]/ def self.blank?(str) str.to_s !~ NON_WHITESPACE_REGEXP end def self.present?(str) !blank?(str) end def self.titleize(str) str.to_s.split('_').map(&:capitalize).join(' ') end def self.truncate(str, length: 30) if str.length > length str[0..length] else str end end def self.pluralize(count, singular, plural) if count == 1 "#{count} #{singular}" else "#{count} #{plural}" end end def self.to_sentence(array, options = {}) default_connectors = { words_connector: ", ", two_words_connector: " and ", last_word_connector: ", and " } options = default_connectors.merge!(options) case array.length when 0 "" when 1 "#{array[0]}" when 2 "#{array[0]}#{options[:two_words_connector]}#{array[1]}" else "#{array[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{array[-1]}" end end end end end flippercloud-flipper-532c39a/lib/flipper/ui/views/000077500000000000000000000000001440444367400221675ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/flipper/ui/views/add_actor.erb000066400000000000000000000013071440444367400246020ustar00rootroot00000000000000<% if params.key?("error") %>
<%= params["error"] %>
<% end %>

Enable Actor for <%= @feature.key %>

Turn on this feature for actors.

<%== csrf_input_tag %>
flippercloud-flipper-532c39a/lib/flipper/ui/views/add_feature.erb000066400000000000000000000015061440444367400251260ustar00rootroot00000000000000<% if params.key?("error") %>
<%= params["error"] %>
<% end %>

Add Feature

<%== csrf_input_tag %>

Recommended naming conventions: lower case, snake case, underscores over dashes. Good: foo_bar, foo. Bad: FooBar, Foo Bar, foo bar, foo-bar.

flippercloud-flipper-532c39a/lib/flipper/ui/views/add_group.erb000066400000000000000000000020361440444367400246260ustar00rootroot00000000000000<% if params.key?("error") %>
<%= params["error"] %>
<% end %>

Enable Group for <%= @feature.key %>

<% if @feature.disabled_groups.empty? %>

All groups are enabled for this feature which means there is nothing to add.

<% else %>

Turn on this feature for an entire group of actors.

<%== csrf_input_tag %>
<% end %>
flippercloud-flipper-532c39a/lib/flipper/ui/views/feature.erb000066400000000000000000000343211440444367400243170ustar00rootroot00000000000000<% if params.key?("error") %>
<%= params["error"] %>
<% end %> <% if @feature.description %>
<%== Sanitize.fragment(@feature.description, Sanitize::Config::BASIC) %>
<% end %>
<%# Gate State Header %>
<%= @feature.gate_state_title %>
<% unless @feature.boolean_value %> <%# Actors Info and Form %>
"> <% if @feature.actors_value.count > 0 %> Enabled for <%= Util.pluralize @feature.actors_value.count, 'actor', 'actors' %> <% else %> No actors enabled <% end %>
<% unless Flipper::UI.configuration.read_only %>
<%== csrf_input_tag %>
<% end %>
<%# Actors list %> <% @feature.actors_value.each do |item| %>
<%= item %>
<% unless Flipper::UI.configuration.read_only %>
<%== csrf_input_tag %>
<% end %>
<% end %> <%# Groups Info and Form %>
"> <% if @feature.groups_value.count > 0 %> Enabled for <%= Util.pluralize @feature.groups_value.count, 'group', 'groups' %> <% else %> No groups enabled <% end %>
<% unless Flipper::UI.configuration.read_only %>
<% if @feature.disabled_groups.empty? %> All groups enabled. <% else %>
<%== csrf_input_tag %>
<% end %>
<% end %>
<%# Groups list %> <% @feature.groups_value.each do |item| %>
<%= item %>
<% unless Flipper::UI.configuration.read_only %>
<%== csrf_input_tag %>
<% end %>
<% end %> <%# % of Actors %>
0 %>">Enabled for <%= @feature.percentage_of_actors_value %>% of actors
<% unless Flipper::UI.configuration.read_only %>
<% end %>
<% unless Flipper::UI.configuration.read_only %>
<%== csrf_input_tag %>
<% @percentages.each do |number| %> disabled<% end %>> <% end %>
<%== csrf_input_tag %> 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
<% end %>
<%# % of Time %>
0 %>">Enabled for <%= @feature.percentage_of_time_value %>% of time
<% unless Flipper::UI.configuration.read_only %>
<% end %>
<% unless Flipper::UI.configuration.read_only %>
<%== csrf_input_tag %>
<% @percentages.each do |number| %> disabled<% end %>> <% end %>
<%== csrf_input_tag %> 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> placeholder="custom (26, 32, etc.)" class="form-control form-control-sm mr-sm-2 mb-2 mb-md-0">
<% end %>
<% end %>
<%== csrf_input_tag %>
<% unless @feature.boolean_value %>
<% end %> <% unless @feature.off? %>
<% end %>
<% if !Flipper::UI.configuration.read_only && Flipper::UI.configuration.feature_removal_enabled %>

<%= Flipper::UI.configuration.delete.title %>

<%= Flipper::UI.configuration.delete.description %>

<%== csrf_input_tag %>
<% end %> flippercloud-flipper-532c39a/lib/flipper/ui/views/feature_creation_disabled.erb000066400000000000000000000003171440444367400300300ustar00rootroot00000000000000
Feature creation is disabled. To enable, you'll need to set Flipper::UI.configuration.feature_creation_enabled = true wherever flipper is running from.
flippercloud-flipper-532c39a/lib/flipper/ui/views/feature_removal_disabled.erb000066400000000000000000000003311440444367400276650ustar00rootroot00000000000000
Feature removal from the UI is disabled. To enable, you'll need to set Flipper::UI.configuration.feature_removal_enabled = true wherever flipper is running from.
flippercloud-flipper-532c39a/lib/flipper/ui/views/features.erb000066400000000000000000000050561440444367400245050ustar00rootroot00000000000000<% if @show_blank_slate %>
<% if Flipper::UI.configuration.fun %>

But I've got a blank space baby...

And I'll flip your features.

<%- if !Flipper::UI.configuration.read_only && Flipper::UI.configuration.feature_creation_enabled -%>

Add Feature

<%- end -%> <% else %>

Getting Started

You have not added any features to configure yet.

<%- if !Flipper::UI.configuration.read_only && Flipper::UI.configuration.feature_creation_enabled -%>

Add Feature

<% else %>

Check the examples to learn how to add one.

<%- end -%> <% end %>
<% else %> <% end %> flippercloud-flipper-532c39a/lib/flipper/ui/views/layout.erb000066400000000000000000000052021440444367400241750ustar00rootroot00000000000000 <%= @page_title ? "#{@page_title} // " : "" %>Flipper
<%- unless Flipper::UI.configuration.banner_text.nil? -%>
<%== Sanitize.fragment(Flipper::UI.configuration.banner_text, Sanitize::Config::BASIC) %>
<%- end -%>
<%== yield %>
<% if Flipper::UI.configuration.cloud_recommendation %>
For support, audit history, finer-grained permissions, multi-environment sync, and all your projects in one place check out Flipper Cloud.
<% end %>
flippercloud-flipper-532c39a/lib/flipper/ui/views/read_only.erb000066400000000000000000000003171440444367400246360ustar00rootroot00000000000000
The UI is currently in read only mode. To change this, you'll need to set Flipper::UI.configuration.read_only = false wherever flipper is running from.
flippercloud-flipper-532c39a/lib/flipper/version.rb000066400000000000000000000000571440444367400224310ustar00rootroot00000000000000module Flipper VERSION = '0.26.2'.freeze end flippercloud-flipper-532c39a/lib/generators/000077500000000000000000000000001440444367400211255ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/generators/flipper/000077500000000000000000000000001440444367400225665ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/generators/flipper/active_record_generator.rb000066400000000000000000000017101440444367400277710ustar00rootroot00000000000000require 'rails/generators/active_record' module Flipper module Generators class ActiveRecordGenerator < ::Rails::Generators::Base include ::Rails::Generators::Migration desc 'Generates migration for flipper tables' source_paths << File.join(File.dirname(__FILE__), 'templates') def self.next_migration_number(dirname) ::ActiveRecord::Generators::Base.next_migration_number(dirname) end def self.migration_version "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if requires_migration_number? end def self.requires_migration_number? Rails::VERSION::MAJOR.to_i >= 5 end def create_migration_file options = { migration_version: migration_version, } migration_template 'migration.erb', 'db/migrate/create_flipper_tables.rb', options end def migration_version self.class.migration_version end end end end flippercloud-flipper-532c39a/lib/generators/flipper/templates/000077500000000000000000000000001440444367400245645ustar00rootroot00000000000000flippercloud-flipper-532c39a/lib/generators/flipper/templates/migration.erb000066400000000000000000000011301440444367400272420ustar00rootroot00000000000000class CreateFlipperTables < ActiveRecord::Migration<%= migration_version %> def self.up create_table :flipper_features do |t| t.string :key, null: false t.timestamps null: false end add_index :flipper_features, :key, unique: true create_table :flipper_gates do |t| t.string :feature_key, null: false t.string :key, null: false t.string :value t.timestamps null: false end add_index :flipper_gates, [:feature_key, :key, :value], unique: true end def self.down drop_table :flipper_gates drop_table :flipper_features end end flippercloud-flipper-532c39a/lib/generators/flipper/templates/sequel_migration.rb000066400000000000000000000011271440444367400304610ustar00rootroot00000000000000class CreateFlipperTablesSequel < Sequel::Migration def up create_table :flipper_features do |_t| String :key, primary_key: true, null: false DateTime :created_at, null: false DateTime :updated_at, null: false end create_table :flipper_gates do |_t| String :feature_key, null: false String :key, null: false String :value DateTime :created_at, null: false DateTime :updated_at, null: false primary_key [:feature_key, :key, :value] end end def down drop_table :flipper_gates drop_table :flipper_features end end flippercloud-flipper-532c39a/script/000077500000000000000000000000001440444367400175125ustar00rootroot00000000000000flippercloud-flipper-532c39a/script/bootstrap000077500000000000000000000005371440444367400214620ustar00rootroot00000000000000#!/bin/sh #/ Usage: bootstrap [bundle options] #/ #/ Bundle install the dependencies. #/ #/ Examples: #/ #/ bootstrap #/ bootstrap --local #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } rm -rf .bundle/{binstubs,config} bundle install --binstubs --quiet "$@" flippercloud-flipper-532c39a/script/console000077500000000000000000000001301440444367400210740ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require "flipper" require "pry" Pry.start flippercloud-flipper-532c39a/script/examples000077500000000000000000000007351440444367400212630ustar00rootroot00000000000000#!/usr/bin/env sh # Exit if any example fails set -e # Run all examples individually for example in examples/**/*.rb; do # Skip examples that have setup in name echo $example | grep -q "setup" && continue; # Skip examples that have cloud in name echo $example | grep -q "cloud" && continue; # Skip examples with a loop for now if ! grep -q "loop do" "$example"; then echo "\n\n>>> START: $example" ruby $example echo ">>> FINISH: $example" fi done flippercloud-flipper-532c39a/script/guard000077500000000000000000000003431440444367400205420ustar00rootroot00000000000000#!/bin/sh #/ Usage: guard #/ #/ Start running guard. #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } script/bootstrap && bundle exec guard flippercloud-flipper-532c39a/script/release000077500000000000000000000004011440444367400210530ustar00rootroot00000000000000#!/bin/sh #/ Usage: release #/ #/ Package and release the gem to rubyforge. #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } script/bootstrap && bundle exec rake release flippercloud-flipper-532c39a/script/server000077500000000000000000000004721440444367400207510ustar00rootroot00000000000000#!/bin/sh #/ Usage: server #/ #/ Starts a server for perusing the UI locally. #/ #/ Examples: #/ #/ server #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } script/bootstrap && bundle exec shotgun examples/ui/basic.ru -p 9999 flippercloud-flipper-532c39a/script/test000077500000000000000000000011661440444367400204230ustar00rootroot00000000000000#!/bin/sh #/ Usage: test #/ #/ Bootstrap and run all tests. #/ set -e cd $(dirname "$0")/.. [ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && { grep '^#/' <"$0"| cut -c4- exit 0 } export RAILS_VERSION=5.0.0 export SQLITE3_VERSION=1.3.11 script/bootstrap || bundle update bundle exec rake export RAILS_VERSION=5.1.4 export SQLITE3_VERSION=1.3.11 script/bootstrap || bundle update bundle exec rake export RAILS_VERSION=5.2.3 export SQLITE3_VERSION=1.3.11 script/bootstrap || bundle update bundle exec rake export RAILS_VERSION=6.0.0 export SQLITE3_VERSION=1.4.1 script/bootstrap || bundle update bundle exec rake flippercloud-flipper-532c39a/spec/000077500000000000000000000000001440444367400171405ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/fixtures/000077500000000000000000000000001440444367400210115ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/fixtures/feature.json000066400000000000000000000007761440444367400233510ustar00rootroot00000000000000{ "key": "my_feature", "state": "on", "gates": [ { "key": "boolean", "name": "boolean", "value": "true" }, { "key": "groups", "name": "group", "value": [] }, { "key": "actors", "name": "actor", "value": [] }, { "key": "percentage_of_actors", "name": "percentage_of_actors", "value": null }, { "key": "percentage_of_time", "name": "percentage_of_time", "value": null } ] } flippercloud-flipper-532c39a/spec/flipper/000077500000000000000000000000001440444367400206015ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/actor_spec.rb000066400000000000000000000033361440444367400232550ustar00rootroot00000000000000RSpec.describe Flipper::Actor do it 'initializes with and knows flipper_id' do actor = described_class.new("User;235") expect(actor.flipper_id).to eq("User;235") end describe '#eql?' do it 'returns true if same class and flipper_id' do actor1 = described_class.new("User;235") actor2 = described_class.new("User;235") expect(actor1.eql?(actor2)).to be(true) end it 'returns false if same class but different flipper_id' do actor1 = described_class.new("User;235") actor2 = described_class.new("User;1") expect(actor1.eql?(actor2)).to be(false) end it 'returns false for different class' do actor1 = described_class.new("User;235") actor2 = Struct.new(:flipper_id).new("User;235") expect(actor1.eql?(actor2)).to be(false) end end describe '#==' do it 'returns true if same class and flipper_id' do actor1 = described_class.new("User;235") actor2 = described_class.new("User;235") expect(actor1.==(actor2)).to be(true) end it 'returns false if same class but different flipper_id' do actor1 = described_class.new("User;235") actor2 = described_class.new("User;1") expect(actor1.==(actor2)).to be(false) end it 'returns false for different class' do actor1 = described_class.new("User;235") actor2 = Struct.new(:flipper_id).new("User;235") expect(actor1.==(actor2)).to be(false) end end describe '#hash' do it 'returns a hash-value based on the flipper id' do h = { described_class.new("User;123") => true } expect(h).to have_key(described_class.new("User;123")) expect(h).not_to have_key(described_class.new("User;456")) end end end flippercloud-flipper-532c39a/spec/flipper/adapter_spec.rb000066400000000000000000000105331440444367400235620ustar00rootroot00000000000000RSpec.describe Flipper::Adapter do let(:source_flipper) { build_flipper } let(:destination_flipper) { build_flipper } let(:default_config) do { boolean: nil, groups: Set.new, actors: Set.new, percentage_of_actors: nil, percentage_of_time: nil, } end describe '.default_config' do it 'returns default config' do adapter_class = Class.new do include Flipper::Adapter end expect(adapter_class.default_config).to eq(default_config) end end describe '#default_config' do it 'returns default config' do adapter_class = Class.new do include Flipper::Adapter end expect(adapter_class.new.default_config).to eq(default_config) end end describe '#import' do it 'returns nothing' do result = destination_flipper.import(source_flipper) expect(result).to be(nil) end it 'can import from one adapter to another' do source_flipper.enable(:search) destination_flipper.import(source_flipper) expect(destination_flipper[:search].boolean_value).to eq(true) expect(destination_flipper.features.map(&:key).sort).to eq(%w(search)) end it 'can import features that have been added but their state is off' do source_flipper.add(:search) destination_flipper.import(source_flipper) expect(destination_flipper.features.map(&:key)).to eq(["search"]) end it 'can import multiple features' do source_flipper.enable(:yep) source_flipper.enable_group(:preview_features, :developers) source_flipper.enable_group(:preview_features, :marketers) source_flipper.enable_group(:preview_features, :company) source_flipper.enable_group(:preview_features, :early_access) source_flipper.enable_actor(:preview_features, Flipper::Actor.new('1')) source_flipper.enable_actor(:preview_features, Flipper::Actor.new('2')) source_flipper.enable_actor(:preview_features, Flipper::Actor.new('3')) source_flipper.enable_percentage_of_actors(:issues_next, 25) source_flipper.enable_percentage_of_time(:verbose_logging, 5) destination_flipper.import(source_flipper) feature = destination_flipper[:yep] expect(feature.boolean_value).to be(true) expect(feature.groups_value).to eq(Set[]) expect(feature.actors_value).to eq(Set[]) expect(feature.percentage_of_actors_value).to be(0) expect(feature.percentage_of_time_value).to be(0) feature = destination_flipper[:preview_features] expect(feature.boolean_value).to be(false) expect(feature.actors_value).to eq(Set['1', '2', '3']) expected_groups = Set['developers', 'marketers', 'company', 'early_access'] expect(feature.groups_value).to eq(expected_groups) expect(feature.percentage_of_actors_value).to be(0) expect(feature.percentage_of_time_value).to be(0) feature = destination_flipper[:issues_next] expect(feature.boolean_value).to eq(false) expect(feature.actors_value).to eq(Set.new) expect(feature.groups_value).to eq(Set.new) expect(feature.percentage_of_actors_value).to be(25) expect(feature.percentage_of_time_value).to be(0) feature = destination_flipper[:verbose_logging] expect(feature.boolean_value).to eq(false) expect(feature.actors_value).to eq(Set.new) expect(feature.groups_value).to eq(Set.new) expect(feature.percentage_of_actors_value).to be(0) expect(feature.percentage_of_time_value).to be(5) end it 'wipes existing enablements for adapter' do destination_flipper.enable(:stats) destination_flipper.enable_percentage_of_time(:verbose_logging, 5) source_flipper.enable_percentage_of_time(:stats, 5) source_flipper.enable_percentage_of_actors(:verbose_logging, 25) destination_flipper.import(source_flipper) feature = destination_flipper[:stats] expect(feature.boolean_value).to be(false) expect(feature.percentage_of_time_value).to be(5) feature = destination_flipper[:verbose_logging] expect(feature.percentage_of_time_value).to be(0) expect(feature.percentage_of_actors_value).to be(25) end it 'wipes existing features for adapter' do destination_flipper.add(:stats) destination_flipper.import(source_flipper) expect(destination_flipper.features.map(&:key)).to eq([]) end end end flippercloud-flipper-532c39a/spec/flipper/adapters/000077500000000000000000000000001440444367400224045ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/adapters/active_record_spec.rb000066400000000000000000000157631440444367400265700ustar00rootroot00000000000000require 'flipper/adapters/active_record' # Turn off migration logging for specs ActiveRecord::Migration.verbose = false RSpec.describe Flipper::Adapters::ActiveRecord do subject { described_class.new } before(:all) do ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') end before(:each) do ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_features ( id integer PRIMARY KEY, key text NOT NULL UNIQUE, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_gates ( id integer PRIMARY KEY, feature_key text NOT NULL, key text NOT NULL, value text DEFAULT NULL, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value) SQL end after(:each) do ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_features`") ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_gates`") end it_should_behave_like 'a flipper adapter' context 'requiring "flipper-active_record"' do before do Flipper.configuration = nil Flipper.instance = nil load 'flipper/adapters/active_record.rb' ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) end it 'configures itself' do expect(Flipper.adapter.adapter).to be_a(Flipper::Adapters::ActiveRecord) end end context "ActiveRecord connection_pool" do before do ActiveRecord::Base.clear_active_connections! end context "#features" do it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.features expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.features expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end context "#get_all" do it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.get_all expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.get_all expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end context "#add / #remove / #clear" do let(:feature) { Flipper::Feature.new(:search, subject) } it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.add(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.remove(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.clear(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.add(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.remove(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.clear(feature) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end context "#get_multi" do let(:feature) { Flipper::Feature.new(:search, subject) } it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.get_multi([feature]) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.get_multi([feature]) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end context "#enable/#disable boolean" do let(:feature) { Flipper::Feature.new(:search, subject) } let(:gate) { feature.gate(:boolean)} it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.enable(feature, gate, gate.wrap(true)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.disable(feature, gate, gate.wrap(false)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.enable(feature, gate, gate.wrap(true)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.disable(feature, gate, gate.wrap(false)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end context "#enable/#disable set" do let(:feature) { Flipper::Feature.new(:search, subject) } let(:gate) { feature.gate(:group) } it "does not hold onto connections" do expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.enable(feature, gate, gate.wrap(:admin)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) subject.disable(feature, gate, gate.wrap(:admin)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(false) end it "does not release previously held connection" do ActiveRecord::Base.connection # establish a new connection expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.enable(feature, gate, gate.wrap(:admin)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) subject.disable(feature, gate, gate.wrap(:admin)) expect(ActiveRecord::Base.connection_handler.active_connections?).to be(true) end end end end flippercloud-flipper-532c39a/spec/flipper/adapters/active_support_cache_store_spec.rb000066400000000000000000000107761440444367400313640ustar00rootroot00000000000000require 'active_support/cache' require 'flipper/adapters/operation_logger' require 'flipper/adapters/active_support_cache_store' RSpec.describe Flipper::Adapters::ActiveSupportCacheStore do let(:memory_adapter) do Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) end let(:cache) { ActiveSupport::Cache::MemoryStore.new } let(:write_through) { false } let(:adapter) { described_class.new(memory_adapter, cache, expires_in: 10.seconds, write_through: write_through) } let(:flipper) { Flipper.new(adapter) } subject { adapter } before do cache.clear end it_should_behave_like 'a flipper adapter' describe '#remove' do let(:feature) { flipper[:stats] } before do adapter.get(feature) adapter.remove(feature) end it 'expires feature and deletes the cache' do expect(cache.read(described_class.key_for(feature))).to be_nil expect(cache.exist?(described_class.key_for(feature))).to be(false) expect(feature).not_to be_enabled end context 'with write-through caching' do let(:write_through) { true } it 'expires feature and writes an empty value to the cache' do expect(cache.read(described_class.key_for(feature))).to eq(adapter.default_config) expect(cache.exist?(described_class.key_for(feature))).to be(true) expect(feature).not_to be_enabled end end end describe '#enable' do let(:feature) { flipper[:stats] } before do adapter.enable(feature, feature.gate(:boolean), flipper.boolean) end it 'enables feature and deletes the cache' do expect(cache.read(described_class.key_for(feature))).to be_nil expect(cache.exist?(described_class.key_for(feature))).to be(false) expect(feature).to be_enabled end context 'with write-through caching' do let(:write_through) { true } it 'expires feature and writes to the cache' do expect(cache.exist?(described_class.key_for(feature))).to be(true) expect(cache.read(described_class.key_for(feature))).to include(boolean: 'true') expect(feature).to be_enabled end end end describe '#disable' do let(:feature) { flipper[:stats] } before do adapter.disable(feature, feature.gate(:boolean), flipper.boolean) end it 'disables feature and deletes the cache' do expect(cache.read(described_class.key_for(feature))).to be_nil expect(cache.exist?(described_class.key_for(feature))).to be(false) expect(feature).not_to be_enabled end context 'with write-through caching' do let(:write_through) { true } it 'expires feature and writes to the cache' do expect(cache.exist?(described_class.key_for(feature))).to be(true) expect(cache.read(described_class.key_for(feature))).to include(boolean: nil) expect(feature).not_to be_enabled end end end describe '#get_multi' do it 'warms uncached features' do stats = flipper[:stats] search = flipper[:search] other = flipper[:other] stats.enable search.enable memory_adapter.reset adapter.get(stats) expect(cache.read(described_class.key_for(search))).to be(nil) expect(cache.read(described_class.key_for(other))).to be(nil) adapter.get_multi([stats, search, other]) expect(cache.read(described_class.key_for(search))[:boolean]).to eq('true') expect(cache.read(described_class.key_for(other))[:boolean]).to be(nil) adapter.get_multi([stats, search, other]) adapter.get_multi([stats, search, other]) expect(memory_adapter.count(:get_multi)).to eq(1) end end describe '#get_all' do let(:stats) { flipper[:stats] } let(:search) { flipper[:search] } before do stats.enable search.add end it 'warms all features' do adapter.get_all expect(cache.read(described_class.key_for(stats))[:boolean]).to eq('true') expect(cache.read(described_class.key_for(search))[:boolean]).to be(nil) expect(cache.read(described_class::GetAllKey)).to be_within(2).of(Time.now.to_i) end it 'returns same result when already cached' do expect(adapter.get_all).to eq(adapter.get_all) end it 'only invokes one call to wrapped adapter' do memory_adapter.reset 5.times { adapter.get_all } expect(memory_adapter.count(:get_all)).to eq(1) end end describe '#name' do it 'is active_support_cache_store' do expect(subject.name).to be(:active_support_cache_store) end end end flippercloud-flipper-532c39a/spec/flipper/adapters/dalli_spec.rb000066400000000000000000000047201440444367400250330ustar00rootroot00000000000000require 'flipper/adapters/operation_logger' require 'flipper/adapters/dalli' require 'logger' RSpec.describe Flipper::Adapters::Dalli do let(:memory_adapter) do Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) end let(:cache) { Dalli::Client.new(ENV['MEMCACHED_URL']) } let(:adapter) { described_class.new(memory_adapter, cache) } let(:flipper) { Flipper.new(adapter) } subject { adapter } before do Dalli.logger = Logger.new('/dev/null') begin cache.flush rescue Dalli::NetworkError ENV['CI'] ? raise : skip('Memcached not available') end end it_should_behave_like 'a flipper adapter' describe '#remove' do it 'expires feature' do feature = flipper[:stats] adapter.get(feature) adapter.remove(feature) expect(cache.get(described_class.key_for(feature))).to be(nil) end end describe '#get_multi' do it 'warms uncached features' do stats = flipper[:stats] search = flipper[:search] other = flipper[:other] stats.enable search.enable memory_adapter.reset adapter.get(stats) expect(cache.get(described_class.key_for(search))).to be(nil) expect(cache.get(described_class.key_for(other))).to be(nil) adapter.get_multi([stats, search, other]) expect(cache.get(described_class.key_for(search))[:boolean]).to eq('true') expect(cache.get(described_class.key_for(other))[:boolean]).to be(nil) adapter.get_multi([stats, search, other]) adapter.get_multi([stats, search, other]) expect(memory_adapter.count(:get_multi)).to eq(1) end end describe '#get_all' do let(:stats) { flipper[:stats] } let(:search) { flipper[:search] } before do stats.enable search.add end it 'warms all features' do adapter.get_all expect(cache.get(described_class.key_for(stats))[:boolean]).to eq('true') expect(cache.get(described_class.key_for(search))[:boolean]).to be(nil) expect(cache.get(described_class::GetAllKey)).to be_within(2).of(Time.now.to_i) end it 'returns same result when already cached' do expect(adapter.get_all).to eq(adapter.get_all) end it 'only invokes one call to wrapped adapter' do memory_adapter.reset 5.times { adapter.get_all } expect(memory_adapter.count(:get_all)).to eq(1) end end describe '#name' do it 'is dalli' do expect(subject.name).to be(:dalli) end end end flippercloud-flipper-532c39a/spec/flipper/adapters/dual_write_spec.rb000066400000000000000000000036751440444367400261150ustar00rootroot00000000000000require 'flipper/adapters/dual_write' require 'flipper/adapters/operation_logger' require 'active_support/notifications' RSpec.describe Flipper::Adapters::DualWrite do let(:local_adapter) do Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new end let(:remote_adapter) do Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new end let(:local) { Flipper.new(local_adapter) } let(:remote) { Flipper.new(remote_adapter) } let(:sync) { Flipper.new(subject) } subject do described_class.new(local_adapter, remote_adapter) end it_should_behave_like 'a flipper adapter' it 'only uses local for #features' do subject.features end it 'only uses local for #get' do subject.get sync[:search] end it 'only uses local for #get_multi' do subject.get_multi [sync[:search]] end it 'only uses local for #get_all' do subject.get_all end it 'updates remote and local for #add' do subject.add sync[:search] expect(remote_adapter.count(:add)).to be(1) expect(local_adapter.count(:add)).to be(1) end it 'updates remote and local for #remove' do subject.remove sync[:search] expect(remote_adapter.count(:remove)).to be(1) expect(local_adapter.count(:remove)).to be(1) end it 'updates remote and local for #clear' do subject.clear sync[:search] expect(remote_adapter.count(:clear)).to be(1) expect(local_adapter.count(:clear)).to be(1) end it 'updates remote and local for #enable' do feature = sync[:search] subject.enable feature, feature.gate(:boolean), local.boolean expect(remote_adapter.count(:enable)).to be(1) expect(local_adapter.count(:enable)).to be(1) end it 'updates remote and local for #disable' do feature = sync[:search] subject.disable feature, feature.gate(:boolean), local.boolean(false) expect(remote_adapter.count(:disable)).to be(1) expect(local_adapter.count(:disable)).to be(1) end end flippercloud-flipper-532c39a/spec/flipper/adapters/failover_spec.rb000066400000000000000000000067621440444367400255650ustar00rootroot00000000000000require 'dalli' require 'flipper/adapters/failover' require 'net/http' require 'pstore' require 'redis' RSpec.describe Flipper::Adapters::Failover do subject { described_class.new(primary, secondary, options) } let(:primary) { Flipper::Adapters::Memory.new } let(:secondary) { Flipper::Adapters::Memory.new } let(:options) { {} } let(:flipper) { Flipper.new(subject) } context 'when the primary is a functioning adapter' do it_should_behave_like 'a flipper adapter' it 'should not call the secondary' do expect(secondary).not_to receive(:features) subject.features end it 'should not write to secondary' do expect(secondary).not_to receive(:add) expect(secondary).not_to receive(:enable) flipper[:flag].enable end context 'when dual_write is enabled' do let(:options) { { dual_write: true } } it_should_behave_like 'a flipper adapter' it 'writes to both primary and secondary' do expect(primary).to receive(:add).and_call_original expect(primary).to receive(:enable).and_call_original expect(secondary).to receive(:add) expect(secondary).to receive(:enable) flipper[:flag].enable end end end context 'when primary fails during read operations' do before do allow(primary).to receive(:features).and_raise(Redis::ConnectionError) allow(primary).to receive(:get).and_raise(Dalli::NetworkError) end it 'fails over to the secondary adapter for reads' do expect(secondary).to receive(:features) subject.features flipper[:flag].enable expect(secondary).to receive(:get).and_call_original flipper[:flag].enabled? end context 'when dual_write is enabled' do let(:options) { { dual_write: true } } it_should_behave_like 'a flipper adapter' end end context 'when primary fails during write operations' do before do allow(primary).to receive(:add).and_raise(PStore::Error) end let(:options) { { dual_write: true } } it 'fails and does not write to secondary adapter' do expect(secondary).not_to receive(:add) expect(secondary).not_to receive(:enable) expect { flipper[:flag].enable }.to raise_error(PStore::Error) end end context 'when primary is instrumented and fails' do before do allow(memory_adapter).to receive(:get).and_raise(Net::ReadTimeout) end let(:memory_adapter) { Flipper::Adapters::Memory.new } let(:primary) do Flipper::Adapters::Instrumented.new( memory_adapter, instrumenter: instrumenter, ) end let(:instrumenter) { Flipper::Instrumenters::Memory.new } it 'logs the raised exception' do flipper[:flag].enabled? expect(instrumenter.events.count).to be 1 payload = instrumenter.events[0].payload expect(payload.keys).to include(:exception, :exception_object) expect(payload[:exception_object]).to be_a Net::ReadTimeout end end context 'when adapter raises a SyntaxError' do before do allow(primary).to receive(:features).and_raise(SyntaxError) end it 'does not rescue this type by default' do expect { subject.features }.to raise_error(SyntaxError) end context 'when Failover adapter is configured to catch SyntaxError' do let(:options) { { errors: [ SyntaxError ] } } it 'fails over to secondary adapter' do expect(secondary).to receive(:features) subject.features end end end end flippercloud-flipper-532c39a/spec/flipper/adapters/failsafe_spec.rb000066400000000000000000000034761440444367400255270ustar00rootroot00000000000000require 'flipper/adapters/failsafe' RSpec.describe Flipper::Adapters::Failsafe do subject { described_class.new(memory_adapter, options) } let(:memory_adapter) { Flipper::Adapters::Memory.new } let(:options) { {} } let(:flipper) { Flipper.new(subject) } it_should_behave_like 'a flipper adapter' context 'when disaster strikes' do before do expect(flipper[feature.name].enable).to be(true) (subject.methods - Object.methods).each do |method_name| allow(memory_adapter).to receive(method_name).and_raise(IOError) end end let(:feature) { Flipper::Feature.new(:my_feature, subject) } it { expect(subject.features).to eq(Set.new) } it { expect(feature.add).to eq(false) } it { expect(feature.remove).to eq(false) } it { expect(feature.clear).to eq(false) } it { expect(subject.get(feature)).to eq({}) } it { expect(subject.get_multi([feature])).to eq({}) } it { expect(subject.get_all).to eq({}) } it { expect(feature.enable).to eq(false) } it { expect(feature.disable).to eq(false) } context 'when used via Flipper' do it { expect(flipper.features).to eq(Set.new) } it { expect(flipper[feature.name].enabled?).to eq(false) } it { expect(flipper[feature.name].enable).to eq(false) } it { expect(flipper[feature.name].disable).to eq(false) } end context 'when there is a syntax error' do let(:test) { flipper[feature.name].enabled? } before do expect(memory_adapter).to receive(:get).and_raise(SyntaxError) end it 'does not catch this type of error' do expect { test }.to raise_error(SyntaxError) end context 'when configured to catch SyntaxError' do let(:options) { { errors: [SyntaxError] } } it { expect(test).to eq(false) } end end end end flippercloud-flipper-532c39a/spec/flipper/adapters/http_spec.rb000066400000000000000000000216571440444367400247350ustar00rootroot00000000000000require 'flipper/adapters/http' require 'flipper/adapters/pstore' require 'rack/handler/webrick' FLIPPER_SPEC_API_PORT = ENV.fetch('FLIPPER_SPEC_API_PORT', 9001).to_i RSpec.describe Flipper::Adapters::Http do context 'adapter' do subject do described_class.new(url: "http://localhost:#{FLIPPER_SPEC_API_PORT}") end before :all do dir = FlipperRoot.join('tmp').tap(&:mkpath) log_path = dir.join('flipper_adapters_http_spec.log') @pstore_file = dir.join('flipper.pstore') @pstore_file.unlink if @pstore_file.exist? api_adapter = Flipper::Adapters::PStore.new(@pstore_file) flipper_api = Flipper.new(api_adapter) app = Flipper::Api.app(flipper_api) server_options = { Port: FLIPPER_SPEC_API_PORT, StartCallback: -> { @started = true }, Logger: WEBrick::Log.new(log_path.to_s, WEBrick::Log::INFO), AccessLog: [ [log_path.open('w'), WEBrick::AccessLog::COMBINED_LOG_FORMAT], ], } @server = WEBrick::HTTPServer.new(server_options) @server.mount '/', Rack::Handler::WEBrick, app Thread.new { @server.start } Timeout.timeout(1) { :wait until @started } end after :all do @server.shutdown if @server end before(:each) do @pstore_file.unlink if @pstore_file.exist? end it_should_behave_like 'a flipper adapter' it "can enable and disable unregistered group" do flipper = Flipper.new(subject) expect(flipper[:search].enable_group(:some_made_up_group)).to be(true) expect(flipper[:search].groups_value).to eq(Set["some_made_up_group"]) expect(flipper[:search].disable_group(:some_made_up_group)).to be(true) expect(flipper[:search].groups_value).to eq(Set.new) end end it "sends default headers" do headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json', 'User-Agent' => "Flipper HTTP Adapter v#{Flipper::VERSION}", } stub_request(:get, "http://app.com/flipper/features/feature_panel") .with(headers: headers) .to_return(status: 404, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') adapter.get(flipper[:feature_panel]) end describe "#get" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features/feature_panel") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.get(flipper[:feature_panel]) }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#get_multi" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features?keys=feature_panel") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.get_multi([flipper[:feature_panel]]) }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#get_all" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.get_all }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#features" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.features }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#add" do it "raises error when not successful" do stub_request(:post, /app.com/) .to_return(status: 503, body: "{}", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.add(Flipper::Feature.new(:search, adapter)) }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#remove" do it "raises error when not successful" do stub_request(:delete, /app.com/) .to_return(status: 503, body: "{}", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.remove(Flipper::Feature.new(:search, adapter)) }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#clear" do it "raises error when not successful" do stub_request(:delete, /app.com/) .to_return(status: 503, body: "{}", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect { adapter.clear(Flipper::Feature.new(:search, adapter)) }.to raise_error(Flipper::Adapters::Http::Error) end end describe "#enable" do it "raises error when not successful" do stub_request(:post, /app.com/) .to_return(status: 503, body: "{}", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') feature = Flipper::Feature.new(:search, adapter) gate = feature.gate(:boolean) thing = gate.wrap(true) expect { adapter.enable(feature, gate, thing) }.to raise_error(Flipper::Adapters::Http::Error, "Failed with status: 503") end it "doesn't raise json error if body cannot be parsed" do stub_request(:post, /app.com/) .to_return(status: 503, body: "barf", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') feature = Flipper::Feature.new(:search, adapter) gate = feature.gate(:boolean) thing = gate.wrap(true) expect { adapter.enable(feature, gate, thing) }.to raise_error(Flipper::Adapters::Http::Error) end it "includes response information if available when raising error" do api_response = { "code" => "error", "message" => "This feature has reached the limit to the number of " + "actors per feature. Check out groups as a more flexible " + "way to enable many actors.", "more_info" => "https://www.flippercloud.io/docs", } stub_request(:post, /app.com/) .to_return(status: 503, body: JSON.dump(api_response), headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') feature = Flipper::Feature.new(:search, adapter) gate = feature.gate(:boolean) thing = gate.wrap(true) error_message = "Failed with status: 503\n\nThis feature has reached the " + "limit to the number of actors per feature. Check out " + "groups as a more flexible way to enable many actors.\n" + "https://www.flippercloud.io/docs" expect { adapter.enable(feature, gate, thing) }.to raise_error(Flipper::Adapters::Http::Error, error_message) end end describe "#disable" do it "raises error when not successful" do stub_request(:delete, /app.com/) .to_return(status: 503, body: "{}", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') feature = Flipper::Feature.new(:search, adapter) gate = feature.gate(:boolean) thing = gate.wrap(false) expect { adapter.disable(feature, gate, thing) }.to raise_error(Flipper::Adapters::Http::Error) end end describe 'configuration' do let(:debug_output) { object_double($stderr) } let(:options) do { url: 'http://app.com/mount-point', headers: { 'X-Custom-Header' => 'foo' }, basic_auth_username: 'username', basic_auth_password: 'password', read_timeout: 100, open_timeout: 40, write_timeout: 40, debug_output: debug_output, } end subject { described_class.new(options) } let(:feature) { flipper[:feature_panel] } before do stub_request(:get, %r{\Ahttp://app.com*}). to_return(body: fixture_file('feature.json')) end it 'allows client to set request headers' do subject.get(feature) expect( a_request(:get, 'http://app.com/mount-point/features/feature_panel') .with(headers: { 'X-Custom-Header' => 'foo' }) ).to have_been_made.once end it 'allows client to set basic auth' do subject.get(feature) expect( a_request(:get, 'http://app.com/mount-point/features/feature_panel') .with(basic_auth: %w(username password)) ).to have_been_made.once end it 'allows client to set debug output' do user_agent = Net::HTTP.new("app.com") allow(Net::HTTP).to receive(:new).and_return(user_agent) expect(user_agent).to receive(:set_debug_output).with(debug_output) subject.get(feature) end end def fixture_file(name) fixtures_path = File.expand_path('../../../fixtures', __FILE__) File.new(fixtures_path + '/' + name) end end flippercloud-flipper-532c39a/spec/flipper/adapters/instrumented_spec.rb000066400000000000000000000113441440444367400264670ustar00rootroot00000000000000require 'flipper/adapters/instrumented' require 'flipper/instrumenters/memory' RSpec.describe Flipper::Adapters::Instrumented do let(:instrumenter) { Flipper::Instrumenters::Memory.new } let(:adapter) { Flipper::Adapters::Memory.new } let(:flipper) { Flipper.new(adapter) } let(:feature) { flipper[:stats] } let(:gate) { feature.gate(:percentage_of_actors) } let(:thing) { flipper.actors(22) } subject do described_class.new(adapter, instrumenter: instrumenter) end it_should_behave_like 'a flipper adapter' it 'forwards missing methods to underlying adapter' do adapter = Class.new do def foo :foo end end.new instrumented = described_class.new(adapter) expect(instrumented.foo).to eq(:foo) end describe '#name' do it 'is instrumented' do expect(subject.name).to be(:instrumented) end end describe '#get' do it 'records instrumentation' do result = subject.get(feature) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:get) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:result]).to be(result) end end describe '#get_multi' do it 'records instrumentation' do result = subject.get_multi([feature]) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:get_multi) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_names]).to eq([:stats]) expect(event.payload[:result]).to be(result) end end describe '#enable' do it 'records instrumentation' do result = subject.enable(feature, gate, thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:enable) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:gate_name]).to eq(:percentage_of_actors) expect(event.payload[:thing_value]).to eq(22) expect(event.payload[:result]).to be(result) end end describe '#disable' do it 'records instrumentation' do result = subject.disable(feature, gate, thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:disable) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:gate_name]).to eq(:percentage_of_actors) expect(event.payload[:thing_value]).to eq(22) expect(event.payload[:result]).to be(result) end end describe '#add' do it 'records instrumentation' do result = subject.add(feature) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:add) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:result]).to be(result) end end describe '#remove' do it 'records instrumentation' do result = subject.remove(feature) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:remove) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:result]).to be(result) end end describe '#clear' do it 'records instrumentation' do result = subject.clear(feature) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:clear) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:feature_name]).to eq(:stats) expect(event.payload[:result]).to be(result) end end describe '#features' do it 'records instrumentation' do result = subject.features event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('adapter_operation.flipper') expect(event.payload[:operation]).to eq(:features) expect(event.payload[:adapter_name]).to eq(:memory) expect(event.payload[:result]).to be(result) end end end flippercloud-flipper-532c39a/spec/flipper/adapters/memoizable_spec.rb000066400000000000000000000223471440444367400260770ustar00rootroot00000000000000require 'flipper/adapters/memoizable' require 'flipper/adapters/operation_logger' RSpec.describe Flipper::Adapters::Memoizable do let(:features_key) { described_class::FeaturesKey } let(:adapter) { Flipper::Adapters::Memory.new } let(:flipper) { Flipper.new(adapter) } let(:cache) { {} } subject { described_class.new(adapter, cache) } it_should_behave_like 'a flipper adapter' it 'forwards missing methods to underlying adapter' do adapter = Class.new do def foo :foo end end.new memoizable = described_class.new(adapter) expect(memoizable.foo).to eq(:foo) end describe '#name' do it 'is instrumented' do expect(subject.name).to be(:memoizable) end end describe '#memoize=' do it 'sets value' do subject.memoize = true expect(subject.memoizing?).to eq(true) subject.memoize = false expect(subject.memoizing?).to eq(false) end it 'clears the local cache' do subject.cache['some'] = 'thing' subject.memoize = true expect(subject.cache).to be_empty end end describe '#memoizing?' do it 'returns true if enabled' do subject.memoize = true expect(subject.memoizing?).to eq(true) end it 'returns false if disabled' do subject.memoize = false expect(subject.memoizing?).to eq(false) end end describe '#get' do context 'with memoization enabled' do before do subject.memoize = true end it 'memoizes feature' do feature = flipper[:stats] result = subject.get(feature) expect(cache[described_class.key_for(feature.key)]).to be(result) end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do feature = flipper[:stats] result = subject.get(feature) adapter_result = adapter.get(feature) expect(result).to eq(adapter_result) end end end describe '#get_multi' do context 'with memoization enabled' do before do subject.memoize = true end it 'memoizes features' do names = %i(stats shiny) features = names.map { |name| flipper[name] } results = subject.get_multi(features) features.each do |feature| expect(cache[described_class.key_for(feature.key)]).not_to be(nil) expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key]) end end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do names = %i(stats shiny) features = names.map { |name| flipper[name] } result = subject.get_multi(features) adapter_result = adapter.get_multi(features) expect(result).to eq(adapter_result) end end end describe '#get_all' do context "with memoization enabled" do before do subject.memoize = true end it 'memoizes features' do names = %i(stats shiny) features = names.map { |name| flipper[name].tap(&:enable) } results = subject.get_all features.each do |feature| expect(cache[described_class.key_for(feature.key)]).not_to be(nil) expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key]) end expect(cache[subject.class::FeaturesKey]).to eq(names.map(&:to_s).to_set) end it 'only calls get_all once for memoized adapter' do adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) cache = {} instance = described_class.new(adapter, cache) instance.memoize = true instance.get_all expect(adapter.count(:get_all)).to be(1) instance.get_all expect(adapter.count(:get_all)).to be(1) end it 'returns default_config for unknown feature keys' do first = subject.get_all expect(first['doesntexist']).to eq(subject.default_config) second = subject.get_all expect(second['doesntexist']).to eq(subject.default_config) end end context "with memoization disabled" do before do subject.memoize = false end it 'returns result' do names = %i(stats shiny) names.map { |name| flipper[name].tap(&:enable) } result = subject.get_all adapter_result = adapter.get_all expect(result).to eq(adapter_result) end it 'calls get_all every time for memoized adapter' do adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) cache = {} instance = described_class.new(adapter, cache) instance.memoize = false instance.get_all expect(adapter.count(:get_all)).to be(1) instance.get_all expect(adapter.count(:get_all)).to be(2) end it 'returns nil for unknown feature keys' do first = subject.get_all expect(first['doesntexist']).to be(nil) second = subject.get_all expect(second['doesntexist']).to be(nil) end end end describe '#enable' do context 'with memoization enabled' do before do subject.memoize = true end it 'unmemoizes feature' do feature = flipper[:stats] gate = feature.gate(:boolean) cache[described_class.key_for(feature.key)] = { some: 'thing' } subject.enable(feature, gate, flipper.bool) expect(cache[described_class.key_for(feature.key)]).to be_nil end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do feature = flipper[:stats] gate = feature.gate(:boolean) result = subject.enable(feature, gate, flipper.bool) adapter_result = adapter.enable(feature, gate, flipper.bool) expect(result).to eq(adapter_result) end end end describe '#disable' do context 'with memoization enabled' do before do subject.memoize = true end it 'unmemoizes feature' do feature = flipper[:stats] gate = feature.gate(:boolean) cache[described_class.key_for(feature.key)] = { some: 'thing' } subject.disable(feature, gate, flipper.bool) expect(cache[described_class.key_for(feature.key)]).to be_nil end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do feature = flipper[:stats] gate = feature.gate(:boolean) result = subject.disable(feature, gate, flipper.bool) adapter_result = adapter.disable(feature, gate, flipper.bool) expect(result).to eq(adapter_result) end end end describe '#features' do context 'with memoization enabled' do before do subject.memoize = true end it 'memoizes features' do flipper[:stats].enable flipper[:search].disable result = subject.features expect(cache[:flipper_features]).to be(result) end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do expect(subject.features).to eq(adapter.features) end end end describe '#add' do context 'with memoization enabled' do before do subject.memoize = true end it 'unmemoizes the known features' do cache[features_key] = { some: 'thing' } subject.add(flipper[:stats]) expect(cache).to be_empty end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do expect(subject.add(flipper[:stats])).to eq(adapter.add(flipper[:stats])) end end end describe '#remove' do context 'with memoization enabled' do before do subject.memoize = true end it 'unmemoizes the known features' do cache[features_key] = { some: 'thing' } subject.remove(flipper[:stats]) expect(cache).to be_empty end it 'unmemoizes the feature' do feature = flipper[:stats] cache[described_class.key_for(feature.key)] = { some: 'thing' } subject.remove(feature) expect(cache[described_class.key_for(feature.key)]).to be_nil end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do expect(subject.remove(flipper[:stats])).to eq(adapter.remove(flipper[:stats])) end end end describe '#clear' do context 'with memoization enabled' do before do subject.memoize = true end it 'unmemoizes feature' do feature = flipper[:stats] cache[described_class.key_for(feature.key)] = { some: 'thing' } subject.clear(feature) expect(cache[described_class.key_for(feature.key)]).to be_nil end end context 'with memoization disabled' do before do subject.memoize = false end it 'returns result' do feature = flipper[:stats] expect(subject.clear(feature)).to eq(adapter.clear(feature)) end end end end flippercloud-flipper-532c39a/spec/flipper/adapters/memory_spec.rb000066400000000000000000000020371440444367400252550ustar00rootroot00000000000000RSpec.describe Flipper::Adapters::Memory do let(:source) { {} } subject { described_class.new(source) } it_should_behave_like 'a flipper adapter' it "can initialize from big hash" do flipper = Flipper.new(subject) flipper.enable :subscriptions flipper.disable :search flipper.enable_percentage_of_actors :pro_deal, 20 flipper.enable_percentage_of_time :logging, 30 flipper.enable_actor :following, Flipper::Actor.new('1') flipper.enable_actor :following, Flipper::Actor.new('3') flipper.enable_group :following, Flipper::Types::Group.new(:staff) dup = described_class.new(subject.get_all) expect(dup.get_all).to eq({ "subscriptions" => subject.default_config.merge(boolean: "true"), "search" => subject.default_config, "logging" => subject.default_config.merge(:percentage_of_time => "30"), "pro_deal" => subject.default_config.merge(:percentage_of_actors => "20"), "following" => subject.default_config.merge(actors: Set["1", "3"], groups: Set["staff"]), }) end end flippercloud-flipper-532c39a/spec/flipper/adapters/moneta_spec.rb000066400000000000000000000003211440444367400252220ustar00rootroot00000000000000require 'flipper/adapters/moneta' RSpec.describe Flipper::Adapters::Moneta do let(:moneta) { Moneta.new(:Memory) } subject { described_class.new(moneta) } it_should_behave_like 'a flipper adapter' end flippercloud-flipper-532c39a/spec/flipper/adapters/mongo_spec.rb000066400000000000000000000017751440444367400250740ustar00rootroot00000000000000require 'flipper/adapters/mongo' Mongo::Logger.logger.level = Logger::INFO RSpec.describe Flipper::Adapters::Mongo do subject { described_class.new(collection) } let(:host) { ENV['MONGODB_HOST'] || '127.0.0.1' } let(:port) { ENV['MONGODB_PORT'] || 27017 } let(:client) do logger = Logger.new('/dev/null') Mongo::Client.new(["#{host}:#{port}"], server_selection_timeout: 0.01, database: 'testing', logger: logger) end let(:collection) { client['testing'] } before do begin collection.drop rescue Mongo::Error::NoServerAvailable ENV['CI'] ? raise : skip('Mongo not available') rescue Mongo::Error::OperationFailure end collection.create end it_should_behave_like 'a flipper adapter' it 'configures itself on load' do Flipper.configuration = nil Flipper.instance = nil load 'flipper/adapters/mongo.rb' ENV["MONGO_URL"] ||= "mongodb://127.0.0.1:27017/testing" expect(Flipper.adapter.adapter).to be_a(Flipper::Adapters::Mongo) end end flippercloud-flipper-532c39a/spec/flipper/adapters/operation_logger_spec.rb000066400000000000000000000047251440444367400273120ustar00rootroot00000000000000require 'flipper/adapters/operation_logger' RSpec.describe Flipper::Adapters::OperationLogger do let(:operations) { [] } let(:adapter) { Flipper::Adapters::Memory.new } let(:flipper) { Flipper.new(adapter) } subject { described_class.new(adapter, operations) } it_should_behave_like 'a flipper adapter' it 'shows itself when inspect' do subject.features output = subject.inspect expect(output).to match(/OperationLogger/) expect(output).to match(/operation_logger/) expect(output).to match(/@type=:features/) expect(output).to match(/@adapter=# { events << now } } let(:interval) { 10 } let(:now) { subject.send(:now) } subject { described_class.new(synchronizer, interval: interval) } it 'synchronizes on first call' do expect(events.size).to be(0) subject.call expect(events.size).to be(1) end it "only invokes wrapped synchronizer every interval seconds" do subject.call events.clear # move time to one millisecond less than last sync + interval 1.upto(interval) do |i| allow(subject).to receive(:now).and_return(now + i - 1) subject.call end expect(events.size).to be(0) # move time to last sync + interval in milliseconds allow(subject).to receive(:now).and_return(now + interval) subject.call expect(events.size).to be(1) end end flippercloud-flipper-532c39a/spec/flipper/adapters/sync/synchronizer_spec.rb000066400000000000000000000053411440444367400274570ustar00rootroot00000000000000require "flipper/adapters/memory" require "flipper/instrumenters/memory" require "flipper/adapters/sync/synchronizer" RSpec.describe Flipper::Adapters::Sync::Synchronizer do let(:local) { Flipper::Adapters::Memory.new } let(:remote) { Flipper::Adapters::Memory.new } let(:local_flipper) { Flipper.new(local) } let(:remote_flipper) { Flipper.new(remote) } let(:instrumenter) { Flipper::Instrumenters::Memory.new } subject { described_class.new(local, remote, instrumenter: instrumenter) } it "instruments call" do subject.call expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0) events = instrumenter.events_by_name("synchronizer_call.flipper") expect(events.size).to be(1) end it "raises errors by default" do exception = StandardError.new expect(remote).to receive(:get_all).and_raise(exception) expect { subject.call }.to raise_error(exception) end context "when raise disabled" do subject do options = { instrumenter: instrumenter, raise: false, } described_class.new(local, remote, options) end it "does not raise, but instruments exceptions for visibility" do exception = StandardError.new expect(remote).to receive(:get_all).and_raise(exception) expect { subject.call }.not_to raise_error events = instrumenter.events_by_name("synchronizer_exception.flipper") expect(events.size).to be(1) event = events[0] expect(event.payload[:exception]).to eq(exception) end end describe '#call' do it 'returns nothing' do expect(subject.call).to be(nil) expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0) end it 'syncs each remote feature to local' do remote_flipper.enable(:search) remote_flipper.enable_percentage_of_time(:logging, 10) subject.call expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0) expect(local_flipper[:search].boolean_value).to eq(true) expect(local_flipper[:logging].percentage_of_time_value).to eq(10) expect(local_flipper.features.map(&:key).sort).to eq(%w(logging search)) end it 'adds features in remote that are not in local' do remote_flipper.add(:search) subject.call expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0) expect(local_flipper.features.map(&:key)).to eq(["search"]) end it 'removes features in local that are not in remote' do local_flipper.add(:stats) subject.call expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0) expect(local_flipper.features.map(&:key)).to eq([]) end end end flippercloud-flipper-532c39a/spec/flipper/adapters/sync_spec.rb000066400000000000000000000145561440444367400247320ustar00rootroot00000000000000require 'flipper/adapters/sync' require 'flipper/adapters/operation_logger' require 'active_support/notifications' RSpec.describe Flipper::Adapters::Sync do let(:local_adapter) do Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new end let(:remote_adapter) do Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new end let(:local) { Flipper.new(local_adapter) } let(:remote) { Flipper.new(remote_adapter) } let(:sync) { Flipper.new(subject) } subject do described_class.new(local_adapter, remote_adapter, interval: 1) end it_should_behave_like 'a flipper adapter' context 'when local has never been synced' do it 'syncs boolean' do remote.enable(:search) expect(sync[:search].boolean_value).to be(true) expect(subject.features.sort).to eq(%w(search)) end it 'syncs actor' do actor = Flipper::Actor.new("User;1000") remote.enable_actor(:search, actor) expect(sync[:search].actors_value).to eq(Set[actor.flipper_id]) expect(subject.features.sort).to eq(%w(search)) end it 'syncs group' do remote.enable_group(:search, :staff) expect(sync[:search].groups_value).to eq(Set["staff"]) expect(subject.features.sort).to eq(%w(search)) end it 'syncs percentage of actors' do remote.enable_percentage_of_actors(:search, 25) expect(sync[:search].percentage_of_actors_value).to eq(25) expect(subject.features.sort).to eq(%w(search)) end it 'syncs percentage of time' do remote.enable_percentage_of_time(:search, 15) expect(sync[:search].percentage_of_time_value).to eq(15) expect(subject.features.sort).to eq(%w(search)) end end it 'enables boolean locally when remote feature boolean enabled' do remote.disable(:search) local.disable(:search) remote.enable(:search) subject # initialize forces sync expect(local[:search].boolean_value).to be(true) end it 'disables boolean locally when remote feature disabled' do remote.enable(:search) local.enable(:search) remote.disable(:search) subject # initialize forces sync expect(local[:search].boolean_value).to be(false) end it 'adds local actor when remote actor is added' do actor = Flipper::Actor.new("User;235") remote.enable_actor(:search, actor) subject # initialize forces sync expect(local[:search].actors_value).to eq(Set[actor.flipper_id]) end it 'removes local actor when remote actor is removed' do actor = Flipper::Actor.new("User;235") remote.enable_actor(:search, actor) local.enable_actor(:search, actor) remote.disable(:search, actor) subject # initialize forces sync expect(local[:search].actors_value).to eq(Set.new) end it 'adds local group when remote group is added' do group = Flipper::Types::Group.new(:staff) remote.enable_group(:search, group) subject # initialize forces sync expect(local[:search].groups_value).to eq(Set["staff"]) end it 'removes local group when remote group is removed' do group = Flipper::Types::Group.new(:staff) remote.enable_group(:search, group) local.enable_group(:search, group) remote.disable(:search, group) subject # initialize forces sync expect(local[:search].groups_value).to eq(Set.new) end it 'updates percentage of actors when remote is updated' do remote.enable_percentage_of_actors(:search, 10) local.enable_percentage_of_actors(:search, 10) remote.enable_percentage_of_actors(:search, 15) subject # initialize forces sync expect(local[:search].percentage_of_actors_value).to eq(15) end it 'updates percentage of time when remote is updated' do remote.enable_percentage_of_time(:search, 10) local.enable_percentage_of_time(:search, 10) remote.enable_percentage_of_time(:search, 15) subject # initialize forces sync expect(local[:search].percentage_of_time_value).to eq(15) end context 'when local and remote match' do it 'does not update boolean enabled' do local.enable(:search) remote.enable(:search) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:enable)).to be(0) end it 'does not update boolean disabled' do local.disable(:search) remote.disable(:search) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:disable)).to be(0) end it 'does not update actors' do actor = Flipper::Actor.new("User;235") local.enable_actor(:search, actor) remote.enable_actor(:search, actor) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:enable)).to be(0) expect(local_adapter.count(:disable)).to be(0) end it 'does not update groups' do group = Flipper::Types::Group.new(:staff) local.enable_group(:search, group) remote.enable_group(:search, group) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:enable)).to be(0) expect(local_adapter.count(:disable)).to be(0) end it 'does not update percentage of actors' do local.enable_percentage_of_actors(:search, 10) remote.enable_percentage_of_actors(:search, 10) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:enable)).to be(0) expect(local_adapter.count(:disable)).to be(0) end it 'does not update percentage of time' do local.enable_percentage_of_time(:search, 10) remote.enable_percentage_of_time(:search, 10) local_adapter.reset subject # initialize forces sync expect(local_adapter.count(:enable)).to be(0) expect(local_adapter.count(:disable)).to be(0) end end it 'synchronizes for #features' do expect(subject).to receive(:synchronize) subject.features end it 'synchronizes for #get' do expect(subject).to receive(:synchronize) subject.get sync[:search] end it 'synchronizes for #get_multi' do expect(subject).to receive(:synchronize) subject.get_multi [sync[:search]] end it 'synchronizes for #get_all' do expect(subject).to receive(:synchronize) subject.get_all end it 'does not raise sync exceptions' do exception = StandardError.new expect(remote_adapter).to receive(:get_all).and_raise(exception) expect { subject.get_all }.not_to raise_error end end flippercloud-flipper-532c39a/spec/flipper/api/000077500000000000000000000000001440444367400213525ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/api/action_spec.rb000066400000000000000000000057261440444367400242000ustar00rootroot00000000000000RSpec.describe Flipper::Api::Action do let(:action_subclass) do Class.new(described_class) do def noooope raise 'should never run this' end def get [200, {}, 'get'] end def post [200, {}, 'post'] end def put [200, {}, 'put'] end def delete [200, {}, 'delete'] end end end describe 'https verbs' do it "won't run method that isn't whitelisted" do fake_request = Struct.new(:request_method, :env, :session).new('NOOOOPE', {}, {}) action = action_subclass.new(flipper, fake_request) expect do action.run end.to raise_error(Flipper::Api::RequestMethodNotSupported) end it 'will run get' do fake_request = Struct.new(:request_method, :env, :session).new('GET', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'get']) end it 'will run post' do fake_request = Struct.new(:request_method, :env, :session).new('POST', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'post']) end it 'will run put' do fake_request = Struct.new(:request_method, :env, :session).new('PUT', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'put']) end it 'will run delete' do fake_request = Struct.new(:request_method, :env, :session).new('DELETE', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'delete']) end end describe '#json_error_response' do describe ':feature_not_found' do it 'locates and serializes error correctly' do action = action_subclass.new({}, {}) response = catch(:halt) do action.json_error_response(:feature_not_found) end status, headers, body = response parsed_body = JSON.parse(body[0]) expect(headers['Content-Type']).to eq('application/json') expect(parsed_body).to eql(api_not_found_response) end end describe ':group_not_registered' do it 'locates and serializes error correctly' do action = action_subclass.new({}, {}) response = catch(:halt) do action.json_error_response(:group_not_registered) end status, headers, body = response parsed_body = JSON.parse(body[0]) expect(headers['Content-Type']).to eq('application/json') expect(parsed_body['code']).to eq(2) expect(parsed_body['message']).to eq('Group not registered.') expect(parsed_body['more_info']).to eq(api_error_code_reference_url) end end describe 'invalid error key' do it 'raises descriptive error' do action = action_subclass.new({}, {}) catch(:halt) do expect { action.json_error_response(:invalid_error_key) }.to raise_error(KeyError) end end end end end flippercloud-flipper-532c39a/spec/flipper/api/json_params_spec.rb000066400000000000000000000054371440444367400252360ustar00rootroot00000000000000RSpec.describe Flipper::Api::JsonParams do let(:app) do app = lambda do |env| request = Rack::Request.new(env) [200, { 'Content-Type' => 'application/json' }, [JSON.generate(request.params)]] end builder = Rack::Builder.new builder.use described_class builder.run app builder end describe 'json post request' do it 'adds request body to params' do response = post '/', JSON.generate(flipper_id: 'User;2'), 'CONTENT_TYPE' => 'application/json' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2') end it 'handles request bodies with multiple params' do response = post '/', JSON.generate(flipper_id: 'User;2', language: 'ruby'), 'CONTENT_TYPE' => 'application/json' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby') end it 'handles request bodies and single query string params' do response = post '/?language=ruby', JSON.generate(flipper_id: 'User;2'), 'CONTENT_TYPE' => 'application/json' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby') end it 'handles request bodies and multiple query string params' do response = post '/?language=ruby&framework=rails', JSON.generate(flipper_id: 'User;2'), 'CONTENT_TYPE' => 'application/json' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby', 'framework' => 'rails') end it 'favors request body params' do response = post '/?language=javascript', JSON.generate(flipper_id: 'User;2', language: 'ruby'), 'CONTENT_TYPE' => 'application/json' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby') end end describe 'url-encoded request' do it 'handles params the same as a json request' do response = post '/', flipper_id: 'User;2' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2') end it 'handles single query string params' do response = post '/?language=ruby', flipper_id: 'User;2' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby') end it 'handles multiple query string params' do response = post '/?language=ruby&framework=rails', flipper_id: 'User;2' params = JSON.parse(response.body) expect(params).to eq('flipper_id' => 'User;2', 'language' => 'ruby', 'framework' => 'rails') end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/000077500000000000000000000000001440444367400217005ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/000077500000000000000000000000001440444367400233405ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/actors_gate_spec.rb000066400000000000000000000115511440444367400271750ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::ActorsGate do let(:app) { build_api(flipper) } let(:actor) { Flipper::Actor.new('1') } describe 'enable' do before do flipper[:my_feature].disable_actor(actor) post '/features/my_feature/actors', flipper_id: actor.flipper_id end it 'enables feature for actor' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(actor)).to be_truthy expect(flipper[:my_feature].enabled_gate_names).to eq([:actor]) end it 'returns decorated feature with actor enabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to eq(['1']) end end describe 'disable' do let(:actor) { Flipper::Actor.new('1') } before do flipper[:my_feature].enable_actor(actor) delete '/features/my_feature/actors', flipper_id: actor.flipper_id end it 'disables feature' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(actor)).to be_falsy expect(flipper[:my_feature].enabled_gate_names).to be_empty end it 'returns decorated feature with boolean gate disabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to be_empty end end describe 'enable feature with slash in name' do before do flipper["my/feature"].disable_actor(actor) post '/features/my/feature/actors', flipper_id: actor.flipper_id end it 'enables feature for actor' do expect(last_response.status).to eq(200) expect(flipper["my/feature"].enabled?(actor)).to be_truthy expect(flipper["my/feature"].enabled_gate_names).to eq([:actor]) end it 'returns decorated feature with actor enabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to eq(['1']) end end describe 'enable feature with space in name' do before do flipper["sp ace"].disable_actor(actor) post '/features/sp%20ace/actors', flipper_id: actor.flipper_id end it 'enables feature for actor' do expect(last_response.status).to eq(200) expect(flipper["sp ace"].enabled?(actor)).to be_truthy expect(flipper["sp ace"].enabled_gate_names).to eq([:actor]) end it 'returns decorated feature with actor enabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to eq(['1']) end end describe 'enable missing flipper_id parameter' do before do flipper[:my_feature].enable post '/features/my_feature/actors' end it 'returns correct error response' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_flipper_id_is_missing_response) end end describe 'disable missing flipper_id parameter' do before do flipper[:my_feature].enable delete '/features/my_feature/actors' end it 'returns correct error response' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_flipper_id_is_missing_response) end end describe 'enable nil flipper_id parameter' do before do flipper[:my_feature].enable post '/features/my_feature/actors', flipper_id: nil end it 'returns correct error response' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_flipper_id_is_missing_response) end end describe 'disable nil flipper_id parameter' do before do flipper[:my_feature].enable delete '/features/my_feature/actors', flipper_id: nil end it 'returns correct error response' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_flipper_id_is_missing_response) end end describe 'enable missing feature' do before do post '/features/my_feature/actors', flipper_id: actor.flipper_id end it 'enables feature for actor' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(actor)).to be_truthy expect(flipper[:my_feature].enabled_gate_names).to eq([:actor]) end it 'returns decorated feature with actor enabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to eq(['1']) end end describe 'disable missing feature' do before do delete '/features/my_feature/actors', flipper_id: actor.flipper_id end it 'disables feature' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(actor)).to be_falsy expect(flipper[:my_feature].enabled_gate_names).to be_empty end it 'returns decorated feature with boolean gate disabled' do gate = json_response['gates'].find { |gate| gate['key'] == 'actors' } expect(gate['value']).to be_empty end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/actors_spec.rb000066400000000000000000000047141440444367400262000ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::Actors do let(:app) { build_api(flipper) } let(:actor) { Flipper::Actor.new('User123') } describe 'GET /actors/:flipper_id' do before do flipper[:my_feature_1].enable flipper[:my_feature_2].disable flipper[:my_feature_3].enable_actor(actor) end context 'when no feature is specified' do before do get "/actors/#{actor.flipper_id}" end it 'responds with success' do expect(last_response.status).to eq(200) end it 'returns all features' do expected_response = { 'flipper_id' => 'User123', 'features' => { 'my_feature_1' => { 'enabled' => true, }, 'my_feature_2' => { 'enabled' => false, }, 'my_feature_3' => { 'enabled' => true, }, }, } expect(json_response).to eq(expected_response) end end context 'when features are specified' do before do get "/actors/#{actor.flipper_id}", keys: "my_feature_2,my_feature_3" end it 'responds with success' do expect(last_response.status).to eq(200) end it 'returns all specified features' do expected_response = { 'flipper_id' => 'User123', 'features' => { 'my_feature_2' => { 'enabled' => false, }, 'my_feature_3' => { 'enabled' => true, }, }, } expect(json_response).to eq(expected_response) end end context 'when non-existent features are specified' do before do get "/actors/#{actor.flipper_id}", keys: "my_feature_3,not_a_feature" end it 'responds with success' do expect(last_response.status).to eq(200) end it 'returns false for a non-existent feature' do expected_response = { 'flipper_id' => 'User123', 'features' => { 'my_feature_3' => { 'enabled' => true, }, 'not_a_feature' => { 'enabled' => false, }, }, } expect(json_response).to eq(expected_response) end end context 'when flipper id is missing' do before do get "/actors" end it 'responds with a 404' do expect(last_response.status).to eq(404) end end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/boolean_gate_spec.rb000066400000000000000000000030061440444367400273150ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::BooleanGate do let(:app) { build_api(flipper) } describe 'enable' do before do flipper[:my_feature].disable post '/features/my_feature/boolean' end it 'enables feature' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].on?).to be_truthy end it 'returns decorated feature with boolean gate enabled' do boolean_gate = json_response['gates'].find { |gate| gate['key'] == 'boolean' } expect(boolean_gate['value']).to be_truthy end end describe 'enable feature with slash in name' do before do flipper["my/feature"].disable post '/features/my/feature/boolean' end it 'enables feature' do expect(last_response.status).to eq(200) expect(flipper["my/feature"].on?).to be_truthy end it 'returns decorated feature with boolean gate enabled' do boolean_gate = json_response['gates'].find { |gate| gate['key'] == 'boolean' } expect(boolean_gate['value']).to be_truthy end end describe 'disable' do before do flipper[:my_feature].enable delete '/features/my_feature/boolean' end it 'disables feature' do expect(last_response.status).to eq(200) expect(flipper[:my_feature].off?).to be_truthy end it 'returns decorated feature with boolean gate disabled' do boolean_gate = json_response['gates'].find { |gate| gate['key'] == 'boolean' } expect(boolean_gate['value']).to be_falsy end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/clear_feature_spec.rb000066400000000000000000000023021440444367400274750ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::ClearFeature do let(:app) { build_api(flipper) } describe 'clear' do before do Flipper.register(:admins) {} actor22 = Flipper::Actor.new('22') feature = flipper[:my_feature] feature.enable flipper.boolean feature.enable flipper.group(:admins) feature.enable flipper.actor(actor22) feature.enable flipper.actors(25) feature.enable flipper.time(45) delete '/features/my_feature/clear' end it 'clears feature' do expect(last_response.status).to eq(204) expect(flipper[:my_feature].off?).to be_truthy end end describe 'clear feature with slash in name' do before do Flipper.register(:admins) {} actor22 = Flipper::Actor.new('22') feature = flipper["my/feature"] feature.enable flipper.boolean feature.enable flipper.group(:admins) feature.enable flipper.actor(actor22) feature.enable flipper.actors(25) feature.enable flipper.time(45) delete '/features/my/feature/clear' end it 'clears feature' do expect(last_response.status).to eq(204) expect(flipper["my/feature"].off?).to be_truthy end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/feature_spec.rb000066400000000000000000000131531440444367400263350ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::Feature do let(:app) { build_api(flipper) } let(:feature) { build_feature } let(:gate) { feature.gate(:boolean) } describe 'get' do context 'enabled feature' do before do flipper[:my_feature].enable get '/features/my_feature' end it 'responds with correct attributes' do response_body = { 'key' => 'my_feature', 'state' => 'on', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => 'true', }, { 'key' => 'actors', 'name' => 'actor', 'value' => [], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], } expect(last_response.status).to eq(200) expect(json_response).to eq(response_body) end end context 'disabled feature' do before do flipper[:my_feature].disable get '/features/my_feature' end it 'responds with correct attributes' do response_body = { 'key' => 'my_feature', 'state' => 'off', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => nil, }, { 'key' => 'actors', 'name' => 'actor', 'value' => [], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], } expect(last_response.status).to eq(200) expect(json_response).to eq(response_body) end end context 'feature does not exist' do before do get '/features/not_a_feature' end it '404s' do expect(last_response.status).to eq(404) expected = { 'code' => 1, 'message' => 'Feature not found.', 'more_info' => api_error_code_reference_url, } expect(json_response).to eq(expected) end end context 'feature with name that ends in "features"' do before do flipper[:search_features].enable get '/features/search_features' end it 'responds with correct attributes' do response_body = { 'key' => 'search_features', 'state' => 'on', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => 'true', }, { 'key' => 'actors', 'name' => 'actor', 'value' => [], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], } expect(last_response.status).to eq(200) expect(json_response).to eq(response_body) end end context 'feature with name that has slash' do before do flipper["my/feature"].enable get '/features/my/feature' end it 'responds with correct attributes' do response_body = { 'key' => 'my/feature', 'state' => 'on', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => 'true', }, { 'key' => 'actors', 'name' => 'actor', 'value' => [], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], } expect(last_response.status).to eq(200) expect(json_response).to eq(response_body) end end end describe 'delete' do context 'succesful request' do it 'deletes feature' do flipper[:my_feature].enable expect(flipper.features.map(&:key)).to include('my_feature') delete '/features/my_feature' expect(last_response.status).to eq(204) expect(flipper.features.map(&:key)).not_to include('my_feature') end end context 'feature not found' do before do delete '/features/my_feature' end it 'responds with 204' do expect(last_response.status).to eq(204) expect(flipper.features.map(&:key)).not_to include('my_feature') end end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/features_spec.rb000066400000000000000000000111501440444367400265130ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::Features do let(:app) { build_api(flipper) } let(:feature) { build_feature } let(:admin) { double 'Fake Fliper Thing', flipper_id: 10 } describe 'get' do context 'with flipper features' do before do flipper[:my_feature].enable flipper[:my_feature].enable(admin) get '/features' end it 'responds with correct attributes' do expected_response = { 'features' => [ { 'key' => 'my_feature', 'state' => 'on', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => 'true', }, { 'key' => 'actors', 'name' => 'actor', 'value' => ['10'], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], }, ], } expect(last_response.status).to eq(200) expect(json_response).to eq(expected_response) end end context 'with keys specified' do before do flipper[:audit_log].enable flipper[:issues].enable flipper[:search].enable flipper[:stats].disable get '/features', 'keys' => 'search,stats' end it 'responds with correct attributes' do expect(last_response.status).to eq(200) keys = json_response.fetch('features').map { |feature| feature.fetch('key') }.sort expect(keys).to eq(%w(search stats)) end end context 'with keys that are not existing features' do before do flipper[:search].disable flipper[:stats].disable get '/features', 'keys' => 'search,stats,not_a_feature,another_feature_that_does_not_exist' end it 'only returns features that exist' do expect(last_response.status).to eq(200) keys = json_response.fetch('features').map { |feature| feature.fetch('key') }.sort expect(keys).to eq(%w(search stats)) end end context 'with no flipper features' do before do get '/features' end it 'returns empty array for features key' do expected_response = { 'features' => [], } expect(last_response.status).to eq(200) expect(json_response).to eq(expected_response) end end end describe 'post' do context 'succesful request' do before do post '/features', name: 'my_feature' end it 'responds 200 ' do expect(last_response.status).to eq(200) end it 'returns decorated feature' do expected_response = { 'key' => 'my_feature', 'state' => 'off', 'gates' => [ { 'key' => 'boolean', 'name' => 'boolean', 'value' => nil, }, { 'key' => 'actors', 'name' => 'actor', 'value' => [], }, { 'key' => 'percentage_of_actors', 'name' => 'percentage_of_actors', 'value' => nil, }, { 'key' => 'percentage_of_time', 'name' => 'percentage_of_time', 'value' => nil, }, { 'key' => 'groups', 'name' => 'group', 'value' => [], }, ], } expect(json_response).to eq(expected_response) end it 'adds feature' do expect(flipper.features.map(&:key)).to include('my_feature') end it 'does not enable feature' do expect(flipper['my_feature'].enabled?).to be_falsy end end context 'bad request' do before do post '/features' end it 'returns correct status code' do expect(last_response.status).to eq(422) end it 'returns formatted error' do expected = { 'code' => 5, 'message' => 'Required parameter name is missing.', 'more_info' => api_error_code_reference_url, } expect(json_response).to eq(expected) end end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/groups_gate_spec.rb000066400000000000000000000126771440444367400272330ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::GroupsGate do let(:app) { build_api(flipper) } describe 'enable' do before do flipper[:my_feature].disable Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end post '/features/my_feature/groups', name: 'admins' end it 'enables feature for group' do person = double allow(person).to receive(:flipper_id).and_return(1) allow(person).to receive(:admin?).and_return(true) expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(person)).to be_truthy end it 'returns decorated feature with group enabled' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq(['admins']) end end describe 'enable feature with slash in name' do before do flipper["my/feature"].disable Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end post '/features/my/feature/groups', name: 'admins' end it 'enables feature for group' do person = double allow(person).to receive(:flipper_id).and_return(1) allow(person).to receive(:admin?).and_return(true) expect(last_response.status).to eq(200) expect(flipper["my/feature"].enabled?(person)).to be_truthy end it 'returns decorated feature with group enabled' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq(['admins']) end end describe 'enable without name params' do before do flipper[:my_feature].disable Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end post '/features/my_feature/groups' end it 'returns correct status code' do expect(last_response.status).to eq(422) end it 'returns formatted error' do expected = { 'code' => 5, 'message' => 'Required parameter name is missing.', 'more_info' => api_error_code_reference_url, } expect(json_response).to eq(expected) end end describe 'disable' do before do flipper[:my_feature].disable Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end flipper[:my_feature].enable_group(:admins) delete '/features/my_feature/groups', name: 'admins' end it 'disables feature for group' do person = double allow(person).to receive(:flipper_id).and_return(1) allow(person).to receive(:admin?).and_return(true) expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(person)).to be_falsey end it 'returns decorated feature with group disabled' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq([]) end end describe 'disable for non-existent feature' do before do Flipper.register(:admins) do |actor| actor.respond_to?(:admin?) && actor.admin? end delete '/features/my_feature/groups', name: 'admins' end it 'disables feature for group' do person = double allow(person).to receive(:flipper_id).and_return(1) allow(person).to receive(:admin?).and_return(true) expect(last_response.status).to eq(200) expect(flipper[:my_feature].enabled?(person)).to be_falsey end it 'returns decorated feature with group disabled' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq([]) end end describe 'disable for group not registered' do before do flipper[:my_feature].disable delete '/features/my_feature/groups', name: 'admins' end it '404s with correct error response when group not registered' do expect(last_response.status).to eq(404) expected = { 'code' => 2, 'message' => 'Group not registered.', 'more_info' => api_error_code_reference_url, } expect(json_response).to eq(expected) end end describe 'enable for group not registered when allow_unregistered_groups is true' do before do Flipper.unregister_groups flipper[:my_feature].disable post '/features/my_feature/groups', name: 'admins', allow_unregistered_groups: 'true' end it 'responds successfully' do expect(last_response.status).to eq(200) end it 'returns decorated feature with group in groups set' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq(['admins']) end it 'enables group' do expect(flipper[:my_feature].groups_value).to eq(Set["admins"]) end end describe 'disable for group not registered when allow_unregistered_groups is true' do before do Flipper.unregister_groups flipper[:my_feature].disable flipper[:my_feature].enable_group(:admins) delete '/features/my_feature/groups', name: 'admins', allow_unregistered_groups: 'true' end it 'responds successfully' do expect(last_response.status).to eq(200) end it 'returns decorated feature with group not in groups set' do group_gate = json_response['gates'].find { |m| m['name'] == 'group' } expect(group_gate['value']).to eq([]) end it 'disables group' do expect(flipper[:my_feature].groups_value).to be_empty end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb000066400000000000000000000112701440444367400320540ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do let(:app) { build_api(flipper) } describe 'enable' do shared_examples 'gates with percentage' do it 'enables gate for feature' do expect(flipper[path].enabled_gate_names).to include(:percentage_of_actors) end it 'returns decorated feature with gate enabled for a percent of actors' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' } expect(gate['value']).to eq(percentage) end end context 'for feature with slash in name' do let(:path) { 'my/feature' } before do flipper[path].disable post "/features/#{path}/percentage_of_actors", percentage: percentage end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end context 'url-encoded request' do let(:path) { :my_feature } before do flipper[:my_feature].disable post "/features/#{path}/percentage_of_actors", percentage: percentage end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end context 'json request' do let(:path) { :my_feature } before do flipper[:my_feature].disable post "/features/#{path}/percentage_of_actors", JSON.generate(percentage: percentage), 'CONTENT_TYPE' => 'application/json' end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end end describe 'disable without percentage' do before do flipper[:my_feature].enable_percentage_of_actors(10) delete '/features/my_feature/percentage_of_actors' end it 'disables gate for feature' do expect(flipper[:my_feature].enabled_gates).to be_empty end it 'returns decorated feature with gate disabled' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' } expect(gate['value']).to eq('0') end end describe 'disable with percentage' do before do flipper[:my_feature].enable_percentage_of_actors(10) delete '/features/my_feature/percentage_of_actors', JSON.generate(percentage: '5'), 'CONTENT_TYPE' => 'application/json' end it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' } expect(gate['value']).to eq('0') end end describe 'non-existent feature' do before do delete '/features/my_feature/percentage_of_actors' end it 'disables gate for feature' do expect(flipper[:my_feature].enabled_gates).to be_empty end it 'returns decorated feature with gate disabled' do expect(last_response.status).to eq(200) gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' } expect(gate['value']).to eq('0') end end describe 'out of range parameter percentage parameter' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_actors', percentage: '300' end it '422s with correct error response when percentage parameter is invalid' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end describe 'percentage parameter not a number' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_actors', percentage: 'foo' end it '422s with correct error response when percentage parameter is invalid' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end describe 'missing percentage parameter' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_actors' end it '422s with correct error response when percentage parameter is missing' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb000066400000000000000000000112261440444367400315200ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do let(:app) { build_api(flipper) } describe 'enable' do shared_examples 'gates with percentage' do it 'enables gate for feature' do expect(flipper[path].enabled_gate_names).to include(:percentage_of_time) end it 'returns decorated feature with gate enabled for a percent of times' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' } expect(gate['value']).to eq(percentage) end end context 'for feature with slash in name' do let(:path) { 'my/feature' } before do flipper[path].disable post "/features/#{path}/percentage_of_time", percentage: percentage end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end context 'url-encoded request' do let(:path) { :my_feature } before do flipper[:my_feature].disable post "/features/#{path}/percentage_of_time", percentage: percentage end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end context 'json request' do let(:path) { :my_feature } before do flipper[:my_feature].disable post "/features/#{path}/percentage_of_time", JSON.generate(percentage: percentage), 'CONTENT_TYPE' => 'application/json' end context 'with integer percentage' do let(:percentage) { '10' } it_behaves_like 'gates with percentage' end context 'with decimal percentage' do let(:percentage) { '0.05' } it_behaves_like 'gates with percentage' end end end describe 'disable without percentage' do before do flipper[:my_feature].enable_percentage_of_time(10) delete '/features/my_feature/percentage_of_time' end it 'disables gate for feature' do expect(flipper[:my_feature].enabled_gates).to be_empty end it 'returns decorated feature with gate disabled' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' } expect(gate['value']).to eq('0') end end describe 'disable with percentage' do before do flipper[:my_feature].enable_percentage_of_time(10) delete '/features/my_feature/percentage_of_time', JSON.generate(percentage: '5'), 'CONTENT_TYPE' => 'application/json' end it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do expect(last_response.status).to eq(200) gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' } expect(gate['value']).to eq('0') end end describe 'non-existent feature' do before do delete '/features/my_feature/percentage_of_time' end it 'disables gate for feature' do expect(flipper[:my_feature].enabled_gates).to be_empty end it 'returns decorated feature with gate disabled' do gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' } expect(gate['value']).to eq('0') end end describe 'out of range parameter percentage parameter' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_time', percentage: '300' end it '422s with correct error response when percentage parameter is invalid' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end describe 'percentage parameter not an number' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_time', percentage: 'foo' end it '422s with correct error response when percentage parameter is invalid' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end describe 'missing percentage parameter' do before do flipper[:my_feature].disable post '/features/my_feature/percentage_of_time' end it '422s with correct error response when percentage parameter is missing' do expect(last_response.status).to eq(422) expect(json_response).to eq(api_positive_percentage_error_response) end end end flippercloud-flipper-532c39a/spec/flipper/api/v1/decorators/000077500000000000000000000000001440444367400240455ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/api/v1/decorators/feature_spec.rb000066400000000000000000000015111440444367400270350ustar00rootroot00000000000000RSpec.describe Flipper::Api::V1::Decorators::Feature do describe "#as_json" do context "with exclude_gates set to true" do subject { described_class.new(flipper[:my_feature]).as_json(exclude_gates: true) } it "returns json WITHOUT feature gate data" do expect(subject.keys).to_not include("gates") end end context "with exclude_gates set to false" do subject { described_class.new(flipper[:my_feature]).as_json(exclude_gates: false) } it "returns json WITH feature gate data" do expect(subject.keys).to include("gates") end end context "without exclude_gates set" do subject { described_class.new(flipper[:my_feature]).as_json } it "returns json WITH feature gate data" do expect(subject.keys).to include("gates") end end end end flippercloud-flipper-532c39a/spec/flipper/api_spec.rb000066400000000000000000000057671440444367400227300ustar00rootroot00000000000000RSpec.describe Flipper::Api do describe 'Initializing middleware with flipper instance' do let(:app) { build_api(flipper) } it 'works' do flipper.enable :a flipper.disable :b get '/features' expect(last_response.status).to be(200) feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') } expect(feature_names).to eq(%w(a b)) end end describe 'Initializing middleware lazily with a block' do let(:app) { build_api(-> { flipper }) } it 'works' do flipper.enable :a flipper.disable :b get '/features' expect(last_response.status).to be(200) feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') } expect(feature_names).to eq(%w(a b)) end end context 'when initialized with flipper instance and flipper instance in env' do let(:app) { build_api(flipper) } it 'uses env instance over initialized instance' do flipper[:built_a].enable flipper[:built_b].disable env_flipper = build_flipper env_flipper[:env_a].enable env_flipper[:env_b].disable params = {} env = { 'flipper' => env_flipper, } get '/features', params, env expect(last_response.status).to eq(200) feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') } expect(feature_names).to eq(%w(env_a env_b)) end end context 'when initialized without flipper instance but flipper instance in env' do let(:app) { described_class.app } it 'uses env instance' do flipper[:a].enable flipper[:b].disable params = {} env = { 'flipper' => flipper, } get '/features', params, env expect(last_response.status).to eq(200) feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') } expect(feature_names).to eq(%w(a b)) end end context 'when initialized with env_key' do let(:app) { build_api(flipper, env_key: 'flipper_api') } it 'uses provided env key instead of default' do flipper[:a].enable flipper[:b].disable default_env_flipper = build_flipper default_env_flipper[:env_a].enable default_env_flipper[:env_b].disable params = {} env = { 'flipper' => default_env_flipper, 'flipper_api' => flipper, } get '/features', params, env expect(last_response.status).to eq(200) feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') } expect(feature_names).to eq(%w(a b)) end end context "when request does not match any api routes" do let(:app) { build_api(flipper) } it "returns 404" do get '/gibberish' expect(last_response.status).to eq(404) expect(json_response).to eq({}) end end describe 'Inspecting the built Rack app' do it 'returns a String' do expect(build_api(flipper).inspect).to eq("Flipper::Api") end end end flippercloud-flipper-532c39a/spec/flipper/cloud/000077500000000000000000000000001440444367400217075ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/cloud/configuration_spec.rb000066400000000000000000000202151440444367400261150ustar00rootroot00000000000000require 'flipper/cloud/configuration' require 'flipper/adapters/instrumented' RSpec.describe Flipper::Cloud::Configuration do let(:required_options) do { token: "asdf" } end it "can set token" do instance = described_class.new(required_options) expect(instance.token).to eq(required_options[:token]) end it "can set token from ENV var" do ENV["FLIPPER_CLOUD_TOKEN"] = "from_env" instance = described_class.new(required_options.reject { |k, v| k == :token }) expect(instance.token).to eq("from_env") end it "can set instrumenter" do instrumenter = Object.new instance = described_class.new(required_options.merge(instrumenter: instrumenter)) expect(instance.instrumenter).to be(instrumenter) end it "can set read_timeout" do instance = described_class.new(required_options.merge(read_timeout: 5)) expect(instance.read_timeout).to eq(5) end it "can set read_timeout from ENV var" do ENV["FLIPPER_CLOUD_READ_TIMEOUT"] = "9" instance = described_class.new(required_options.reject { |k, v| k == :read_timeout }) expect(instance.read_timeout).to eq(9) end it "can set open_timeout" do instance = described_class.new(required_options.merge(open_timeout: 5)) expect(instance.open_timeout).to eq(5) end it "can set open_timeout from ENV var" do ENV["FLIPPER_CLOUD_OPEN_TIMEOUT"] = "9" instance = described_class.new(required_options.reject { |k, v| k == :open_timeout }) expect(instance.open_timeout).to eq(9) end it "can set write_timeout" do instance = described_class.new(required_options.merge(write_timeout: 5)) expect(instance.write_timeout).to eq(5) end it "can set write_timeout from ENV var" do ENV["FLIPPER_CLOUD_WRITE_TIMEOUT"] = "9" instance = described_class.new(required_options.reject { |k, v| k == :write_timeout }) expect(instance.write_timeout).to eq(9) end it "can set sync_interval" do instance = described_class.new(required_options.merge(sync_interval: 1)) expect(instance.sync_interval).to eq(1) end it "can set sync_interval from ENV var" do ENV["FLIPPER_CLOUD_SYNC_INTERVAL"] = "5" instance = described_class.new(required_options.reject { |k, v| k == :sync_interval }) expect(instance.sync_interval).to eq(5) end it "passes sync_interval into sync adapter" do # The initial sync of http to local invokes this web request. stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}") instance = described_class.new(required_options.merge(sync_interval: 1)) poller = instance.send(:poller) expect(poller.interval).to eq(1) end it "can set debug_output" do instance = described_class.new(required_options.merge(debug_output: STDOUT)) expect(instance.debug_output).to eq(STDOUT) end it "defaults adapter block" do # The initial sync of http to local invokes this web request. stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}") instance = described_class.new(required_options) expect(instance.adapter).to be_instance_of(Flipper::Adapters::Poll) end it "can override adapter block" do # The initial sync of http to local invokes this web request. stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}") instance = described_class.new(required_options) instance.adapter do |adapter| Flipper::Adapters::Instrumented.new(adapter) end expect(instance.adapter).to be_instance_of(Flipper::Adapters::Instrumented) end it "defaults url" do instance = described_class.new(required_options.reject { |k, v| k == :url }) expect(instance.url).to eq("https://www.flippercloud.io/adapter") end it "can override url using options" do options = required_options.merge(url: "http://localhost:5000/adapter") instance = described_class.new(options) expect(instance.url).to eq("http://localhost:5000/adapter") instance = described_class.new(required_options) instance.url = "http://localhost:5000/adapter" expect(instance.url).to eq("http://localhost:5000/adapter") end it "can override URL using ENV var" do ENV["FLIPPER_CLOUD_URL"] = "https://example.com" instance = described_class.new(required_options.reject { |k, v| k == :url }) expect(instance.url).to eq("https://example.com") end it "defaults sync_method to :poll" do instance = described_class.new(required_options) expect(instance.sync_method).to eq(:poll) end it "sets sync_method to :webhook if sync_secret provided" do instance = described_class.new(required_options.merge({ sync_secret: "secret", })) expect(instance.sync_method).to eq(:webhook) expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite) end it "sets sync_method to :webhook if FLIPPER_CLOUD_SYNC_SECRET set" do ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "abc" instance = described_class.new(required_options) expect(instance.sync_method).to eq(:webhook) expect(instance.adapter).to be_instance_of(Flipper::Adapters::DualWrite) end it "can set sync_secret" do instance = described_class.new(required_options.merge(sync_secret: "from_config")) expect(instance.sync_secret).to eq("from_config") end it "can override sync_secret using ENV var" do ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "from_env" instance = described_class.new(required_options.reject { |k, v| k == :sync_secret }) expect(instance.sync_secret).to eq("from_env") end it "can sync with cloud" do body = JSON.generate({ "features": [ { "key": "search", "state": "on", "gates": [ { "key": "boolean", "name": "boolean", "value": true }, { "key": "groups", "name": "group", "value": [] }, { "key": "actors", "name": "actor", "value": [] }, { "key": "percentage_of_actors", "name": "percentage_of_actors", "value": 0 }, { "key": "percentage_of_time", "name": "percentage_of_time", "value": 0 } ] }, { "key": "history", "state": "off", "gates": [ { "key": "boolean", "name": "boolean", "value": false }, { "key": "groups", "name": "group", "value": [] }, { "key": "actors", "name": "actor", "value": [] }, { "key": "percentage_of_actors", "name": "percentage_of_actors", "value": 0 }, { "key": "percentage_of_time", "name": "percentage_of_time", "value": 0 } ] } ] }) stub = stub_request(:get, "https://www.flippercloud.io/adapter/features"). with({ headers: { 'Flipper-Cloud-Token'=>'asdf', }, }).to_return(status: 200, body: body, headers: {}) instance = described_class.new(required_options) instance.sync # Check that remote was fetched. expect(stub).to have_been_requested # Check that local adapter really did sync. local_adapter = instance.local_adapter all = local_adapter.get_all expect(all.keys).to eq(["search", "history"]) expect(all["search"][:boolean]).to eq("true") expect(all["history"][:boolean]).to eq(nil) end it "can setup brow to report events to cloud" do # skip logging brow Brow.logger = Logger.new(File::NULL) brow = described_class.new(required_options).brow stub = stub_request(:post, "https://www.flippercloud.io/adapter/events") .with { |request| data = JSON.parse(request.body) data.keys == ["uuid", "messages"] && data["messages"] == [{"n" => 1}] } .to_return(status: 201, body: "{}", headers: {}) brow.push({"n" => 1}) brow.worker.stop expect(stub).to have_been_requested.times(1) end end flippercloud-flipper-532c39a/spec/flipper/cloud/dsl_spec.rb000066400000000000000000000050741440444367400240360ustar00rootroot00000000000000require 'flipper/cloud/configuration' require 'flipper/cloud/dsl' require 'flipper/adapters/operation_logger' require 'flipper/adapters/instrumented' RSpec.describe Flipper::Cloud::DSL do it 'delegates everything to flipper instance' do cloud_configuration = Flipper::Cloud::Configuration.new({ token: "asdf", sync_secret: "tasty", }) dsl = described_class.new(cloud_configuration) expect(dsl.features).to eq(Set.new) expect(dsl.enabled?(:foo)).to be(false) end it 'delegates sync to cloud configuration' do stub = stub_request(:get, "https://www.flippercloud.io/adapter/features"). with({ headers: { 'Flipper-Cloud-Token'=>'asdf', }, }).to_return(status: 200, body: '{"features": {}}', headers: {}) cloud_configuration = Flipper::Cloud::Configuration.new({ token: "asdf", sync_secret: "tasty", }) dsl = described_class.new(cloud_configuration) dsl.sync expect(stub).to have_been_requested end it 'delegates sync_secret to cloud configuration' do cloud_configuration = Flipper::Cloud::Configuration.new({ token: "asdf", sync_secret: "tasty", }) dsl = described_class.new(cloud_configuration) expect(dsl.sync_secret).to eq("tasty") end context "when sync_method is webhook" do let(:local_adapter) do Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new end let(:cloud_configuration) do cloud_configuration = Flipper::Cloud::Configuration.new({ token: "asdf", sync_secret: "tasty", local_adapter: local_adapter }) end subject do described_class.new(cloud_configuration) end it "sends reads to local adapter" do subject.features subject.enabled?(:foo) expect(local_adapter.count(:features)).to be(1) expect(local_adapter.count(:get)).to be(1) end it "sends writes to cloud and local" do add_stub = stub_request(:post, "https://www.flippercloud.io/adapter/features"). with({headers: {'Flipper-Cloud-Token'=>'asdf'}}). to_return(status: 200, body: '{}', headers: {}) enable_stub = stub_request(:post, "https://www.flippercloud.io/adapter/features/foo/boolean"). with(headers: {'Flipper-Cloud-Token'=>'asdf'}). to_return(status: 200, body: '{}', headers: {}) subject.enable(:foo) expect(local_adapter.count(:add)).to be(1) expect(local_adapter.count(:enable)).to be(1) expect(add_stub).to have_been_requested expect(enable_stub).to have_been_requested end end end flippercloud-flipper-532c39a/spec/flipper/cloud/engine_spec.rb000066400000000000000000000053401440444367400245150ustar00rootroot00000000000000require 'rails' require 'flipper/cloud' RSpec.describe Flipper::Cloud::Engine do let(:env) do { "FLIPPER_CLOUD_TOKEN" => "test-token" } end let(:application) do Class.new(Rails::Application) do config.eager_load = false config.logger = ActiveSupport::Logger.new($stdout) end end # App for Rack::Test let(:app) { application.routes } before do Rails.application = nil ActiveSupport::Dependencies.autoload_paths = ActiveSupport::Dependencies.autoload_paths.dup ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_once_paths.dup # Force loading of flipper to configure itself load 'flipper/cloud.rb' end it "initializes cloud configuration" do stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}") ENV.update(env) application.initialize! expect(Flipper.instance).to be_a(Flipper::Cloud::DSL) expect(Flipper.instance.instrumenter).to be(ActiveSupport::Notifications) end context "with CLOUD_SYNC_SECRET" do before do env.update "FLIPPER_CLOUD_SYNC_SECRET" => "test-secret" end let(:request_body) do JSON.generate({ "environment_id" => 1, "webhook_id" => 1, "delivery_id" => SecureRandom.uuid, "action" => "sync", }) end let(:timestamp) { Time.now } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: env["FLIPPER_CLOUD_SYNC_SECRET"]).generate(request_body, timestamp) } let(:signature_header_value) { Flipper::Cloud::MessageVerifier.new(secret: "").header(signature, timestamp) } it "configures webhook app" do ENV.update(env) application.initialize! stub = stub_request(:get, "https://www.flippercloud.io/adapter/features").with({ headers: { "Flipper-Cloud-Token" => ENV["FLIPPER_CLOUD_TOKEN"] }, }).to_return(status: 200, body: JSON.generate({ features: {} }), headers: {}) post "/_flipper", request_body, { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value } expect(last_response.status).to eq(200) expect(stub).to have_been_requested end end context "without CLOUD_SYNC_SECRET" do it "does not configure webhook app" do ENV.update(env) application.initialize! post "/_flipper" expect(last_response.status).to eq(404) end end context "without FLIPPER_CLOUD_TOKEN" do it "gracefully skips configuring webhook app" do ENV["FLIPPER_CLOUD_TOKEN"] = nil application.initialize! expect(silence { Flipper.instance }).to match(/Missing FLIPPER_CLOUD_TOKEN/) expect(Flipper.instance).to be_a(Flipper::DSL) post "/_flipper" expect(last_response.status).to eq(404) end end end flippercloud-flipper-532c39a/spec/flipper/cloud/message_verifier_spec.rb000066400000000000000000000107421440444367400265710ustar00rootroot00000000000000require 'flipper/cloud/message_verifier' RSpec.describe Flipper::Cloud::MessageVerifier do let(:payload) { "some payload" } let(:secret) { "secret" } let(:timestamp) { Time.now } describe "#generate" do it "generates signature that can be verified" do message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret) signature = message_verifier.generate(payload, timestamp) header = generate_header(timestamp: timestamp, signature: signature) expect(message_verifier.verify(payload, header)).to be(true) end end describe "#header" do it "generates a header in valid format" do version = "v1" message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret, version: version) signature = message_verifier.generate(payload, timestamp) header = message_verifier.header(signature, timestamp) expect(header).to eq("t=#{timestamp.to_i},#{version}=#{signature}") end end describe ".header" do it "generates a header in valid format" do version = "v1" message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret, version: version) signature = message_verifier.generate(payload, timestamp) header = Flipper::Cloud::MessageVerifier.header(signature, timestamp, version) expect(header).to eq("t=#{timestamp.to_i},#{version}=#{signature}") end end describe "#verify" do it "raises a InvalidSignature when the header does not have the expected format" do header = "i'm not even a real signature header" expect { message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret") message_verifier.verify(payload, header) }.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, "Unable to extract timestamp and signatures from header") end it "raises a InvalidSignature when there are no signatures with the expected version" do header = generate_header(version: "v0") expect { message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret") message_verifier.verify(payload, header) }.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, /No signatures found with expected version/) end it "raises a InvalidSignature when there are no valid signatures for the payload" do header = generate_header(signature: "bad_signature") expect { message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret") message_verifier.verify(payload, header) }.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, "No signatures found matching the expected signature for payload") end it "raises a InvalidSignature when the timestamp is not within the tolerance" do header = generate_header(timestamp: Time.now - 15) expect { message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret) message_verifier.verify(payload, header, tolerance: 10) }.to raise_error(Flipper::Cloud::MessageVerifier::InvalidSignature, /Timestamp outside the tolerance zone/) end it "returns true when the header contains a valid signature and the timestamp is within the tolerance" do header = generate_header message_verifier = Flipper::Cloud::MessageVerifier.new(secret: "secret") expect(message_verifier.verify(payload, header, tolerance: 10)).to be(true) end it "returns true when the header contains at least one valid signature" do header = generate_header + ",v1=bad_signature" message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret) expect(message_verifier.verify(payload, header, tolerance: 10)).to be(true) end it "returns true when the header contains a valid signature and the timestamp is off but no tolerance is provided" do header = generate_header(timestamp: Time.at(12_345)) message_verifier = Flipper::Cloud::MessageVerifier.new(secret: secret) expect(message_verifier.verify(payload, header)).to be(true) end end private def generate_header(options = {}) options[:secret] ||= secret options[:version] ||= "v1" message_verifier = Flipper::Cloud::MessageVerifier.new(secret: options[:secret], version: options[:version]) options[:timestamp] ||= timestamp options[:payload] ||= payload options[:signature] ||= message_verifier.generate(options[:payload], options[:timestamp]) Flipper::Cloud::MessageVerifier.header(options[:signature], options[:timestamp], options[:version]) end end flippercloud-flipper-532c39a/spec/flipper/cloud/middleware_spec.rb000066400000000000000000000220351440444367400253650ustar00rootroot00000000000000require 'securerandom' require 'flipper/cloud' require 'flipper/cloud/middleware' require 'flipper/adapters/operation_logger' RSpec.describe Flipper::Cloud::Middleware do let(:flipper) { Flipper::Cloud.new(token: "regular") do |config| config.local_adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) config.sync_secret = "regular_tasty" end } let(:env_flipper) { Flipper::Cloud.new(token: "env") do |config| config.local_adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new) config.sync_secret = "env_tasty" end } let(:app) { Flipper::Cloud.app(flipper) } let(:response_body) { JSON.generate({features: {}}) } let(:request_body) { JSON.generate({ "environment_id" => 1, "webhook_id" => 1, "delivery_id" => SecureRandom.uuid, "action" => "sync", }) } let(:timestamp) { Time.now } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: flipper.sync_secret).generate(request_body, timestamp) } let(:signature_header_value) { Flipper::Cloud::MessageVerifier.new(secret: "").header(signature, timestamp) } context 'when initializing middleware with flipper instance' do let(:app) { Flipper::Cloud.app(flipper) } it 'uses instance to sync' do Flipper.register(:admins) { |*args| false } Flipper.register(:staff) { |*args| false } Flipper.register(:basic) { |*args| false } Flipper.register(:plus) { |*args| false } Flipper.register(:premium) { |*args| false } stub = stub_request_for_token('regular') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(200) expect(JSON.parse(last_response.body)).to eq({ "groups" => [ {"name" => "admins"}, {"name" => "staff"}, {"name" => "basic"}, {"name" => "plus"}, {"name" => "premium"}, ], }) expect(stub).to have_been_requested end end context 'when signature is invalid' do let(:app) { Flipper::Cloud.app(flipper) } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: "nope").generate(request_body, timestamp) } it 'uses instance to sync' do stub = stub_request_for_token('regular') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(400) expect(stub).not_to have_been_requested end end context "when flipper cloud responds with 402" do let(:app) { Flipper::Cloud.app(flipper) } it "results in 402" do Flipper.register(:admins) { |*args| false } Flipper.register(:staff) { |*args| false } Flipper.register(:basic) { |*args| false } Flipper.register(:plus) { |*args| false } Flipper.register(:premium) { |*args| false } stub = stub_request_for_token('regular', status: 402) env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(402) expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Flipper::Adapters::Http::Error") expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to include("Failed with status: 402") expect(stub).to have_been_requested end end context "when flipper cloud responds with non-402 and non-2xx code" do let(:app) { Flipper::Cloud.app(flipper) } it "results in 500" do Flipper.register(:admins) { |*args| false } Flipper.register(:staff) { |*args| false } Flipper.register(:basic) { |*args| false } Flipper.register(:plus) { |*args| false } Flipper.register(:premium) { |*args| false } stub = stub_request_for_token('regular', status: 503) env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(500) expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Flipper::Adapters::Http::Error") expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to include("Failed with status: 503") expect(stub).to have_been_requested end end context "when flipper cloud responds with timeout" do let(:app) { Flipper::Cloud.app(flipper) } it "results in 500" do Flipper.register(:admins) { |*args| false } Flipper.register(:staff) { |*args| false } Flipper.register(:basic) { |*args| false } Flipper.register(:plus) { |*args| false } Flipper.register(:premium) { |*args| false } stub = stub_request_for_token('regular', status: :timeout) env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(500) expect(last_response.headers["Flipper-Cloud-Response-Error-Class"]).to eq("Net::OpenTimeout") expect(last_response.headers["Flipper-Cloud-Response-Error-Message"]).to eq("execution expired") expect(stub).to have_been_requested end end context 'when initialized with flipper instance and flipper instance in env' do let(:app) { Flipper::Cloud.app(flipper) } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: env_flipper.sync_secret).generate(request_body, timestamp) } it 'uses env instance to sync' do stub = stub_request_for_token('env') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, 'flipper' => env_flipper, } post '/', request_body, env expect(last_response.status).to eq(200) expect(stub).to have_been_requested end end context 'when initialized without flipper instance but flipper instance in env' do let(:app) { Flipper::Cloud.app } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: env_flipper.sync_secret).generate(request_body, timestamp) } it 'uses env instance to sync' do stub = stub_request_for_token('env') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, 'flipper' => env_flipper, } post '/', request_body, env expect(last_response.status).to eq(200) expect(stub).to have_been_requested end end context 'when initialized with env_key' do let(:app) { Flipper::Cloud.app(flipper, env_key: 'flipper_cloud') } let(:signature) { Flipper::Cloud::MessageVerifier.new(secret: env_flipper.sync_secret).generate(request_body, timestamp) } it 'uses provided env key instead of default' do stub = stub_request_for_token('env') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, 'flipper' => flipper, 'flipper_cloud' => env_flipper, } post '/', request_body, env expect(last_response.status).to eq(200) expect(stub).to have_been_requested end end context 'when initializing lazily with a block' do let(:app) { Flipper::Cloud.app(-> { flipper }) } it 'works' do stub = stub_request_for_token('regular') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/', request_body, env expect(last_response.status).to eq(200) expect(stub).to have_been_requested end end context 'when using older /webhooks path' do let(:app) { Flipper::Cloud.app(flipper) } it 'uses instance to sync' do Flipper.register(:admins) { |*args| false } Flipper.register(:staff) { |*args| false } Flipper.register(:basic) { |*args| false } Flipper.register(:plus) { |*args| false } Flipper.register(:premium) { |*args| false } stub = stub_request_for_token('regular') env = { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value, } post '/webhooks', request_body, env expect(last_response.status).to eq(200) expect(JSON.parse(last_response.body)).to eq({ "groups" => [ {"name" => "admins"}, {"name" => "staff"}, {"name" => "basic"}, {"name" => "plus"}, {"name" => "premium"}, ], }) expect(stub).to have_been_requested end end describe 'Request method unsupported' do it 'skips middleware' do get '/' expect(last_response.status).to eq(404) expect(last_response.content_type).to eq("application/json") expect(last_response.body).to eq("{}") end end describe 'Inspecting the built Rack app' do it 'returns a String' do expect(Flipper::Cloud.app(flipper).inspect).to eq("Flipper::Cloud") end end private def stub_request_for_token(token, status: 200) stub = stub_request(:get, "https://www.flippercloud.io/adapter/features"). with({ headers: { 'Flipper-Cloud-Token' => token, }, }) if status == :timeout stub.to_timeout else stub.to_return(status: status, body: response_body, headers: {}) end end end flippercloud-flipper-532c39a/spec/flipper/cloud_spec.rb000066400000000000000000000156521440444367400232570ustar00rootroot00000000000000require 'flipper/cloud' require 'flipper/adapters/instrumented' require 'flipper/instrumenters/memory' RSpec.describe Flipper::Cloud do before do stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}") end context "initialize with token" do let(:token) { 'asdf' } before do @instance = described_class.new(token: token) end it 'returns Flipper::DSL instance' do expect(@instance).to be_instance_of(Flipper::Cloud::DSL) end it 'can read the cloud configuration' do expect(@instance.cloud_configuration).to be_instance_of(Flipper::Cloud::Configuration) end it 'configures the correct adapter' do # pardon the nesting... memoized_adapter = @instance.adapter poll_adapter = memoized_adapter.adapter dual_write_adapter = poll_adapter.adapter expect(poll_adapter).to be_instance_of(Flipper::Adapters::Poll) expect(dual_write_adapter).to be_instance_of(Flipper::Adapters::DualWrite) http_adapter = dual_write_adapter.remote client = http_adapter.client expect(client.uri.scheme).to eq('https') expect(client.uri.host).to eq('www.flippercloud.io') expect(client.uri.path).to eq('/adapter') expect(client.headers['Flipper-Cloud-Token']).to eq(token) expect(@instance.instrumenter).to be(Flipper::Instrumenters::Noop) end end context 'initialize with token and options' do it 'sets correct url' do @instance = described_class.new(token: 'asdf', url: 'https://www.fakeflipper.com/sadpanda') uri = @instance.adapter.adapter.adapter.remote.client.uri expect(uri.scheme).to eq('https') expect(uri.host).to eq('www.fakeflipper.com') expect(uri.path).to eq('/sadpanda') end end it 'can initialize with no token explicitly provided' do ENV['FLIPPER_CLOUD_TOKEN'] = 'asdf' expect(described_class.new).to be_instance_of(Flipper::Cloud::DSL) end it 'can set instrumenter' do instrumenter = Flipper::Instrumenters::Memory.new instance = described_class.new(token: 'asdf', instrumenter: instrumenter) expect(instance.instrumenter).to be(instrumenter) end it 'allows wrapping adapter with another adapter like the instrumenter' do instance = described_class.new(token: 'asdf') do |config| config.adapter do |adapter| Flipper::Adapters::Instrumented.new(adapter) end end # instance.adapter is memoizable adapter instance expect(instance.adapter.adapter).to be_instance_of(Flipper::Adapters::Instrumented) end it 'can set debug_output' do expect(Flipper::Adapters::Http::Client).to receive(:new) .with(hash_including(debug_output: STDOUT)).at_least(:once) described_class.new(token: 'asdf', debug_output: STDOUT) end it 'can set read_timeout' do expect(Flipper::Adapters::Http::Client).to receive(:new) .with(hash_including(read_timeout: 1)).at_least(:once) described_class.new(token: 'asdf', read_timeout: 1) end it 'can set open_timeout' do expect(Flipper::Adapters::Http::Client).to receive(:new) .with(hash_including(open_timeout: 1)).at_least(:once) described_class.new(token: 'asdf', open_timeout: 1) end if RUBY_VERSION >= '2.6.0' it 'can set write_timeout' do expect(Flipper::Adapters::Http::Client).to receive(:new) .with(hash_including(open_timeout: 1)).at_least(:once) described_class.new(token: 'asdf', open_timeout: 1) end end it 'can import' do stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/). with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_return(status: 200, body: "{}", headers: {}) flipper = Flipper.new(Flipper::Adapters::Memory.new) flipper.enable(:test) flipper.enable(:search) flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker")) flipper.enable_percentage_of_time(:logging, 5) cloud_flipper = Flipper::Cloud.new(token: "asdf") get_all = { "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"}, "search" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "test" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, } expect(flipper.adapter.get_all).to eq(get_all) cloud_flipper.import(flipper) expect(flipper.adapter.get_all).to eq(get_all) expect(cloud_flipper.adapter.get_all).to eq(get_all) end it 'raises error for failure while importing' do stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/). with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_return(status: 500, body: "{}") flipper = Flipper.new(Flipper::Adapters::Memory.new) flipper.enable(:test) flipper.enable(:search) flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker")) flipper.enable_percentage_of_time(:logging, 5) cloud_flipper = Flipper::Cloud.new(token: "asdf") get_all = { "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"}, "search" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "test" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, } expect(flipper.adapter.get_all).to eq(get_all) expect { cloud_flipper.import(flipper) }.to raise_error(Flipper::Adapters::Http::Error) expect(flipper.adapter.get_all).to eq(get_all) end it 'raises error for timeout while importing' do stub_request(:post, /www\.flippercloud\.io\/adapter\/features.*/). with(headers: {'Flipper-Cloud-Token'=>'asdf'}).to_timeout flipper = Flipper.new(Flipper::Adapters::Memory.new) flipper.enable(:test) flipper.enable(:search) flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker")) flipper.enable_percentage_of_time(:logging, 5) cloud_flipper = Flipper::Cloud.new(token: "asdf") get_all = { "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"}, "search" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "stats" => {actors: Set["jnunemaker"], boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, "test" => {actors: Set.new, boolean: "true", groups: Set.new, percentage_of_actors: nil, percentage_of_time: nil}, } expect(flipper.adapter.get_all).to eq(get_all) expect { cloud_flipper.import(flipper) }.to raise_error(Net::OpenTimeout) expect(flipper.adapter.get_all).to eq(get_all) end end flippercloud-flipper-532c39a/spec/flipper/configuration_spec.rb000066400000000000000000000020341440444367400250060ustar00rootroot00000000000000require 'flipper/configuration' RSpec.describe Flipper::Configuration do describe '#adapter' do it 'returns instance using Memory adapter' do expect(subject.adapter).to be_a(Flipper::Adapters::Memory) end it 'can be set' do instance = Flipper::Adapters::Memory.new expect(subject.adapter).not_to be(instance) subject.adapter { instance } expect(subject.adapter).to be(instance) # All adapters are wrapped in Memoizable expect(subject.default.adapter.adapter).to be(instance) end end describe '#default' do it 'returns instance using Memory adapter' do expect(subject.default).to be_a(Flipper::DSL) # All adapters are wrapped in Memoizable expect(subject.default.adapter.adapter).to be_a(Flipper::Adapters::Memory) end it 'can be set default' do instance = Flipper.new(Flipper::Adapters::Memory.new) expect(subject.default).not_to be(instance) subject.default { instance } expect(subject.default).to be(instance) end end end flippercloud-flipper-532c39a/spec/flipper/dsl_spec.rb000066400000000000000000000246331440444367400227320ustar00rootroot00000000000000require 'flipper/dsl' RSpec.describe Flipper::DSL do subject { described_class.new(adapter) } let(:adapter) { Flipper::Adapters::Memory.new } describe '#initialize' do context 'when using default memoize strategy' do it 'wraps the given adapter with Flipper::Adapters::Memoizable' do dsl = described_class.new(adapter) expect(dsl.adapter.class).to be(Flipper::Adapters::Memoizable) expect(dsl.adapter.adapter).to be(adapter) end end context 'when disabling memoization' do it 'uses the given adapter directly' do dsl = described_class.new(adapter, memoize: false) expect(dsl.adapter).to be(adapter) end end it 'defaults instrumenter to noop' do dsl = described_class.new(adapter) expect(dsl.instrumenter).to be(Flipper::Instrumenters::Noop) end context 'with overriden instrumenter' do let(:instrumenter) { double('Instrumentor', instrument: nil) } it 'overrides default instrumenter' do dsl = described_class.new(adapter, instrumenter: instrumenter) expect(dsl.instrumenter).to be(instrumenter) end end end describe '#feature' do it_should_behave_like 'a DSL feature' do let(:method_name) { :feature } let(:instrumenter) { double('Instrumentor', instrument: nil) } let(:feature) { dsl.send(method_name, :stats) } let(:dsl) { described_class.new(adapter, instrumenter: instrumenter) } end end describe '#preload' do let(:instrumenter) { double('Instrumentor', instrument: nil) } let(:dsl) { described_class.new(adapter, instrumenter: instrumenter) } let(:names) { %i(stats shiny) } let(:features) { dsl.preload(names) } it 'returns array of features' do expect(features).to all be_instance_of(Flipper::Feature) end it 'sets names' do expect(features.map(&:name)).to eq(names) end it 'sets adapter' do features.each do |feature| expect(feature.adapter.name).to eq(dsl.adapter.name) end end it 'sets instrumenter' do features.each do |feature| expect(feature.instrumenter).to eq(dsl.instrumenter) end end it 'memoizes the feature' do features.each do |feature| expect(dsl.feature(feature.name)).to equal(feature) end end end describe '#preload_all' do let(:instrumenter) { double('Instrumentor', instrument: nil) } let(:dsl) do names.each { |name| adapter.add subject[name] } described_class.new(adapter, instrumenter: instrumenter) end let(:names) { %i(stats shiny) } let(:features) { dsl.preload_all } it 'returns array of features' do expect(features).to all be_instance_of(Flipper::Feature) end it 'sets names' do expect(features.map(&:key)).to eq(names.map(&:to_s)) end it 'sets adapter' do features.each do |feature| expect(feature.adapter.name).to eq(dsl.adapter.name) end end it 'sets instrumenter' do features.each do |feature| expect(feature.instrumenter).to eq(dsl.instrumenter) end end it 'memoizes the feature' do features.each do |feature| expect(dsl.feature(feature.name)).to equal(feature) end end end describe '#[]' do it_should_behave_like 'a DSL feature' do let(:method_name) { :[] } let(:instrumenter) { double('Instrumentor', instrument: nil) } let(:feature) { dsl.send(method_name, :stats) } let(:dsl) { described_class.new(adapter, instrumenter: instrumenter) } end end describe '#boolean' do it_should_behave_like 'a DSL boolean method' do let(:method_name) { :boolean } end end describe '#bool' do it_should_behave_like 'a DSL boolean method' do let(:method_name) { :bool } end end describe '#group' do context 'for registered group' do before do @group = Flipper.register(:admins) {} end it 'delegates to Flipper' do expect(Flipper).to receive(:group).with(:admins).and_return(@group) expect(subject.group(:admins)).to be(@group) end end end describe '#actor' do context 'for a thing' do it 'returns actor instance' do thing = Flipper::Actor.new(33) actor = subject.actor(thing) expect(actor).to be_instance_of(Flipper::Types::Actor) expect(actor.value).to eq('33') end end context 'for nil' do it 'raises argument error' do expect do subject.actor(nil) end.to raise_error(ArgumentError) end end context 'for something that is not actor wrappable' do it 'raises argument error' do expect do subject.actor(Object.new) end.to raise_error(ArgumentError) end end end describe '#time' do before do @result = subject.time(5) end it 'returns percentage of time' do expect(@result).to be_instance_of(Flipper::Types::PercentageOfTime) end it 'sets value' do expect(@result.value).to eq(5) end it 'is aliased to percentage_of_time' do expect(@result).to eq(subject.percentage_of_time(@result.value)) end end describe '#actors' do before do @result = subject.actors(17) end it 'returns percentage of actors' do expect(@result).to be_instance_of(Flipper::Types::PercentageOfActors) end it 'sets value' do expect(@result.value).to eq(17) end it 'is aliased to percentage_of_actors' do expect(@result).to eq(subject.percentage_of_actors(@result.value)) end end describe '#features' do context 'with no features enabled/disabled' do it 'defaults to empty set' do expect(subject.features).to eq(Set.new) end end context 'with features enabled and disabled' do before do subject[:stats].enable subject[:cache].enable subject[:search].disable end it 'returns set of feature instances' do expect(subject.features).to be_instance_of(Set) subject.features.each do |feature| expect(feature).to be_instance_of(Flipper::Feature) end expect(subject.features.map(&:name).map(&:to_s).sort).to eq(%w(cache search stats)) end end end describe '#enable/disable' do it 'enables and disables the feature' do expect(subject[:stats].boolean_value).to eq(false) subject.enable(:stats) expect(subject[:stats].boolean_value).to eq(true) subject.disable(:stats) expect(subject[:stats].boolean_value).to eq(false) end end describe '#enable_actor/disable_actor' do it 'enables and disables the feature for actor' do actor = Flipper::Actor.new(5) expect(subject[:stats].actors_value).to be_empty subject.enable_actor(:stats, actor) expect(subject[:stats].actors_value).to eq(Set['5']) subject.disable_actor(:stats, actor) expect(subject[:stats].actors_value).to be_empty end end describe '#enable_group/disable_group' do it 'enables and disables the feature for group' do actor = Flipper::Actor.new(5) group = Flipper.register(:fives) { |actor| actor.flipper_id == 5 } expect(subject[:stats].groups_value).to be_empty subject.enable_group(:stats, :fives) expect(subject[:stats].groups_value).to eq(Set['fives']) subject.disable_group(:stats, :fives) expect(subject[:stats].groups_value).to be_empty end end describe '#enable_percentage_of_time/disable_percentage_of_time' do it 'enables and disables the feature for percentage of time' do expect(subject[:stats].percentage_of_time_value).to be(0) subject.enable_percentage_of_time(:stats, 6) expect(subject[:stats].percentage_of_time_value).to be(6) subject.disable_percentage_of_time(:stats) expect(subject[:stats].percentage_of_time_value).to be(0) end it 'can enable/disable float values' do expect(subject[:stats].percentage_of_time_value).to be(0) subject.enable_percentage_of_time(:stats, 0.01) expect(subject[:stats].percentage_of_time_value).to be(0.01) subject.disable_percentage_of_time(:stats) expect(subject[:stats].percentage_of_time_value).to be(0) end end describe '#enable_percentage_of_actors/disable_percentage_of_actors' do it 'enables and disables the feature for percentage of time' do expect(subject[:stats].percentage_of_actors_value).to be(0) subject.enable_percentage_of_actors(:stats, 6) expect(subject[:stats].percentage_of_actors_value).to be(6) subject.disable_percentage_of_actors(:stats) expect(subject[:stats].percentage_of_actors_value).to be(0) end it 'can enable/disable float values' do expect(subject[:stats].percentage_of_actors_value).to be(0) subject.enable_percentage_of_actors(:stats, 0.01) expect(subject[:stats].percentage_of_actors_value).to be(0.01) subject.disable_percentage_of_actors(:stats) expect(subject[:stats].percentage_of_actors_value).to be(0) end end describe '#add' do it 'adds the feature' do expect(subject.features).to eq(Set.new) subject.add(:stats) expect(subject.features).to eq(Set[subject[:stats]]) end end describe '#exist?' do it 'returns true if the feature is added in adapter' do subject.add(:stats) expect(subject.exist?(:stats)).to be(true) end it 'returns false if the feature is NOT added in adapter' do expect(subject.exist?(:stats)).to be(false) end end describe '#remove' do it 'removes the feature' do subject.adapter.add(subject[:stats]) expect(subject.features).to eq(Set[subject[:stats]]) subject.remove(:stats) expect(subject.features).to eq(Set.new) end end describe '#import' do it 'delegates to adapter' do destination_flipper = build_flipper expect(subject.adapter).to receive(:import).with(destination_flipper.adapter) subject.import(destination_flipper) end end describe '#memoize=' do it 'delegates to adapter' do expect(subject.adapter).not_to be_memoizing subject.memoize = true expect(subject.adapter).to be_memoizing end end describe '#memoizing?' do it 'delegates to adapter' do subject.memoize = false expect(subject.adapter.memoizing?).to eq(subject.memoizing?) subject.memoize = true expect(subject.adapter.memoizing?).to eq(subject.memoizing?) end end end flippercloud-flipper-532c39a/spec/flipper/feature_check_context_spec.rb000066400000000000000000000036431440444367400265020ustar00rootroot00000000000000RSpec.describe Flipper::FeatureCheckContext do let(:feature_name) { :new_profiles } let(:values) { Flipper::GateValues.new({}) } let(:thing) { Flipper::Actor.new('5') } let(:options) do { feature_name: feature_name, values: values, thing: thing, } end it 'initializes just fine' do instance = described_class.new(**options) expect(instance.feature_name).to eq(feature_name) expect(instance.values).to eq(values) expect(instance.thing).to eq(thing) end it 'requires feature_name' do options.delete(:feature_name) expect do described_class.new(**options) end.to raise_error(ArgumentError) end it 'requires values' do options.delete(:values) expect do described_class.new(**options) end.to raise_error(ArgumentError) end it 'requires thing' do options.delete(:thing) expect do described_class.new(**options) end.to raise_error(ArgumentError) end it 'knows actors_value' do args = options.merge(values: Flipper::GateValues.new(actors: Set['User;1'])) expect(described_class.new(**args).actors_value).to eq(Set['User;1']) end it 'knows groups_value' do args = options.merge(values: Flipper::GateValues.new(groups: Set['admins'])) expect(described_class.new(**args).groups_value).to eq(Set['admins']) end it 'knows boolean_value' do instance = described_class.new(**options.merge(values: Flipper::GateValues.new(boolean: true))) expect(instance.boolean_value).to eq(true) end it 'knows percentage_of_actors_value' do args = options.merge(values: Flipper::GateValues.new(percentage_of_actors: 14)) expect(described_class.new(**args).percentage_of_actors_value).to eq(14) end it 'knows percentage_of_time_value' do args = options.merge(values: Flipper::GateValues.new(percentage_of_time: 41)) expect(described_class.new(**args).percentage_of_time_value).to eq(41) end end flippercloud-flipper-532c39a/spec/flipper/feature_spec.rb000066400000000000000000000600401440444367400235730ustar00rootroot00000000000000require 'flipper/feature' require 'flipper/instrumenters/memory' RSpec.describe Flipper::Feature do subject { described_class.new(:search, adapter) } let(:adapter) { Flipper::Adapters::Memory.new } describe '#initialize' do it 'sets name' do feature = described_class.new(:search, adapter) expect(feature.name).to eq(:search) end it 'sets adapter' do feature = described_class.new(:search, adapter) expect(feature.adapter).to eq(adapter) end it 'defaults instrumenter' do feature = described_class.new(:search, adapter) expect(feature.instrumenter).to be(Flipper::Instrumenters::Noop) end context 'with overriden instrumenter' do let(:instrumenter) { double('Instrumentor', instrument: nil) } it 'overrides default instrumenter' do feature = described_class.new(:search, adapter, instrumenter: instrumenter) expect(feature.instrumenter).to be(instrumenter) end end end describe '#to_s' do it 'returns name as string' do feature = described_class.new(:search, adapter) expect(feature.to_s).to eq('search') end end describe '#to_param' do it 'returns name as string' do feature = described_class.new(:search, adapter) expect(feature.to_param).to eq('search') end end describe '#gate_for' do context 'with percentage of actors' do it 'returns percentage of actors gate' do percentage = Flipper::Types::PercentageOfActors.new(10) gate = subject.gate_for(percentage) expect(gate).to be_instance_of(Flipper::Gates::PercentageOfActors) end end end describe '#gates' do it 'returns array of gates' do instance = described_class.new(:search, adapter) expect(instance.gates).to be_instance_of(Array) instance.gates.each do |gate| expect(gate).to be_a(Flipper::Gate) end expect(instance.gates.size).to be(5) end end describe '#gate' do context 'with symbol name' do it 'returns gate by name' do expect(subject.gate(:boolean)).to be_instance_of(Flipper::Gates::Boolean) end end context 'with string name' do it 'returns gate by name' do expect(subject.gate('boolean')).to be_instance_of(Flipper::Gates::Boolean) end end context 'with name that does not exist' do it 'returns nil' do expect(subject.gate(:poo)).to be_nil end end end describe '#add' do it 'adds feature to adapter' do expect(adapter.features).to eq(Set.new) subject.add expect(adapter.features).to eq(Set[subject.key]) end end describe '#exist?' do it 'returns true if feature is added in adapter' do subject.add expect(subject.exist?).to be(true) end it 'returns false if feature is NOT added in adapter' do expect(subject.exist?).to be(false) end end describe '#remove' do it 'removes feature from adapter' do adapter.add(subject) expect(adapter.features).to eq(Set[subject.key]) subject.remove expect(adapter.features).to eq(Set.new) end end describe '#clear' do it 'clears feature using adapter' do subject.enable expect(subject).to be_enabled subject.clear expect(subject).not_to be_enabled end end describe '#inspect' do it 'returns easy to read string representation' do string = subject.inspect expect(string).to include('Flipper::Feature') expect(string).to include('name=:search') expect(string).to include('state=:off') expect(string).to include('enabled_gate_names=[]') expect(string).to include("adapter=#{subject.adapter.name.inspect}") subject.enable string = subject.inspect expect(string).to include('state=:on') expect(string).to include('enabled_gate_names=[:boolean]') end end describe 'instrumentation' do let(:instrumenter) { Flipper::Instrumenters::Memory.new } subject do described_class.new(:search, adapter, instrumenter: instrumenter) end it 'is recorded for enable' do thing = Flipper::Types::Actor.new(Flipper::Actor.new('1')) gate = subject.gate_for(thing) subject.enable(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:enable) expect(event.payload[:thing]).to eq(thing) expect(event.payload[:result]).not_to be_nil end it 'always instruments flipper type instance for enable' do thing = Flipper::Actor.new('1') gate = subject.gate_for(thing) subject.enable(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(thing)) end it 'is recorded for disable' do thing = Flipper::Types::Boolean.new gate = subject.gate_for(thing) subject.disable(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:disable) expect(event.payload[:thing]).to eq(thing) expect(event.payload[:result]).not_to be_nil end user = Flipper::Actor.new('1') actor = Flipper::Types::Actor.new(user) boolean_true = Flipper::Types::Boolean.new(true) boolean_false = Flipper::Types::Boolean.new(false) group = Flipper::Types::Group.new(:admins) percentage_of_time = Flipper::Types::PercentageOfTime.new(10) percentage_of_actors = Flipper::Types::PercentageOfActors.new(10) { user => actor, actor => actor, true => boolean_true, false => boolean_false, boolean_true => boolean_true, boolean_false => boolean_false, :admins => group, group => group, percentage_of_time => percentage_of_time, percentage_of_actors => percentage_of_actors, }.each do |thing, wrapped_thing| it "always instruments #{thing.inspect} as #{wrapped_thing.class} for enable" do Flipper.register(:admins) {} subject.enable(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.payload[:operation]).to eq(:enable) expect(event.payload[:thing]).to eq(wrapped_thing) end end it 'always instruments flipper type instance for disable' do thing = Flipper::Actor.new('1') gate = subject.gate_for(thing) subject.disable(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.payload[:operation]).to eq(:disable) expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(thing)) end it 'is recorded for add' do subject.add event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:add) expect(event.payload[:result]).not_to be_nil end it 'is recorded for exist?' do subject.exist? event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:exist?) expect(event.payload[:result]).not_to be_nil end it 'is recorded for remove' do subject.remove event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:remove) expect(event.payload[:result]).not_to be_nil end it 'is recorded for clear' do subject.clear event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:clear) expect(event.payload[:result]).not_to be_nil end it 'is recorded for enabled?' do thing = Flipper::Types::Actor.new(Flipper::Actor.new('1')) gate = subject.gate_for(thing) subject.enabled?(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.name).to eq('feature_operation.flipper') expect(event.payload[:feature_name]).to eq(:search) expect(event.payload[:operation]).to eq(:enabled?) expect(event.payload[:thing]).to eq(thing) expect(event.payload[:result]).to eq(false) end user = Flipper::Actor.new('1') actor = Flipper::Types::Actor.new(user) { nil => nil, user => actor, actor => actor, }.each do |thing, wrapped_thing| it "always instruments #{thing.inspect} as #{wrapped_thing.class} for enabled?" do subject.enabled?(thing) event = instrumenter.events.last expect(event).not_to be_nil expect(event.payload[:operation]).to eq(:enabled?) expect(event.payload[:thing]).to eq(wrapped_thing) end end end describe '#state' do context 'fully on' do before do subject.enable end it 'returns :on' do expect(subject.state).to be(:on) end it 'returns true for on?' do expect(subject.on?).to be(true) end it 'returns false for off?' do expect(subject.off?).to be(false) end it 'returns false for conditional?' do expect(subject.conditional?).to be(false) end end context 'percentage of time set to 100' do before do subject.enable_percentage_of_time 100 end it 'returns :on' do expect(subject.state).to be(:on) end it 'returns true for on?' do expect(subject.on?).to be(true) end it 'returns false for off?' do expect(subject.off?).to be(false) end it 'returns false for conditional?' do expect(subject.conditional?).to be(false) end end context 'percentage of actors set to 100' do before do subject.enable_percentage_of_actors 100 end it 'returns :on' do expect(subject.state).to be(:conditional) end it 'returns false for on?' do expect(subject.on?).to be(false) end it 'returns false for off?' do expect(subject.off?).to be(false) end it 'returns true for conditional?' do expect(subject.conditional?).to be(true) end end context 'fully off' do before do subject.disable end it 'returns :off' do expect(subject.state).to be(:off) end it 'returns false for on?' do expect(subject.on?).to be(false) end it 'returns true for off?' do expect(subject.off?).to be(true) end it 'returns false for conditional?' do expect(subject.conditional?).to be(false) end end context 'partially on' do before do subject.enable Flipper::Types::PercentageOfTime.new(5) end it 'returns :conditional' do expect(subject.state).to be(:conditional) end it 'returns false for on?' do expect(subject.on?).to be(false) end it 'returns false for off?' do expect(subject.off?).to be(false) end it 'returns true for conditional?' do expect(subject.conditional?).to be(true) end end end describe '#enabled_groups' do context 'when no groups enabled' do it 'returns empty set' do expect(subject.enabled_groups).to eq(Set.new) end end context 'when one or more groups enabled' do before do @staff = Flipper.register(:staff) { |_thing| true } @preview_features = Flipper.register(:preview_features) { |_thing| true } @not_enabled = Flipper.register(:not_enabled) { |_thing| true } @disabled = Flipper.register(:disabled) { |_thing| true } subject.enable @staff subject.enable @preview_features subject.disable @disabled end it 'returns set of enabled groups' do expect(subject.enabled_groups).to eq(Set.new([ @staff, @preview_features, ])) end it 'does not include groups that have not been enabled' do expect(subject.enabled_groups).not_to include(@not_enabled) end it 'does not include disabled groups' do expect(subject.enabled_groups).not_to include(@disabled) end it 'is aliased to groups' do expect(subject.enabled_groups).to eq(subject.groups) end end end describe '#disabled_groups' do context 'when no groups enabled' do it 'returns empty set' do expect(subject.disabled_groups).to eq(Set.new) end end context 'when one or more groups enabled' do before do @staff = Flipper.register(:staff) { |_thing| true } @preview_features = Flipper.register(:preview_features) { |_thing| true } @not_enabled = Flipper.register(:not_enabled) { |_thing| true } @disabled = Flipper.register(:disabled) { |_thing| true } subject.enable @staff subject.enable @preview_features subject.disable @disabled end it 'returns set of groups that are not enabled' do expect(subject.disabled_groups).to eq(Set[ @not_enabled, @disabled, ]) end end end describe '#groups_value' do context 'when no groups enabled' do it 'returns empty set' do expect(subject.groups_value).to eq(Set.new) end end context 'when one or more groups enabled' do before do @staff = Flipper.register(:staff) { |_thing| true } @preview_features = Flipper.register(:preview_features) { |_thing| true } @not_enabled = Flipper.register(:not_enabled) { |_thing| true } @disabled = Flipper.register(:disabled) { |_thing| true } subject.enable @staff subject.enable @preview_features subject.disable @disabled end it 'returns set of enabled groups' do expect(subject.groups_value).to eq(Set.new([ @staff.name.to_s, @preview_features.name.to_s, ])) end it 'does not include groups that have not been enabled' do expect(subject.groups_value).not_to include(@not_enabled.name.to_s) end it 'does not include disabled groups' do expect(subject.groups_value).not_to include(@disabled.name.to_s) end end end describe '#actors_value' do context 'when no groups enabled' do it 'returns empty set' do expect(subject.actors_value).to eq(Set.new) end end context 'when one or more actors are enabled' do before do subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User;5')) subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User;22')) end it 'returns set of actor ids' do expect(subject.actors_value).to eq(Set.new(['User;5', 'User;22'])) end end end describe '#boolean_value' do context 'when not enabled or disabled' do it 'returns false' do expect(subject.boolean_value).to be(false) end end context 'when enabled' do before do subject.enable end it 'returns true' do expect(subject.boolean_value).to be(true) end end context 'when disabled' do before do subject.disable end it 'returns false' do expect(subject.boolean_value).to be(false) end end end describe '#percentage_of_actors_value' do context 'when not enabled or disabled' do it 'returns nil' do expect(subject.percentage_of_actors_value).to be(0) end end context 'when enabled' do before do subject.enable Flipper::Types::PercentageOfActors.new(5) end it 'returns true' do expect(subject.percentage_of_actors_value).to eq(5) end end context 'when disabled' do before do subject.disable end it 'returns nil' do expect(subject.percentage_of_actors_value).to be(0) end end end describe '#percentage_of_time_value' do context 'when not enabled or disabled' do it 'returns nil' do expect(subject.percentage_of_time_value).to be(0) end end context 'when enabled' do before do subject.enable Flipper::Types::PercentageOfTime.new(5) end it 'returns true' do expect(subject.percentage_of_time_value).to eq(5) end end context 'when disabled' do before do subject.disable end it 'returns nil' do expect(subject.percentage_of_time_value).to be(0) end end end describe '#gate_values' do context 'when no gates are set in adapter' do it 'returns default gate values' do expect(subject.gate_values).to eq(Flipper::GateValues.new(adapter.default_config)) end end context 'with gate values set in adapter' do before do subject.enable Flipper::Types::Boolean.new(true) subject.enable Flipper::Types::Actor.new(Flipper::Actor.new(5)) subject.enable Flipper::Types::Group.new(:admins) subject.enable Flipper::Types::PercentageOfTime.new(50) subject.enable Flipper::Types::PercentageOfActors.new(25) end it 'returns gate values' do expect(subject.gate_values).to eq(Flipper::GateValues.new(actors: Set.new(['5']), groups: Set.new(['admins']), boolean: 'true', percentage_of_time: '50', percentage_of_actors: '25')) end end end describe '#enable_actor/disable_actor' do context 'with object that responds to flipper_id' do it 'updates the gate values to include the actor' do actor = Flipper::Actor.new(5) expect(subject.gate_values.actors).to be_empty subject.enable_actor(actor) expect(subject.gate_values.actors).to eq(Set['5']) subject.disable_actor(actor) expect(subject.gate_values.actors).to be_empty end end context 'with actor instance' do it 'updates the gate values to include the actor' do actor = Flipper::Actor.new(5) instance = Flipper::Types::Actor.new(actor) expect(subject.gate_values.actors).to be_empty subject.enable_actor(instance) expect(subject.gate_values.actors).to eq(Set['5']) subject.disable_actor(instance) expect(subject.gate_values.actors).to be_empty end end end describe '#enable_group/disable_group' do context 'with symbol group name' do it 'updates the gate values to include the group' do actor = Flipper::Actor.new(5) group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 } expect(subject.gate_values.groups).to be_empty subject.enable_group(:five_only) expect(subject.gate_values.groups).to eq(Set['five_only']) subject.disable_group(:five_only) expect(subject.gate_values.groups).to be_empty end end context 'with string group name' do it 'updates the gate values to include the group' do actor = Flipper::Actor.new(5) group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 } expect(subject.gate_values.groups).to be_empty subject.enable_group('five_only') expect(subject.gate_values.groups).to eq(Set['five_only']) subject.disable_group('five_only') expect(subject.gate_values.groups).to be_empty end end context 'with group instance' do it 'updates the gate values for the group' do actor = Flipper::Actor.new(5) group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 } expect(subject.gate_values.groups).to be_empty subject.enable_group(group) expect(subject.gate_values.groups).to eq(Set['five_only']) subject.disable_group(group) expect(subject.gate_values.groups).to be_empty end end end describe '#enable_percentage_of_time/disable_percentage_of_time' do context 'with integer' do it 'updates the gate values' do expect(subject.gate_values.percentage_of_time).to be(0) subject.enable_percentage_of_time(56) expect(subject.gate_values.percentage_of_time).to be(56) subject.disable_percentage_of_time expect(subject.gate_values.percentage_of_time).to be(0) end end context 'with string' do it 'updates the gate values' do expect(subject.gate_values.percentage_of_time).to be(0) subject.enable_percentage_of_time('56') expect(subject.gate_values.percentage_of_time).to be(56) subject.disable_percentage_of_time expect(subject.gate_values.percentage_of_time).to be(0) end end context 'with percentage of time instance' do it 'updates the gate values' do percentage = Flipper::Types::PercentageOfTime.new(56) expect(subject.gate_values.percentage_of_time).to be(0) subject.enable_percentage_of_time(percentage) expect(subject.gate_values.percentage_of_time).to be(56) subject.disable_percentage_of_time expect(subject.gate_values.percentage_of_time).to be(0) end end end describe '#enable_percentage_of_actors/disable_percentage_of_actors' do context 'with integer' do it 'updates the gate values' do expect(subject.gate_values.percentage_of_actors).to be(0) subject.enable_percentage_of_actors(56) expect(subject.gate_values.percentage_of_actors).to be(56) subject.disable_percentage_of_actors expect(subject.gate_values.percentage_of_actors).to be(0) end end context 'with string' do it 'updates the gate values' do expect(subject.gate_values.percentage_of_actors).to be(0) subject.enable_percentage_of_actors('56') expect(subject.gate_values.percentage_of_actors).to be(56) subject.disable_percentage_of_actors expect(subject.gate_values.percentage_of_actors).to be(0) end end context 'with percentage of actors instance' do it 'updates the gate values' do percentage = Flipper::Types::PercentageOfActors.new(56) expect(subject.gate_values.percentage_of_actors).to be(0) subject.enable_percentage_of_actors(percentage) expect(subject.gate_values.percentage_of_actors).to be(56) subject.disable_percentage_of_actors expect(subject.gate_values.percentage_of_actors).to be(0) end end end describe '#enabled/disabled_gates' do before do subject.enable_percentage_of_time 5 subject.enable_percentage_of_actors 5 end it 'can return enabled gates' do expect(subject.enabled_gates.map(&:name).to_set).to eq(Set[ :percentage_of_actors, :percentage_of_time, ]) expect(subject.enabled_gate_names.to_set).to eq(Set[ :percentage_of_actors, :percentage_of_time, ]) end it 'can return disabled gates' do expect(subject.disabled_gates.map(&:name).to_set).to eq(Set[ :actor, :boolean, :group, ]) expect(subject.disabled_gate_names.to_set).to eq(Set[ :actor, :boolean, :group, ]) end end end flippercloud-flipper-532c39a/spec/flipper/gate_spec.rb000066400000000000000000000013621440444367400230620ustar00rootroot00000000000000RSpec.describe Flipper::Gate do let(:feature_name) { :stats } subject do described_class.new end describe '#inspect' do context 'for subclass' do let(:subclass) do Class.new(described_class) do def name :name end def key :key end def data_type :set end end end subject do subclass.new end it 'includes attributes' do string = subject.inspect expect(string).to include(subject.object_id.to_s) expect(string).to include('name=:name') expect(string).to include('key=:key') expect(string).to include('data_type=:set') end end end end flippercloud-flipper-532c39a/spec/flipper/gate_values_spec.rb000066400000000000000000000054501440444367400244430ustar00rootroot00000000000000require 'flipper/gate_values' RSpec.describe Flipper::GateValues do { nil => false, '' => false, 0 => false, 1 => true, '0' => false, '1' => true, true => true, false => false, 'true' => true, 'false' => false, }.each do |value, expected| context "with #{value.inspect} boolean" do it "returns #{expected}" do expect(described_class.new(boolean: value).boolean).to be(expected) end end end { nil => 0, '' => 0, 0 => 0, 1 => 1, '1' => 1, '99' => 99, }.each do |value, expected| context "with #{value.inspect} percentage of time" do it "returns #{expected}" do expect(described_class.new(percentage_of_time: value).percentage_of_time).to be(expected) end end end { nil => 0, '' => 0, 0 => 0, 1 => 1, '1' => 1, '99' => 99, }.each do |value, expected| context "with #{value.inspect} percentage of actors" do it "returns #{expected}" do expect(described_class.new(percentage_of_actors: value).percentage_of_actors) .to be(expected) end end end { nil => Set.new, '' => Set.new, Set.new([1, 2]) => Set.new([1, 2]), [1, 2] => Set.new([1, 2]), }.each do |value, expected| context "with #{value.inspect} actors" do it "returns #{expected}" do expect(described_class.new(actors: value).actors).to eq(expected) end end end { nil => Set.new, '' => Set.new, Set.new([:admins, :preview_features]) => Set.new([:admins, :preview_features]), [:admins, :preview_features] => Set.new([:admins, :preview_features]), }.each do |value, expected| context "with #{value.inspect} groups" do it "returns #{expected}" do expect(described_class.new(groups: value).groups).to eq(expected) end end end it 'raises argument error for percentage of time value that cannot be converted to an integer' do expect do described_class.new(percentage_of_time: ['asdf']) end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a percentage)) end it 'raises argument error for percentage of actors value that cannot be converted to an int' do expect do described_class.new(percentage_of_actors: ['asdf']) end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a percentage)) end it 'raises argument error for actors value that cannot be converted to a set' do expect do described_class.new(actors: 'asdf') end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set)) end it 'raises argument error for groups value that cannot be converted to a set' do expect do described_class.new(groups: 'asdf') end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set)) end end flippercloud-flipper-532c39a/spec/flipper/gates/000077500000000000000000000000001440444367400217045ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/gates/actor_spec.rb000066400000000000000000000001711440444367400243520ustar00rootroot00000000000000RSpec.describe Flipper::Gates::Actor do let(:feature_name) { :search } subject do described_class.new end end flippercloud-flipper-532c39a/spec/flipper/gates/boolean_spec.rb000066400000000000000000000034741440444367400246720ustar00rootroot00000000000000RSpec.describe Flipper::Gates::Boolean do let(:feature_name) { :search } subject do described_class.new end def context(bool) Flipper::FeatureCheckContext.new( feature_name: feature_name, values: Flipper::GateValues.new(boolean: bool), thing: Flipper::Types::Actor.new(Flipper::Actor.new(1)) ) end describe '#enabled?' do context 'for true value' do it 'returns true' do expect(subject.enabled?(true)).to eq(true) end end context 'for false value' do it 'returns false' do expect(subject.enabled?(false)).to eq(false) end end end describe '#open?' do context 'for true value' do it 'returns true' do expect(subject.open?(context(true))).to be(true) end end context 'for false value' do it 'returns false' do expect(subject.open?(context(false))).to be(false) end end end describe '#protects?' do it 'returns true for boolean type' do expect(subject.protects?(Flipper::Types::Boolean.new(true))).to be(true) end it 'returns true for true' do expect(subject.protects?(true)).to be(true) end it 'returns true for false' do expect(subject.protects?(false)).to be(true) end end describe '#wrap' do it 'returns boolean type for boolean type' do expect(subject.wrap(Flipper::Types::Boolean.new(true))) .to be_instance_of(Flipper::Types::Boolean) end it 'returns boolean type for true' do expect(subject.wrap(true)).to be_instance_of(Flipper::Types::Boolean) expect(subject.wrap(true).value).to be(true) end it 'returns boolean type for true' do expect(subject.wrap(false)).to be_instance_of(Flipper::Types::Boolean) expect(subject.wrap(false).value).to be(false) end end end flippercloud-flipper-532c39a/spec/flipper/gates/group_spec.rb000066400000000000000000000030351440444367400244000ustar00rootroot00000000000000RSpec.describe Flipper::Gates::Group do let(:feature_name) { :search } subject do described_class.new end def context(set) Flipper::FeatureCheckContext.new( feature_name: feature_name, values: Flipper::GateValues.new(groups: set), thing: Flipper::Types::Actor.new(Flipper::Actor.new('5')) ) end describe '#open?' do context 'with a group in adapter, but not registered' do before do Flipper.register(:staff) { |_thing| true } end it 'ignores group' do thing = Flipper::Actor.new('5') expect(subject.open?(context(Set[:newbs, :staff]))).to be(true) end end context 'thing that does not respond to method in group block' do before do Flipper.register(:stinkers, &:stinker?) end it 'raises error' do expect do subject.open?(context(Set[:stinkers])) end.to raise_error(NoMethodError) end end end describe '#wrap' do it 'returns group instance for symbol' do group = Flipper.register(:admins) {} expect(subject.wrap(:admins)).to eq(group) end it 'returns group instance for group instance' do group = Flipper.register(:admins) {} expect(subject.wrap(group)).to eq(group) end end describe '#protects?' do it 'returns true for group' do group = Flipper.register(:admins) {} expect(subject.protects?(group)).to be(true) end it 'returns true for symbol' do expect(subject.protects?(:admins)).to be(true) end end end flippercloud-flipper-532c39a/spec/flipper/gates/percentage_of_actors_spec.rb000066400000000000000000000045521440444367400274250ustar00rootroot00000000000000RSpec.describe Flipper::Gates::PercentageOfActors do let(:feature_name) { :search } subject do described_class.new end def context(percentage_of_actors_value, feature = feature_name, thing = nil) Flipper::FeatureCheckContext.new( feature_name: feature, values: Flipper::GateValues.new(percentage_of_actors: percentage_of_actors_value), thing: Flipper::Types::Actor.new(thing || Flipper::Actor.new(1)) ) end describe '#open?' do context 'when compared against two features' do let(:percentage) { 0.05 } let(:percentage_as_integer) { percentage * 100 } let(:number_of_actors) { 10_000 } let(:actors) do (1..number_of_actors).map { |n| Flipper::Actor.new(n) } end let(:feature_one_enabled_actors) do actors.select { |actor| subject.open? context(percentage_as_integer, :name_one, actor) } end let(:feature_two_enabled_actors) do actors.select { |actor| subject.open? context(percentage_as_integer, :name_two, actor) } end it 'does not enable both features for same set of actors' do expect(feature_one_enabled_actors).not_to eq(feature_two_enabled_actors) end it 'enables feature for accurate number of actors for each feature' do margin_of_error = 0.02 * number_of_actors # 2 percent margin of error expected_enabled_size = number_of_actors * percentage [ feature_one_enabled_actors.size, feature_two_enabled_actors.size, ].each do |actual_enabled_size| expect(actual_enabled_size).to be_within(margin_of_error).of(expected_enabled_size) end end end context 'for fractional percentage' do let(:decimal) { 0.001 } let(:percentage) { decimal * 100 } let(:number_of_actors) { 10_000 } let(:actors) do (1..number_of_actors).map { |n| Flipper::Actor.new(n) } end subject { described_class.new } it 'enables feature for accurate number of actors' do margin_of_error = 0.02 * number_of_actors expected_open_count = number_of_actors * decimal open_count = actors.select do |actor| context = context(percentage, :feature, actor) subject.open?(context) end.size expect(open_count).to be_within(margin_of_error).of(expected_open_count) end end end end flippercloud-flipper-532c39a/spec/flipper/gates/percentage_of_time_spec.rb000066400000000000000000000021721440444367400270640ustar00rootroot00000000000000RSpec.describe Flipper::Gates::PercentageOfTime do let(:feature_name) { :search } subject do described_class.new end def context(percentage_of_time_value, feature = feature_name, thing = nil) Flipper::FeatureCheckContext.new( feature_name: feature, values: Flipper::GateValues.new(percentage_of_time: percentage_of_time_value), thing: thing || Flipper::Types::Actor.new(Flipper::Actor.new(1)) ) end describe '#open?' do context 'for fractional percentage' do let(:decimal) { 0.001 } let(:percentage) { decimal * 100 } let(:number_of_invocations) { 10_000 } subject { described_class.new } it 'enables feature for accurate percentage of time' do margin_of_error = 0.02 * number_of_invocations expected_open_count = number_of_invocations * decimal open_count = (1..number_of_invocations).select do |_actor| context = context(percentage, :feature, Flipper::Actor.new("1")) subject.open?(context) end.size expect(open_count).to be_within(margin_of_error).of(expected_open_count) end end end end flippercloud-flipper-532c39a/spec/flipper/identifier_spec.rb000066400000000000000000000004261440444367400242640ustar00rootroot00000000000000require 'flipper/identifier' RSpec.describe Flipper::Identifier do describe '#flipper_id' do class User < Struct.new(:id) include Flipper::Identifier end it 'uses class name and id' do expect(User.new(5).flipper_id).to eq('User;5') end end end flippercloud-flipper-532c39a/spec/flipper/instrumentation/000077500000000000000000000000001440444367400240445ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/instrumentation/log_subscriber_spec.rb000066400000000000000000000054021440444367400304100ustar00rootroot00000000000000require 'logger' require 'flipper/adapters/instrumented' require 'flipper/instrumentation/log_subscriber' RSpec.describe Flipper::Instrumentation::LogSubscriber do let(:adapter) do memory = Flipper::Adapters::Memory.new Flipper::Adapters::Instrumented.new(memory, instrumenter: ActiveSupport::Notifications) end let(:flipper) do Flipper.new(adapter, instrumenter: ActiveSupport::Notifications) end before do Flipper.register(:admins) do |thing| thing.respond_to?(:admin?) && thing.admin? end @io = StringIO.new logger = Logger.new(@io) logger.formatter = proc { |_severity, _datetime, _progname, msg| "#{msg}\n" } described_class.logger = logger end after do described_class.logger = nil end let(:log) { @io.string } context 'feature enabled checks' do before do clear_logs flipper[:search].enabled? end it 'logs feature calls with result after operation' do feature_line = find_line('Flipper feature(search) enabled? false') expect(feature_line).to include('[ thing=nil ]') end it 'logs adapter calls' do adapter_line = find_line('Flipper feature(search) adapter(memory) get') expect(adapter_line).to include('[ result={') expect(adapter_line).to include('} ]') end end context 'feature enabled checks with a thing' do let(:user) { Flipper::Types::Actor.new(Flipper::Actor.new('1')) } before do clear_logs flipper[:search].enabled?(user) end it 'logs thing for feature' do feature_line = find_line('Flipper feature(search) enabled?') expect(feature_line).to include(user.inspect) end end context 'changing feature enabled state' do let(:user) { Flipper::Types::Actor.new(Flipper::Actor.new('1')) } before do clear_logs flipper[:search].enable(user) end it 'logs feature calls with result in brackets' do feature_line = find_line('Flipper feature(search) enable true') expect(feature_line).to include("[ thing=#{user.inspect} gate_name=actor ]") end it 'logs adapter value' do adapter_line = find_line('Flipper feature(search) adapter(memory) enable') expect(adapter_line).to include('[ result=') end end context 'getting all the features from the adapter' do before do clear_logs flipper.features end it 'logs adapter calls' do adapter_line = find_line('Flipper adapter(memory) features') expect(adapter_line).to include('[ result=') end end def find_line(str) regex = /#{Regexp.escape(str)}/ lines = log.split("\n") lines.detect { |line| line =~ regex } || raise("Could not find line matching #{str.inspect} in #{lines.inspect}") end def clear_logs @io.string = '' end end flippercloud-flipper-532c39a/spec/flipper/instrumentation/statsd_subscriber_spec.rb000066400000000000000000000037741440444367400311430ustar00rootroot00000000000000require 'flipper/adapters/instrumented' require 'flipper/instrumentation/statsd' require 'statsd' RSpec.describe Flipper::Instrumentation::StatsdSubscriber do let(:statsd_client) { Statsd.new } let(:socket) { FakeUDPSocket.new } let(:adapter) do memory = Flipper::Adapters::Memory.new Flipper::Adapters::Instrumented.new(memory, instrumenter: ActiveSupport::Notifications) end let(:flipper) do Flipper.new(adapter, instrumenter: ActiveSupport::Notifications) end let(:user) { user = Flipper::Actor.new('1') } before do described_class.client = statsd_client Thread.current[:statsd_socket] = socket end after do described_class.client = nil Thread.current[:statsd_socket] = nil end def assert_timer(metric) regex = /#{Regexp.escape metric}\:\d+\|ms/ result = socket.buffer.detect { |op| op.first =~ regex } expect(result).not_to be_nil end def assert_counter(metric) result = socket.buffer.detect { |op| op.first == "#{metric}:1|c" } expect(result).not_to be_nil end context 'for enabled feature' do it 'updates feature metrics when calls happen' do flipper[:stats].enable(user) assert_timer 'flipper.feature_operation.enable' flipper[:stats].enabled?(user) assert_timer 'flipper.feature_operation.enabled' assert_counter 'flipper.feature.stats.enabled' end end context 'for disabled feature' do it 'updates feature metrics when calls happen' do flipper[:stats].disable(user) assert_timer 'flipper.feature_operation.disable' flipper[:stats].enabled?(user) assert_timer 'flipper.feature_operation.enabled' assert_counter 'flipper.feature.stats.disabled' end end it 'updates adapter metrics when calls happen' do flipper[:stats].enable(user) assert_timer 'flipper.adapter.memory.enable' flipper[:stats].enabled?(user) assert_timer 'flipper.adapter.memory.get' flipper[:stats].disable(user) assert_timer 'flipper.adapter.memory.disable' end end flippercloud-flipper-532c39a/spec/flipper/instrumenters/000077500000000000000000000000001440444367400235235ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/instrumenters/memory_spec.rb000066400000000000000000000023301440444367400263700ustar00rootroot00000000000000require 'flipper/instrumenters/memory' RSpec.describe Flipper::Instrumenters::Memory do describe '#initialize' do it 'sets events to empty array' do instrumenter = described_class.new expect(instrumenter.events).to eq([]) end end describe '#instrument' do it 'adds to events' do instrumenter = described_class.new name = 'user.signup' payload = { email: 'john@doe.com' } block_result = :yielded result = instrumenter.instrument(name, payload) { block_result } expect(result).to eq(block_result) event = described_class::Event.new(name, payload, block_result) expect(instrumenter.events).to eq([event]) end context 'when an error is raised' do subject do instrumenter.instrument(:name) { raise IOError } end let(:instrumenter) { described_class.new } it 'captures and propagates the error' do expect { subject }.to raise_error(IOError) expect(instrumenter.events.count).to be 1 payload = instrumenter.events[0].payload expect(payload.keys).to include(:exception, :exception_object) expect(payload[:exception_object]).to be_a IOError end end end end flippercloud-flipper-532c39a/spec/flipper/instrumenters/noop_spec.rb000066400000000000000000000012341440444367400260350ustar00rootroot00000000000000RSpec.describe Flipper::Instrumenters::Noop do describe '.instrument' do context 'with name' do it 'yields block' do expect { |block| described_class.instrument(:foo, &block) }.to yield_control end end context 'with name and payload' do let(:payload) { { pay: :load } } it 'yields block' do expect { |block| described_class.instrument(:foo, payload, &block) }.to yield_control end it 'yields the payload' do described_class.instrument(:foo, payload) do |block_payload| expect(block_payload).to eq payload end end end end end flippercloud-flipper-532c39a/spec/flipper/middleware/000077500000000000000000000000001440444367400227165ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/middleware/memoizer_spec.rb000066400000000000000000000256161440444367400261160ustar00rootroot00000000000000require 'rack/test' require 'active_support/cache' require 'flipper/adapters/active_support_cache_store' require 'flipper/adapters/operation_logger' RSpec.describe Flipper::Middleware::Memoizer do include Rack::Test::Methods let(:memory_adapter) { Flipper::Adapters::Memory.new } let(:adapter) do Flipper::Adapters::OperationLogger.new(memory_adapter) end let(:flipper) { Flipper.new(adapter) } let(:env) { { 'flipper' => flipper } } it 'raises if initialized with app and flipper instance' do expect do described_class.new(app, flipper) end.to raise_error(/no longer initializes with a flipper/) end it 'raises if initialized with app and block' do block = -> { flipper } expect do described_class.new(app, block) end.to raise_error(/no longer initializes with a flipper/) end RSpec.shared_examples_for 'flipper middleware' do it 'delegates' do called = false app = lambda do |_env| called = true [200, {}, nil] end middleware = described_class.new(app) middleware.call(env) expect(called).to eq(true) end it 'clears the local cache with a successful request' do flipper.adapter.cache['hello'] = 'world' get '/', {}, 'flipper' => flipper expect(flipper.adapter.cache).to be_empty end it 'clears the local cache even when the request raises an error' do flipper.adapter.cache['hello'] = 'world' begin get '/fail', {}, 'flipper' => flipper rescue nil end expect(flipper.adapter.cache).to be_empty end it 'caches getting a feature for duration of request' do flipper[:stats].enable # clear the log of operations adapter.reset app = lambda do |_env| flipper[:stats].enabled? flipper[:stats].enabled? flipper[:stats].enabled? flipper[:stats].enabled? flipper[:stats].enabled? flipper[:stats].enabled? [200, {}, []] end middleware = described_class.new(app) middleware.call(env) expect(adapter.count(:get)).to be(1) end end context 'with preload: true' do let(:app) do # ensure scoped for builder block, annoying... instance = flipper middleware = described_class Rack::Builder.new do use middleware, preload: true map '/' do run ->(_env) { [200, {}, []] } end map '/fail' do run ->(_env) { raise 'FAIL!' } end end.to_app end include_examples 'flipper middleware' it 'eagerly caches known features for duration of request' do flipper[:stats].enable flipper[:shiny].enable # clear the log of operations adapter.reset app = lambda do |_env| flipper[:stats].enabled? flipper[:stats].enabled? flipper[:shiny].enabled? flipper[:shiny].enabled? [200, {}, []] end middleware = described_class.new(app, preload: true) middleware.call(env) expect(adapter.operations.size).to be(1) expect(adapter.count(:get_all)).to be(1) end it 'caches unknown features for duration of request' do # clear the log of operations adapter.reset app = lambda do |_env| flipper[:other].enabled? flipper[:other].enabled? [200, {}, []] end middleware = described_class.new(app, preload: true) middleware.call(env) expect(adapter.count(:get)).to be(1) expect(adapter.last(:get).args).to eq([flipper[:other]]) end end context 'with preload specific' do let(:app) do # ensure scoped for builder block, annoying... instance = flipper middleware = described_class Rack::Builder.new do use middleware, preload: %i(stats) map '/' do run ->(_env) { [200, {}, []] } end map '/fail' do run ->(_env) { raise 'FAIL!' } end end.to_app end include_examples 'flipper middleware' it 'eagerly caches specified features for duration of request' do # clear the log of operations adapter.reset app = lambda do |_env| flipper[:stats].enabled? flipper[:stats].enabled? flipper[:shiny].enabled? flipper[:shiny].enabled? [200, {}, []] end middleware = described_class.new app, preload: %i(stats) middleware.call(env) expect(adapter.count(:get_multi)).to be(1) expect(adapter.last(:get_multi).args).to eq([[flipper[:stats]]]) end it 'caches unknown features for duration of request' do # clear the log of operations adapter.reset app = lambda do |_env| flipper[:other].enabled? flipper[:other].enabled? [200, {}, []] end middleware = described_class.new app, preload: %i(stats) middleware.call(env) expect(adapter.count(:get)).to be(1) expect(adapter.last(:get).args).to eq([flipper[:other]]) end end context 'with multiple instances' do let(:app) do # ensure scoped for builder block, annoying... instance = flipper middleware = described_class Rack::Builder.new do use middleware, preload: %i(stats) # Second instance should be a noop use middleware, preload: true map '/' do run ->(_env) { [200, {}, []] } end map '/fail' do run ->(_env) { raise 'FAIL!' } end end.to_app end def get(uri, params = {}, env = {}, &block) silence { super(uri, params, env, &block) } end include_examples 'flipper middleware' it 'does not call preload in second instance' do expect(flipper).not_to receive(:preload_all) output = get '/', {}, 'flipper' => flipper expect(output).to match(/Flipper::Middleware::Memoizer appears to be running twice/) expect(adapter.count(:get_multi)).to be(1) expect(adapter.last(:get_multi).args).to eq([[flipper[:stats]]]) end end context 'when an app raises an exception' do it 'resets memoize' do begin app = ->(_env) { raise } middleware = described_class.new(app) middleware.call(env) rescue RuntimeError expect(flipper.memoizing?).to be(false) end end end context 'with flipper setup in env' do let(:app) do # ensure scoped for builder block, annoying... instance = flipper middleware = described_class Rack::Builder.new do use middleware map '/' do run ->(_env) { [200, {}, []] } end map '/fail' do run ->(_env) { raise 'FAIL!' } end end.to_app end include_examples 'flipper middleware' end context 'with Flipper setup in env' do it 'caches getting a feature for duration of request' do Flipper.configure do |config| config.adapter do memory = Flipper::Adapters::Memory.new Flipper::Adapters::OperationLogger.new(memory) end end Flipper.enable(:stats) Flipper.adapter.reset # clear the log of operations app = lambda do |_env| Flipper.enabled?(:stats) Flipper.enabled?(:stats) Flipper.enabled?(:stats) [200, {}, []] end middleware = described_class.new(app) middleware.call('flipper' => Flipper) expect(Flipper.adapter.count(:get)).to be(1) end end context 'defaults to Flipper' do it 'caches getting a feature for duration of request' do Flipper.configure do |config| config.default do memory_adapter = Flipper::Adapters::Memory.new logged_adapter = Flipper::Adapters::OperationLogger.new(memory_adapter) Flipper.new(logged_adapter) end end Flipper.enable(:stats) Flipper.adapter.reset # clear the log of operations app = lambda do |_env| Flipper.enabled?(:stats) Flipper.enabled?(:stats) Flipper.enabled?(:stats) [200, {}, []] end middleware = described_class.new(app) middleware.call({}) expect(Flipper.adapter.count(:get)).to be(1) end end context 'with preload:true' do let(:options) { {preload: true} } let(:app) do # ensure scoped for builder block, annoying... middleware = described_class opts = options Rack::Builder.new do use middleware, opts map '/' do run ->(_env) { [200, {}, []] } end map '/fail' do run ->(_env) { raise 'FAIL!' } end end.to_app end context 'and unless option' do before do options[:unless] = ->(request) { request.path.start_with?("/assets") } end it 'does NOT preload if request matches unless block' do expect(flipper).to receive(:preload_all).never get '/assets/foo.png', {}, 'flipper' => flipper end it 'does preload if request does NOT match unless block' do expect(flipper).to receive(:preload_all).once get '/some/other/path', {}, 'flipper' => flipper end end context 'and if option' do before do options[:if] = ->(request) { !request.path.start_with?("/assets") } end it 'does NOT preload if request does not match if block' do expect(flipper).to receive(:preload_all).never get '/assets/foo.png', {}, 'flipper' => flipper end it 'does preload if request matches if block' do expect(flipper).to receive(:preload_all).once get '/some/other/path', {}, 'flipper' => flipper end end end context 'with preload:true and caching adapter' do let(:app) do app = lambda do |_env| flipper[:stats].enabled? flipper[:stats].enabled? flipper[:shiny].enabled? flipper[:shiny].enabled? [200, {}, []] end described_class.new(app, preload: true) end it 'eagerly caches known features for duration of request' do memory = Flipper::Adapters::Memory.new logged_memory = Flipper::Adapters::OperationLogger.new(memory) cache = ActiveSupport::Cache::MemoryStore.new cache.clear cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache, expires_in: 10) logged_cached = Flipper::Adapters::OperationLogger.new(cached) memo = {} flipper = Flipper.new(logged_cached) flipper[:stats].enable flipper[:shiny].enable # clear the log of operations logged_memory.reset logged_cached.reset get '/', {}, 'flipper' => flipper expect(logged_cached.count(:get_all)).to be(1) expect(logged_memory.count(:get_all)).to be(1) get '/', {}, 'flipper' => flipper expect(logged_cached.count(:get_all)).to be(2) expect(logged_memory.count(:get_all)).to be(1) get '/', {}, 'flipper' => flipper expect(logged_cached.count(:get_all)).to be(3) expect(logged_memory.count(:get_all)).to be(1) end end end flippercloud-flipper-532c39a/spec/flipper/middleware/setup_env_spec.rb000066400000000000000000000047731440444367400263000ustar00rootroot00000000000000RSpec.describe Flipper::Middleware::SetupEnv do context 'with flipper instance' do let(:app) do app = lambda do |env| [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]] end builder = Rack::Builder.new builder.use described_class, flipper builder.run app builder end it 'sets flipper in env' do get '/' expect(last_response.body).to eq(flipper.object_id.to_s) end end context 'with block that returns flipper instance' do let(:flipper_block) do -> { flipper } end let(:app) do app = lambda do |env| [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]] end builder = Rack::Builder.new builder.use described_class, flipper_block builder.run app builder end it 'sets flipper in env' do get '/' expect(last_response.body).to eq(flipper.object_id.to_s) end end context 'when env already has flipper setup' do let(:app) do app = lambda do |env| [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]] end builder = Rack::Builder.new builder.use described_class, flipper builder.run app builder end it 'leaves env flipper alone' do env_flipper = build_flipper get '/', {}, 'flipper' => env_flipper expect(last_response.body).to eq(env_flipper.object_id.to_s) end end context 'when flipper instance or block are nil but env flipper is configured' do let(:app) do app = lambda do |env| [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]] end builder = Rack::Builder.new builder.use described_class builder.run app builder end it 'can use env flipper' do env_flipper = build_flipper get '/', {}, 'flipper' => env_flipper expect(last_response.body).to eq(env_flipper.object_id.to_s) end end context 'when flipper instance or block are nil and default Flipper is configured' do let(:app) do Flipper.configure do |config| config.default { flipper } end app = lambda do |env| [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]] end builder = Rack::Builder.new builder.use described_class builder.run app builder end it 'can use env flipper' do get '/', {}, {} expect(last_response.body).to eq(Flipper.object_id.to_s) end end end flippercloud-flipper-532c39a/spec/flipper/poller_spec.rb000066400000000000000000000024311440444367400234350ustar00rootroot00000000000000require "flipper/poller" RSpec.describe Flipper::Poller do let(:remote_adapter) { Flipper::Adapters::Memory.new } let(:remote) { Flipper.new(remote_adapter) } let(:local) { Flipper.new(subject.adapter) } subject do described_class.new( remote_adapter: remote_adapter, start_automatically: false, interval: Float::INFINITY ) end before do allow(subject).to receive(:loop).and_yield # Make loop just call once allow(subject).to receive(:sleep) # Disable sleep allow(Thread).to receive(:new).and_yield # Disable separate thread end describe "#adapter" do it "always returns same memory adapter instance" do expect(subject.adapter).to be_a(Flipper::Adapters::Memory) expect(subject.adapter.object_id).to eq(subject.adapter.object_id) end end describe "#sync" do it "syncs remote adapter to local adapter" do remote.enable :polling expect(local.enabled?(:polling)).to be(false) subject.sync expect(local.enabled?(:polling)).to be(true) end end describe "#start" do it "starts the poller thread" do expect(Thread).to receive(:new).and_yield expect(subject).to receive(:loop).and_yield expect(subject).to receive(:sync) subject.start end end end flippercloud-flipper-532c39a/spec/flipper/railtie_spec.rb000066400000000000000000000056471440444367400236050ustar00rootroot00000000000000require 'rails' require 'flipper/railtie' RSpec.describe Flipper::Railtie do let(:application) do Class.new(Rails::Application).create(railties: [Flipper::Railtie]) do config.eager_load = false end end before do ActiveSupport::Dependencies.autoload_paths = ActiveSupport::Dependencies.autoload_paths.dup ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_once_paths.dup end let(:config) { application.config.flipper } subject { application.initialize! } describe 'initializers' do it 'can set env_key from ENV' do ENV['FLIPPER_ENV_KEY'] = 'flopper' subject expect(config.env_key).to eq('flopper') end it 'can set memoize from ENV' do ENV['FLIPPER_MEMOIZE'] = 'false' subject expect(config.memoize).to eq(false) end it 'can set preload from ENV' do ENV['FLIPPER_PRELOAD'] = 'false' subject expect(config.preload).to eq(false) end it 'can set instrumenter from ENV' do stub_const('My::Cool::Instrumenter', Class.new) ENV['FLIPPER_INSTRUMENTER'] = 'My::Cool::Instrumenter' subject expect(config.instrumenter).to eq(My::Cool::Instrumenter) end it 'can set log from ENV' do ENV['FLIPPER_LOG'] = 'false' subject expect(config.log).to eq(false) end it 'sets defaults' do subject # initialize expect(config.env_key).to eq("flipper") expect(config.memoize).to be(true) expect(config.preload).to be(true) end it "configures instrumentor on default instance" do subject # initialize expect(Flipper.instance.instrumenter).to eq(ActiveSupport::Notifications) end it 'uses Memoizer middleware if config.memoize = true' do initializer { config.memoize = true } expect(subject.middleware).to include(Flipper::Middleware::Memoizer) end it 'does not use Memoizer middleware if config.memoize = false' do initializer { config.memoize = false } expect(subject.middleware).not_to include(Flipper::Middleware::Memoizer) end it 'passes config to memoizer' do initializer do config.update( env_key: 'my_flipper', preload: [:stats, :search] ) end expect(subject.middleware).to include(Flipper::Middleware::Memoizer) middleware = subject.middleware.detect { |m| m.klass == Flipper::Middleware::Memoizer } expect(middleware.args[0]).to eq({ env_key: config.env_key, preload: config.preload, if: nil }) end it "defines #flipper_id on AR::Base" do subject require 'active_record' expect(ActiveRecord::Base.ancestors).to include(Flipper::Identifier) end end # Add app initializer in the same order as config/initializers/* def initializer(&block) application.initializer 'spec', before: :load_config_initializers do block.call end end end flippercloud-flipper-532c39a/spec/flipper/registry_spec.rb000066400000000000000000000055151440444367400240160ustar00rootroot00000000000000require 'flipper/registry' RSpec.describe Flipper::Registry do subject { described_class.new(source) } let(:source) { {} } describe '#add' do it 'adds to source' do value = 'thing' subject.add(:admins, value) expect(source[:admins]).to eq(value) end it 'converts key to symbol' do value = 'thing' subject.add('admins', value) expect(source[:admins]).to eq(value) end it 'raises exception if key already registered' do subject.add(:admins, 'thing') expect do subject.add('admins', 'again') end.to raise_error(Flipper::Registry::DuplicateKey) end end describe '#get' do context 'key registered' do before do source[:admins] = 'thing' end it 'returns value' do expect(subject.get(:admins)).to eq('thing') end it 'returns value if given string key' do expect(subject.get('admins')).to eq('thing') end end context 'key not registered' do it 'returns nil' do expect(subject.get(:admins)).to be(nil) end end end describe '#key?' do before do source[:admins] = 'admins' end it 'returns true if the key exists' do expect(subject.key?(:admins)).to eq true end it 'returns false if the key does not exists' do expect(subject.key?(:unknown_key)).to eq false end end describe '#each' do before do source[:admins] = 'admins' source[:devs] = 'devs' end it 'iterates source keys and values' do results = {} subject.each do |key, value| results[key] = value end expect(results).to eq(admins: 'admins', devs: 'devs') end end describe '#keys' do before do source[:admins] = 'admins' source[:devs] = 'devs' end it 'returns the keys' do expect(subject.keys.map(&:to_s).sort).to eq(%w(admins devs)) end it 'returns the keys as symbols' do subject.keys.each do |key| expect(key).to be_instance_of(Symbol) end end end describe '#values' do before do source[:admins] = 'admins' source[:devs] = 'devs' end it 'returns the values' do expect(subject.values.map(&:to_s).sort).to eq(%w(admins devs)) end end describe 'enumeration' do before do source[:admins] = 'admins' source[:devs] = 'devs' end it 'works' do keys = [] values = [] subject.map do |key, value| keys << key values << value end expect(keys.map(&:to_s).sort).to eq(%w(admins devs)) expect(values.sort).to eq(%w(admins devs)) end end describe '#clear' do before do source[:admins] = 'admins' end it 'clears the source' do subject.clear expect(source).to be_empty end end end flippercloud-flipper-532c39a/spec/flipper/typecast_spec.rb000066400000000000000000000055071440444367400240030ustar00rootroot00000000000000require 'flipper/typecast' RSpec.describe Flipper::Typecast do { nil => false, '' => false, 0 => false, 1 => true, '0' => false, '1' => true, true => true, false => false, 'true' => true, 'false' => false, }.each do |value, expected| context "#to_boolean for #{value.inspect}" do it "returns #{expected}" do expect(described_class.to_boolean(value)).to be(expected) end end end { nil => 0, '' => 0, 0 => 0, 1 => 1, '1' => 1, '99' => 99, }.each do |value, expected| context "#to_integer for #{value.inspect}" do it "returns #{expected}" do expect(described_class.to_integer(value)).to be(expected) end end end { nil => 0.0, '' => 0.0, 0 => 0.0, 1 => 1.0, 1.1 => 1.1, '0.01' => 0.01, '1' => 1.0, '99' => 99.0, }.each do |value, expected| context "#to_float for #{value.inspect}" do it "returns #{expected}" do expect(described_class.to_float(value)).to be(expected) end end end { nil => 0, '' => 0, 0 => 0, 0.0 => 0, 1 => 1, 1.1 => 1.1, '0.01' => 0.01, '1' => 1, '1.1' => 1.1, '99' => 99, '99.9' => 99.9, }.each do |value, expected| context "#to_percentage for #{value.inspect}" do it "returns #{expected}" do expect(described_class.to_percentage(value)).to be(expected) end end end { nil => Set.new, '' => Set.new, Set.new([1, 2]) => Set.new([1, 2]), [1, 2] => Set.new([1, 2]), }.each do |value, expected| context "#to_set for #{value.inspect}" do it "returns #{expected}" do expect(described_class.to_set(value)).to eq(expected) end end end it 'raises argument error for integer value that cannot be converted to an integer' do expect do described_class.to_integer(['asdf']) end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to an integer)) end it 'raises argument error for float value that cannot be converted to an float' do expect do described_class.to_float(['asdf']) end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a float)) end it 'raises argument error for bad integer percentage' do expect do described_class.to_percentage(['asdf']) end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a percentage)) end it 'raises argument error for bad float percentage' do expect do described_class.to_percentage(['asdf.0']) end.to raise_error(ArgumentError, %(["asdf.0"] cannot be converted to a percentage)) end it 'raises argument error for set value that cannot be converted to a set' do expect do described_class.to_set('asdf') end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set)) end end flippercloud-flipper-532c39a/spec/flipper/types/000077500000000000000000000000001440444367400217455ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/types/actor_spec.rb000066400000000000000000000056551440444367400244270ustar00rootroot00000000000000require 'flipper/types/actor' RSpec.describe Flipper::Types::Actor do subject do thing = thing_class.new('2') described_class.new(thing) end let(:thing_class) do Class.new do attr_reader :flipper_id def initialize(flipper_id) @flipper_id = flipper_id end def admin? true end end end describe '.wrappable?' do it 'returns true if actor' do thing = thing_class.new('1') actor = described_class.new(thing) expect(described_class.wrappable?(actor)).to eq(true) end it 'returns true if responds to flipper_id' do thing = thing_class.new(10) expect(described_class.wrappable?(thing)).to eq(true) end it 'returns false if nil' do expect(described_class.wrappable?(nil)).to be(false) end end describe '.wrap' do context 'for actor' do it 'returns actor' do actor = described_class.wrap(subject) expect(actor).to be_instance_of(described_class) expect(actor).to be(subject) end end context 'for other thing' do it 'returns actor' do thing = thing_class.new('1') actor = described_class.wrap(thing) expect(actor).to be_instance_of(described_class) end end end it 'initializes with thing that responds to id' do thing = thing_class.new('1') actor = described_class.new(thing) expect(actor.value).to eq('1') end it 'raises error when initialized with nil' do expect do described_class.new(nil) end.to raise_error(ArgumentError) end it 'raises error when initalized with non-wrappable object' do unwrappable_thing = Struct.new(:id).new(1) expect do described_class.new(unwrappable_thing) end.to raise_error(ArgumentError, "#{unwrappable_thing.inspect} must respond to flipper_id, but does not") end it 'converts id to string' do thing = thing_class.new(2) actor = described_class.new(thing) expect(actor.value).to eq('2') end it 'proxies everything to thing' do thing = thing_class.new(10) actor = described_class.new(thing) expect(actor.admin?).to eq(true) end it 'exposes thing' do thing = thing_class.new(10) actor = described_class.new(thing) expect(actor.thing).to be(thing) end describe '#respond_to?' do it 'returns true if responds to method' do thing = thing_class.new('1') actor = described_class.new(thing) expect(actor.respond_to?(:value)).to eq(true) end it 'returns true if thing responds to method' do thing = thing_class.new(10) actor = described_class.new(thing) expect(actor.respond_to?(:admin?)).to eq(true) end it 'returns false if does not respond to method and thing does not respond to method' do thing = thing_class.new(10) actor = described_class.new(thing) expect(actor.respond_to?(:frankenstein)).to eq(false) end end end flippercloud-flipper-532c39a/spec/flipper/types/boolean_spec.rb000066400000000000000000000010441440444367400247220ustar00rootroot00000000000000require 'flipper/types/boolean' RSpec.describe Flipper::Types::Boolean do it 'defaults value to true' do boolean = described_class.new expect(boolean.value).to be(true) end it 'allows overriding default value' do boolean = described_class.new(false) expect(boolean.value).to be(false) end it 'returns true for nil value' do boolean = described_class.new(nil) expect(boolean.value).to be(true) end it 'typecasts value' do boolean = described_class.new(1) expect(boolean.value).to be(true) end end flippercloud-flipper-532c39a/spec/flipper/types/group_spec.rb000066400000000000000000000063541440444367400244500ustar00rootroot00000000000000require 'flipper/types/group' RSpec.describe Flipper::Types::Group do let(:fake_context) { double('FeatureCheckContext') } subject do Flipper.register(:admins, &:admin?) end describe '.wrap' do context 'with group instance' do it 'returns group instance' do expect(described_class.wrap(subject)).to eq(subject) end end context 'with Symbol group name' do it 'returns group instance' do expect(described_class.wrap(subject.name)).to eq(subject) end end context 'with String group name' do it 'returns group instance' do expect(described_class.wrap(subject.name.to_s)).to eq(subject) end end end it 'initializes with name' do group = described_class.new(:admins) expect(group).to be_instance_of(described_class) end describe '#name' do it 'returns name' do expect(subject.name).to eq(:admins) end end describe '#match?' do let(:admin_actor) { double('Actor', admin?: true) } let(:non_admin_actor) { double('Actor', admin?: false) } it 'returns true if block matches' do expect(subject.match?(admin_actor, fake_context)).to eq(true) end it 'returns false if block does not match' do expect(subject.match?(non_admin_actor, fake_context)).to eq(false) end it 'returns true for truthy block results' do group = described_class.new(:examples) do |actor| actor.email =~ /@example\.com/ end expect(group.match?(double('Actor', email: 'foo@example.com'), fake_context)).to be_truthy end it 'returns false for falsey block results' do group = described_class.new(:examples) do |_actor| nil end expect(group.match?(double('Actor'), fake_context)).to be_falsey end it 'returns true for truthy shortand block results' do actor = Class.new do def admin? true end end.new group = described_class.new(:admin, &:admin?) expect(group.match?(actor, fake_context)).to be_truthy end it 'returns false for falsy shortand block results' do actor = Class.new do def admin? false end end.new group = described_class.new(:admin, &:admin?) expect(group.match?(actor, fake_context)).to be_falsey end it 'can yield without context as block argument' do context = Flipper::FeatureCheckContext.new( feature_name: :my_feature, values: Flipper::GateValues.new({}), thing: Flipper::Types::Actor.new(Flipper::Actor.new(1)) ) group = Flipper.register(:group_with_context) { |actor| actor } yielded_actor = group.match?(admin_actor, context) expect(yielded_actor).to be(admin_actor) end it 'can yield with context as block argument' do context = Flipper::FeatureCheckContext.new( feature_name: :my_feature, values: Flipper::GateValues.new({}), thing: Flipper::Types::Actor.new(Flipper::Actor.new(1)) ) group = Flipper.register(:group_with_context) { |actor, context| [actor, context] } yielded_actor, yielded_context = group.match?(admin_actor, context) expect(yielded_actor).to be(admin_actor) expect(yielded_context).to be(context) end end end flippercloud-flipper-532c39a/spec/flipper/types/percentage_of_actors_spec.rb000066400000000000000000000002161440444367400274570ustar00rootroot00000000000000require 'flipper/types/percentage_of_actors' RSpec.describe Flipper::Types::PercentageOfActors do it_should_behave_like 'a percentage' end flippercloud-flipper-532c39a/spec/flipper/types/percentage_of_time_spec.rb000066400000000000000000000002121440444367400271160ustar00rootroot00000000000000require 'flipper/types/percentage_of_time' RSpec.describe Flipper::Types::PercentageOfTime do it_should_behave_like 'a percentage' end flippercloud-flipper-532c39a/spec/flipper/types/percentage_spec.rb000066400000000000000000000022701440444367400254220ustar00rootroot00000000000000require 'flipper/types/percentage_of_actors' RSpec.describe Flipper::Types::Percentage do subject do described_class.new(5) end it_should_behave_like 'a percentage' describe '.wrap' do context 'with percentage instance' do it 'returns percentage instance' do expect(described_class.wrap(subject)).to eq(subject) end end context 'with Integer' do it 'returns percentage instance' do expect(described_class.wrap(subject.value)).to eq(subject) end end context 'with String' do it 'returns percentage instance' do expect(described_class.wrap(subject.value.to_s)).to eq(subject) end end end describe '#eql?' do it 'returns true for same class and value' do expect(subject.eql?(described_class.new(subject.value))).to eq(true) end it 'returns false for different value' do expect(subject.eql?(described_class.new(subject.value + 1))).to eq(false) end it 'returns false for different class' do expect(subject.eql?(Object.new)).to eq(false) end it 'is aliased to ==' do expect((subject == described_class.new(subject.value))).to eq(true) end end end flippercloud-flipper-532c39a/spec/flipper/ui/000077500000000000000000000000001440444367400212165ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/ui/action_spec.rb000066400000000000000000000042261440444367400240360ustar00rootroot00000000000000RSpec.describe Flipper::UI::Action do describe 'request methods' do let(:action_subclass) do Class.new(described_class) do def noooope raise 'should never run this' end def get [200, {}, 'get'] end def post [200, {}, 'post'] end def put [200, {}, 'put'] end def delete [200, {}, 'delete'] end end end it "won't run method that isn't whitelisted" do fake_request = Struct.new(:request_method, :env, :session).new('NOOOOPE', {}, {}) action = action_subclass.new(flipper, fake_request) expect do action.run end.to raise_error(Flipper::UI::RequestMethodNotSupported) end it 'will run get' do fake_request = Struct.new(:request_method, :env, :session).new('GET', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'get']) end it 'will run post' do fake_request = Struct.new(:request_method, :env, :session).new('POST', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'post']) end it 'will run put' do fake_request = Struct.new(:request_method, :env, :session).new('PUT', {}, {}) action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, {}, 'put']) end end describe 'FeatureNameFromRoute' do let(:action_subclass) do Class.new(described_class) do |parent| include parent::FeatureNameFromRoute route %r{\A/features/(?.*)\Z} def get [200, { feature_name: feature_name }, 'get'] end end end it 'decodes feature_name' do requested_feature_name = Rack::Utils.escape("team:side_pane") fake_request = Struct .new(:request_method, :env, :session, :path_info) .new('GET', {}, {}, "/features/#{requested_feature_name}") action = action_subclass.new(flipper, fake_request) expect(action.run).to eq([200, { feature_name: "team:side_pane" }, 'get']) end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/000077500000000000000000000000001440444367400226565ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/ui/actions/actors_gate_spec.rb000066400000000000000000000143171440444367400265160ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::ActorsGate do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'GET /features/:feature/actors' do before do get 'features/search/actors' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders add new actor form' do form = '
' expect(last_response.body).to include(form) end end describe 'GET /features/:feature/actors with slash in feature name' do before do get 'features/a/b/actors' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders add new actor form' do form = '' expect(last_response.body).to include(form) end end describe 'POST /features/:feature/actors' do context 'enabling an actor' do let(:value) { 'User;6' } let(:multi_value) { 'User;5, User;7, User;9, User;12' } before do post 'features/search/actors', { 'value' => value, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session end it 'adds item to members' do expect(flipper[:search].actors_value).to include(value) end it 'adds item to multiple members' do post 'features/search/actors', { 'value' => multi_value, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session expect(flipper[:search].actors_value).to include('User;5') expect(flipper[:search].actors_value).to include('User;7') expect(flipper[:search].actors_value).to include('User;9') expect(flipper[:search].actors_value).to include('User;12') end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end context "when feature name contains space" do before do post 'features/sp%20ace/actors', { 'value' => value, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session end it 'adds item to members' do expect(flipper["sp ace"].actors_value).to include('User;6') end it "redirects back to feature" do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/sp%20ace') end end context 'value contains whitespace' do let(:value) { ' User;6 ' } let(:multi_value) { ' User;5 , User;7 , User;9 , User;12 ' } it 'adds item without whitespace' do expect(flipper[:search].actors_value).to include('User;6') end it 'adds item to multi members without whitespace' do post 'features/search/actors', { 'value' => multi_value, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session expect(flipper[:search].actors_value).to include('User;5') expect(flipper[:search].actors_value).to include('User;7') expect(flipper[:search].actors_value).to include('User;9') expect(flipper[:search].actors_value).to include('User;12') end end context 'for an invalid actor value' do context 'empty value' do let(:value) { '' } it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22%20is%20not%20a%20valid%20actor%20value.') end end context 'nil value' do let(:value) { nil } it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search/actors?error=%22%22%20is%20not%20a%20valid%20actor%20value.') end end end end context 'disabling an actor' do let(:value) { 'User;6' } let(:multi_value) { 'User;5, User;7, User;9, User;12' } before do flipper[:search].enable_actor Flipper::Actor.new(value) post 'features/search/actors', { 'value' => value, 'operation' => 'disable', 'authenticity_token' => token }, 'rack.session' => session end it 'removes item from members' do expect(flipper[:search].actors_value).not_to include(value) end it 'removes item from multi members' do multi_value.split(',').map(&:strip).each do |value| flipper[:search].enable_actor Flipper::Actor.new(value) end post 'features/search/actors', { 'value' => multi_value, 'operation' => 'disable', 'authenticity_token' => token }, 'rack.session' => session expect(flipper[:search].actors_value).not_to eq(Set.new(multi_value.split(',').map(&:strip))) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end context 'value contains whitespace' do let(:value) { ' User;6 ' } let(:multi_value) { ' User;5 , User;7 , User;9 , User;12 ' } it 'removes item without whitespace' do expect(flipper[:search].actors_value).not_to include('User;6') end it 'removes item without whitespace' do multi_value.split(',').map(&:strip).each do |value| flipper[:search].enable_actor Flipper::Actor.new(value) end post 'features/search/actors', { 'value' => multi_value, 'operation' => 'disable', 'authenticity_token' => token }, 'rack.session' => session expect(flipper[:search].actors_value).not_to eq(Set.new(multi_value.split(',').map(&:strip))) end end end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/add_feature_spec.rb000066400000000000000000000024771440444367400264720ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::AddFeature do describe 'GET /features/new with feature_creation_enabled set to true' do before do @original_feature_creation_enabled = Flipper::UI.configuration.feature_creation_enabled Flipper::UI.configuration.feature_creation_enabled = true get '/features/new' end after do Flipper::UI.configuration.feature_creation_enabled = @original_feature_creation_enabled end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do form = '' expect(last_response.body).to include(form) end end describe 'GET /features/new with feature_creation_enabled set to false' do before do @original_feature_creation_enabled = Flipper::UI.configuration.feature_creation_enabled Flipper::UI.configuration.feature_creation_enabled = false get '/features/new' end after do Flipper::UI.configuration.feature_creation_enabled = @original_feature_creation_enabled end it 'returns 403' do expect(last_response.status).to be(403) end it 'renders feature creation disabled template' do expect(last_response.body).to include('Feature creation is disabled.') end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/boolean_gate_spec.rb000066400000000000000000000036361440444367400266440ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::BooleanGate do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'POST /features/:feature/boolean' do context 'with enable' do before do flipper.disable :search post 'features/search/boolean', { 'action' => 'Enable', 'authenticity_token' => token }, 'rack.session' => session end it 'enables the feature' do expect(flipper.enabled?(:search)).to be(true) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end end context "with space in feature name" do before do flipper.disable :search post 'features/sp%20ace/boolean', { 'action' => 'Enable', 'authenticity_token' => token }, 'rack.session' => session end it 'updates feature' do expect(flipper.enabled?("sp ace")).to be(true) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/sp%20ace') end end context 'with disable' do before do flipper.enable :search post 'features/search/boolean', { 'action' => 'Disable', 'authenticity_token' => token }, 'rack.session' => session end it 'disables the feature' do expect(flipper.enabled?(:search)).to be(false) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/feature_spec.rb000066400000000000000000000077741440444367400256670ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::Feature do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'DELETE /features/:feature' do before do flipper.enable :search delete '/features/search', { 'authenticity_token' => token }, 'rack.session' => session end it 'removes feature' do expect(flipper.features.map(&:key)).not_to include('search') end it 'redirects to features' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features') end context "with space in feature name" do before do flipper.enable "sp ace" delete '/features/sp%20ace', { 'authenticity_token' => token }, 'rack.session' => session end it 'removes feature' do expect(flipper.features.map(&:key)).not_to include('sp ace') end it 'redirects to features' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features') end end context 'when feature_removal_enabled is set to false' do around do |example| begin @original_feature_removal_enabled = Flipper::UI.configuration.feature_removal_enabled Flipper::UI.configuration.feature_removal_enabled = false example.run ensure Flipper::UI.configuration.feature_removal_enabled = @original_feature_removal_enabled end end it 'returns with 403 status' do expect(last_response.status).to be(403) end it 'renders feature removal disabled template' do expect(last_response.body).to include('Feature removal from the UI is disabled') end end end describe 'POST /features/:feature with _method=DELETE' do before do flipper.enable :search post '/features/search', { '_method' => 'DELETE', 'authenticity_token' => token }, 'rack.session' => session end it 'removes feature' do expect(flipper.features.map(&:key)).not_to include('search') end it 'redirects to features' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features') end end describe 'GET /features/:feature' do before do Flipper::UI.configure do |config| config.descriptions_source = lambda { |_keys| { "stats" => "Most awesome stats", "search" => "Most in-depth search", } } end get '/features/search' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('search') expect(last_response.body).to include('Enable') expect(last_response.body).to include('Disable') expect(last_response.body).to include('No actors enabled') expect(last_response.body).to include('No groups enabled') expect(last_response.body).to include('Enabled for 0% of time') expect(last_response.body).to include('Enabled for 0% of actors') expect(last_response.body).to include('Most in-depth search') end end describe 'GET /features/:feature with _features in feature name' do before do get '/features/search_features' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('search_features') end end describe 'GET /features/:feature with slash in feature name' do before do get '/features/a/b' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('a/b') end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/features_spec.rb000066400000000000000000000113501440444367400260330ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::Features do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'GET /features' do context "when there are some features" do before do flipper[:stats].enable flipper[:search].enable get '/features' end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('stats') expect(last_response.body).to include('search') end end context "when there are no features to list" do before do @original_fun_enabled = Flipper::UI.configuration.fun Flipper::UI.configuration.fun = fun_mode end after do Flipper::UI.configuration.fun = @original_fun_enabled end context "when fun mode is enabled" do let(:fun_mode) { true } before { get '/features' } it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('And I\'ll flip your features.') end end context "when fun mode is disabled" do let(:fun_mode) { false } before { get '/features' } it 'responds with success' do expect(last_response.status).to be(200) end it 'renders template' do expect(last_response.body).to include('You have not added any features to configure yet.') end end end end describe 'POST /features' do let(:feature_name) { 'notifications_next' } before do @original_feature_creation_enabled = Flipper::UI.configuration.feature_creation_enabled Flipper::UI.configuration.feature_creation_enabled = feature_creation_enabled post '/features', { 'value' => feature_name, 'authenticity_token' => token }, 'rack.session' => session end after do Flipper::UI.configuration.feature_creation_enabled = @original_feature_creation_enabled end context 'feature_creation_enabled set to true' do let(:feature_creation_enabled) { true } it 'adds feature' do expect(flipper.features.map(&:key)).to include('notifications_next') end it 'redirects to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/notifications_next') end context 'feature name has whitespace at beginning and end' do let(:feature_name) { ' notifications_next ' } it 'adds feature without whitespace' do expect(flipper.features.map(&:key)).to include('notifications_next') end end context 'feature name contains space' do let(:feature_name) { 'notifications next' } it 'adds feature with space' do expect(flipper.features.map(&:key)).to include('notifications next') end it 'redirects to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/notifications%20next') end end context 'for an invalid feature name' do context 'empty feature name' do let(:feature_name) { '' } it 'does not add feature' do expect(flipper.features.map(&:key)).to eq([]) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/new?error=%22%22%20is%20not%20a%20valid%20feature%20name.') end end context 'nil feature name' do let(:feature_name) { nil } it 'does not add feature' do expect(flipper.features.map(&:key)).to eq([]) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/new?error=%22%22%20is%20not%20a%20valid%20feature%20name.') end end end end context 'feature_creation_enabled set to false' do let(:feature_creation_enabled) { false } it 'does not add feature' do expect(flipper.features.map(&:key)).not_to include('notifications_next') end it 'returns 403' do expect(last_response.status).to be(403) end it 'renders feature creation disabled template' do expect(last_response.body).to include('Feature creation is disabled.') end end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/file_spec.rb000066400000000000000000000006401440444367400251340ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::File do describe 'GET /images/logo.png' do before do get '/images/logo.png' end it 'responds with 200' do expect(last_response.status).to be(200) end end describe 'GET /css/application.css' do before do get '/css/application.css' end it 'responds with 200' do expect(last_response.status).to be(200) end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/groups_gate_spec.rb000066400000000000000000000103541440444367400265370ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::GroupsGate do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'GET /features/:feature/groups' do before do Flipper.register(:admins, &:admin?) get 'features/search/groups' end after do Flipper.unregister_groups end it 'responds with success' do expect(last_response.status).to be(200) end it 'renders add new group form' do form = '' expect(last_response.body).to include(form) end end describe 'POST /features/:feature/groups' do let(:group_name) { 'admins' } before do Flipper.register(:admins, &:admin?) end after do Flipper.unregister_groups end context 'enabling a group' do before do post 'features/search/groups', { 'value' => group_name, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session end it 'adds item to members' do expect(flipper[:search].groups_value).to include('admins') end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end context 'feature name contains space' do before do post 'features/sp%20ace/groups', { 'value' => group_name, 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session end it 'adds item to members' do expect(flipper["sp ace"].groups_value).to include('admins') end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/sp%20ace') end end context 'group name contains whitespace' do let(:group_name) { ' admins ' } it 'adds item without whitespace' do expect(flipper[:search].groups_value).to include('admins') end end context 'for an unregistered group' do context 'unknown group name' do let(:group_name) { 'not_here' } it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22not_here%22%20has%20not%20been%20registered.') end end context 'empty group name' do let(:group_name) { '' } it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22%22%20has%20not%20been%20registered.') end end context 'nil group name' do let(:group_name) { nil } it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search/groups?error=The%20group%20named%20%22%22%20has%20not%20been%20registered.') end end end end context 'disabling a group' do let(:group_name) { 'admins' } before do flipper[:search].enable_group :admins post 'features/search/groups', { 'value' => group_name, 'operation' => 'disable', 'authenticity_token' => token }, 'rack.session' => session end it 'removes item from members' do expect(flipper[:search].groups_value).not_to include('admins') end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end context 'group name contains whitespace' do let(:group_name) { ' admins ' } it 'removes item without whitespace' do expect(flipper[:search].groups_value).not_to include('admins') end end end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/home_spec.rb000066400000000000000000000005111440444367400251420ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::Home do describe 'GET /' do before do flipper[:stats].enable flipper[:search].enable get '/' end it 'responds with redirect' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features') end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb000066400000000000000000000041041440444367400313700ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'POST /features/:feature/percentage_of_actors' do context 'with valid value' do before do post 'features/search/percentage_of_actors', { 'value' => '24', 'authenticity_token' => token }, 'rack.session' => session end it 'enables the feature' do expect(flipper[:search].percentage_of_actors_value).to be(24) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end end context 'with space in feature name' do before do post 'features/sp%20ace/percentage_of_actors', { 'value' => '24', 'authenticity_token' => token }, 'rack.session' => session end it 'enables the feature' do expect(flipper["sp ace"].percentage_of_actors_value).to be(24) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/sp%20ace') end end context 'with invalid value' do before do post 'features/search/percentage_of_actors', { 'value' => '555', 'authenticity_token' => token }, 'rack.session' => session end it 'does not change value' do expect(flipper[:search].percentage_of_actors_value).to be(0) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search?error=Invalid%20percentage%20of%20actors%20value:%20value%20must%20be%20a%20positive%20number%20less%20than%20or%20equal%20to%20100,%20but%20was%20555') end end end end flippercloud-flipper-532c39a/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb000066400000000000000000000040621440444367400310360ustar00rootroot00000000000000RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end describe 'POST /features/:feature/percentage_of_time' do context 'with valid value' do before do post 'features/search/percentage_of_time', { 'value' => '24', 'authenticity_token' => token }, 'rack.session' => session end it 'enables the feature' do expect(flipper[:search].percentage_of_time_value).to be(24) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search') end end context 'with space in feature name' do before do post 'features/sp%20ace/percentage_of_time', { 'value' => '24', 'authenticity_token' => token }, 'rack.session' => session end it 'enables the feature' do expect(flipper["sp ace"].percentage_of_time_value).to be(24) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/sp%20ace') end end context 'with invalid value' do before do post 'features/search/percentage_of_time', { 'value' => '555', 'authenticity_token' => token }, 'rack.session' => session end it 'does not change value' do expect(flipper[:search].percentage_of_time_value).to be(0) end it 'redirects back to feature' do expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/search?error=Invalid%20percentage%20of%20time%20value:%20value%20must%20be%20a%20positive%20number%20less%20than%20or%20equal%20to%20100,%20but%20was%20555') end end end end flippercloud-flipper-532c39a/spec/flipper/ui/configuration_spec.rb000066400000000000000000000114011440444367400254210ustar00rootroot00000000000000RSpec.describe Flipper::UI::Configuration do let(:configuration) { described_class.new } describe "#delete" do it "has default text" do expect(configuration.delete.title).to eq("Danger Zone") expect(configuration.delete.description).to eq("Deleting a feature removes it from the list of features and disables it for everyone.") end end describe "#banner_text" do it "has no default" do expect(configuration.banner_text).to eq(nil) end it "can be updated" do configuration.banner_text = 'Production Environment' expect(configuration.banner_text).to eq('Production Environment') end end describe "#banner_class" do it "has default color" do expect(configuration.banner_class).to eq('danger') end it "can be updated" do configuration.banner_class = 'info' expect(configuration.banner_class).to eq('info') end it "raises if set to invalid value" do expect { configuration.banner_class = :invalid_class } .to raise_error(Flipper::InvalidConfigurationValue) end end describe "#application_breadcrumb_href" do it "has default value" do expect(configuration.application_breadcrumb_href).to eq(nil) end it "can be updated" do configuration.application_breadcrumb_href = 'http://www.myapp.com' expect(configuration.application_breadcrumb_href).to eq('http://www.myapp.com') end end describe "#feature_creation_enabled" do it "has default value" do expect(configuration.feature_creation_enabled).to eq(true) end it "can be updated" do configuration.feature_creation_enabled = false expect(configuration.feature_creation_enabled).to eq(false) end end describe "#cloud_recommendation" do it "has default value" do expect(configuration.cloud_recommendation).to eq(true) end it "can be updated" do configuration.cloud_recommendation = false expect(configuration.cloud_recommendation).to eq(false) end end describe "#feature_removal_enabled" do it "has default value" do expect(configuration.feature_removal_enabled).to eq(true) end it "can be updated" do configuration.feature_removal_enabled = false expect(configuration.feature_removal_enabled).to eq(false) end end describe "#fun" do it "has default value" do expect(configuration.fun).to eq(true) end it "can be updated" do configuration.fun = false expect(configuration.fun).to eq(false) end end describe "#descriptions_source" do it "has default value" do expect(configuration.descriptions_source.call(%w[foo bar])).to eq({}) end context "descriptions source is provided" do it "can be updated" do configuration.descriptions_source = lambda do |_keys| YAML.load_file(FlipperRoot.join('spec/support/descriptions.yml')) end keys = %w[some_awesome_feature foo] result = configuration.descriptions_source.call(keys) expected = { "some_awesome_feature" => "Awesome feature description", } expect(result).to eq(expected) end end end describe "#confirm_fully_enable" do it "has default value" do expect(configuration.confirm_fully_enable).to eq(false) end it "can be updated" do configuration.confirm_fully_enable = true expect(configuration.confirm_fully_enable).to eq(true) end end describe "#show_feature_description_in_list" do it "has default value" do expect(configuration.show_feature_description_in_list).to eq(false) end it "can be updated" do configuration.show_feature_description_in_list = true expect(configuration.show_feature_description_in_list).to eq(true) end end describe "#show_feature_description_in_list?" do subject { configuration.show_feature_description_in_list? } context 'when using_descriptions? is false and show_feature_description_in_list is false' do it { is_expected.to eq(false) } end context 'when using_descriptions? is false and show_feature_description_in_list is true' do before { configuration.show_feature_description_in_list = true } it { is_expected.to eq(false) } end context 'when using_descriptions? is true and show_feature_description_in_list is false' do before { allow(configuration).to receive(:using_descriptions?).and_return(true) } it { is_expected.to eq(false) } end context 'when using_descriptions? is true and show_feature_description_in_list is true' do before do allow(configuration).to receive(:using_descriptions?).and_return(true) configuration.show_feature_description_in_list = true end it { is_expected.to eq(true) } end end end flippercloud-flipper-532c39a/spec/flipper/ui/decorators/000077500000000000000000000000001440444367400233635ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/flipper/ui/decorators/feature_spec.rb000066400000000000000000000025141440444367400263570ustar00rootroot00000000000000RSpec.describe Flipper::UI::Decorators::Feature do let(:source) { {} } let(:adapter) { Flipper::Adapters::Memory.new(source) } let(:flipper) { build_flipper } let(:feature) { flipper[:some_awesome_feature] } subject do described_class.new(feature) end describe '#initialize' do it 'sets the feature' do expect(subject.feature).to be(feature) end end describe '#pretty_name' do it 'capitalizes each word separated by underscores' do expect(subject.pretty_name).to eq('Some Awesome Feature') end end describe '#<=>' do let(:on) do flipper.enable(:on_a) described_class.new(flipper[:on_a]) end let(:on_b) do flipper.enable(:on_b) described_class.new(flipper[:on_b]) end let(:conditional) do flipper.enable_percentage_of_time :conditional_a, 5 described_class.new(flipper[:conditional_a]) end let(:off) do described_class.new(flipper[:off_a]) end it 'sorts :on before :conditional' do expect((on <=> conditional)).to be(-1) end it 'sorts :on before :off' do expect((on <=> off)).to be(-1) end it 'sorts :conditional before :off' do expect((conditional <=> off)).to be(-1) end it 'sorts on key for identical states' do expect((on <=> on_b)).to be(-1) end end end flippercloud-flipper-532c39a/spec/flipper/ui/decorators/gate_spec.rb000066400000000000000000000016611440444367400256460ustar00rootroot00000000000000require 'flipper/ui/decorators/gate' RSpec.describe Flipper::UI::Decorators::Gate do let(:source) { {} } let(:adapter) { Flipper::Adapters::Memory.new(source) } let(:flipper) { build_flipper } let(:feature) { flipper[:some_awesome_feature] } let(:gate) { feature.gate(:boolean) } subject do described_class.new(gate, false) end describe '#initialize' do it 'sets gate' do expect(subject.gate).to be(gate) end it 'sets value' do expect(subject.value).to eq(false) end end describe '#as_json' do before do @result = subject.as_json end it 'returns Hash' do expect(@result).to be_instance_of(Hash) end it 'includes key' do expect(@result['key']).to eq('boolean') end it 'includes pretty name' do expect(@result['name']).to eq('boolean') end it 'includes value' do expect(@result['value']).to be(false) end end end flippercloud-flipper-532c39a/spec/flipper/ui/util_spec.rb000066400000000000000000000007171440444367400235370ustar00rootroot00000000000000require 'flipper/ui/util' RSpec.describe Flipper::UI::Util do describe '#blank?' do context 'with a string' do it 'returns true if blank' do expect(described_class.blank?(nil)).to be(true) expect(described_class.blank?('')).to be(true) expect(described_class.blank?(' ')).to be(true) end it 'returns false if not blank' do expect(described_class.blank?('nope')).to be(false) end end end end flippercloud-flipper-532c39a/spec/flipper/ui_spec.rb000066400000000000000000000147661440444367400225730ustar00rootroot00000000000000RSpec.describe Flipper::UI do let(:token) do if Rack::Protection::AuthenticityToken.respond_to?(:random_token) Rack::Protection::AuthenticityToken.random_token else 'a' end end let(:session) do { :csrf => token, 'csrf' => token, '_csrf_token' => token } end let(:configuration) { described_class.configuration } describe 'Initializing middleware with flipper instance' do let(:app) { build_app(flipper) } it 'works' do flipper.enable :some_great_feature get '/features' expect(last_response.status).to be(200) expect(last_response.body).to include('some_great_feature') end end describe 'Request method unsupported by action' do it 'raises error' do expect do head '/features' end.to raise_error(Flipper::UI::RequestMethodNotSupported) end end describe 'Inspecting the built Rack app' do it 'returns a String' do expect(build_app(flipper).inspect).to eq("Flipper::UI") end end # See https://github.com/jnunemaker/flipper/issues/80 it 'can route features with names that match static directories' do post 'features/refactor-images/actors', { 'value' => 'User;6', 'operation' => 'enable', 'authenticity_token' => token }, 'rack.session' => session expect(last_response.status).to be(302) expect(last_response.headers['Location']).to eq('/features/refactor-images') end describe 'configure' do it 'yields configuration instance' do described_class.configure do |config| expect(config).to be_instance_of(Flipper::UI::Configuration) end end describe 'banner' do it 'does not include the banner if banner_text is not set' do get '/features' expect(last_response.body).not_to include('Production Environment') end describe 'when set' do around do |example| begin @original_banner_text = described_class.configuration.banner_text described_class.configuration.banner_text = 'Production Environment' example.run ensure described_class.configuration.banner_text = @original_banner_text end end it 'includes banner' do get '/features' expect(last_response.body).to include('Production Environment') end end end describe "application_breadcrumb_href" do it 'does not have an application_breadcrumb_href by default' do expect(configuration.application_breadcrumb_href).to be(nil) end context 'with application_breadcrumb_href not set' do before do @original_application_breadcrumb_href = configuration.application_breadcrumb_href configuration.application_breadcrumb_href = nil end after do configuration.application_breadcrumb_href = @original_application_breadcrumb_href end it 'does not add App breadcrumb' do get '/features' expect(last_response.body).not_to include('App') end end context 'with application_breadcrumb_href set' do before do @original_application_breadcrumb_href = configuration.application_breadcrumb_href configuration.application_breadcrumb_href = '/myapp' end after do configuration.application_breadcrumb_href = @original_application_breadcrumb_href end it 'does add App breadcrumb' do get '/features' expect(last_response.body).to include('App') end end context 'with application_breadcrumb_href set to full url' do before do @original_application_breadcrumb_href = configuration.application_breadcrumb_href configuration.application_breadcrumb_href = 'https://myapp.com/' end after do configuration.application_breadcrumb_href = @original_application_breadcrumb_href end it 'does add App breadcrumb' do get '/features' expect(last_response.body).to include('App') end end end describe "feature_creation_enabled" do it 'sets feature_creation_enabled to true by default' do expect(configuration.feature_creation_enabled).to be(true) end context 'with feature_creation_enabled set to true' do before do @original_feature_creation_enabled = configuration.feature_creation_enabled configuration.feature_creation_enabled = true end it 'has the add_feature button' do get '/features' expect(last_response.body).to include('Add Feature') end after do configuration.feature_creation_enabled = @original_feature_creation_enabled end end context 'with feature_creation_enabled set to false' do before do @original_feature_creation_enabled = configuration.feature_creation_enabled configuration.feature_creation_enabled = false end it 'does not have the add_feature button' do get '/features' expect(last_response.body).not_to include('Add Feature') end after do configuration.feature_creation_enabled = @original_feature_creation_enabled end end end describe "feature_removal_enabled" do it 'sets feature_removal_enabled to true by default' do expect(configuration.feature_removal_enabled).to be(true) end context 'with feature_removal_enabled set to true' do before do @original_feature_removal_enabled = configuration.feature_removal_enabled configuration.feature_removal_enabled = true end it 'has the add_feature button' do get '/features/test' expect(last_response.body).to include('Delete') end after do configuration.feature_removal_enabled = @original_feature_removal_enabled end end context 'with feature_removal_enabled set to false' do before do @original_feature_removal_enabled = configuration.feature_removal_enabled configuration.feature_removal_enabled = false end it 'does not have the add_feature button' do get '/features/test' expect(last_response.body).not_to include('Delete') end after do configuration.feature_removal_enabled = @original_feature_removal_enabled end end end end end flippercloud-flipper-532c39a/spec/flipper_integration_spec.rb000066400000000000000000000352211440444367400245460ustar00rootroot00000000000000require 'flipper/feature' RSpec.describe Flipper do let(:adapter) { Flipper::Adapters::Memory.new } let(:flipper) { described_class.new(adapter) } let(:feature) { flipper[:search] } let(:admin_group) { flipper.group(:admins) } let(:dev_group) { flipper.group(:devs) } let(:admin_thing) do double 'Non Flipper Thing', flipper_id: 1, admin?: true, dev?: false end let(:dev_thing) do double 'Non Flipper Thing', flipper_id: 10, admin?: false, dev?: true end let(:admin_truthy_thing) do double 'Non Flipper Thing', flipper_id: 1, admin?: 'true-ish', dev?: false end let(:admin_falsey_thing) do double 'Non Flipper Thing', flipper_id: 1, admin?: nil, dev?: false end let(:pitt) { Flipper::Actor.new(1) } let(:clooney) { Flipper::Actor.new(10) } let(:five_percent_of_actors) { flipper.actors(5) } let(:five_percent_of_time) { flipper.time(5) } before do described_class.register(:admins, &:admin?) described_class.register(:devs, &:dev?) end describe '#enable' do context 'with no arguments' do before do @result = feature.enable end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for all' do expect(feature.enabled?).to eq(true) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a group' do before do @result = feature.enable(admin_group) end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for non flipper thing in group' do expect(feature.enabled?(admin_thing)).to eq(true) end it 'does not enable feature for non flipper thing in other group' do expect(feature.enabled?(dev_thing)).to eq(false) end it 'enables feature for flipper actor in group' do expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true) end it 'does not enable for flipper actor not in group' do expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false) end it 'does not enable feature for all' do expect(feature.enabled?).to eq(false) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with an actor' do before do @result = feature.enable(pitt) end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for actor' do expect(feature.enabled?(pitt)).to eq(true) end it 'does not enable feature for other actors' do expect(feature.enabled?(clooney)).to eq(false) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a percentage of actors' do before do @result = feature.enable(five_percent_of_actors) end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for actor within percentage' do enabled = (1..100).select do |i| thing = Flipper::Actor.new(i) feature.enabled?(thing) end.size expect(enabled).to be_within(2).of(5) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a float percentage of actors' do before do @result = feature.enable_percentage_of_actors 5.1 end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for actor within percentage' do enabled = (1..100).select do |i| thing = Flipper::Actor.new(i) feature.enabled?(thing) end.size expect(enabled).to be_within(2).of(5) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a percentage of time' do before do @gate = feature.gate(:percentage_of_time) @result = feature.enable(five_percent_of_time) end it 'returns true' do expect(@result).to eq(true) end it 'enables feature for time within percentage' do allow(@gate).to receive_messages(rand: 0.04) expect(feature.enabled?).to eq(true) end it 'does not enable feature for time not within percentage' do allow(@gate).to receive_messages(rand: 0.10) expect(feature.enabled?).to eq(false) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with argument that has no gate' do it 'raises error' do thing = Object.new expect do feature.enable(thing) end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}") end end end describe '#disable' do context 'with no arguments' do before do # ensures that time gate is stubbed with result that would be true for pitt @gate = feature.gate(:percentage_of_time) allow(@gate).to receive_messages(rand: 0.04) feature.enable admin_group feature.enable pitt feature.enable five_percent_of_actors feature.enable five_percent_of_time @result = feature.disable end it 'returns true' do expect(@result).to be(true) end it 'disables feature' do expect(feature.enabled?).to eq(false) end it 'disables for individual actor' do expect(feature.enabled?(pitt)).to eq(false) end it 'disables actor in group' do expect(feature.enabled?(admin_thing)).to eq(false) end it 'disables actor in percentage of actors' do enabled = (1..100).select do |i| thing = Flipper::Actor.new(i) feature.enabled?(thing) end.size expect(enabled).to be(0) end it 'disables percentage of time' do expect(feature.enabled?(pitt)).to eq(false) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a group' do before do feature.enable dev_group feature.enable admin_group @result = feature.disable(admin_group) end it 'returns true' do expect(@result).to eq(true) end it 'disables the feature for non flipper thing in the group' do expect(feature.enabled?(admin_thing)).to eq(false) end it 'does not disable feature for non flipper thing in other groups' do expect(feature.enabled?(dev_thing)).to eq(true) end it 'disables feature for flipper actor in group' do expect(feature.enabled?(flipper.actor(admin_thing))).to eq(false) end it 'does not disable feature for flipper actor in other groups' do expect(feature.enabled?(flipper.actor(dev_thing))).to eq(true) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with an actor' do before do feature.enable pitt feature.enable clooney @result = feature.disable(pitt) end it 'returns true' do expect(@result).to eq(true) end it 'disables feature for actor' do expect(feature.enabled?(pitt)).to eq(false) end it 'does not disable feature for other actors' do expect(feature.enabled?(clooney)).to eq(true) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a percentage of actors' do before do @result = feature.disable(flipper.actors(0)) end it 'returns true' do expect(@result).to eq(true) end it 'disables feature' do enabled = (1..100).select do |i| thing = Flipper::Actor.new(i) feature.enabled?(thing) end.size expect(enabled).to be(0) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with a percentage of time' do before do @gate = feature.gate(:percentage_of_time) @result = feature.disable(flipper.time(0)) end it 'returns true' do expect(@result).to eq(true) end it 'disables feature for time within percentage' do allow(@gate).to receive_messages(rand: 0.04) expect(feature.enabled?).to eq(false) end it 'disables feature for time not within percentage' do allow(@gate).to receive_messages(rand: 0.10) expect(feature.enabled?).to eq(false) end it 'adds feature to set of features' do expect(flipper.features.map(&:name)).to include(:search) end end context 'with argument that has no gate' do it 'raises error' do thing = Object.new expect do feature.disable(thing) end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}") end end end describe '#enabled?' do context 'with no arguments' do it 'defaults to false' do expect(feature.enabled?).to eq(false) end end context 'with no arguments, but boolean enabled' do before do feature.enable end it 'returns true' do expect(feature.enabled?).to eq(true) end end context 'for actor in enabled group' do before do feature.enable admin_group end it 'returns true' do expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true) expect(feature.enabled?(admin_thing)).to eq(true) end it 'returns true for truthy block values' do expect(feature.enabled?(flipper.actor(admin_truthy_thing))).to eq(true) end end context 'for actor in disabled group' do it 'returns false' do expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false) expect(feature.enabled?(dev_thing)).to eq(false) end it 'returns false for falsey block values' do expect(feature.enabled?(flipper.actor(admin_falsey_thing))).to eq(false) end end context 'for enabled actor' do before do feature.enable pitt end it 'returns true' do expect(feature.enabled?(pitt)).to eq(true) end end context 'for not enabled actor' do it 'returns false' do expect(feature.enabled?(clooney)).to eq(false) end it 'returns true if boolean enabled' do feature.enable expect(feature.enabled?(clooney)).to eq(true) end end context 'for enabled percentage of time' do before do # ensure percentage of time returns percentage that makes five percent # of time true @gate = feature.gate(:percentage_of_time) allow(@gate).to receive_messages(rand: 0.04) feature.enable five_percent_of_time end it 'returns true' do expect(feature.enabled?).to eq(true) expect(feature.enabled?(nil)).to eq(true) expect(feature.enabled?(pitt)).to eq(true) expect(feature.enabled?(admin_thing)).to eq(true) end end context 'for enabled float percentage of time' do before do # ensure percentage of time returns percentage that makes 4.1 percent # of time true @gate = feature.gate(:percentage_of_time) allow(@gate).to receive_messages(rand: 0.04) feature.enable_percentage_of_time 4.1 end it 'returns true' do expect(feature.enabled?).to eq(true) expect(feature.enabled?(nil)).to eq(true) expect(feature.enabled?(pitt)).to eq(true) expect(feature.enabled?(admin_thing)).to eq(true) end end context 'for NOT enabled integer percentage of time' do before do # ensure percentage of time returns percentage that makes enabled? false @gate = feature.gate(:percentage_of_time) allow(@gate).to receive_messages(rand: 0.10) feature.enable five_percent_of_time end it 'returns false' do expect(feature.enabled?).to eq(false) expect(feature.enabled?(nil)).to eq(false) expect(feature.enabled?(pitt)).to eq(false) expect(feature.enabled?(admin_thing)).to eq(false) end it 'returns true if boolean enabled' do feature.enable expect(feature.enabled?).to eq(true) expect(feature.enabled?(nil)).to eq(true) expect(feature.enabled?(pitt)).to eq(true) expect(feature.enabled?(admin_thing)).to eq(true) end end context 'for NOT enabled float percentage of time' do before do # ensure percentage of time returns percentage that makes enabled? false @gate = feature.gate(:percentage_of_time) allow(@gate).to receive_messages(rand: 0.10) feature.enable_percentage_of_time 9.9 end it 'returns false' do expect(feature.enabled?).to eq(false) expect(feature.enabled?(nil)).to eq(false) expect(feature.enabled?(pitt)).to eq(false) expect(feature.enabled?(admin_thing)).to eq(false) end it 'returns true if boolean enabled' do feature.enable expect(feature.enabled?).to eq(true) expect(feature.enabled?(nil)).to eq(true) expect(feature.enabled?(pitt)).to eq(true) expect(feature.enabled?(admin_thing)).to eq(true) end end context 'for a non flipper thing' do before do feature.enable admin_group end it 'returns true if in enabled group' do expect(feature.enabled?(admin_thing)).to eq(true) end it 'returns false if not in enabled group' do expect(feature.enabled?(dev_thing)).to eq(false) end it 'returns true if boolean enabled' do feature.enable expect(feature.enabled?(admin_thing)).to eq(true) expect(feature.enabled?(dev_thing)).to eq(true) end end end context 'enabling multiple groups, disabling everything, then enabling one group' do before do feature.enable(admin_group) feature.enable(dev_group) feature.disable feature.enable(admin_group) end it 'enables feature for object in enabled group' do expect(feature.enabled?(admin_thing)).to eq(true) end it 'does not enable feature for object in not enabled group' do expect(feature.enabled?(dev_thing)).to eq(false) end end end flippercloud-flipper-532c39a/spec/flipper_spec.rb000066400000000000000000000270271440444367400221500ustar00rootroot00000000000000require 'flipper/cloud' RSpec.describe Flipper do describe '.new' do it 'returns new instance of dsl' do instance = described_class.new(double('Adapter')) expect(instance).to be_instance_of(Flipper::DSL) end end describe '.configure' do it 'yield configuration instance' do described_class.configure do |config| expect(config).to be_instance_of(Flipper::Configuration) end end end describe '.configuration' do it 'returns configuration instance' do expect(described_class.configuration).to be_instance_of(Flipper::Configuration) end end describe '.configuration=' do it "sets configuration instance" do configuration = Flipper::Configuration.new described_class.configuration = configuration expect(described_class.configuration).to be(configuration) end end describe '.instance' do it 'returns DSL instance using result of default invocation' do instance = described_class.new(Flipper::Adapters::Memory.new) described_class.configure do |config| config.default { instance } end expect(described_class.instance).to be(instance) expect(described_class.instance).to be(described_class.instance) # memoized end it 'is reset when configuration is changed' do described_class.configure do |config| config.default { described_class.new(Flipper::Adapters::Memory.new) } end original_instance = described_class.instance new_config = Flipper::Configuration.new new_config.default { described_class.new(Flipper::Adapters::Memory.new) } described_class.configuration = new_config expect(described_class.instance).not_to be(original_instance) end end describe '.instance=' do it 'updates Flipper.instance' do instance = described_class.new(Flipper::Adapters::Memory.new) described_class.instance = instance expect(described_class.instance).to be(instance) end end describe "delegation to instance" do let(:group) { Flipper::Types::Group.new(:admins) } let(:actor) { Flipper::Actor.new("1") } before do described_class.configure do |config| config.default { described_class.new(Flipper::Adapters::Memory.new) } end end it 'delegates enabled? to instance' do expect(described_class.enabled?(:search)).to eq(described_class.instance.enabled?(:search)) described_class.instance.enable(:search) expect(described_class.enabled?(:search)).to eq(described_class.instance.enabled?(:search)) end it 'delegates enable to instance' do described_class.enable(:search) expect(described_class.instance.enabled?(:search)).to be(true) end it 'delegates disable to instance' do described_class.disable(:search) expect(described_class.instance.enabled?(:search)).to be(false) end it 'delegates bool to instance' do expect(described_class.bool).to eq(described_class.instance.bool) end it 'delegates boolean to instance' do expect(described_class.boolean).to eq(described_class.instance.boolean) end it 'delegates enable_actor to instance' do described_class.enable_actor(:search, actor) expect(described_class.instance.enabled?(:search, actor)).to be(true) end it 'delegates disable_actor to instance' do described_class.disable_actor(:search, actor) expect(described_class.instance.enabled?(:search, actor)).to be(false) end it 'delegates actor to instance' do expect(described_class.actor(actor)).to eq(described_class.instance.actor(actor)) end it 'delegates enable_group to instance' do described_class.enable_group(:search, group) expect(described_class.instance[:search].enabled_groups).to include(group) end it 'delegates disable_group to instance' do described_class.disable_group(:search, group) expect(described_class.instance[:search].enabled_groups).not_to include(group) end it 'delegates enable_percentage_of_actors to instance' do described_class.enable_percentage_of_actors(:search, 5) expect(described_class.instance[:search].percentage_of_actors_value).to be(5) end it 'delegates disable_percentage_of_actors to instance' do described_class.disable_percentage_of_actors(:search) expect(described_class.instance[:search].percentage_of_actors_value).to be(0) end it 'delegates actors to instance' do expect(described_class.actors(5)).to eq(described_class.instance.actors(5)) end it 'delegates percentage_of_actors to instance' do expected = described_class.instance.percentage_of_actors(5) expect(described_class.percentage_of_actors(5)).to eq(expected) end it 'delegates enable_percentage_of_time to instance' do described_class.enable_percentage_of_time(:search, 5) expect(described_class.instance[:search].percentage_of_time_value).to be(5) end it 'delegates disable_percentage_of_time to instance' do described_class.disable_percentage_of_time(:search) expect(described_class.instance[:search].percentage_of_time_value).to be(0) end it 'delegates time to instance' do expect(described_class.time(56)).to eq(described_class.instance.time(56)) end it 'delegates percentage_of_time to instance' do expected = described_class.instance.percentage_of_time(56) expect(described_class.percentage_of_time(56)).to eq(expected) end it 'delegates features to instance' do described_class.instance.add(:search) expect(described_class.features).to eq(described_class.instance.features) expect(described_class.features).to include(described_class.instance[:search]) end it 'delegates feature to instance' do expect(described_class.feature(:search)).to eq(described_class.instance.feature(:search)) end it 'delegates [] to instance' do expect(described_class[:search]).to eq(described_class.instance[:search]) end it 'delegates preload to instance' do described_class.instance.enable(:search) expect(described_class.preload([:search])).to eq(described_class.instance.preload([:search])) end it 'delegates preload_all to instance' do described_class.instance.enable(:search) described_class.instance.enable(:stats) expect(described_class.preload_all).to eq(described_class.instance.preload_all) end it 'delegates add to instance' do expect(described_class.add(:search)).to eq(described_class.instance.add(:search)) end it 'delegates exist? to instance' do expect(described_class.exist?(:search)).to eq(described_class.instance.exist?(:search)) end it 'delegates remove to instance' do expect(described_class.remove(:search)).to eq(described_class.instance.remove(:search)) end it 'delegates import to instance' do other = described_class.new(Flipper::Adapters::Memory.new) other.enable(:search) described_class.import(other) expect(described_class.enabled?(:search)).to be(true) end it 'delegates adapter to instance' do expect(described_class.adapter).to eq(described_class.instance.adapter) end it 'delegates memoize= to instance' do expect(described_class.adapter.memoizing?).to be(false) described_class.memoize = true expect(described_class.adapter.memoizing?).to be(true) end it 'delegates memoizing? to instance' do expect(described_class.memoizing?).to eq(described_class.adapter.memoizing?) end it 'delegates sync stuff to instance and does nothing' do expect(described_class.sync).to be(nil) expect(described_class.sync_secret).to be(nil) end it 'delegates sync stuff to instance for Flipper::Cloud' do stub = stub_request(:get, "https://www.flippercloud.io/adapter/features"). with({ headers: { 'Flipper-Cloud-Token'=>'asdf', }, }).to_return(status: 200, body: '{"features": {}}', headers: {}) cloud_configuration = Flipper::Cloud::Configuration.new({ token: "asdf", sync_secret: "tasty", }) described_class.configure do |config| config.default { Flipper::Cloud::DSL.new(cloud_configuration) } end described_class.sync expect(described_class.sync_secret).to eq("tasty") expect(stub).to have_been_requested end end describe '.register' do it 'adds a group to the group_registry' do registry = Flipper::Registry.new described_class.groups_registry = registry group = described_class.register(:admins, &:admin?) expect(registry.get(:admins)).to eq(group) end it 'adds a group to the group_registry for string name' do registry = Flipper::Registry.new described_class.groups_registry = registry group = described_class.register('admins', &:admin?) expect(registry.get(:admins)).to eq(group) end it 'raises exception if group already registered' do described_class.register(:admins) {} expect do described_class.register(:admins) {} end.to raise_error(Flipper::DuplicateGroup, 'Group :admins has already been registered') end end describe '.groups' do it 'returns array of group instances' do admins = described_class.register(:admins, &:admin?) preview_features = described_class.register(:preview_features, &:preview_features?) expect(described_class.groups).to eq(Set[ admins, preview_features, ]) end end describe '.group_names' do it 'returns array of group names' do described_class.register(:admins, &:admin?) described_class.register(:preview_features, &:preview_features?) expect(described_class.group_names).to eq(Set[ :admins, :preview_features, ]) end end describe '.unregister_groups' do it 'clear group registry' do expect(described_class.groups_registry).to receive(:clear) described_class.unregister_groups end end describe '.group_exists' do it 'returns true if the group is already created' do group = described_class.register('admins', &:admin?) expect(described_class.group_exists?(:admins)).to eq(true) end it 'returns false when the group is not yet registered' do expect(described_class.group_exists?(:non_existing)).to eq(false) end end describe '.group' do context 'for registered group' do before do @group = described_class.register(:admins) {} end it 'returns group' do expect(described_class.group(:admins)).to eq(@group) end it 'returns group with string key' do expect(described_class.group('admins')).to eq(@group) end end context 'for unregistered group' do before do @group = described_class.group(:cats) end it 'returns group' do expect(@group).to be_instance_of(Flipper::Types::Group) expect(@group.name).to eq(:cats) end it 'does not add group to registry' do expect(described_class.group_exists?(@group.name)).to be(false) end end end describe '.groups_registry' do it 'returns a registry instance' do expect(described_class.groups_registry).to be_instance_of(Flipper::Registry) end end describe '.groups_registry=' do it 'sets groups_registry registry' do registry = Flipper::Registry.new described_class.groups_registry = registry expect(described_class.instance_variable_get('@groups_registry')).to eq(registry) end end end flippercloud-flipper-532c39a/spec/spec_helper.rb000066400000000000000000000053441440444367400217640ustar00rootroot00000000000000$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) require 'pp' require 'pathname' FlipperRoot = Pathname(__FILE__).dirname.join('..').expand_path require 'rubygems' require 'bundler' Bundler.setup(:default) require 'debug' require 'webmock/rspec' WebMock.disable_net_connect!(allow_localhost: true) require 'flipper' require 'flipper/api' require 'flipper/spec/shared_adapter_specs' require 'flipper/ui' Dir[FlipperRoot.join('spec/support/**/*.rb')].sort.each { |f| require f } RSpec.configure do |config| config.before(:example) do Flipper::Poller.reset if defined?(Flipper::Poller) Flipper.unregister_groups Flipper.configuration = nil end config.disable_monkey_patching! config.filter_run focus: true config.run_all_when_everything_filtered = true end RSpec.shared_examples_for 'a percentage' do it 'initializes with value' do percentage = described_class.new(12) expect(percentage).to be_instance_of(described_class) end it 'converts string values to integers when initializing' do percentage = described_class.new('15') expect(percentage.value).to eq(15) end it 'has a value' do percentage = described_class.new(19) expect(percentage.value).to eq(19) end it 'raises exception for value higher than 100' do expect do described_class.new(101) end.to raise_error(ArgumentError, 'value must be a positive number less than or equal to 100, but was 101') end it 'raises exception for negative value' do expect do described_class.new(-1) end.to raise_error(ArgumentError, 'value must be a positive number less than or equal to 100, but was -1') end end RSpec.shared_examples_for 'a DSL feature' do it 'returns instance of feature' do expect(feature).to be_instance_of(Flipper::Feature) end it 'sets name' do expect(feature.name).to eq(:stats) end it 'sets adapter' do expect(feature.adapter.name).to eq(dsl.adapter.name) end it 'sets instrumenter' do expect(feature.instrumenter).to eq(dsl.instrumenter) end it 'memoizes the feature' do expect(dsl.send(method_name, :stats)).to equal(feature) end it 'raises argument error if not string or symbol' do expect do dsl.send(method_name, Object.new) end.to raise_error(ArgumentError, /must be a String or Symbol/) end end RSpec.shared_examples_for 'a DSL boolean method' do it 'returns boolean with value set' do result = subject.send(method_name, true) expect(result).to be_instance_of(Flipper::Types::Boolean) expect(result.value).to be(true) result = subject.send(method_name, false) expect(result).to be_instance_of(Flipper::Types::Boolean) expect(result.value).to be(false) end end flippercloud-flipper-532c39a/spec/support/000077500000000000000000000000001440444367400206545ustar00rootroot00000000000000flippercloud-flipper-532c39a/spec/support/descriptions.yml000066400000000000000000000000641440444367400241050ustar00rootroot00000000000000some_awesome_feature: 'Awesome feature description' flippercloud-flipper-532c39a/spec/support/fake_udp_socket.rb000066400000000000000000000004721440444367400243320ustar00rootroot00000000000000class FakeUDPSocket attr_reader :buffer def initialize @buffer = [] end def send(message, *_rest) @buffer.push [message] end def recv @buffer.shift end def clear @buffer = [] end def to_s inspect end def inspect "" end end flippercloud-flipper-532c39a/spec/support/spec_helpers.rb000066400000000000000000000034411440444367400236570ustar00rootroot00000000000000require 'ice_age' require 'json' require 'rack/test' module SpecHelpers def self.included(base) base.let(:flipper) { build_flipper } base.let(:app) { build_app(flipper) } end def build_app(flipper, options = {}) Flipper::UI.app(flipper, options) do |builder| builder.use Rack::Session::Cookie, secret: 'test' end end def build_api(flipper, options = {}) Flipper::Api.app(flipper, options) end def build_flipper(adapter = build_memory_adapter) Flipper.new(adapter) end def build_memory_adapter Flipper::Adapters::Memory.new end def json_response JSON.parse(last_response.body) end def api_error_code_reference_url 'https://flippercloud.io/docs/api#error-code-reference' end def api_not_found_response { 'code' => 1, 'message' => 'Feature not found.', 'more_info' => api_error_code_reference_url, } end def api_flipper_id_is_missing_response { 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => api_error_code_reference_url, } end def api_positive_percentage_error_response { 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => api_error_code_reference_url, } end def silence # Store the original stderr and stdout in order to restore them later original_stderr = $stderr original_stdout = $stdout # Redirect stderr and stdout output = $stderr = $stdout = StringIO.new yield $stderr = original_stderr $stdout = original_stdout # Return output output.string end end RSpec.configure do |config| config.order = :random Kernel.srand config.seed config.include Rack::Test::Methods config.include SpecHelpers end flippercloud-flipper-532c39a/test/000077500000000000000000000000001440444367400171655ustar00rootroot00000000000000flippercloud-flipper-532c39a/test/adapters/000077500000000000000000000000001440444367400207705ustar00rootroot00000000000000flippercloud-flipper-532c39a/test/adapters/active_record_test.rb000066400000000000000000000041201440444367400251620ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/active_record' # Turn off migration logging for specs ActiveRecord::Migration.verbose = false class ActiveRecordTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') def setup @adapter = Flipper::Adapters::ActiveRecord.new ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_features ( id integer PRIMARY KEY, key text NOT NULL UNIQUE, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE TABLE flipper_gates ( id integer PRIMARY KEY, feature_key text NOT NULL, key text NOT NULL, value text DEFAULT NULL, created_at datetime NOT NULL, updated_at datetime NOT NULL ) SQL ActiveRecord::Base.connection.execute <<-SQL CREATE UNIQUE INDEX index_gates_on_keys_and_value on flipper_gates (feature_key, key, value) SQL end def teardown ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_features`") ActiveRecord::Base.connection.execute("DROP table IF EXISTS `flipper_gates`") end def test_models_honor_table_name_prefixes_and_suffixes ActiveRecord::Base.table_name_prefix = :foo_ ActiveRecord::Base.table_name_suffix = :_bar Flipper::Adapters::ActiveRecord.send(:remove_const, :Feature) Flipper::Adapters::ActiveRecord.send(:remove_const, :Gate) load("flipper/adapters/active_record.rb") assert_equal "foo_flipper_features_bar", Flipper::Adapters::ActiveRecord::Feature.table_name assert_equal "foo_flipper_gates_bar", Flipper::Adapters::ActiveRecord::Gate.table_name ensure ActiveRecord::Base.table_name_prefix = "" ActiveRecord::Base.table_name_suffix = "" Flipper::Adapters::ActiveRecord.send(:remove_const, :Feature) Flipper::Adapters::ActiveRecord.send(:remove_const, :Gate) load("flipper/adapters/active_record.rb") end end flippercloud-flipper-532c39a/test/adapters/dalli_test.rb000066400000000000000000000010701440444367400234370ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/dalli' require 'logger' class DalliTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup url = ENV.fetch('MEMCACHED_URL', 'localhost:11211') @cache = Dalli::Client.new(url) Dalli.logger = Logger.new('/dev/null') @cache.flush memory_adapter = Flipper::Adapters::Memory.new @adapter = Flipper::Adapters::Dalli.new(memory_adapter, @cache) rescue Dalli::NetworkError ENV['CI'] ? raise : skip('Memcached not available') end def teardown @cache.flush end end flippercloud-flipper-532c39a/test/adapters/memory_test.rb000066400000000000000000000002511440444367400236620ustar00rootroot00000000000000require 'test_helper' class MemoryTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup @adapter = Flipper::Adapters::Memory.new end end flippercloud-flipper-532c39a/test/adapters/mongo_test.rb000066400000000000000000000014021440444367400234700ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/mongo' class MongoTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup host = ENV.fetch('MONGODB_HOST', '127.0.0.1') port = '27017' logger = Logger.new('/dev/null') client = Mongo::Client.new(["#{host}:#{port}"], server_selection_timeout: 0.01, database: 'testing', logger: logger) collection = client['testing'] begin collection.drop collection.create rescue Mongo::Error::NoServerAvailable ENV['CI'] ? raise : skip('Mongo not available') rescue Mongo::Error::OperationFailure end @adapter = Flipper::Adapters::Mongo.new(collection) end end flippercloud-flipper-532c39a/test/adapters/pstore_test.rb000066400000000000000000000007321440444367400236720ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/pstore' class PstoreTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup dir = FlipperRoot.join('tmp').tap(&:mkpath) pstore_file = dir.join('flipper.pstore') pstore_file.unlink if pstore_file.exist? @adapter = Flipper::Adapters::PStore.new(pstore_file) end def test_defaults_path_to_flipper_pstore assert_equal Flipper::Adapters::PStore.new.path, 'flipper.pstore' end end flippercloud-flipper-532c39a/test/adapters/redis_cache_test.rb000066400000000000000000000010161440444367400246030ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/redis_cache' class RedisCacheTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup url = ENV.fetch('REDIS_URL', 'redis://localhost:6379') @cache = Redis.new(url: url).tap(&:flushdb) memory_adapter = Flipper::Adapters::Memory.new @adapter = Flipper::Adapters::RedisCache.new(memory_adapter, @cache) rescue Redis::CannotConnectError ENV['CI'] ? raise : skip('Reids not available') end def teardown @cache.flushdb end end flippercloud-flipper-532c39a/test/adapters/redis_test.rb000066400000000000000000000006221440444367400234620ustar00rootroot00000000000000require 'test_helper' require 'flipper/adapters/redis' class RedisTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def setup url = ENV.fetch('REDIS_URL', 'redis://localhost:6379') client = Redis.new(url: url).tap(&:flushdb) @adapter = Flipper::Adapters::Redis.new(client) rescue Redis::CannotConnectError ENV['CI'] ? raise : skip('Redis not available') end end flippercloud-flipper-532c39a/test/adapters/sequel_test.rb000066400000000000000000000013231440444367400236510ustar00rootroot00000000000000require 'test_helper' require 'sequel' Sequel::Model.db = Sequel.sqlite(':memory:') Sequel.extension :migration, :core_extensions require 'flipper/adapters/sequel' require 'generators/flipper/templates/sequel_migration' class SequelTest < MiniTest::Test prepend Flipper::Test::SharedAdapterTests def feature_class Flipper::Adapters::Sequel::Feature end def gate_class Flipper::Adapters::Sequel::Gate end def setup CreateFlipperTablesSequel.new(Sequel::Model.db).up feature_class.dataset = feature_class.dataset gate_class.dataset = gate_class.dataset @adapter = Flipper::Adapters::Sequel.new end def teardown CreateFlipperTablesSequel.new(Sequel::Model.db).down end end flippercloud-flipper-532c39a/test/test_helper.rb000066400000000000000000000003321440444367400220260ustar00rootroot00000000000000require 'bundler/setup' require 'flipper' require 'minitest/autorun' require 'minitest/unit' Dir['./lib/flipper/test/*.rb'].sort.each { |f| require(f) } FlipperRoot = Pathname(__FILE__).dirname.join('..').expand_path flippercloud-flipper-532c39a/test_rails/000077500000000000000000000000001440444367400203575ustar00rootroot00000000000000flippercloud-flipper-532c39a/test_rails/generators/000077500000000000000000000000001440444367400225305ustar00rootroot00000000000000flippercloud-flipper-532c39a/test_rails/generators/flipper/000077500000000000000000000000001440444367400241715ustar00rootroot00000000000000flippercloud-flipper-532c39a/test_rails/generators/flipper/active_record_generator_test.rb000066400000000000000000000026241440444367400324400ustar00rootroot00000000000000require 'helper' require 'active_record' require 'rails/generators/test_case' require 'generators/flipper/active_record_generator' class FlipperActiveRecordGeneratorTest < Rails::Generators::TestCase tests Flipper::Generators::ActiveRecordGenerator destination File.expand_path('../../../../tmp', __FILE__) setup :prepare_destination def test_generates_migration run_generator migration_version = if Rails::VERSION::MAJOR.to_i < 5 "" else "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" end assert_migration 'db/migrate/create_flipper_tables.rb', <<~MIGRATION class CreateFlipperTables < ActiveRecord::Migration#{migration_version} def self.up create_table :flipper_features do |t| t.string :key, null: false t.timestamps null: false end add_index :flipper_features, :key, unique: true create_table :flipper_gates do |t| t.string :feature_key, null: false t.string :key, null: false t.string :value t.timestamps null: false end add_index :flipper_gates, [:feature_key, :key, :value], unique: true end def self.down drop_table :flipper_gates drop_table :flipper_features end end MIGRATION end end flippercloud-flipper-532c39a/test_rails/helper.rb000066400000000000000000000004071440444367400221640ustar00rootroot00000000000000require 'rubygems' require 'bundler' Bundler.setup(:default) require 'rails' require 'rails/test_help' begin ActiveSupport::TestCase.test_order = :random rescue NoMethodError # no biggie, means we are on older version of AS that doesn't have this option end