cursive-tabs-0.8.0/.cargo_vcs_info.json0000644000000001360000000000100134500ustar { "git": { "sha1": "12998c24702f3e537946235db3a372e1bce3809c" }, "path_in_vcs": "" }cursive-tabs-0.8.0/.editorconfig000064400000000000000000000003511046102023000147140ustar 00000000000000# EditorConfig is awesome: https://EditorConfig.org root = true [*] end_of_line = lf insert_final_newline = true charset = utf-8 indent_style = space indent_size = 4 [Makefile] indent_style = tab [{*.json,*.yml}] indent_size = 2 cursive-tabs-0.8.0/.github/workflows/test_and_push.yml000064400000000000000000000015141046102023000212210ustar 00000000000000name: "test_and_badge" on: push: branches: - master - main jobs: test_and_badge: name: "Test and Badge" runs-on: "ubuntu-latest" steps: - name: "Prepare" run: | sudo apt-get update sudo apt-get install -y curl git curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y source $HOME/.cargo/env rustup toolchain install nightly git clone https://github.com/deinstapel/cursive-tabs.git repo - name: "Build and Test" run: | cd repo RUST_CHAIN=stable ./scripts/ci-build.sh RUST_CHAIN=nightly ./scripts/ci-build.sh - name: "Deploy Badge" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd repo ./scripts/deploy-badges.sh cursive-tabs-0.8.0/.gitignore000064400000000000000000000000671046102023000142330ustar 00000000000000/target **/*.rs.bk Cargo.lock node_modules *.temp .org cursive-tabs-0.8.0/CHANGELOG.md000064400000000000000000000042371046102023000140570ustar 00000000000000## version 0.6.0 - Removal of tab ids, this release replace the usage of the internal generic key used in `cursive-tabs` with the `cursive` native `NamedView`, this implies that only `NamedView`s can be added now to tabs. Any code adding views will need to be migrated. This migration can easily be done by using the `cursive::view::Nameable` trait, a small example on how to do this is shown below. ```rust // old style add tabs.add_tab(42, TextView::new("Crabs! 🦀")); // new add tabs.add(TextView::new("Crabs! 🦀").with_name("42")); ``` Notable differences between both styles is that only strings are taken as identifier, for most use cases this will be perfectly fine. Other use cases requiring the usage of more complex structures can use the `format!` macro to attain a `String` value. ```rust tabs.add(TextView::new("Crabs! 🦀").with_name(format!("{}", my_super_key)); ``` - Many small changes were required to allow for a more `cursive` like interface, mainly to do with other methods receiving identifiers to remove, or switch views, all methods operating on keys now receive `&str` Affected Methods: ```rust pub fn remove_tab(&mut self, id: &str) -> Result<(),()> pub fn swap_tabs(&mut self, fst: &str, snd: &str) pub fn add_tab_at(&mut self, view: NamedView, pos: usize) pub fn with_tab_at(mut self, view: NamedView, pos: usize) -> Self pub fn add_tab(&mut self, view: NamedView) pub fn with_tab(mut self, view: NamedView) -> Self pub fn with_active_tab(mut self, id: &str) -> Result pub fn set_active_tab(&mut self, id: &str) -> Result<(), ()> ``` ## version 0.5.0 - Change `remove_tab` and `swap_tab` to take references to values instead of values itself - Change trait requirements to less specific `Clone` instead of `Copy` - `with_active_tab` will now return also in error cases `self` encapsulated in the `Result` ## version 0.3.0 - Addition of `set_bar_placement` and `with_bar_placement` to control the position of the bar inside of the `TabPanel` - Change `HAlign` to `Align` to represent all directions. Possible values are `Start`, `Center` and `End` - New examples can be found that show the updated usage cursive-tabs-0.8.0/Cargo.lock0000644000000542440000000000100114340ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ahash" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", "zerocopy", ] [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "castaway" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "compact_str" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", "rustversion", "ryu", "static_assertions", ] [[package]] name = "console" version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "windows-sys", ] [[package]] name = "crossbeam" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", "crossbeam-queue", "crossbeam-utils", ] [[package]] name = "crossbeam-channel" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags", "crossterm_winapi", "mio", "parking_lot", "rustix", "signal-hook", "signal-hook-mio", "winapi", ] [[package]] name = "crossterm_winapi" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" dependencies = [ "winapi", ] [[package]] name = "cursive" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "386d5a36020bb856e9a34ecb8a4e6c9bd6b0262d1857bae4db7bc7e2fdaa532e" dependencies = [ "ahash", "cfg-if", "crossbeam-channel", "crossterm", "cursive_core", "lazy_static", "libc", "log", "signal-hook", "unicode-segmentation", "unicode-width", ] [[package]] name = "cursive-macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac7ac0eb0cede3dfdfebf4d5f22354e05a730b79c25fd03481fc69fcfba0a73e" dependencies = [ "proc-macro2", ] [[package]] name = "cursive-tabs" version = "0.8.0" dependencies = [ "crossbeam", "cursive", "cursive_core", "insta", "log", "num", "serde_json", ] [[package]] name = "cursive_core" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321ec774d27fafc66e812034d0025f8858bd7d9095304ff8fc200e0b9f9cc257" dependencies = [ "ahash", "compact_str", "crossbeam-channel", "cursive-macros", "enum-map", "enumset", "lazy_static", "log", "num", "parking_lot", "serde_json", "time", "unicode-segmentation", "unicode-width", "xi-unicode", ] [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "syn", ] [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "enum-map" version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" dependencies = [ "enum-map-derive", ] [[package]] name = "enum-map-derive" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "enumset" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ "darling", "proc-macro2", "quote", "syn", ] [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "insta" version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mio" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", "log", "wasi", "windows-sys", ] [[package]] name = "num" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", "num-integer", "num-iter", "num-rational", "num-traits", ] [[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-complex" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-iter" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ "num-bigint", "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_threads" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.206" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.124" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-mio" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio", "signal-hook", ] [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "similar" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", "num-conv", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xi-unicode" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] cursive-tabs-0.8.0/Cargo.toml0000644000000023230000000000100114460ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "cursive-tabs" version = "0.8.0" authors = [ "Fin Christensen ", "Johannes Wünsche ", ] description = "Tabs for gyscos/cursive views" documentation = "https://docs.rs/cursive-tabs" readme = "README.md" keywords = [ "cursive", "tui", "terminal", "tabs", "view", ] license = "BSD-3-Clause" repository = "https://github.com/deinstapel/cursive-tabs" [dependencies.crossbeam] version = "0.8" [dependencies.cursive_core] version = "0.4" [dependencies.log] version = "0.4" [dependencies.num] version = "0.4" [dev-dependencies.cursive] version = "0.21.0" [dev-dependencies.insta] version = "1.10.0" [dev-dependencies.serde_json] version = "1.0.74" cursive-tabs-0.8.0/Cargo.toml.orig000064400000000000000000000012601046102023000151260ustar 00000000000000[package] name = "cursive-tabs" version = "0.8.0" authors = ["Fin Christensen ", "Johannes Wünsche "] edition = "2018" description = "Tabs for gyscos/cursive views" license = "BSD-3-Clause" readme = "README.md" keywords = ["cursive", "tui", "terminal", "tabs", "view"] repository = "https://github.com/deinstapel/cursive-tabs" documentation = "https://docs.rs/cursive-tabs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] cursive_core = "0.4" crossbeam = "0.8" log = "0.4" num = "0.4" [dev-dependencies] serde_json = "1.0.74" cursive = "0.21.0" insta = "1.10.0" cursive-tabs-0.8.0/LICENSE000064400000000000000000000027561046102023000132570ustar 00000000000000BSD 3-Clause License Copyright (c) 2019, deinstapel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cursive-tabs-0.8.0/README.md000064400000000000000000000160631046102023000135250ustar 00000000000000

Welcome to cursive-tabs 👋

stable build nightly build crates.io Docs.rs GitHub PRs Welcome
A tab view wrapper for gyscos/cursive views

--- > This project is work-in-progress This project provides a wrapper view to be able to easily handle multiple tabs that can be switched to at any time without having to change the order of the views for [gyscos/cursive](https://github.com/gyscos/cursive) views. ## How does it look like? `demo` [![terminalizer](https://img.shields.io/badge/GIF-terminalizer-blueviolet.svg)](https://github.com/faressoft/terminalizer)
Expand to view tabs demo
## Usage Simply add to your `Cargo.toml` ```toml [dependencies] cursive-tabs = "^0" ``` ### Creating a `TabPanel` The easiest way to use this crate is by creating a `TabPanel` and add your views to it. In the `TabPanel` included is a bar that shows all tabs and allows to switch between them by clicking the desired tab. Below it is the `TabView` showing the current tab. It can be created by simply calling new on `TabPanel` and views and customize it as you want, have a look at the [documentation](https://docs.rs/cursive-tabs) to see all options. ```rust use cursive::views::TextView; use cursive_tabs::TabPanel; use cursive::view::Nameable; let mut siv = cursive::default(); //Create your panel and add tabs let mut panel = TabPanel::new() .with_tab(TextView::new("This is the first view!").with_name("First")) .with_tab(TextView::new("This is the second view!").with_name("Second")); siv.add_layer(panel); siv.run(); ``` ### Creating a `TabView` This crate also provides a struct `TabView` you can use to add tabs and switch between them, if you do not need a separate bar to switch and e.g. want to switch programmatically. The `TabView` can also be used to create your own Panel/Bar if you want to design your cursive environment a different way. ```rust use cursive::{views::TextView}; use cursive_tabs::TabView; use cursive::view::Nameable; let mut siv = cursive::default(); let tabs = TabView::new().with_tab(TextView::new("Our first tab!").with_name("0")); // We can continue to add as many tabs as we want! siv.add_layer(tabs); siv.run(); ``` Look into the [documentation](https://docs.rs/cursive-tabs) for more examples and a detailed explanation. ### Creating your own Panel :hammer::construction: When you create a `TabBar` it will more or less look similar to the view e.g. also used in the example. To customize it you then need to create a view, creating a `TabBar` and a `TabView` events between them can be exchanged e.g. with channels. Channels have been chosen in this case by us, because they provide the easiest way to communicate between to instances of views in cursive. To make these channels work you have to create two separate channels transmitting both keys, once for the direction from the bar to the tab view, transmitting keys that have been selected by e.g. buttons, and the other from the tab view to the bar. An example for such a button would look like this. ```rust let button_tx_clone = button_tx.clone(); let button = Button::new_raw(format!(" {} ", key), move |_| { match button_tx_clone.send(key) { Ok(_) => {} Err(err) => { debug!("button could not send key: {:?}", err); } } }); ``` To make the `TabView` respond to messages over this channel pass the receiving end to the tab view via the method `set_bar_rx`. The other direction can be set by passing the Sender to `TabView` via the method `set_active_key_tx`. In this channel the currently active is send everytime a switch between tabs occurs. You can use this to register switches in your tab bar. The rest is depending on how you want to style your panel, but if you have anymore questions or problems have a look at the source of the provided `TabPanel`. ## Troubleshooting If you find any bugs/unexpected behaviour or you have a proposition for future changes open an issue describing the current behaviour and what you expected. ## Development [![cargo test](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fdeinstapel.github.io%2Fcursive-tabs%2Fcargo-test.json)](https://github.com/deinstapel/cursive-tabs/actions) ### Running the tests #### Running all test suites Just run ``` $ cargo test ``` to execute all available tests. #### Investigating failed tests In case some test fails with your changes, you can use the `cargo-insta` tool to investigate the test case. To install ``` $ cargo install cargo-insta ``` and to run the tests and investigate all failing tests interactively. ``` $ cargo insta review ``` Any changes between the expected and received screen will be then displayed. #### shields.io endpoints [shields.io](https://shields.io) endpoints are generated inside the `./target/shields` folder. They are used in this README. ### Public API naming The current public API of this crate is not consistent with [RFC 344](https://github.com/rust-lang/rfcs/pull/344). This is due to `cursive` itself not being consistent with `RFC 344`. This crate tries to implement a smooth user experience for cursive users. Therefore, the `cursive` naming convention was adapted. When `cursive` upstream converts their API to a `RFC 344` consistent naming scheme, this crate will adapt to the changes. ## Authors **Fin Christensen** > [:octocat: `@fin-ger`](https://github.com/fin-ger) > [:elephant: `@fin_ger@weirder.earth`](https://weirder.earth/@fin_ger) > [:bird: `@fin_ger_github`](https://twitter.com/fin_ger_github)
**Johannes Wünsche** > [:octocat: `@jwuensche`](https://github.com/jwuensche) > [:elephant: `@fredowald@mastodon.social`](https://mastodon.social/web/accounts/843376) > [:bird: `@Fredowald`](https://twitter.com/fredowald) ## Show your support Give a :star: if this project helped you! cursive-tabs-0.8.0/assets/README.md000064400000000000000000000004751046102023000150270ustar 00000000000000# Creating a new GIF ## Recording Inside a `80x24` terminal record it using ``` $ cargo build --example simple $ cd assets $ terminalizer record --config ./config.yml demo ``` ## Rendering ``` $ terminalizer render demo.yml -o demo.gif ``` ## Optimizing ``` $ gifsicle --colors 24 -O3 demo.gif -o demo.gif ``` cursive-tabs-0.8.0/assets/config.yml000064400000000000000000000050661046102023000155410ustar 00000000000000 # Specify a command to be executed # like `/bin/bash -l`, `ls`, or any other commands # the default is bash for Linux # or powershell.exe for Windows command: ../target/debug/examples/simple # Specify the current working directory path # the default is the current working directory path cwd: null # Export additional ENV variables env: recording: true # Explicitly set the number of columns # or use `auto` to take the current # number of columns of your shell cols: 80 # Explicitly set the number of rows # or use `auto` to take the current # number of rows of your shell rows: 24 # Amount of times to repeat GIF # If value is -1, play once # If value is 0, loop indefinitely # If value is a positive number, loop n times repeat: 0 # Quality # 1 - 100 quality: 100 # Delay between frames in ms # If the value is `auto` use the actual recording delays frameDelay: auto # Maximum delay between frames in ms # Ignored if the `frameDelay` isn't set to `auto` # Set to `auto` to prevent limiting the max idle time maxIdleTime: 1000 # The surrounding frame box # The `type` can be null, window, floating, or solid` # To hide the title use the value null # Don't forget to add a backgroundColor style with a null as type frameBox: type: solid title: null style: border: 0px black solid # boxShadow: none # margin: 0px # Add a watermark image to the rendered gif # You need to specify an absolute path for # the image on your machine or a URL, and you can also # add your own CSS styles watermark: imagePath: null style: position: absolute right: 15px bottom: 15px width: 100px opacity: 0.9 # Cursor style can be one of # `block`, `underline`, or `bar` cursorStyle: block # Font family # You can use any font that is installed on your machine # in CSS-like syntax fontFamily: "Roboto Mono" # The size of the font fontSize: 12 # The height of lines lineHeight: 1 # The spacing between letters letterSpacing: 0 # Theme theme: background: "transparent" foreground: "#afafaf" cursor: "#c7c7c7" black: "#232628" red: "#fc4384" green: "#b3e33b" yellow: "#ffa727" blue: "#003366" magenta: "#ae89fe" cyan: "#708387" white: "#d5d5d0" brightBlack: "#626566" brightRed: "#ff7fac" brightGreen: "#c8ed71" brightYellow: "#ebdf86" brightBlue: "#75dff2" brightMagenta: "#ae89fe" brightCyan: "#b1c6ca" brightWhite: "#f9f9f4" cursive-tabs-0.8.0/assets/demo.gif000064400000000000000000007432531046102023000151730ustar 00000000000000GIF89a|KKZfVܧj}||zzzxvwvtutqrqmnnjlkghhcedfba_a`\^]Z\\YZZWXXUVVSSTQRROPQMOPLNOKNNIKKFHHBEFJCD?BC>@A=?@<=>;=>;<=:<=8;<7:;589478357246134024023.12-01,/1+.0+-/*-.*,.*,.*+-)+-)+-(*,(*,')+')+')+')+#######""#""#""#""*##I!!pa$32X4_ 5c 5c 5c 6d 6d 6dar½ÿǶ! NETSCAPE2.0!d,| H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'>GӨS^ͺװc˞M۸sͻ ~;0W[ǼУKNسkνOӫ_߭#͜}lϿ(h& 6F(Vh sFZE}L3 4(6,0(4h8<@)DiH&#И}tV8"~'1\`)dihlp)tix|&$bOFyΔ\d9M3[K/b-f馜v駠*ꨤjꩨꪬ*무j뭥Z.h3g(Y 7%Bj BK,ʴVkfv+k覫+B-4lx(39>.J *b )G,Wlgw ,$l(,;| *˽l&*K.)'tPG-TWmXg\w`-dmhl6M{I(R3-sf5_4c ±M'N[R4NI$G.Wngw砇.褗n騧ꬷ;NXI'ԽJ,3| -bJG/Wogw/o觯>7t)r<Ѕ DGL:D_%#X78Z( GH(L gwȟcMp)@ HD!_2!-FWAX̢./X o>F.`qOpH::P8,r1rÌ ` RtŽL"EvIE3H`(0Nz 1 1b8X,gIZڒ{*b; x OTL2JxB #60h[):qLfz + SQg4d'EzQr(M)(=J$&"ҚqC["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*yxͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x` ؀8Xx  ؁ x!@%x(&028 ؂xwyl)Wa+@B8D"Gf`@MȄ0R8 7j9x;xbE`bIp"p}eh`~@(HuyS@ WoZj<}68XVVpl0q{8nXh(` ` ޣyHk~f8(mȀ`#`g`Xh (p药S8@ȍ ËyׅV88Ǹp`px8ssY`( ~`RX Xd[yha qn0hP%y),&0.)5ْ+i91ɓ43=>)?97IJL MNYj@؀k`W`x (@8gIQ(`4x)u~6=bU0@B0pY٘) 9I)9ɚ 1p!WɀY8c(280@8(YPÎ\^xYx xZH ؛ x( H~ٖ28ПrɀVhۃVp!) ڠaȞ ؕax Xi `~l)ٜx 븗: z8h`>Y!ʊȖhEꂁ-Jb_\ڥ  `j gn[Jc݇4*6z Xfjh*nzVZ.z0ڋwUʧʧJBZzЉW:bY_5jI𪰚Vzi ȝb`{ kD`BE(Һp(X: Zʢ Pj7+ȫ*Ǒ2:B+ @?k ϪP hA?8 < ʱ `m⩱( +$ &{1! F qd   "@ Ht |a  S[ ȶ hˣ 0dP Z˵`[sc0xsi? pK۸ t in @f w cʷ[ kKB> okKcjp[+Bګ 'vJ5:'0@)0е `:Pl * + H@< H˿.0! h%# @ (,,$l (8Pp+0&  HX>\Š-|^?2a Wlpl@l܀cܵ< @A$zɈY݉x+P>ň-{R ,@:o0:܀]lz. DP?`˿{7˥ &E:  \ ʬ| lŠ <̊P-L0ι0aeK`ŁCEk/٩3jx*7Bβߺf,i"*-t X̀ s@$7o'`I@!00 S].=KMMcp+3 AfNTMƀ-Wy@oBhuP  }݀M[@;Dۯͨ} Ғ*-`9`+F0]q|k@5 +h?`B Eݵ/muss h0pD`z$ @{< ѭdP07mn0Ѝݵ݊pڝp-B--uL.vPܠ=HȾܑqG7bpqI {1)l$;.P<֊Ӷ=s/n1*F@Rkغ݀(]nU~ A@w*.лZ~=}t@Zڨ<"a hA:y.Rnd`+.Pg)0.j0-@~빾܊)3m (:9݊Nמ޶>?+0CЮخܞӎz~̚N{#p; uڌ\"}`n ;_{pzA A{@b>QMdp 8E xI SdjDnez%ꊂ3$~ϥu/sJm]۩Ib(`$]0o2O46HJڧ?="t$qH/?ѷu$쪭ڀWN-ЬN݀ (oG _l +J!;%ˀD " DPB 15-2f!D=~LH$XRʕfLLSf/5męSN=}PE[ÚawU^9pؚ ԦHG͚ VZmstyi9DG:Cv <YcōapbdӀ0,أ(P +$HMh!A0)'`LfEdLԊBV;8B7i nIArW]Q dJ7hL,Jjj*G+K,@ /&^2h,(:  P q:QDÈ>耮8fI n$H(EXa4P >X=& &hl ^x ` 4AؠF*:-1(2N!uW_5(bʰV[}Pr *b**[ev Bt<#b4 Ć6! i($ A#s%qACnSC;:"*$G@2]s`M8-H^UC5UsX!& s2`@T 2(eKf8?pTXYE Vuf5Yv=WjXizg;D N0!~W_c6b Ll6VT~a:x8 CCj҉{_` Bc;f`$W\f.uYbEpAo'')f?RWꭈj{.C`Cų>|1@GIAa# , !m-h">8"n/ۃ>%d A?Ћ 5BN20T:(P;o#!"ʛ(zQKgbIYM\b>(FxYQ^/nA꠆]khz08c 8E qQXAV9\~{c.>䏇eA|0:L"`FDyIhr! AA`JTAtR,"L ?ZM8*fi>!tA>DI@/ 4$n$1KP * `"⠂̠gkhACHG*pt(D@jA H02`p& BP"!AEgJ4s~@Gt-IS<.xO'NghA ` U~0?fv5!Τ8jNojSf7M&\L MǸ:Z`@Ox=B{Bs1-xU`IB DYEalAY8 jjJ.O&Ie '\SKl!xۭC4U!0 ׹m fZvYF[]q{nuC@w^eo|׺,Ԥݴֻϳ/4 rFpDiu_l6&(wj` /MP-ZV~! pU G2 8v6^ܬgw.7ٺ/#HPX$J\0d̜IA xd6xTfVdEy3׾F (eMh5( j`@8~:ׁA8(8OA < j Awz ͻFs{ml ^3c# t 0Ll @"uec"R2 _ٗै m\|AhyՋ!)w š63 xAvm"fYu¥nąS@O1#)[Vpup@C55?$c284xS6}x]З: kV f>u^w~a; ۹ƼVnon_C~/  !W;^߭"x+C :@vd|/b+9ob~bq׷.AwWѮQxd, k'$$$ 6*B%>#R @}%5KõkEeGFodv@p t&btYܺ2tDyut~GFa$E4dL} ~DždH[HkDG 'Eȍx.cshC߾j,]:m9<:H׾DSV3CtVe3؀GlJkkL<Ȓͽ F}d݌Q\aSaa#5Ѿ^˅Xa$ba bB .F+,_4b/6ec(&)>7>`%~&6ƘFO>G:;ĞL=nV>EFc%aublZ-b.fK.465F-VdLeWd_|OT.@GDd<1F2V[FQγRS\ftdy `Vf9ve&ܥdeZvefǬab;6<6=l&t,f>fK.ve8Kg|kng`~g*gvl%gT` !a~.he>K b[n>~h heqhlh裘hzhL Yh.P>iJiYini{>gqiiil_`nfd!d{jG.j*~i jo6蟞n.^giVvɪik82NkU^kj~kjjNdk/֚u뿮veen+kƾd+Nd7lplO6.h"6V>7>j}:Xk.F(mkia֢&6knp>c"~jncAmojܦnON8p1@8pof~oooopGpOWppp  _܇qqq!qr#/rO Gr ?r%w%r&r"+,or--.s*)'s)7s,1?s.Ws42QV4on9W.;<7f >stf'vCDEfvtֆ7ttYK'L?Mj@nt=bQ7cRkSonUtVbWnXJOBGO[mVt]fOtZt4͕=<`@'v.OGP7"$HP܋0i?j?Nmm=UKLܖR: 0wP; *3xS4Wctl?x\BHpzSp5U!-R`0xSy2UmWeTImP p H W(< U:ڃP(W_˞n8Wyۜkmmm5{͕ Wh#P7+ |DQ{c{t$tCp$[֝VEYXrHM}ɃO(oڬ||tjy46ѿ[OR- pۃP' H)\48=O^E_'?dP`#*/-X]_tكmE,h „ 2l!Ĉ'Rhqbx#Ȑ"G,i$JOVleM\ sgO}΁l-W6EziʨRR=|&@\ǒ-k,ڴjnh-ܸ'esf͛9wO(QH2[W.Ċ=m1Ȓ'SF6̚M{M:y ѤKv5쑕gӮm69yE-`ՅҧSn:ڷs;Ǔ|U0e;V=? 8ș}+ ^\jE j!zH!z |Aɗ$>!"(}8#5x#h W܃+FX;)RI*$Mw={)BHrB]z%7nxQ qW*7}cB&q9xnWf{9XF9(sxXe eh(Zʤ)z枥bx)mc~}Jx**gz+튢k;-F>ڬ~{Պ;.&,|v l.;NjB (/.: k+0 ;=|1kp<2%lr;К21ˬ)y2(&<3=\5/ZW I+"2=5U/4P]{Xs!}6io),q=7tlv\ӽ7}eF7>@Q<;w [~vuk{yՙѝ~:=+錧:8~Bsq;>|^<;ods[*gh_=Fu>f>5>?Q>Gײfy3#(?N|`(X`Ӡ<(}#Ef]s5,/a#UtMzb376G~$Aj,,!$"/fȕ$ [)IArW%/IxerV'?)JYrDT)OJ%H[]MY(%H-e~l ab'Kb2SWfRSYXm"F[ qJE<'2dR+D'<wͧs'H̉{s? ЀsÄ́* m(3 aJt2эr(HC*ґ&=)JSҐwJc*әҴ6)Nsӝ8-e4{ӡF=*RԥДP*թRV*Vb3Z*X*ֱ%Z+_5Jn}+\*WRi:׽~UVu=,bŖT6#lPsfNT,f3{Jti(cfSղ65,ra mQAZXm-p+8El ԶWʭ1v T|Ľ.vݎZ.HF/hP`b}/|㻉PH4Y^rcj:^zьi藼P`J{O0O 7s.rt ">1S]]]%pI9Tبg8M`pĶ5Jtk N~rJ=`  6rdq^bzP,1XE,rq_WCp3`Xa Ȭ=Ôaq^TXβ{.&,QISҔ3MsӞ4C-QԦ>5SUծ~5c-YӺֶu-MIW'Hq 4" f'C^XEC Otқ6ms6-q>7ӭu~7-yӻw=P{g";04[F)La RP83s8C.򑓼&?9S򕳼.9c.\@aAbot7rE. X8N:ԣ.SV:ֳs^:.f?;Ӯ\gz,v]:F)F00z "8<3<#/S<3s3>/Sֿ>so}3|> /2p·}/ӿ??  &. 6>  4L4N8Xٞ Ơ ֠   `]Nd`m!<,y>!FN!V^!fn!v~!!!!6an`P(2!!!  "!!""&"."#6#>"$F$N"%V#ҡh'b"(("))"**"++",Ƣ,"-֢-".."/'z"'c"11#2&2.#363>#4F4N#5V5^#6f6n#7v7n08#99#::#;;#<ƣ<#=֣=#>>#??#@@$AA$B&B.$C6C>$DFDN$EVE^$FfFn$GvG~$HH$I:!,|###MOPHJJ?BC;=>9;<468024+./(*,(*,(*,###############################""5 CP`piZL 5c 5c 5c 5c 5c 5c 5c 5c 5c 6dDtJJKKKO[ss`SUxZa][WYY[]]_a`bccgihlnmrtsvxwz{z~~½ÿĿĿųūŶ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)֝߿ LÈ+^̸ǐ#KL˘3k̹sb'&GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#ܚhϿ(h& 6F(Vh g sFZE}0 2(,0(4h8<@)DiH&#Ș}tV8"~'L/\`)dihlp)tix|&bOFIΔ\d%9L1[b-b+f馜v駠*ꨤjꩨꪬ*무j뭥Z,h1g(Yɍ5%B *BʴVkfv+k覫+Ѣ++3lx(39>,J 't&G,Wlgw ,$l(,;0(r/oJdl4*,%P2$4PG-TWmXg\w`-dmhlk4%\'4:GIlV5L1 ѕ0#/"4G.Wngw砇.褗n騧K"3H$T27(2KCV" -J'L`@7G/Wogw/o觯r+xWC3 40B HLx݀,A$ *z GH @Vbw ѱ?WL@w@ b RF!CH*ZX̢LJM7"D%6pxJxЅ 1Ɔ`o#&&1BL$"' #ڢIc HC[lP&7NzmD>NN *؈O򕰌,;bwH$0Lݘ40D:!ϗ@ kєrTKԦ:DJ;RXͪVՅUTmO%zխh^W7 VVC\J׺xͫ^׾ `KMb:՜d--"z hGKҚdmUNdkjgKͭnwkյRMrܽVGlRJZͮvݹ>SuX3+Mz;ղUn[׎wկLJK]JWބ7{n|(N%=*8WLy q[/@9cD ސ&q7*[؅rp{<c`m`)XbNY2x]F3L:5sl>t3aЈGa&ѐvL=k8Ҙt'M$ϚOx҆W-R ԅgMvtwk:&m_W:#\c7:̎gl.#zζ{b־7p6Mrwڄֵ;wfz[7~盼oyw|; tNmGղƳ}qqOo vz7Ur/naNL5o>qX3u>^ ؁:(:8FH+LXIXG3~x6xa@\؅^ȅD| 6`say 3yЀOȄ0 y 0Є qHQ(FVhj7}؈ hh( 3@4Іy8+  K脡h 8Syjh~8؃ixy60XA Hm@l؉su012 Ȃ}8xXa_8xXo8j؉pQȄ'yP {hȈ눐 ËwyVv bO`")$i''9%ْ,/)*3i598 7>?ɓ@IAyF=E"9h0ɎXܘL 2`S Ei٣Fv@@闀9)i yi)9y9)i8TyZpBY ؕɀ8 1P*(_nٗYQHcpzHؙh8GJ` aҨi{IXi#G:[ȝxM`o@ hnm@비1@) nyß8a J0hhFؠhȆ2 Ѱ g)i連h뉟(V7~~-*ZHٛ iݸ../*QJ=*f,s.\zxZ^{cZejjnZTwwXxIy2ڧ] mSZ肒ڪ *jT0Cr hY=(@X?Эʏ (Ji0pj?(HIP /諿J|7Zt*>Z xZ`CH:H:xkF`F @A@`\:#($k( [P8@[#{+jpƊȺ: MJc@s:[KPHprLPi;\PKp\[Jpg{xF@Qa;Pg@F`@xEdH{@@@}[{[k8 WP^Fm跀˷{+㹀 +``v۵۸yj0T‹@G ~;Qa_9EAp @d> x;9Ap[@@|0B@?`<<K0@9ЀK>BX{PK;3\7|<-©K`8,<@L !,ƒ@ 6 48%;KH,>``[Puh<L3"|J+Uhب H0? [L(4_@ˋ|lk0 k|p`w+;C`Xl|l_˃ 9O;z@ H˶ h̶[|<Mp<,K\@Clʜ_ _|ڋj:] :΃`χ @[|BpKl4}epXl2-5MNmG1<`N+8p= \WANPj+>:]; 88EfQb=LX={qP`ph}fD9в HdMpd@x=鴕|n8;pt IMSbZp Ԝ}ki Xۜ=fd܂SML@==lm݆= j`[Qm X=|+ ݇ҽםp|=9p~ݝG ґlOkn 0]1p (޻ԭ;H,=FRwe p0]pkDKMQ.޺GNI^ XP 25>00 P܂ NKVNHhc[|RLuҒ<Ҕ,t=M c<J U0@=p_>Y@p M0gN\A9pӊ;0X~NپA Ҿt hLYY{: XǞ`N 8֎~(حN?-. oM=ڃ Gh>mp@8 tэ0/ 8BvXGDOQq4߀:>_OoAπMO_oJ_[/a;σ'. <D`@`xy{k)ߟuzxL OP#X?/(oξ VZڢ'tb??iK$=⧏z*HPꅢֿ.?  a{_;%{)۳d;FԿW@KOۅ۟/1  DP: ,QD @(<~RH%MDrd-]|(&Rtƍ||3;rܢԦH`>}RTU,x2 .H*%g%DTTHҵKy"#m *ȜAvD (x5B& e&5wnݻt^ZƬ ƎUn}˕^,n\4A (сCsN=K%w V<͟uǍWݿ2iĩgP=*D0 !!a~€2p CrС+F,Yb N$ >x+cwAa! p8T,` MDó*#D6́I'bHb 3!, RH"ْ.#N 8T(A=b t@mđSOcO#G%糯rک(bʩS*5W]Ibp+9I;x/.A`+#88X8-c,0Ap"bt aA,o%H\r#;VH߂܅WyEx <)8GU&~7F $>B 5WS<9g ]V֧Rŏ^)i%P藀:^ w$Ec-8jPkH;638 ځ".} ꗠ2TKU m!ڛNқoGV7! ]s"=pIDZ9w5ԨwfR5VN(YUx> z ؐk=`~CƳm['8n2'c8: v w +m A,jS怆rgA\Q($HQ*BNqv ` -iHv+gw7|=y,!ڪ*)mV{ĩLρ*>YJDpoN> Ap'U X0NŠo R 5i#ܶqC 85%dzݸ"`q!D򆑜&GDŽ@GDa%uɩޱ H yBQҐ(ә"z 98@c2j^3dD@0$ x;b'^p#-g8@@XQ0H̳;B@ &23$D%ƒa/I`B? XԞKy&0D"BdAu O 6A؅pfڜְL3A̬\&N,\gNF," Xw׼w|NmTιm/ ,NV3{z)9!wCgG7ۙ-kmk\!f s":[8z[{|yqG553l; |}{A.cB8==g_w'd98v]@>{]U~s+Lwzƹ B8;ppe8* n?"9\S~A/M5;^*mUeC*xK1{cw'=1($|}+¨'&l0A@BL9yAH}Ah!CuAݩ?r0㯿[*3ૉ@+",;*,bBB$, 27r3[Rؒ1;|2tC@{ۿ|k= 83;e@$E OtW$0=$ES,BTDY&@X^/YDZD[dC\EhE_Te,`Da?;BL<ƓHFfl$CG̭j h?,[FFmDt4*nrDpTCq:w&+u|\2zǠXyGE}4ȃLgy sDȉFCLiLEcȈ,HI|H7Hd[$ǎH{ ɖǑ4ÒSQɕ&Il$ɀ|țȝ$J* 9CћV=խ]< P` +U(Mp?f}A;cx"P;NLOu+ȔL8|b!0/ Ԉ!cS.Xu}!8{`+K-G 4.0&qb5cXscc(=c6;&<6a>&}~+&,3B(`DcEDUf-Iecd.8eP +ea4Ne5^ke\dXexeTeB9OIf*9ANfUcledfa\ffhB^fzf\gmGLd12~dwtq.rft9gsP <ʁ.JvxT ^pV4.h:'⌮am^E`~&(iBVFNVi giO霾vdl6YcMhN6cz`VhTJ阖_"j6ꮞFNChNih~FN]rg>ЯfH$fhvdw^dxnjyjf`^"ƻfl*녔St.꟞lvʺlePξʾllӮCn~wfjk1'1n&FnVnn.n^vfnn~n&o6>oVfovF^n>&Ind&m @(7p&gp?wp _p p p pppG?7q/qqwqVvN\kN؎"ocpOv$?ær&+G,-mĮs sn&s%345?67rsPM;7<=G>??G]pBGsC?aDkEOmGs$L%801t˦tFF/b=D\]qQROS72UorMS?Bل&1@dŚ/N(vAv%q\ %ж0:t_[Lсċ}6:vsro с(P @TB[3M  }GjWT$(+p=y[υɇɈwb|I+XcE liXY5-芠W 1$hVTl#[/GJʺ?ptu/7rXXY@U{w1* !{/0Jj8m7zp٬ZY⁺=- 'HC \A|?0K_r/EZy||{;AG0էL7~NB]j]70M'vTRvr~pq~w,X„ 2l!Ĉ'6`"ƌEDʕbѸsʔ*QsG[b\)Ɯ9)'Љ-j(ҤJ2m)ԨRRjUiЬZJ"G E4쿖/cάyWr҅x.޼zߺG5ǐ#KDR-L4m40<s3Тj.-rNcu2oQg4m6Ѻw7V11YƮ_ܖrqç n:ڧR^8EՊ6^[ཱིϺ=}|/gigbKM9睃kZkε祛ިn~:륧N<;.ڹG;}y\+Oc/ͯ=MKOz}}{3?>N>*?w틼~2;o0?7{< 7<2P^ L^#zE4ur r+ AamL]P:+|ZxҐU2H sH>(DIqFgNUK!Oۢ9{X"´,m͋`<#5Hl|#{9JLbq~L(HrJ!H$r=i%3yHv$(PVrt)OTҐl _ K?rz-C񲗾%0)a<&2e2ĔDgRּ&6mr&j)<':өuL8H.'>}3?'ρ=(BOƍi[B#*щR "Piv (HC*ґ3&9 Q.})LiRd$ӝ>)#& 1R/bq N}*TZРvErQ&H)XZ-JVUC7W|uz2w8؄f W%mci *4n:p60V>/|+_fj4dyz5E0B@XpvKj[^t`s6`S~&*1H46^>1,!F>2%3N~2,)SV2e x6fD#"1Jl vaaw¶*H _Č#=~3-AІ>4E3ю~4#-ISҖ43mh6e3~6-iS&vgS&.\?`AgW&-yӻ7}7.?83ݍnb&ACkѭ [BݱX+R򕳼.9c.Ӽ69s>9Ѓ.F?:k~\8\H-/Y] t:.f?;Ӯn;.ӽv;~w{ן^ p3#%QMnwXg3S<3s3>/Sֿ>s>/k?G<{ki/ӿ??  &. 6>zխKhn v~  Ơ ֠ Q`Z` !!&.!6>!FN!V^!fn!v~!a !!!ơ!֡!!! b!!!""&"."#6#>"$F$N"%V%^"&f&n"'v'~"(("))"**"++",Ƣ,"-֢-".."//"00#11#(!,|###*,.*,-(*,(*,')+')+####""#""#""#""4""I!!gH*B2X 5c 5c 5c 5c 5c 5c 6c 6c 6c 6c 6c3Q(07,/1-01/120231342463685898:;9<<;<=;=><=>=@A?ABHBCCFFHJJKMNLNOMOPPRSTUUWYXZ\\`_^baabccfhgjlknportsvwvz{z~~|~SKKK`xX_wÿ; H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'>GӨS^ͺװc˞M۸sͻ ~;0W[ǼУKNسkνOӫ_߭#|}kϿ(h& 6F(Vh ڷ sFZE}DM3(*,0(4h8<@)DiH&#͘}tV8"~'h0\`)dihlp)tix|&bOFyΔ\d9׌2[.b,f馜v駠*ꨤjꩨꪬ*무j뭥Zj .2g(Y 6%B: J+ʴVkfv+k覫+Ѷ,k4lx(39>,.JK('G,Wlgw ,$l(,;(ʽl&*-BJ(hr%X2PG-TWmXg\w`-dmhl6M/'-%Yh4 p+G{$0"/G.Wngw砇.褗n騧ꬷ:/8#Lb%t,>?d%2* $G/Wogw/o觯> 7, )̒7|;$0A34kHL:t7c x GH(Lad0WN CBthoT@ %"3F46Q )2dR'UhW9TZueJ׺xͫ^׾ `KMb:ldVVdd7z hGKҚxMӰp,jgKͭnw[ZmH_*[MrշS]kJ9ͮvjR1[]yMz^Â-l3kͯ~^>L~L^e_^'LaUupl\{ V ׼!NW͎XnQ8*Vu{9 Ǘ -o&;YFnD7L'[Xn</9`q wYrfϖy&F3L:9[0{΀WsF%>J[ʐ泤K{Ct} aP!uIaTV5eָ&Iw:zw-^{NKˎE\y}>n17 moܟm[vuMz6ެo;or{wm%wxa<||{?Nrnˋ.ʣ}r9z0Ov}m3oxn[<=q}Dї.褏PSyꔶՌo`zNvLK\f8m{_3r{߾m|wwcW:/񐟰#xӳumkx߼ϋy-}Pz}w_<>wwOO|juG_vΏl/ꓖ~;Oq^;b/;Obѯߛ|^ە7x ~ h`q8Xx؁ "xJ@=0X'5X284X,؂/bX`<8DXFxH*8K8O 0XR(DhN?XxgmWl|dXfxe؄xA@=}X8VP~PV0U]]]PwdgqTahxH&7-\`Z(V ~HuЈ\]pH؅&`HYa،F(@qJX(QHU` }Ї6H8}u[X[jh'8a<9E+qȋ8V~ Xuh)|HXdvH&k bL 2)4i7795ٓ:@y9hFڦڨHhxWX=8q9I鹨Zl*HYz:vvגʘqګ0Jڬ쉅JǺzhPBPihS0E(IX!>Юڐ*AЮD`9("kȮZ:PH㺉$<jBwJ0/ G8>0Z!;$kS`FA`Kp;*H%<ʞ+/3[ڲPXD"Z'ziJpb?P 0 <J?P6= B@700qxqC\Ē{):h8# 9.2(,;2|PL>O,xpI:K׈A XE$d`xRl(*>0(Xz?ꅟEg?p.O[Ak>j|A\"a"y0L\· n +z|#pkгK,K %0|0q0լ(Fa@k"0zhȬ x鬹ל K;MJ`C

p6P/] RP͂,Zk۵5A@hqP /q+ (.dM\=?M@`# HfS>nDpzMd@iqMUXN?Zp^t-gjP-n6;+.v-nW~!êdۥ\̵z;PK"gιZM., Q/; Nz,0.}Ǟuq /'~K"6 9#PK\M>܎qŎk<,)>S>,N~.hlI^QJݷ ӹX6<7n`^ypnjMIZ\j`!1PhM_S_k!h"(8E(x2>;v m:@3 ʂn@)P9OS,I㈏GϨwAςj˃Ϧ|fr: lb0W_Ӷ?Ӹ_㻎K7+]n`. {-^ozP?_5@@\@PB >QDq,>aq#!YbQHeF HkNSL5GJI/6}$F&N?9R"PTT-E]~jPXDC*_]·6ik}ZDcE.HpL RV7Ʊp^wjvFDp<`7ſW)~ͬl6kѻp5aƨXaKpdk;a?Et4Hf 뵚9r߫dg{N"s~;~`d)\Rbeh)\vRD 9CnL_&9fr;x`F;s8 HF\lczGb4ډlr?w`g>[?|47q~>#2 qUQD.RhcUbӔ ?{, ۨ2YS dz>6>ʣ> [['*1 =+1xr볗ª~':bǰ*)$A'99$̩Ĩ8p C&|h@^_/ Ĩ@19B2ʲ :*. bãpCH:'C,d)d8¯C-jޢ!R:DDd;n5<7C{F ; "xC_Q/EO0KdG<2m!EXE;8V=I<,E5 Ya0FD]<^@?EDbh\DfCSd3* +0fFxFiq"clDdD2ZFVFpBrxsCt|@e\eiGw Fy|Bjܞ}ty-TFUFv$HGȈ zC{LE>\H}lH|Gȏ BLH\ɕGB n#\JtJ:JJnjTTʳ<ɫWD˸ZōdKķH$&<lȻʼɷ q&ɜ;  L"1&NLXLJ|< ˟L̎T 2̩؈ܜ @%ڭ*҃3XA|Jּ:T|Klʽ< F Ak(R4LJ (4Oɨ˰𩙃O쾤Ϣ:PHI{Q+:x $LN Gl06Ǩ[{1 oC77s+qؑ1[Z‘8Ҹ7# # 571^)?8$4Y,57y#PXL|3} [9"Y8(< {sIuir?ԚC%DQbwQ!X!(AԨ -E:I͍QS%9{ ~}SJ>N󬜓P׻yi;#ٛ4V 9VQ mՌMf=(͚0W)@<@`mSĢƅ Ѡ:SaQ"&S$ pkg ̟kδ ҋ iWd їU7]Eo$pT?I!e2*=ʃgzX"x& 89#DjM'aF%~a(b*J4bn0 ^^^b+vb1vc-bE^]tQ&fc0~=-n.~9] >&>c34;bBdc"d_U=LΚC9.dM6M&nJK.SedceYXe8jdN^\F^cGn]Ff\E_>`&QWNj>YZ[dkfl0mN=fo>epV0qarfset/uX"ngwex/ybvf{Ff|.}}dc. .g.d-zgef-.gdn+fDhNV`O>EÔjBNjtZ >TbFviKa7jn.鰆j^efVFkVo,몞돮kkk~>nƾfl(m"lWNŎkNkc`jAnl>bF#lЦkFmQabG4m7i NiniQlR؞an-VEnm~EmVmem.b"mnEnnn~1&1`oVvnoo~ooo7p?Gwppp  ?.j)P8oqqwqqqr q"!q!/$7r%?%q&(r$r(,-.w/o0r-r12s.?\]kf;.~c7kNh/?@AgBF0Vft9wt[HIOJ9:t(VTu,.uRCX$*A[Mb/3c?GdletֈֵLwL(vs0hOP[wl0mGn[ؼ%w+!1x(x1 , :k8v+ғww[m!`MOSC ԑ2xy!3$pUc$8໑?yK\y7\(ȍo qw(PV8z1(聤780q网b{zK7`,b5Oܓ=p+2y$ -g|{$!YDY8|,|xğS")ȧ|ͫ0(ϗ9u$)8_k|tmW G($4\4P3@|_A}~ ~6tT !5\7sP$x „ 2l!Ĉ'Ʃh"ƌ7r#Ȑ"G,i$ʔ*WdI%LgҬif:")լ_ʬ;/߿J&8k~JiZ+ؖbǒ-k,ڴjr ,ܸrn3ТGTV*02n1Ȓ'Sl2̚7s3NJGVXx]BEwߨS^z:gܺ7‡/N|7szk&`칍s;Ak/_:ls5o_}:DO>Wk5uFZx!Iй7lz!e"-_zsEG|A# #A Yhs)`%G`E9%UZeyG޵^ X݃aɣeycf5.P"uyws"f]Ƞ:xz(\x#9yݢ&z):Viihkp>#iz*:nz)'j_j欉MJ*k ;,~Bkr,J;>*qJ-z-Y j_ Ji骻.>4َ:{/k|n/ <jK/ ;0_p 1kV<<2v /W2-<{/|3Ε̗֜3A -L I+4#(oo黯]{[?83 :ZOL - feo`<(B}#MKF=*Rԥ~(XTfԩRV*V ѐjV*ֱ<*מZͨn}+\P(,~k_a P5GaUu},d#+٣ 0JQibWK8v=-jS RF\"F3X%E+DqF-p ڈK-Q +m4x)4Rֽ$4AW؆]=h(cuӫFwم2^ZtYhEbAk/;=N'j3E'{v5TNx[PD0YPQ8\Ppc,Cpf1_n)PF@Vآb_e ü (p^2 nbyckr(4E3ю~4#-ISҖ43MZϋ3#1 K\X,_Tޅ-^ RY3']׾5-a>6e3~6-iS־6mlK05)R&g)&afx*f-P7}7.?838#.S'xC!p>2b ^({)6%1 [w*b.Ӽ69s>9Ѓ.F?:ғ3N:Vx81V 95؝_b+gn;.ӽv;~;/?<ow[Fc˚tF}ne|C/ѓ?=Sճ=c/Ӿ=s=ye<qQ()|4ьֿ>s>/??ӯ?/͈7/S88` &. 6> FN V^ fn v~  nm]ߛlV9 ֠   !!&.!6>! V 6f N!v~!!!!!ơ!֡!!`ZM !!!""&"."#6#>"$F$N"%V%^"&f&n"'v'~"(F ""**"++",Ƣ,"-֢-".."//"00)1#2&2.#36\3>#4F4N#5V5^#6f6n#7v7~#88#99#::#;;#<ƣ<#=֣=#>>#??#@@$AA$Bc@!C,|~}}{zzyuwvrtrnonjlkfggacbda`]_^Z\[WXXSUUPRSMOPLNOKMNHIJCFFHBC?AB=@A<=>;=>;<=:<<8:;69:478357135034023.12-01,/0+.0+-.*,.*,.*,-)+-(*,(*,(*,')+')+')+####""#""#""#""*##I!!i".9-K2X 5c 5c 5c 6d 6d 6djxZKKKX}eTq̶þ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'6GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#\jϿ(h& 6F(Vh ڧ sFZE}DM3(&,0(4h8<@)DiH&#͘}tV8"~'X0\`)dihlp)tix|&bOFiΔ\d9L2[ .b,f馜v駠*ꨤjꩨꪬ*무j뭥ZJ-h2g(Y6%B* +ʴVkfv+k覫+Ѳ,k4lx(39>-JKJ(|I'G,Wlgw ,$l(,; J(ʽ l&* -B (dbI%PPG-TWmXg\w`-dmhl6MWbI&R+sf5_$# ¬݉%N7r!x!D.Wngw砇.褗n騧ꬷ[x!H$XR7)bCV"+8.B'L@0 G/Wogw/o觯,zܷC3t1C+$L:`$ DgbyGH(L W Kǐ+*@@ H"o @ o!kT3=t>Gud|Oo^k4WwM|#ooһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W ؀8Xx2؀{p&x(*R !؀- 24x~7jͦrV@B8DX8@%LȄ P(QS9x<ybFb8d(Ju`o J؄&hVOX]|0Wxy{(}x|h߳y\j=~V8X>>nK[͐t؀|ȊH节Ȋ]Xj:؈a8Cx(>0{p>PGM } ؄u]{Ȁ؆ 8ZHxLօFXH؀e 茗HMW脨Hv \ȍ(8Xb` f`cS#I&I )*(ɒ/y% 3)+i.Y-I78ɓ9ٓ1D9FG)HT@_>@(phx\p娐 a ȍg) i=Xx7|_aO,`{ٗ~ |Yy))ɘ9I0QSPW y+^(z"Pq=s>'bxy >{ oy؍c{țh(W؜fpfؚ؉Ul@Ȅ\i^0_ ڀɜ츈~F *./ 8،MؠdI٣ถAʣpYry;xآ0ZV :i]y Ȑ(8KHh+Tzr: (إ^fj{Z}ZxLNʋR:qJ vx湨hh=}7بZZ4(2a_y:WȪk (CGQn` ` .CFج P*ʀP2X%XY H֚ʀQ ɺ8cP*xxl n,{P P)`  (B@H'`٪  bPP׉2ȱ "ˮ$0 hZUKf<{Igp0Q.InpqL{ rP+ rеpShqU{Y[۵dhN8hT * HtP {p ز(Tc  `u{08u{KyKh۸u0+)k;+;zKws{@+گ7SJ``K x @p%p{ a0.[ 'pH˿K P P0p{j-|,$l H$P޹ `|#\'. <1<  Y, z0amd>\ @,/&ګQjJl<.z0,Sܾ X Zd@&pt@{a00Ȼ mqa*[0 L|#@\˻D {0G&s\ʮ ׬˼<̺ƼȼL \Kb,ͨ\<ϭ 8ܤ̫HGbI 0?̀ۚR\TP[j.0iP (܀-ͷ/h#dP2 `S]FSKMMG{;l<Ξ܀?c`LԚ,`Mե|P-Հۚ!@C\zH0bM=@[BM ȡڇ0s${}{Ppf0 `͜=l+Xihh0|kj"k XZ j "` <ZjR< w[ .   Hqj@{ژ:˯쎨-q#=b)lm2-x/xJ`Mt<|SpN)0 }ۗ0pN *Pbp (S-N,(! 8KoI6E~cndT.ֽ,~VZH O ۀ^Nz`瀻H "-pJ{/')Ppo2N0PR @>:{@h>ַdp `%0*@\>>n=پݾNh?`ݽ^ZԾoN?>)~{, 8I//C6<]{%z~t =opn, Cp`(ۑSON`@Fٛ ( Ufl/F/!Ȩ~j @k21-_UzDkaF_*uq[o ml6肧?_-zڦNMGҔjhTH/_5RWbՉֺx X PJހ/8 ί=w{2!; A qDPB >p: 1'VR$C FTGA-]R 5eSN=}TО7E4)HP7sÚU+|}̗,T"R6-QmݾaL#$"* YŚ̻%s `R$L@2v Є0 T"'I8{T/Ȑ$Ik'$KX2q{8{ޓ Gc&mp PA8>!$&F!=^"{T|_?D{l֌+@J-b)ʪ<@ 70dJ. .c4 Dh ".\  nqB"= p'3 ";@JQ0pǻHr=L:ZK0ŌJ+H,`*<΃d(@̃ !K4 9TŃ/VY?:5נ040 JB",x]KWgú \0HA=\! 8k jC(Nr.8L(u9=6BC10A <% r=aތsNy{M"nX9Q]*FGKQ1mL`>;f3H\#=6~uYޯV[eWfu`$6+c)L§jkg qQ2SA~V :റ>;eDzq {>(|$8A)2@ȁ.l v8K-n4 4&?GN{=0!rwx*66iOR?p륚Aaګc+Ty@pm-`[X=/?uǠ @ рD:jۀ 2 )M03\~! < زtݭ!r&ƒp$2=p.d-840 @kw=L>CFg5q(C֠aQjȲвD.{`p'M| "4p b$cJ5!.XN`9"$ @p027x sCqSH㤠#V9\nC(xp! N"9>>D!pbTF5ܤC@:D*3b-59=`&љY"8M@(Qa-ּ5|2 8`G!A˾Eta\g;w (O2\` h|̐ xg`@CǸ=$@ X@w"ddB zЄ~w]B *у!qHgꁉԤ(uH60t I$ ` J*=!{fV[sĦfnb1{jZq'%&lkD;pOR?pЃbj `@L2&u^F4i q6ә$/1z&Ϙ>2DAz C[P( qdhfZ{rК 7ɸ0L)\$@ҍNF=gtT$;JO5:^7թO)Ny!  ]D  k=u;33' 8+&$]{x,CAyAhD3Wu_%,@ dƧ~[zIKp?o5f^3):GEqooV'pγ[77o󇤡0@ 0P#d΀rx>=oIxuM%[[="R̡q)xB@2|DF믰 yzw"eGGSߵw_7_C`غwIc }~!!@qe\.X3$H̀ (YKdz':6S ܐX(8o((WɩX'v"=hAJ 쨚 h&0JA‘l⛗3%H ȑFC,: $ɗEB4sdɐTGuI(InIs䕜I}ɤ,Ɇ/| J7ʏ4JTʪßȕʝʯDL@TIT˴DW/$˨ʳJ5'*:Hti$1GKd/ D˪ُ&HlkSLh1{&'8(UJ,J,<0{ /s̏A 0˅M9$zs&9d=d͞4CrJDJp`R=Hݢ5M ΄ i 싿 19K;X;O5U ֘JKR;BXRKelKN #J aּʶ(ك#1TQ;=XG9 8sӓ`7r;pP)SITBs =q$u3A+R*ݒ+͓7y!e˖m*9T C=%UToX숹3##T8PER#B !PЇK*$F8 Ѷ+0QC(@3U;3R `ci5cp0VJ g-t ; @ 5K U7L ի !ӽˀ1਽!"ԄѓL!ԉ}M'U% 2K==хU6}<7>,_U+ \bc2?]"?%E#55O 5 EYv݃U)ORٱп=шQK&W\}>WrU8X9EtH*)r*"9AC;2=xA}z'}(RH tR'TRx'E-!҄R5\ $+W]'[> "1DΊ.<5l^8<3\8+E:Ն޷ÓSU:28dK:\dMUXLK:%HP[&׸}Eu}NH< f:x`5` a5E^F>տYaa`a 6.Ƶ fa Fbb9#bNL(n%N&ǝb)ƻ)[ "#3.`^$6F96*n2+_,'::Vctゼ_3BF ;&@&AcCvdcmT,`B~K~DVIJWP v5bZl L',WnM1N6^OveXeU/^`7`2Ge> aK.K>KfPabnf4~ff=\fffB gYF.gghLY[ngw]h] jT7a&zsx>qfg6wFF6 g^{G^g_N,KVሖhcfh]tpZgPK&e|g=U'6d虦>iy靦Vf~dN+fdFj)Vjbj=~Xjn`6^_`8vnF(VkqbvF⸦6뺶bc~~ixWvȕ~L.2Nhv"l$NNIcljSl`6v lmȎ鶮$&ަ߾1lֆ 01h)0nnn.o6^>oFofnov~oFp'Wpo7Pnqd~Yc*0 pqp7'q_qq?qWOqqoqrr'#7$%&'r$gr(/)r%w ]lh.m_E-r@p27 FSFݮIsvs)>sINs1^s <=>ft$&6F;EGFGl1䝥t t0MNOWPsM_dNʼLi`WSgBT_n9sIt t&3"h u u]U vE<Hj0HM('2HOQf߰gHUJ;'8%0(lD6,uQ ~7x=Hw-shuOxyG0zH{J|o$LNSzTڀb]H(yP 3h @R @wݣy70?IFNJ)pWi%wee؀ y(7+EQzbtNtփ& ٱ"V ۋQuȀ @'oz):g|Ҝ{{w<C]蘆=+#8}?JZ)x8; }dF'm)Ǎ0q]7A ~ ,~V?~WG54w~s>0>_/I_ Qy2,HPNdJV훹wQhbwUKKN,Yʔ*Wl%̘2gl)&Μ:w'РB-j(ҤJ2miͨRRi@ :("7vr$VUײmӸrҭk.޼z}jګZ6|qbXc9z)d'Sv2̚7s2(#utAѲgl6ܺw -삦 {EZFf'yhҧSn)5gMH> oon[r8|c>R7 UwjƩr 0x!jX| zwZq!G!+i"-VvnE\xAy=cf;B'w xc' !M:dSKF$5G^(%Pz%=qI4 ܘ &O)[9&N8|q'|V6*(:(*$hj)z X} Rz*)*;Nj%&gz+ʝy[:,g*,JjygXyjz-%-Xڮ [骻.0{$r. *g*/K0 +,/ K<16F\1{<ƫ1i|2l\疜21˼9q͊A pϫ|I+n1KrdKK=5MQS5Z}3]=_/iOgqhX=7y Ywy7Swo%8≧Ga+81ί[~h'nߍ94~:4k^ 5ܩ˞:~k=;n;y};6h??WN=[[pKދ?4ߎ>2{~1߿_?'? d {! |`RP]tN3 D>+!! eW+l! YS3!5unDsڡ8&$*╵8 N~xٍX|1.NR$G5>Gl|#X(4Npce܈=E|ZAFE

}=1B=(BP[bhЉR; Ї 1c(HC*ґte:ZOP%})Lc*S@1m `Kg*ԡ'TA a'TN9Ҍ8Qխru(*fƩ"BTa V*\*׹ֲ+l1ևhF1p Rdt=,bHdE1.KH.C r%*%dDc}ͦ9Q&,lc+[aʚbl+Sp^Q1,!F>2%3N~2,eǸ.>D#"A Kt^IPV+PAg#!9ӹv3=~3-AІ>4E3ю~4#g8W2)P3=1S[fx* P|x5c-YӺֶ5s]׾5-a>6e3uAL`p2:Yi1l1l ZH5*ҭu~7-yӻ7}7.?8}nVXp1Ĭ!˨n_e!򑓼&?9S򕳼.9c.Ӽ69s>9Ѓr;FjT\?cوjELj 꾸:ֳs^:.f?;Ӯn;.ӽv7{ՑlGAm\ܹQF413<#/S<3s3>/Sֿ>C?;t;Ͻӯ?/ӿ?? `셟ٞE1`) V^ fn v~  얠 JKS  !!&.!6>!FN!V^a ޞ~!!!!!ơ!֡!av!!  "!!""&"."#6#>"$F$N"%V%^"&f&n"'v'~"(("))"**"++",Ƣ,"-֢-".."//"&!d,|###VXXPRRLNOHJKACD=?@;=>9;<69:367134/23+-/*,-)+-(*,(*,')+')+##############################"## 5c 5c 5c 5c 5c 5c 5c 5c 6c 6cETb`bbhjioqpwyx~|VKKKLdvSFN`kt±ƩȱƼ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)֝߿ LÈ+^̸ǐ#KL˘3k̹sb'&GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#͜gϿ(h& 6F(Vh G sFZE}$ 1(,0(4h8<@)DiH&#Ę}tV8"~'-\`)dihlp)tix|&bOFIΔ\d%9G/[J+bi)f馜v駠*ꨤjꩨꪬ*무j뭥Z*/g(Y 5%Bz 'pɴVkfv+k覫+zJ)k2lx(39>*JKI$8"G,Wlgw ,$l(,;H$dɽl&* *dB#B PG-TWmXg\w`-dmhl6M B! #pR<ЅBGL:D_"X7P(o GH(L 7wȟc%#@ HD .!-"F)X̢./ oo>ţ*8B,pH::px'PARÌ `EŽL"EbIl/H!Nz p1b88ODx,gIZڒ{x D$<; AL28B 30h[#qLfz GQg$G5g9f7Iz##tҚs'< 8MB?@. ͨF7Ά>4d'EzQr(M)(=J$ޠҚq|C["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*xͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_|@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`e ؀8XxIȀA,3P&x( # ؁. xwyl)Wa4@B8DG0?Ȃ$LȄ+(@xQ?(7j9x;xbE`bVUXc؜ɌV9i Yxh_iũ=} Ȝ YA؜e98 HL 8`U ؜g_g`z 㨞Xǖȅ=bj$ZShx؂)I 穕2J5ZĹh^(&zH׹W1fФ :J=Y؎mDFdZPRKjoZ\ʡ cj~Zq:j'HUprh:`*t"Z36a_#z KZV*]꡻Ze`G B0pfNĊ?xCHP   *v |aJRXP\L/ ~ʬ HHh@ *ȭxt&*媀*/pLHB (Z[*A @RD@+'˪/0.J7029۲ ? JF[ 8Ke@#+ Bzd@$[˳;۳?4k XMKd[],P#˪RT;EK۱^ZV{ JN ȶee[S? :1;ʭʧ e yڀX@-eA`.3_@`-%[p+{+X[+`,dL0P?*@ۀֻ ׋˻K_۫-˿  e *@+ *4 +KЋKiKƋ{* 3 p^pe\JJ8Y{\}l\, \]0@jŊK Lp/ # H\\p ,lhƱȳ@pU^Bp ʊe;˕l YTȯȲ,żۡȹp ƀe,ҺpZG  *eP ЀO0 8}RP,`ťk?0*+m=I0 xe0\= S//<(+$<>}.6U 2 S + HD-- ` x=]݀]X0 pٞ} XNp) x؉ͪK ֖ٔ A- ؐ{ L̨]XQ]?ˀxL pz~\\0ef ӭp}r]at_v iLgI$ Ĝ\ڊH]TK>MR0ޱp] Q[J eQp}]L̀+">= h %+Fμ 6Nᕝ*>姝 ోܰZ  p]ݭmlYP`D>PVEހQ覜.7zlX问Kp P[4~ `.P*}LˀnePe{z%GZ~ \}Nd[Z]댭^Ͼ s=qG7bZ^PHXX@]p-l.(HHO>4 xm/D$f m;+(dm Y]l.bBO\o_ .@C-pC QSoUϺWt6vxO ykoN[ƃߪ߭/tߧ陸AZxC:/\\l鈜OZ_ O_:猏-ԟ=yu$ޟπ?|l=gqB@ DPB xdD-^ĘQF=~RH%MDRH#L5m̐SN^D"U*)Uf*+hڵ RL n޽}1Kζ3͕tӀU3emu<_ 9vݽ/Y.v7Z/У> j_~Ǵ/+=J\C,6!B /$鿹"P sZ4{=0W4hB _1 5|S q=4A>1He$H#{ @#q{p<2K-L+I+0|NM7K5k'w,) y=f7e`pwZeqyhNNq6cgK ziF.Q闟ۨ>Oakjk:k&ζ)βmߞ鲱{&&\Fbz6;/< :q`>yn%ZrϹ|gK|s ]ѝ2Mo/fM+|5=qG޷S~}-㓧Cn=-z;>8ϼ{_|#_bE_}w'π*_< `x7-~t`"$`D3h'T¸o$ğ Q8Õ], ,4YlH. {`8Dn1gJ#aHC&Rˉ k7E.DwZU 62b5REf]qxA6ˍ"!O 9G>qU A2̐"U#$EEI3ZL&1I(2򇠄(!EE񓨜* ,Ĥ,DKi.K840DD2<: 炦 fp"xlr #dP$g9(M+Qs<d&?r<%?YUrdtmp  3͌49e>8әj3 =Yr3@Yvt?3Ѝ%mhJ҄ƴ#]L;Ӛɬuwዙz^2Za~5bM*5|Ǚk-x' ث3#\ φx^_+{nhyc;n{s`^7=nyA Z,% -D~ygށ2r x[@D@I -᧳ r+(r~CD ? 0$Pd MFD$` ea.- yBU \NA AF^r< @T 2.{5'@~vc]Q` ZtS @d - 꽑:ب2*_ $@Y(8 d0U2,6gZsc^QX $"'ygDm 2y]@2 0~ ( ^P%d!&<`Gs?c>p>(us520+)`)@)9s<;2,P @402躊,@8@IZ5&@  #,P B\h8%LS+܍tx#$';P+-`) +-P.Ȃ=-0!,(0)@CӶc5e^%KP2=4DiC1B JŧQCaCsXĵYZt[N,&v1cDdTedftghijkFiT*`\Db]uz{|GllGdK5ja}$Ȃ4ȃDȄd~dSao$FȊȋjdHnDoHal&p$ɒ4ɓDȍn{HHHəɚF Z{xHɟʠJ >,I=ʥdʦHT GItʫʬG BJʱ$˲HjI I4˷˸4F HXLJ˽˲ˮ6˰˾4\ʿT\7jKȔ,ɣ̔WmId@ZU"BU_m^.`%I,dcUeu~bV|iM[uATjjwJl%mWVys.kmMUtuW*|y Gw{E|q~Xj؁}ւ5XeE؄-օeX`u؇ՈXZ؊eՋXS؍ԎXKِԑ%YF5ٓ5ԔUY@eٖӗY;ٙӚY5ݪٟٞڠڡ%ڢ5ڣEڤUڥeڦ%EuکڪګڬڭڮZ[ϯ%۲5۳E۴U۵eۢZڶ۹ۺۻ[}nۼۿ%ܽ-UeuǝZ8o͊ąe\ k\%5]6]KG88ׅؕŋ8GP]OTu]%]\uN@[X|ݝ b]NxA^%8GP[ pEaUH <ߢBOP5W=j ]hPCЃ5F?8JVb0jH_h h_H _G  EU^ Xϒm`G@!)EG .mHԞ`G*63FcUG0b}o J H<=NJT`$`! 0 Jp!PHHV6Rh78 NRGXB?8Pe8`e8xWXYZ[\]^_`a&b6cFdVeffvfZne8P8?BXGR_6͂bV@PJ C ASwxyz{|}~&6FVfv}ngA CfJȄPd%.6_"DVRN8HxGpE`v闆阖陦隶&6FVi~HhNn_ .Wa`]iT iO΄6FVfv뷆븖빦뺶&kOjTPMod%xqjZhVPTR&6FVfv׆ؖ٦ڶ^mV̦[h= Ԉl`! HgHq[&6FVfvonNgx@l&ndxnWgw '7pbPqojxlr@ pon !'"7#G$W%g&w'()*+&wow6_ɶ73G4W5g6w789:;<=>?@A't=q/E 1Ot/?HIJKLMNOPQ'R7SGTWUgVwR/sF1u\]^_`a'b7cGdWegfwghive[lknopq'r7sGtWugvwwxyqzwy|}~'7GWgw'7GWgw!d,|87ZY|utz{zvxwprqjlkcdd^__Y[[VXXSUUOQRMOPMOPLNOKMNIJKDFG@CC?AB=>?<=>;=>;<=:<<9;<8:;69:579367246134024023/13-/1+-/+-.*,.*,.*,-)+-(*,(*,(*,')+')+')+')+')+######"##"##"##"##"##"## 5c 5c 5c 5c 6d 6d 6d6eJKKMY[ziSl࿏Ůü H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#|}gϿ(h& 6F(Vh 7 sFZE} 1(,0(4h8<@)DiH&#Ę}t؎V8"~' .\`)dihlp)tix|&bOF9Δ\d8GL0[ ,b)*f馜v駠*ꨤjꩨꪬ*무j뭥ZJ+h0g(Y4%B B (|ɴVkfv+k覫+тB *+2lx(38>l+JI%H" $G,Wlgw ,$l(,;$$|<Ѕ[GL:Dְ"X78R( GH(L wȟc!$@ HDAa/!-"F1X̢.o qo>ū*>1DpH::0'VQNÌ ` HŽL"EIE0H #Nz 1b88PT",gIZڒ{HD%@; 8 DL2? B 30h[$fz gpIQgG5Eg9f7Iz#$tҚs'< Y>MBׇB@B ͨF7Ά>4d'EzQr(M)(=J$i!Қq܃C["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*xͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_| @OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`u ؀8XxWȀP #XF*,؂ ؁$0 8xwyl)Wa4@B8DU?hI( ؂N x@(?H7j9x;xbE`bVd0dhJ lHPlPG X ghH8~ƃGacc (kȄpȉN vP suP b(FXxc@xޣyHk~688Xru@bPȆ'e H`lP wyH Xc茔Ȍ88xKVxHdP؏xu0(-hn ȂQ(i~6=b ^[ Wp'+ɒ.(20I7-;36ٓ5ɓ?9@IAY9iLN)OP)N XF`x Xf=Hx'|]aQX'0YI阊iɘ٘Yi ٙ陚YyY` b@X ؕh sXhou0X9Uٚtّv:\8^9H ȉ\,`h|Ɏ TXshY h(9'X_IM N j l٠ ȟVpz01*Y詂H > h6J)Ik(2ZVƩ$Jq(`jrI:JzLNɗTztZc_gIb_wjJZy{ʧh: n p*t$Y:3z-a_:^iIzɩ) Q0)0v[ʊMجC @  * &kyxʫ _0 1NJҪZN@x د: )x |ǢRZ@u ZVP ڀi O _MPuM qON2[6XP7[9=ۀC5^ Wf :+ HA{YqP kK[G˳/(?˴uбVk:k0+;Z\_0#;mmL@3Kr{ [ "[$[۸OpNky+^[ hJ zt#v_i@ _ x@[)P @ ԛ @L в+۾P `qˀg;K` ܻqZPM0 ۀ㋿˿{ ;K [ 0ˊ+hݛXi[)L[<\[?) D콯ɰ;*k hsP|Slu)q܀̼ `Nu) |Ȏ\j#`k[ ʤlL[lȈP`ZpLPך뀕|əLȨ|ɪ  \Pwɏ Ѐ!`m;>U|ȉ\S,.[iu0̠<͍\  ;[ S^z\W0 ZP_  Mˀf0 Y'̀,0![:Vu`f 0fKz51& 8u 2֌R=L-E-ʕm H{e;0MV}m}o]}ma_j% 0}ѽf@__0a݀h`P[ h-{fPE}܀г٢=u@ `YP (ۭ-د}ջu; X]ܗ; g`ʻk-I<0MٖX  u |&ޡ-u}&mp0PZ`݀Mp͜\ (L^+S,2N0 _<$L孰▬|pPmP3]3^53;-NMⰌ>^u jI*LO<3[nM#96>(N\ ! غ"I\<S P}] 7^0!ȈѺК@ JƎ$uЭ&P!^$p f~٭~WKڥM (Ԏޭ>qހkPо非.)oM~^ %PZ^a_h mf0._gkX IAH+'Oij 8`(oi@VPj "?ozjo@ ALwk{iܤkLgk?P@>jPU.Lcle_LJ*6U f_?_Og~ M?߰>Oڣ?Xm֢mDc Yx*N PB <QD-^ĘQ:<RH%MDRJ-]SL5m|c =[lTС S'T96]U^8m4؄D͞E6NmݾW\u3^}ɒMӨSׁWkAŜYfΝ=ě'eҥ)B>SR"WbQ;4[D˟}\pF.vCՃ[mU6c/Ltoݽ>q9¯':q G]~&O/#̵ @ 90C 7D0D D96 ʼn0Fg0@\DVKRϺp$FD2IlHt=:sE#K/b2' ,[LE!3-3N9W M3|E;%|sNA?93lP+;@ # TDF|tLRSOQK۴as!W5TToŵ;UiU=MTy\E]EaEdVeE) VZ?lEmEpM7_}L&(]3Zz2|ea1+3^X a C?$,o;xތ!-Cu[.Pq9c{>fxQF,5Y&ԡWxiQ6騿tj`mlm8l`u>ibmj{A&:BrA'G%rѿļgͱ>;T?(_߯tO?Li=guaF[+ۧ"{G^K>oxOz!>Ny '߿#֞{S/?/|ܧ~>qj޸Ё7Y7<.0 |`eAM0< 58–p0ad_(B%& ׾΅7 G@ˀf Bqr H9sѳ mhD*G(JUeQ4øF~~*ഈ`r8樮IlJTLgֹJ-gzjF EO.U|#p6Ԟ0i,?'Š\=7Jn:r<>ds#h> ˌfzSR7iI͛Y uADf. TqC(·&Kerj̶꤮6" kJ⦦I׊b'X6ͫ^ZFlsㄨ:6/O%,QRՑJlc,fYy6BH"Ӟ}jsTXjfa;:Jݭ`D.@DU#[.t[]]nl9FsTޖS$&V}{WӯK\T =}ivon`<ov)4 ߉sTe ðb[uX;).Z$-H ^1mc851!xFr|d!3YH^rd);yOr e.gXle2W[r\f4_p8w6p9{g=Ѓ?+ь.4C;:ҕ'}Ecӝ5CiQԧt5LӦVGWyNphS{"fY:y34Q{_+ql.{Ju]TnmmGӮmo{r챋 ʮ;t7NաfvnhNw}ol\on`[$L(@pjHpA6"ٍߑ\N FN/a\qwF9Urq! @ԡ Ph6ޑ/4A q, (9L4!T:5x kO 7?Gq/ P@t +X:@H|a[=up; H P)h g @$@!{g^g'x}W,*)``,UP8T h)T`e>hh }[j4^$Ysݫά X+0S=` 4h3 / /( 8B& p A  p!,B#&¥c.l',8~ɚߣl-`  C&@.hC*h<+p(h @<͐=e76CC @KD:HйQ,SD 4(4 DKLÈ7D7'84༸`F3h`aZGkGzG ȆtȇȈȉȊHƄ5{ƞȏɐɑ$ɒH\C:S2ɗɘə{DɃ7_[,wɟʠʡț6ƞHtʧʨI<7IYJw\ ʮʯKJ𶕜좰T˵d˶K(˽;Ktɶ˻ˠ|˿KcIL˼$LK@ɬTȭ,DŽ <ƿʅΌGLļJ 7 DM6ԔٴdḼ߬Tʄ)4DdtNM$Nz)ON 4DOϕ"tOX4O%P\O`CU5ЌTDd P;ʜtM݄ I %Q,UNM>%NQ 0PNQ!E$ U&uR`'R))$+-R/S 15 -3USM5ul7S9;L= @%BETa+H<=v\+a&ֆ_`2FvG6_ghBVJbP4xHPeQ6PX~8d\OIQ\H(O@XKdaFNHHC(??>>؃klmnopq&r6sFtVufvvwxyzgn>fh.CHHSF^bcܓ DU NefD@CpB&6FVfv闆阖陦iFD`N TU&q℆V@RIIꪶ&6FVfv뷆븖kIRhaaVـb慤^OhjN`vdžȖɦʶ&6FVl^Vf^(ihbX gH®XVXF6FVfv&oZVp N (vdH`(l\Z'7GWgw _p^`kdpK&j* ݞcFo !'"7#G$W%g&wr'(oGrmXq01'273G4W5g6w789:;<=>?*oB.FwGHIJKLMNOPQ'R7SGTWU+B7t-HtX/tW\]^_`a'b7cGdWegfwghijgv[7CuHn_lpq'r7sGtWugvwwxyz{|}wom-'7GWgw'7GWgw'7GWgw!d,|Z\\dffikjjlkhjjdfe`bb_aa^`_[]\YZZVXXTVVQSTPRRNPQMOPLNOLNOJLMHIJEGHCEF@BC>AB=?@<=>;=>;<=9<<8:;689368357135024/23-01+.0+-.*,.*+-(*,(*,(*,')+')+#######################################"##"##"##"## 5c 5c 5c 5c 6d 6c 6d 6d6eIJKKQ[i{uusutuwvyzy{}{~~þֿcMXvǩʾ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)6߿ LÈ+^̸ǐ#KL˘3k̹sb'FGӨS^ͺװc˞M۸sͻ ~;0W[ǼУKNسkνOӫ_߭#ͼlϿ(h& 6F(Vh sFZE}TS4(:,0(4h8<@)DiH&#Ҙ}tV8"~' 2\`)dihlp)tix|&(bOFΔ\d%:'3[ 0bi.f馜v駠*ꨤjꩨꪬ*무j뭥Zʋ/3g(YN7%B b,ʴVkfv+k覫+bK.k5lx(3:>/JJ*)G,Wlgw ,$l(,;0+r/oJdl6Ë-,l('L7PG-TWmXg\w`-dmhY+( ͶQ|V 2btҟl$%7G.Wngw砇.褗n騧+lr"K.o=?d%K,F!7G/Wogw/o?~ +rz;$0@+#0 HL8>vvSFxp(z GHV/EA1$DǪrT8̡w C*^!?eTcC[% H*ZX L "c|G ^"A6pLDX,FbA EIB ) gq/ Qp̤&7Irb]8%12p@&:V򕰼&(M/hTB 0ILBj"u%)D+QbZ& ;挙pfB g6vS}$"g'2~^&>\̓J9ih}h6ZЃBr gC)юz{x%dC v0'Mi; C+NtR]SM?VhX5C^:4bF4f6WMjŧU/,mY,\prƶn#|hw]ww~[-no;YO i+f[ S[ݎ7lۛ ?zY6=rnaNsE5oNj<<χH7ѧsyvԥ uOH:uky]3pǰۗZ˵qMrxnxk/&[W8Nj{ћ?ꁛջ~xm[A/?;M[_Ϳ}ә߾lKw?/ӿϿ?F8Xx ؀hi8X,  "ȁxvy&l`*X6x8:xg6؃5X(DH$?5n~VxXXȄ\h(PO  NPGh^ Jx|yQj2X} 8~~~؅HDu4 p hHhyh؂j~ ƈ~ x"(M@#(HX(؈H8=w{T`؎ ؋8~`hx xո qwx؄ydR8y`hr p0jّ " &Y$+9!ɒ/')*) 3y45-@BiCDi\Ѝ(0iXʨ08Pȃvx=H83wax&$xI(0y|ux}I)~i٘阘y9əiə ȔȔX(YYdOILIrw9hh~gUZ|0^Y)OP 0ӨXh8)Lֳ۶oIΩҹyϩ(Rɏ&P OH(ȔpI8ڛ9I mixٟ(IxٕSIU O م왚=J(Cg%'Lڤ 7X3ZV 'ءCj8k PjڤimUjF+%kڧ~*:rztjuʧ@c臤̩+HZz,(k@zȄʩ*X&y]0X` p0(Lw[pJc0jZP&qzڪzw)x:sp R] jR:x*Y@:ӊ+k[PVbhKJh`:_#`php^@^`Z0mh``_yn`H[@jpw9K<(Nj05 _^0[_۵ǯxIk8Y nǴj+ .)Pe{xxa0@+D[q vpo dtjtpkZ gn!pjhXj{pbHVPUPUP]T XpU#`m z  `\VUPk <;ٺGXכW @paH ȶ׾k<+`@[yPP '\[)0SP l諾ߋ( UPЯp0Z)Oj!  &@T *I:+[!uP۱ Jny7&r\p$Q,p{x[ p,ɔKȑz *qP˺  ʑ0QK\ğ^xٞV0#@u^_P(`# ~nN;nz+n n `"֜܎H&`ºѺ >B\O&p (*!o ^nBy@*K-<0z tyP 8 h8|xp  ߫/ jJ)}L*kZ~} -ѵ*iS@Yϟ~ht"=⟀__v %8(Չ%@ 4 B!@!D-VTx†)hRYDJ-]SL5mđSNnUBRyD8q %?s_5+Iq/SB+ 5.8UHdE :Rx HG("8 l{+ Ը=v"52h %= ÂH ;J8lO]w҇/Mn"AHp\m8_yF\ X<܁CfԵa "dk Ix;ua!fִ !qg9E*iUԶ({)@`@TQA:B@ƈĺ@?B(q+ % ɾA=r qGFvi D4 Zhc"Dlt"A$dERi1"h9!@4d @y fN17ͩC:v.us CvӟV,EkQ2UFb)=BP&-%uO qU`?8r# x8E"E\ D@KBkX_$KҚ!TSElߡ"dEZ =h [70eO"pSlg =v`sYv"#ސGV wջ^V$CҮ~E,v>r͂k˗SSЀ德^=|؀f.H4,qEVld'w_G-iAv,a2saslc(5g&d-_#͏͜=hB/~ )|=9ƈ6қ =iJW'v.-hXXΰ$iKԧ.ȡE=NWY4W~W Rַg V -خ_Sk\]ֵRןu9@s=^rfBll܆>Žl9Cnw _zu jY;,w.}?16|=15f^IoK6A8M**A.rO7Z?9M* * ȗ1LFWA  MZFLt bAxsE &ܽB ˥!KXa /W0 l1"2 JA:,Hڻ?<{>IW>ҡs2 d?oXdF =z{#>&pÂŞ7H*^=hxG1ni$>:{?t)pLy+yS~-:V"-YϊDVժWu+x&-S[━0  SV9w)j+)Д,  xXlq2d@PR0A3`ӣ+?7z8Òt$ iYj?)9P!>Q0A'\'R(C=BHB +ɛy9Á8ßɘ+<ܛ#6;t3{4!l .;`1+ڠC@"A!1ۀ;҈4<+i XTPdٝ ш:bcqD >I+K6k%%j' `5*#,,3"] x9tt:;| 0G#d$"5HHb+6jƏAm4$n Fp$†&٦Q*S+H-cB&e,Wb0'q 1yLehlE%WJ>x@A'q'xZI`I#1x H72B$I$+")Ntɂh*""y?Y+8  9AL4īl+DŽL$ JHЫZJDͅŔЂ2)C:N;$$*β pN,=.0RN!>D, N( ICZMPKzcD $4 P; \pƔO@+БPŸ\ =7{P IKO QkOqN -eb+m; {P]S{iQ>PK$R'QeQp7!e\klM{6P &8+Ra;қ3 ,S]n 1eSt>uvk648MQ+]6+Y;]=%Ue ABu;TQK;A}KHմIJmTLUTH*ɤPULSqE9Ų#UxR ҀS5]5ֆUD+$Uѹc7Vc3c6F76:c;c8>c??BC&dDEFGH6DdFIdIVdGd.@bUeb">U5؀  S>TVFexeVveXeZe[e\Ve_X[f]_fcVf^^bfgvhNfhfi&jkfdcffnon^NIX]_mbbBMg^gnm|aRxyPg 6g|hufӃلN|^Ջӌ܍{HX%,%i5i)Q}n&}i`]!iN<{{ʃ)-ƛ&Nߝf؀VQ؀i"!,9`tT Sjcj vjUh醎<[2J9@`(x8(NhhE Ȁh끰+fqh˿ -p-ʶH ls)ЃFm^ gna|%N,Ȁ ,,)B4)4=5 0n.9ȃC) 7nf0eFTiP `4 @2e$QR,6 o񑂮6pSp `pohFkɮTGȀl0K5!2Bhz(@ g(::jj!e6':~¹**U/*&EJi+Ɔk]ʚ : X}*Z{Yg)b-Gik{nO.[.;׻RRKޛopV/ lG.;pJ|_-z9vF9Y>P@>׍~g:g[:>;~;;;<+<;髿>~nި'???wL< @eq R`(!. C(N#,Ph.|! cBM'Qd"+l (!4DE,x |u!GXE"0ID4&,R!Mln|#g QBCpxnHC+F8<'F [C =Սj8y !3M1M3I=.ݸJ8Nđq[ِ/i `E1NF1I?$h&4)M! i(!'8# Qi]PA%fc,e) V'>i$E#TśȰd,X0$ |(DA0w3)P*ԡF=*Rԥ23ei&RM|"0E*;JdM``HGQPn}+\*׹ҵv+^׽~+`+=,b2zeUGUV]@`Ô/raX5@*Lղ}-lc+Ҷ-ns򶷾-p+=.r{[ԚV30Br))E,H񒷼=/zӫ}/|+ҷ/~/,W/2v4%F./ s0C,&>1S.~1c,Ӹ61sg&2ƸȌ2Q:c@1,)SV2-s^2,1f>3Ӭ5n~3 ;cPr;(#H)=Z i0Ά>4E3ю~4#-ISҖ43MsӞ4C-Q҄F56e3~6-iS{ـn0DZD#ovvge=q>7ӭu~7-yӻ7}7~pz~5X8#.S83s8C.򑓼&?9S>{G]rӼ69s>9Ѓ.F?:ғ3N:ԅ.sü:ֳs^:.f?;Ӯng;.[v;~;/?<3<#/S<3s<>?=@A@BCBDEDFGGIJJLLLNOLOOMOPPRRSTTXWWYYYZ\\^__`baceeikjlnmqsrtvuwyw{|{~~ŸyeWfdLKKnƿÿ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'6GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#iϿ(h& 6F(Vh ڇ sFZE}8L2(,0(4h8<@)DiH&#ɘ}tV8"~'/\`)dihlp)tix|&bOFiΔ\d91[r-b+f馜v駠*ꨤjꩨꪬ*무j뭥Z* -1g(Y 6%B *BʴVkfv+k覫+Ѣ+3lx(39>,-J J'l &G,Wlgw ,$l(,;I'bʽl&*|, 'P $ 0&<Ѕc GL:D ذ#X7U( GH(L wȟc9 '@ HDI_0!-JFAX̢.O }o>ۅ,LHxpH::)dbÌ `G L<ŽL"E>I1H %Nz A 1b88Tt",gIZڒ{D'P;  HL2HC@B 40h[&qLfz MQg4G5ic9f7Iz#&tҚs'< ɈBMBWF@b ͨF7Ά>4d'EzQr(M)(=J$)#ҚqC["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*xͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_ॼ@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`z ؀8XxY0؀ "$hz,؂.0!x 8xwyl)Wa4@B8DW@x?KX P ?H7j9x;xbE`bVxphXe Lh2lqJPnJ ȆGFWoZj<}68X( xHHeNg xXP { P8x=׈waxȘC h8z  0{I 88xKWH̨ngz`xe`zQKƐ/؋Hb_ a^@Y)-0*ɒ492i9/=ɓ5 87AYBiCy;NٔPIQɔRID ȉ YONpOe ѐ +d=Hx'|]aS%0%pY٘) 9I)9ɚ!  ؉hUo( Yyi)~6=bُٝs]ـ_Z$p9J`J(ɋȎTܳ\^hyJrȐ XhO9Jpn %*$|i~Ii z8Ji XڀʀFFj詂Q ٢8,ڟөթzi6`XnX8('ȆVx13*tbڧ~h&zjlzoʧ(Vp胊 zjQ"y=}ڥXx3HZa%_p a*Th08zZRuP8 43J/`fЏC^0TB0S  jxu |4Ϫ0P62XphA (Xڀ*ˀZ@D>@S XP (˯ja_ljКXP*P^ TP C (kR mmAp B` (CܘdMQS G+ Hz@Kr ( ȵ^N P+maK PP1@ cx|ۀPpE U{Y+ H[zPD+ C  | c{KplʺPຘYWPuدƊtFZR:09j5p=hE`4 )/p !iP3@5D>P+Q`2@ Kz` =о/1P[<@*  \z l=+p;:@) ;*13|*='0z%0Kc\Ƹ( ,-Ъ7R _;;0u\0+03KxʥRZ<=E X3i%|p H/g0Z'C^l0ʥ|ʩ\ۀ\̡DlQ_ˬͼzz ( 5lƳ9p\@zHz ͦ5@ˊ+'@ʋ xzЭY _*M(p'__2 ذ-oL&ZP(!Az0U 6ݝOQ_B>uKڞ4q.bn\.i U]?π/_ߊύup߸gOukImɺ{}ZBP ϏMx.TNɢڏk~5[a7_2x8ԯRLG o@@ DPB :Qĉa"ji̽OH"3m]Hab$̙męSA==Th![`RM&uUT=/c*h 3b`#zJ V !h')-B "A%4/ƦA=  ӣCB,R#:<=$p)O(So5*.tc.MȦ8 l; :؜HM=ˮ.XAp844zazpohHAB Rz`BbFeJTG!MRJ%d) \azbxAV]U("jXCt2^{Q`ԣ!*Ub0 fO[}5V=rVSIbzVQFOe@-ߞEU+i d#S43{۳b"JN9Qr0b3(!)rQ`m:B" j-1 ٩5g8@B:Nyi"Ѓ X駣&4hW,;ZC [V꘽B/@w4("Z޺{:|l%ک(Vn/j'#6kbNF!Mno)e2^|(bASJ"n*h=@\+x10!H^;Pa^8!0ִގ{nl)TBep4*|=PnzK]~GA vN-#nLܕ :du `Bb' j` »Dx7 ˃ջ8 1TqCGV@=@ 1_'E*Z}Sx~miUA A!c0/z萇>{I;gBS q'Cr,N{FBMR ) #QN1 {E1 z`xR*$$  kR+:6TSUxpD̞Sxf SV0=8'$sl Q %BM=|9bf',J=0/3Jep18ٺvٝl8@(8 <%D=l!*(`hi@JӔz R0Hj2P4*Th%pȔ,`%7@Rr4kQ  mIPZT=0թP=iOWeR>ҵfB054,$G-6ѾTr¨jG2]Ɣ4< jH9:E  6%f%JZ03) oB[@W, v"::g*818h1} Hg)B _׿L,{+6^YFI+Թ p p5L3XMy' Jfa/U: CQnspۼg>oƳ] tpgF7\-hvbM<,џuhNd: RCӢueH;9U^<1ugRZuhumU(=m> 5C2_['vck{! Cߎ=o'D7y|m]=p@f 8w%> s;$+xEG i ]p239CN6bh*NOQ* UGB!q$:y}~'Ľ?zh,eq:"\KB "Ͻm8-G(p;[tdloA^#g?RD$Dҡ]bR|.̈>#2{SQOhoñJ|'< =C1&-J_94򣙼+Ϝo:<PՃsIySܥ8e1Y2>xG_: ]ỖdYfi  z)@=؀0))`?=A!,t_ a,Ҡ ˍ8;?=#@hi#$rnbĉ+؀aYBazё¬) )a)1 # C̡;-<Z#AԼ :ڎ8 , Y"; 2*IE ,VHO$ _ܞ`ܠ BDSD:FGHĂX4࣡%BBJ5jBH/ IA2)$9ŞpG6 Y|y YpR̡2ȞH .gƲ[Ķ3l Ok)'\%^Mܦn& .駦ا{莓& =ߓIhREɞ(  Otj2`ƞ*z&< )h̬lD 裮 ª)ѫکzHț*4j@$̠H KZ,(@(Ŝ,L‘,D(!Ё7ʶKk<ך BM\=풮X<$9|@ r |dk<(9eq+ձ}˸ PjPc#3@ 5`P_P;3OIQ6K[[Qn7DjNF [,K5R5Vі|#!KR[F}(RRN-% Ie 1-2u;DK %};%UQ:1A9=K>4?e! 6%G8 9mFSHTauY5^-a=JaEVEe=^nP~8X0*Ȃ0pb%f'&~%b'b)*b.).b1c//1Fc2N2V3c37c76c658?=@:>&9YaK͂(dIdKLINMdQdLeS.eONPFeP>eUvU~eVeR[\ne]]^fZY&fY6f\a>f^Vfdb^`!.i`Y`fn.UjƓm_~sRo6W vl~dvg-}e]}n~&؀灆:5h(EhUhl&g6҈F܉vg}nhKNop掮 !4#f iFh|Niס(i0֌ʺi"ig UaGVF( ѫ> Q؂10ȀjIp/mj!{E>jꅃ(hjXkw!X?P!Xl=ȁ`$ t@=plȮkkiGh6K С*))8y–RЂ8֮צ 4"Y(&ྡྷўӮvXL#)=2E=ENX.T9$o)z N_fhv:8+ԃsLP(p 70o48@ 0A _H  McZɖā*o" @Hʀ!qqhsjˠL D#` **4 Q9J*YjiN2.:߰+,g>_iBOCPDn}XFOGWQHUNOPQ'R7SGTWUgVwWOfsqM\]^_`vYO{LMtagfwghvW'Vp|FmdXe_iopqj_gk^pdWvrwwxwV7w6ac_my~z{jVtWgxjbjf҃φ xRv'yx[Ւwyg?yHF6xјVy@wlnm7YnGWNyy=yoo=y{zxz7WO{#f7Y?iu?{}?tOl''YL|5g]Ŀ{wɧʷ7/g|'G7gwؗO|}t{Gkk_yW~4|7Gw&~7,h „ 2l!Ĉ :h"Fa"ji̽$ʔ'3m]Hab$(#Nw'РB-j(ҤJ2ō?YR%,]”I&TMǒ-k,ڴjײm4SA$i2kʭ/cάyN.l0Ċ)jݫxQlc7s3Ѓ3k8ɔ[EѲgӮmvgQMCzWue_m8ʗ31]ߪW<ܹڷs0v%.׳o>bxSOs>O,.???dg?< V$ժ R W w#`C(`wЄ.|! c(C䡐]6C̰>!}hGl†K>D YB< "()p0,||ȉWI|I2h N@T<#ӨF/Q.H`-PщHb~# )CDEErDb#*@AD$&A PH6n;l8#0UR-P.YFWȉHx%0EHQ:ceHHbB|&4Y@`c^o3)qs}XG<9Ko,å1M8'>WMXSӠ;Si@>Ѕbm*dIlrG[/Ni Plb 5CC*ҐS)^q 4,%_QPN`"`! B)P*ԡF=*Rԥ2N}*T*թRV*Vխr^%jO SC&8W R*Df2y Y)%"Gܔ~+`+=,b2},d#+R,f3r+$"A):H\iU®&6򶷾-p+=.r2}.t+Rֽ.v]()V֕C(pR#lQaڒ/~/,>03~0#, SxExeAíK-n^Y.~1c,Ӹ61s>1,!F>2%3:fS _tXuKr΀}!]f>3Ӭ5n~3,9ӹv3=~3-AYT6Fy1ˬ;F.;`F43MsӞ4C-QԦ>5SUծ~5c-YӺֶ6u2r]Ec jeHz]^4e3~6-iS־6ms6-qv?Hs1 #=_{7}7.?838#.S\w8C.򑓼&?9S򕳼.9c.Ӽ69s475û:ғ3N:ԣ.SV:ֳs^:.F7:Nn;.ӽv;~;/'3<#/S<3s> !d,|KMf^Q`}~~z{zwxwrtslnmhjiegfacb]_^Z[[WYYUVVQSSNPPMOPLNOLNOIKLFHICEFBDE@BC=@A<=>;=>;<=:<=9;<8:;69:368246134034023.12,/1+./+-.*,.*,.)+-(*,(*,(*,')+')+')+')+######"##"##"##"##"## 3Z 5c 5c 6d 6d 6d 6d5Tsȿ嬿 H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)֝߿ LÈ+^̸ǐ#KL˘3k̹sb'&GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#ܚhϿ(h& 6F(Vh g sFZE}01(,0(4h8<@)DiH&#ǘ}tV8"~'.\`)dihlp)tix|&bOFIΔ\d%9w0[",b*f馜v駠*ꨤjꩨꪬ*무j뭥Z+0g(Yɍ5%B b(ɴVkfv+k覫+юb*+3lx(39>+J%PBI$G,Wlgw ,$l(,;\%|ʽl&*,K+|I%4"RPG-TWmXg\w`-dmhl6M#H#TR)sf5_ £"N|4GG.Wngw砇.褗n騧ꬷ;}Q!ԭ(3| .*hB G/Wogw/o觯>+T%<ЅSGL:D԰"X78S( GH(L wȟc1D%@ HD=/!-:F5X̢. uo>ŻE+BQ EpH::D(ZqZÌ `HŽL"E"I0H`#Nz 1b8Q\B~,gIZڒ{~P%F; X DL2> B c40h[#%qLfz pJQg$G5h 9f7Iz#'%tҚs'< Y>MBLJB@RF ͨF7Ά>4d'EzQr(M)(=J$i!Қq̃C["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*%xͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`s ؀8XxUp؀ "N,؂.&%$ww~'j̖r6>@B8HFG8'sP13xWoG F`b8bh`l fl؆ ikMP(bb R8y(tE6j8Gad8rȀPXl膡 q 0(2(xX=Ljwa8Bx 8aPbpȀ،x/8q008ghxd\yha8h ɘxslpx8Q .8 ݳyi]Q` "I%y%#*- (i1I3ɒ765<=>)?YDy;C;Ӹb؏ hP8 bטEC h܃؈]s!T$3$Зy9-~ٗ)Yɘ9iy ٙ陡 9OQYaTـVXi h 9P-QEzɗٖ騋~6t x(ȚY Xؕ,(fYH hn=pYn=X@h y)ٚ^ق9IIyϙr (J逳ɏz/( 0*TH\)=}Yx*PzUyRj.(HX$veXhYkZg:Z?Z"(b:bd_f|ڧN +ʄ8*^uFokȤ95觔ZZ$8ay_{z:Vȩꩋّ -0 Xd+XʀZʦKx:*tud`  حa jP@x ȮUک'K&:m WZP  hPpX_@ x ;X  NPN h (.  j H6+s`$ N˯Z88JP@' H+ x,  0 -sMۀ5``!sPs@,s #`Xmq @3 0"k+;K H0PO *J눟 M X+c 0[i`.kP; hKp `s5"s+s#˽k.;{( x黾<\s@H;hS UWKf Js j [wˀ @sZ0^P0+ ۀ^ Z2,;eFJ_nܮNœ:‹ZqG7b:/3,4܀+*X@ h]?ADLO[p0V̀d4 Xd˳,tvKūǛg{bL(@ $s`l/2p C6RĘЛ/ p2+@(; ̀J=L{ʚϺy},b@--1X p/; 0 BL #`-h'0Af #0ly9' ª: 1U @ XG"a=t- Ȼѣja_,R hniKhd ؑmhS=|]וxlj%}p' }۸= ҪLDZhȝCۀ&( :] *mGg*ۦ=M]a_]ޑx]Q+¼Ŧ8=`_] D:# QGb ` _">`^!>*%`'^)2~-kνZ+3]>^?wٓmnR~@_B^D>ZHMߏ.)h~٭_awi^u!rt.*1C pMn}7qnu?>HXb{+ 6P hMZTM[t+N > N@ #['-C1~?0"7.m #Wv,и,=,f`[W`h f/1{ !/s/ڍ 'Kh2 s@ɖ 8IO,Q_ʧ (ssė ]hW8д O4?ysngpL"{/ M ϯ^ϟ݀jl|#91̜PqF=~$f@DRJ-]S̗T6đS'$=}TPEE*RM:S('UEF]]~9nцR)RO0HB-RQY$(GOE5UUd/BF> 2N-RXwIU6XF#e0 ߬(Ġ (Ё ,ĔB 6-S'&7LbD' LA3fHl"E 6b# C2ю 'J08!~$xAj 1@?GJts8ZЃ&ӄ˜ ̡w:U4g@6Da^XB@P \*X⁇`AG0b3 ABh΍^հndوVU~4*8 "֦:ukŲ!1ŐjfҜ!XA_4Ā~U-`C$ _ָ&=2`K7\ d7E,9! ,ɞjȨZndmQ Bq4m_3iMqrnY>}g<x9ba׳6ejz L[DLnZ9Y5S(:LJN1O'>8szLk: C ;>ǕEH['>uW9w=gYڈ;ېOMy[~1,oNpL\Z1=ḟL=7wW_og?(⼃wxO}I}gOgLI췿K lXZ9[k\'zPe.?sܖ?5 >l@(\;*.Ђ(.@  $A4AL A<TDAA\ABB4DBTd $'<%t(l"L&D & <+ @p/0l28C24C4lC6TC7C8\2C;47C9;C?D:>$C4D DD\DEFGD@?BDJKdJdCS;9 Pѳ ӻSEjcE㛻3E[ 7\{+E?_[\?um҇JeptT]; WATUUeVuWXYZ[\UZ6L-@-USM]%b5cEdUeeeP}F.TI fklmV]}VWqi VSsEtUueWWV#lV-Wk{|}wxLWa׃E؄UXuW!{$Wօ؊؋Ն=HWTّؐ%Y%bF#ٖuٗW%TXsٜٝVULY{٢5ڣzVyׁ]5XکO]ǥ _ا-تگ=ZmGuɃZU۵uY} UZۺۻ»۽[Wbسݼ%5ڲ2`uDž\ZM%ҍ 5UEuօ٥]•-] \Rm5Ͽϔ}E^L[ˍ^D/Xu^55=MEuCU-Ǖ__-ΒF`f`^}` &aF=fa]}aϝ˽b &"FbS=$f0[&bWdZ%,'.VHel3`/&c2Fcm^M7vb\؃:5Zuc;pU\>&dUG6BfdEnIG~+cJfcLMP6HSfeKeE^Ve{e1Yy[_3a]n^=bVͭeKefmj_cbRekНfipagn&rsFghVuVvvaxyg\{|gV~6hP6IFffhBv㈖:~h4&.?P!6FVfv闆阖陦fidm&6jfv꧆ꨖꩦji^kc6FVVf빦뺶i>H0klk*ZefvdJ8>Ζ>8JlfɇхVlfvמkЦPh]|.c8PDXm&f>@Jm]8mօWKP?0EQxƊ.2k8_SЄFvBhM0Yc*6Fk`a96G>됅a`6o)@JPH Pn]$~ W? V npNJ񧮂J ~pn ŭ foJ@(rJqێGLMM01'2hMVoo%ׅM(0==_ +U,-nUJE8B>t>t>ȃLMNOPQ'R7SGTWUgVwWXYZ[uOt>>B8EJPsUo!cXrYhSMpFPD8H/lmnopq'r7sGtWugvwwxyz{rvDPFvMS`sv## g9U0PKJH'7GWgwyKP0c/ah~s p_`VpQ'7GWgwoQVxBd?AtfG[YxV0|U@WgwLJȗɧʷ'7ӯ|ç]h`f]'7GWgw~}gh'mw{}f}a,h „ 2l!Ĉ'Rh"ƌ7rxHf̢Ef9r%̘2gloeJnLZ#'РB-j(ҤJ2m)ԨRRj*֬ZrSgʕ6i-[Jw)sl-ܸrҭk.޼z/.l0Ċ^RlK'y3Zǚ7s3ТG.m4ԪWn5زgӮm5fc)l‡/n8ʗ3o9ҧSn:ڷsǿo<׳o=ӯo>?7 x * : J8!Zx!j!z!!8"%x")"-"18#5x#9#=#A 9$Ey$A!d,|KKNttZPUÊXo^tfbfeebcc_aa\^^YZZWXXTVVQSSNPQMOPMOPLNOKMNIJKFHICFFBDE@BC>AB=?@<=>;=>;<=9<<8;<68:367145034023.01+.0+-.*,.*,-)+,(*,(*,')+')+')+')+####""#""#""#""-!!R%/S 5c 5c 5c 5c 6d 6d?Thjnnstsyxw||{ȿíŻ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)֝߿ LÈ+^̸ǐ#KL˘3k̹sb'&GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#}iϿ(h& 6F(Vh w sFZE} 1(,0(4h8<@)DiH&#Ƙ}tV8"~'K.\`)dihlp)tix|&bOFIΔ\d%9gL0[ ,bi*f馜v駠*ꨤjꩨꪬ*무j뭥ZJ+h0g(Y5%B R(ɴVkfv+k覫+ъRJ*+2lx(39>l+J%PBI$G,Wlgw ,$l(,;\%xʽl&* *xI%4"$bPG-TWmXg\w`-dmhl6M/H$\*%Y(2䂰(GG´!~4GG.Wngw砇.褗n騧ꬷ;bH"Э(3{K*hB  PG/Wogw/o觯>%4%J<ЄKGL:DҰ;"X'R( GH(L Wwȟc!D%@ HD=/!-2F5X̢. so>ų*@Q EpH::(Va^Ì H$ŽL"E&"IE0H`#Nz 1b(8Q\B,gIZڒ{P%D; F@L2 <҈nHnz=C"(Js4afsՒp-k&z hGKҚkeq V .fSKͭnw[ӮlB Mr:w*KaKͮvz׮mmUY~Mz6m}mAbͯ~{\^m}K~/,\M'L ׿ e;` { qo1<޷nWsӝoub8α^aֱXӱLXyW&;=p%CX2v`*Zǜ[.kK+6=q: Kc9x 6S&LF;p61MG[Xt' aӠ&Ԩe\հ 獏<_=[>7c;GOw饗֫ws {ڭp{r/W[χ{x;i[տzޢ߾?7?Xwh ׀V8Xx؁ "8h@h(X*0P.1(p8:؂bh`38DXFxHTPDMȄ325X;xX8N@H}WudXfxf80hjh08m(H dU78g@Y(vH]|}~WahXxH`hpȆ(X Sxf;ȇVHg gu؈޳glXȘxXou0@(艥XXHHh( up;0hH،=(zHa˸؏،h0uЉԈXoh@X(8W8@xZhx(n_au 6h_Qp,. 191ْ/y6Y9ɓ4)= ?CBAIHYIiJKP9GIOYG 9xXxJي(Ȋ6xgБ<a"ɇx|H0+pyIɘ)Y9əi9IYٚ隩$`XcЍeɁdYؕj9MPi:H@)P8S%I'u)ٝgЈi)b;y:ptY[(Cq8aziIؠxhhS W09 ʜً؟{I}yHba03x)Y9k ;`nI':):62:Tb xxdሑXRJzj^#עLjUrOXT$ȎejpZ=vاszqʥYj.yJ{:vhj:g`J胈ڂ0P vڪܹjڦ(9ZI8&:h %!Y`Z jP8Zvw7n*h:aHP `xfBLEh՚tZ؊&J``Zꊁ[V Z+O@O`M+XQuU0YKhNW_0;(2Ji˲x9ˬuZR |j(T e@ ;ekYPhKph` $PjPE0I  Pe`ppXG pG IqIE + rG@`LHP[깠+)SVX[OQڧePj@J@{Xmj['i KvQw[L0hoLPYu@x˽(ip\u e[H0x <,h"{ڭK_O+WuPyP]P00U0u@ , L\kD03\ \``S,6eIA}w^E5RMfZ*,ýbJՃvNm N"$w>}߮}#lj*H@nDhM|m~JߍZ:n4̷R.жX- %%[\iϮ,D+>L&޼.. 2;GO;Mr[k HE^nNj.j`0u[.u爺z(k(+*pFpKxFF@(+`fipzͭP% e l;;\Ɋp4_`fD Q LMnj a<,,E|IMܾFߊd_f,c;ubٝ]__@@j nL[X*_d|#+ʤ\\o XVh0e6>8>&-оf@+QG -uPSGa Pea$ (`% RIi\#HNlAÈ!mĹ0%@TPEETR TPCTA^ŚUV]~vk e͞=P$Olƍ|k7;rܤS$C}>,Vbƍk]t @tLIBC&\hDԙ`bE HI!Qa D,H P9၉,uT4`bBѤM7eC._Ǟ]v%ס:x"F_V-[p޾.>y_ *M!5~ ܐ2 P$B@"Π2C?“?OJ?k뭸K> 03LPG 1H!$H#iD}dDc.1'jKD2L1$1 K52Zt*plӬ54/4PA%(+oI>=/OtFrtQMPO?5 ѤjSSr7tNTk8PT]wW$*[M8VKLuXgWiSR{bWTNW+SL5jE7"-\>e[d7KLtVs_cW|Մ)'Wpms`S7b'NXE`VMecF!d XS5`WSYU^oYڔiQcy^YyMsF:iOw.EZ>.V:kϴ8MO|oc}zkR/VxFW{H6On[fG1g\gen~yjD?|ޠɦ:Ss_+=,gxY׻P7 MmJ2bAص}juwJ@ #<0앢"4`ppyƶzŠNv10pN%dS. T.t˩ 9? j9 Yhա|B:ya·]  *dr)*`P <<`&@!aI@.DF31 0P:lQTD(( ) 9_ uQ+ʦ:Krzu8R$ `H*.\DeF @il:- t>\xeNʐrT&* J rt I& & "R3r ,IcPӝeh$!,P@a0BH{*9QgZwNStDYHvӛk9D-DޓDOFT X7t@ L": 4(PЁ8 u|f)B_әĥ: ,Z&d4q6Q}<]6iĕ~%Z#)P0%8QծuKRK d P@D%MF@ps]XU!kQDdYVI/w*m+Xޠ$ LnG!ͭBn(Z@.8-8 b oy!ƺnI5uQ;1vsbsAHy ,ȱ>DC)C߅ f0 *_8$P"R|o^p'*vRi PDYa G{ou9AX5< smq+÷8tRk9a[c3QB@vȯJrJud'_0Hfentke|o׋}xelsɹ~t0K3=cЇtEYЎs5mD3ԲGoGt>R5zye]T;ok~^ k/enmKԿFv=aϏ>vek5稣=mn3ڣmwGvj]zs7,Egѳtx)ܺswG ';c:R8'xoquUl6Wَgsj7_ei@uNhHw5twIq}/4:2\׿r zv[t};r=xǺشNl3}uiwSG|2#'/h |Չ>K+=<:CȎfϥvރ{l_ڻ~OӎO_oo|K;^~n۽ʀo[gϟ>?K̹4@SۿgQ@#<t:*/(/A 4ADA\ALdTAAlAB$ ,B DTBdt!4(L&)|#\'d<+;;X0C3DC5\6\3L87C;tC6C=C9:C:C??D@$D<,ETF DGLGHDDCDCDFKDHDNL4Cl7OBC3C@x;ū ?0WŻST<3ʷ:]X,69_0Fƴ#Fn3F3=U C\g4hY,)UFVlFhEY`9+dFtF3SGcGswxt7q $0#)#H%,}~L@[lFW 8&0}/*qj.{H«\\2Q '8HZ0(1: I< Y*븂앓\-h$(]٠ڡ՜E՘: Xuڧ[SoYبڭZmW,ԟU5۳EYE S]Z۸[ۻc\W׫]ڼ\InmVUeܟT׿mɥʭǭ}[%*\\ջeup@ ץU\̵]%^YTVz[eUIu^…^ْ兩eg5_{$eu_mM]&6նU5vF9ݰ\] Ɗ _ v_._&^F.^f]]f]\\!^\!&#\#F%[%f'X')~5)+V[7SYe`,㼵0&cD=\/2fc_e6c9cp{c5;c<>dǍ]5C6mbԝ`8VIfdmTWeBZŻN.]fU`6f[cVfHevfhgfe elva&\on^Krh6gmFtufgvw^xcz{]}~gW>&hP6VHfF䇆@㊶;茞6.0&훕fv闆阖陦隶f[&6FVfVEuꩦꪶꭦNc\&6kbFv뷆븖kVJ?kd6FVl&d(ŖɦʶnejD&mDͮ|\XP.٦ڶm>mJUȅ8kfm0JP@VfEcx~\hQE@PKV`d,cX(MhA 0FЄR^0kd$dXDwD`Xd k6Oݎ!VHwqHon4ְo+ !j+p ` oJhq"*vJޖ orwoM(3G4PMU/ \PpPJ(5@h(rPHXr/rwEpN1HPD0@??t?OPQ'R7SGTWUgVwWXYZ[\]^uRt?t@CHEJpTP OocsXXRMFPJtCopq'r7sGtWugvwwxyz{|}~WD8vEhdO(8m9gFrpiVHRkJJ'7GWgwyJR`vE&9p煈_PxO'7GWgw{_VPt^pkFiXVXOwLJȗɧʷ'7GWg}_G|[Oz d`h\[ 7GWgw~`(zd.gxhwqG}`,h „ 2l!Ĉ'Rh"ƌ7rFc"!&5nȹs/߿2gҬYe+qCy$РB-j(ҤJ2m)ԨRRj*֬Zr굪O+[i͗-ݭ-ܸrҭk.޼z/.l0Ċ3n<-K/Rgεl!s3ТG.m4ԪWn5زgӮm6ؚ]2w/n8ʗ3o9ҧSn:ڷst/S<׳o=ӯo>? 8yx * : J8!Zx!j!z!!8"%x")"-"18#5x#9#=#A 9$Ey$I*9d@!d,| 6dKKLYkSHO\nϺ|}|uvuprqikj^__WXXRSTNPQMOPLNOLNOEGH?AB<=><=>;<=;<=9;<8:;69:356134023023.01+-.*,-*,-*,-)+,(*,(*,')+')+')+')+######"##"##"##"##"##"##"##"## 5c 5c 5c 6d 6d 6d 6d 6d 6d 6d 6dbxſǿ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'.GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#ܚhϿ(h& 6F(Vh g sFZE},1(,0(4h8<@)DiH&#ǘ}tV8"~' /\`)dihlp)tix|&bOFYΔ\de9w0[2,b)+f馜v駠*ꨤjꩨꪬ*무j뭥Z ,0g(Yɍ5%B J)ʴVkfv+k覫+і +2lx(3_9>,,JI&\r %G,Wlgw ,$l(,;I&2ʽl&*<+ &<"$bPG-TWmXg\w`-dmhl6M/#`R3*sf5_ ¥M #NG}8xD.Wngw砇.褗n騧ꬷ[8!0R7'CV"+,%CG/Wogw/o觯p2 +zܷC3/C+\BL:`BDgby%GH(L W=ǐkd(0@ H"~ o!k1< JЈX̢.z|!'`ȋ 1^.\1 L0"_H:1`&F\gl4 8}؇D\:f9(Fc!79z_9/D?;iHC!MB=>"(PxeF7Qq:d;+JP(MJC~pfI 8#!hAkӢ?)H_*R4ETJU*0LJԪz`UU#TjQBhWֶ5{c Yա xͫ^׾ `KMb:d'KZֲkYzt޻hGKҚMjWֺ)Y}zɺͭnw .kcTRԶQ p:ЍtK\6Orz xZfh,nob+Wij\]|fknN ظ1-v [ϋ]䂶{GLnm\0l4 wsWcH1[N{I%O-.{rr2̼sLd(psil㴎rγX:w2MBBgF;:ψNJ[ʑn K{G4AfAԨnBjN3:հ5W-V/z{εLؚһ_xN6w[kOЎs=cK-ee[+۹ =m+쎷=t>Gud|Oo^k4WwM|#oһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W ؀8Xx! ؀"8 X&; (.02*Ȃwyl*ga&@B8D@AxI@?X-(R(4goW!V`b8dȀN x @n_ l(s؀u_ PS0Uh߳y[j<~VXx ؆L  HЉ؀`g8Xk~xHgp  p^ my^8 而=Xx7|^ax_p ؊|P0Ig ( TȋVxd\yxa ZWp)-0*ɒ492i9/=ɓ5 87AYBiCy;NٔPIQɔRI@ ؏ (ވ x8(菀ؑSs@븑*x[=(؅>'b 84-P1iy阕陜 9)`x y( )8X0^@r!Izh_ߨYdȏj^㩀ɛHـ 1 {=}inW>(iK؛؞ɇjZ^Hli3ȑȠYb`٠:IءAʀi 昢S803Jb5_7ZF^j*8z NjؑY=} Z(\zxʞa ڄ{:Rڎm@ ܩItjvcʧ~ڧ):ljo=q:s (zz%ڪ;aW_mZN読 (#IZZz?立 C'zʁʪ :Zzzʬ5zݺZ:Zڬ:*[Jz~* ڰ۱Kˮ{({!K|kب;)[6 +krHGb&{B;8˄ʲ #DRG-k +qNK;^fhˊp˴Zh:VX۳Zt@[m붐ե*ګgkl :z;|۷<*:e[zRWbP{X~U*uj;4릗X#+Yq[믣[۸{:1۵ u۝;껿+FU+x+ԛXkf7y˻5[;{Krfb[X; Wk۽\˿̋ϫWGkL߬w]km,|\4MgULl|9"=:8@ͬ܃MOEmG?mՌ{k=m;=-IM h|οh=j-Nnp=~]JMvlzRղ<ו8}KȺ`Mـţψ{Tt,BM.m֨yLۡڏӛ֮|2M]6}٭=lڱۿ<==ܤM{<\ۖԘך]{}M}čޜٞ]`- ]`ު߻ 7ݮ]ڰ )>Pݭ-iM i =/>1ߒH܍Mޝ ګNl v-MBD%')+n ce.߭#XnCN-NwNy35ުLFNHUNn.}7~;^=߾ ̝NN^֣~n݉mo^_ ^"v~wy-' @.nZg_a^~E>ǽ >W~Yn[].iWYP].~7žɎ[.4 xh>"JnО,_~pG>6ƊOQ@Z͞&^TBͫF`\^LO_Zz\~߮t~u ?ЍQ_яOnx|?~ac/1='@U9SYƟ̯͟Wi۪_df/⏚ߚ?_OoE* $hD6;>ED-^ĘQF/Rȑx Q*װhʽOL2+-ڰ\@Q2СETEM>UTU^ŚUV]~VX.EV-GmCDK5sgϟAݺ]Xd FXbƍ?j!aʕ7m 7ʖ/cW'O@b.jYj][le%MPr;=S~w̝\um͝?ʭ/=^湞 9"_+]z݇^~op}Oן/#@.>>z,l 8 A [CC?Q,7Cn #A_(DgaD"nB3@7j:؀Ǹ 4F)!1.S +J R 8x!ƴ؁D@+#N3,M.W\RAM>h M!-4`6)+hA Z=sE8!+rؗ!z;nG!^>' /1d1.H)fh آt%^68p< D'U6_'+Gq }|ᩑgmF9Wϝ:as Ng}6>pFnI% /~o0y@)_9\)X8 > .. |V!@( \`Ǹ 0 (7/G<넖 Ze 5$aieA'z792:DbC#j$OKԀ5&FW"mXŏpGNu.:gDcոF6эoc8G:юwcXF<1,"h=ҐDd"HF6D(">qd&5INvғ%"^Qy.e`@JVҕeSqyP 8˵җf0iG^^ƋZ$sÄf49MW:3#)u)Hk.g8ʼnnv_ ".5fS)g<9OwV+)wYOӟh09{B D@P6ԡT( Ń0yhF5;Ft5E/Ԥ'=Gɧ[fsH1RT*='2uTiPOǖç6П6թO*V+4lgTUvFEgKRzլgEL?1ok\yV\u1k^ҕBv_:X6O~`jX6ֱzX>ֲlZfֳ=#LuEmjٶZR^YKYֶ5lXַݪnzZ׸nO{\6W5mm;]6Deu]WnxK0bEozqT޻ VoFӱ*Wo~ 6_ZuoY"տý`c攥(m` W"m[,aKp%qOx,q \1qs]x;|\ guGnϋd&VxmradJʨroefY\eL0y3"H[b3YcNW8dwkg>UsQ%4H&:CtQsTYuWMw_{>vgG;fꇧo{>wO_;}w|?_G|x7$)‡W|5u>–V*7?zҗ_|yp}e?n.>~>$׺M+^b?}Wſ(\#xYNQ`b~~տ~yqxr?/`QL0L?`L(X=KcY@NxA @CxN@YcseY(DdDYe7k FҋF?W87?X@J t' J@kX L(1$øLH An+Kh2;êKBtíDR.>E>`a$b4cDdTedftghijklmnoGcE>E?CHFLDVP 98W@PZ|FXDECȁ$Ȃ4ȃDȄTȅdȆtȇȈȉȊȋȌȍȎȏɐtH\FxuP@IJlrzDX`T}LKʠʡ$ʢ4ʣDʤTʥdʦtʧʨʩʪʫʬʭʮJILȄTpGVAKAt,QIP˼˽˾˿$4DTdtDŽȔɤʴ4L,tX`_GkȏWDЋA̅YXpdؔ٤ڴ$4DTdtN|d\Ф Bhe̅DTdt%5P,\rNNk\ %5EUeuMceh?|ЗxI P PnB %e&u'()*+,-./01%253E4S MRNyw0#E;<=>?@A%B5CEDUEeFuGHIJ]TP:m8%R8PMPQ%R5SETUUeVuWXYZ[\]^ZT:T"%KTO dUeefughijklmnopq%rc=bUW9Evuwxyz{|}~؀؁%؂5؃%uU؅e؆u؇؈؉؊؋،؍؎؏ِّ%ْ5ٓEٔUٕeٖuٜٟٗ٘ٙٚٛٝٞڠڡ%ڢ5ڣEڤUڥeڦuډ !,|###*,-)+,(*,(*,')+')+################"##"")""3 >WK@3 '#& 5c 5c 5c 6d 7e:g9f5Q038845;899;<;<=<=>A?@JLMLNONPQZUUeVVm^]mkjrsrxyx~ʴ[HPbs忈×ƤȰȻ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)v߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#ܙ}dϿ(h& 6F(Vh sFZE}L/(,0(4h8<@)DiH&#}tV8"~'(+\`)dihlp)tix|&ȢbOF Δ\d%8 .[B )xb&f馜v駠*ꨤjꩨꪬ*무j뭥Z ((.g(Yi3%B hR$<ȴVkfv+k覫+JR&k0lx(38>,(J{ G,Wlgw ,$l(,; 0Ƚl&*L'0rH |1GpPG-TWmXg\w`-dmhl6M1|Rs%sf5_ ’N!Fa8]D.Wngw砇.褗n騧ꬷ[81pQ!hCV"+8)ЈX̢.z|(!` 1*<@_H:C  Ugl=t>Gud|Oo^k4WwM|#oһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8Wv ؀8Xx( ؀"8 X&&*Ȃwyl*ga-:<؃>(6p:C(9H-91j3x5yb?XVxXxHG`GH@bOЅGah^Ȅ*H(goPj6~Xxx hCPC@m(Ho8C hDЈ(sHyHMXb`= (C`WpC@؋\WrJPICЌpȇ߳yXk~&8XxJ0GPCPhxD8x(8RK0L,`=Xx7|Sa >;080Yiّ #I'i"!+, -%)8y:;i<0 FP(8f ȉȈQy@+1? = dQyxa h0!0t rYv9uzI|w)}9~IY٘ɘYI NIЎ8L)8EJ0ȕـA/pȕ(.td}Hxh Rs"֖YXxOpWИx (l H( ո( e=gin8E(x ؎H8F蹀K@P)VɜɊOȐiIٜ*HxhU[IW3W'NHW : Rj*T*ɁĹ~F% M jxX*XFhڃgʤWl:)Ɂ hJb_'ZzZJE:Zʩ9t˙:ZꩂW'ȩKʖjZƊFza_zʀʫק t:Zzbꇾʭ:䪪zI &گ 檐cJJ ۰:fYk효֬[&+Ijt$F';4kkJڪ. [B+)*q0;b2;Nyj+IKWhT{,˳f8赊RKʪIqYAˀlXnʪdkڪMjyX{{};{ZsqRWbM{XqWg~z Xubګf{욶 ۵KXzۺkS;G[K˴ۻ,˸p帶KkkX !+ W'K Xۼ[˹X[ZkM 80仼|[۫djxfH|U [a"_{W췍 {ߛ\K ,Lz[ vHk̽\+.&|過⟭Ƶ^ɡ>ί.~r}BD. ԛPC;^`EmGز^>'pNαn׌>~Ѯ z *>>^*?n~4j¾W1> ̞]~HO RIN-􀎶dϦ^Vzwe i~.02z' w-M/{_xco?ۊ᨞=^<ߜ_;%_O??/=?oo/A~o=x y9/ؿ߿_?/OK?.A@ DPB <QD-^ĘQF=~RH%M$R%-]5m޼م FTBf :|Ej:p֐R6]p^*SV]O~VXe͞EkqʮmݾN> %T¥MFZUܷ MXbƍ?>6aʕKϠCE)TTbjYꂐ][썒Y 5Nv;}T҂w̝2m͝?NҶr2ܹ™lFG^zͿW8~nvx?wN:id/@$%Cp h3S;[/4@ 7C0? oAWȅZx h^СCw>0 +Za| PZK/aQI催k383Arxhha+ AJ.Ӈ@h ,&b zTbC5Բl72>4O"M E$|+^ AI\N&(<)HS,uV Fieڅ~N'HAO33n]g|)LUAKBo. PUX`% ~=XᅈtX!`.i`kA.!s"nr&qzN1IsXUӃ!fz!f~K) G|cQqcv"׈.ng{NBXUB 'e9o:fo"N~Fq[^uc}+,w5&hx`Vx ר&>~D&@a 45P]K_L7UիUH҄g:@z t3_(L e8C Q{ˡa0!9]u#6Ob8E*VъWbE.vы_cD-hD+s32ZFoc8G:юwccF!--!X<ҐDd"0&HL#xֈ>Gd&5INV)c Їd'UJVҕ]DeC I/I+uK^r\,+XKS \e2LfG %GRr7Df3Mnr& hS~&Dg:Js$Zkg>iH{*:8Y~dEhBX S&jjPVԢ-b%ybp hHEPn4)JG2R4%(i/zJT7iNuliP:T>2hZT6թ;w ZS<ժWj;4_kXouubEkZ&kk\Jsk^{l`{Nsդ`X.ČhU;Yje5٫^lhET*Emj ZIR-6YKNcֶha)(N&0np8ہTEY&׹-r}V׺.h]&onxWƓ~uxջ^ W'kz;_V>g4,o1 =^woˉ`wWmp`Jϥq/fX1p=8#UZSw/mcoVƩq{cǃ1r~r'GyUr/ye>s ڼ7us?zЅ>t}06snt7Ozԥ>uLvյuw_{bG{վvWvw{]s՜?xj;m{|%?y'~7W0P}E_0OpQ/Wxe?{^#<#JYSzG"b}|] q]ыv+@!Aa ?~a(̓)*q;φ;^p;/g\ Ҁct@ڃ \lusp@x$<>P?>@AC>??!$"| = | 훿?xA#+BS?AC |̃Ch4T5CO$d WHG8?6APGRB0 ;@ӄ2 >8`100D0PQ$R4SDTTUdVtWXYZ[\]^_FSD0D169@CMP? T<$OF8J98D6pq$r4sDtTudvtwxyz{|}~dGo9e<F9=:t;/p^FPЄJxl@?ȏɐɑ$ɒ4ɓDɔTɕdɖtɗɘəɚɛɜɝI@|JpFF:=OHIxdʬʭʮʯ˰˱$˲4˳D˴T˵d˶t˷˸˹˺˲JI JOFhtxD;@|JU RJOJMȔɤʴ$4DTdtLTWK L鋦@dątWPDTdt$4O,Ną dBM,` `N\%5EUeu M^PtOg\´Qdu !%"5#E$U t(D,-./01%253E4U5e6u789:S6-LR+>R<A%B5CEDUEeFuGHIJKLMNO*S>R?SQUUeVuWXYZ[\]^_`a%b5V\EUT5e5dughijklmnopq%r5sEWreuevuwxyz{|}P~؀؁%؂5؃E؄U؅e؆u؇؈؉؊؋،؍؎؏ِّ%ْ5ٓEٔUٕeٖuك !,|### 5c 5c 6d 5c 5c /RA*!!#""#""#""###')+')+')+')+')+')+')+')+(*,(*,)+-*,-*,-+-.+-./12023534:3496779:9;<:;<;<=;<=<=>A@ADGJEMVKNQLNOOPQ[UUfVVj[[hikipuosuwyxѳRGPcs忈×ƤȰȻ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#,)JH"b G,Wlgw ,$l(,;H"DRɽl&*(D"v1PG-TWmXg\w`-dmhl6MG R&sf5_" —-NjxeD.Wngw砇.褗n騧ꬷ[xvQ7#xBCV"+8*Tˆ!8G/Wogw/o觯0R'zܷC3-C+ZL:`ZpDgiby+GH(L W=OۅǐD$@ H"lQ o!k0<AЈX̢.z|-#`( 1+DQ D_H:"*! W0glHwL"F2P"*v@08DGz (I>L(]"z`(gIZcqep5ćFDa L2LE{E%)D+ a54 8};:8(.c!79z_9 D?;i9!MB4!(PxPeF7Qq:d;+JP(MJC~pfIP8c!hAkӢ?)H_*R4ETJU*0LJԪz`UU#TjQBhWֶ5{c Yաoxͫ^׾ `KMb:d'KZֲkYzt޻hGKҚMjWֺ)Y}zɺͭnw .kcTRԶQ p:ЍtK\6Orz xZfh,nob+Wij\]|fknN ظ1-v [ϋ]䂶{GLnm\0l4 wsWcH1[N{I%O-.{rr2̼sLd(psil㴎rγX:w2MBBgF;:ψNJ[ʑn K{G4AfAԨnBjN3:հ5W-V/z{εLؚһ_xN6w[kOЎs=cK-ee[+۹ =m+쎷=t>Gud|Oo^k4WwM|#oһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W ؀8Xx- ؀"8 X&x &*Ȃwyl*ga-:<؃>(=:C(9H-91j3x5yb?XVxXxHPPQ@bXЅPah^Ȅh0`H(goPj6~Xxx LLm(QM`nLoMxyȇ߳yXk~X؃ZOPS耟X( 8N 8(5p0=Xx7|Sax蘎_0L_e؀`(c_،ZT0Tp2ٍ{}HxL'WpG@9Y$9'" +-i10/)697I8ɒ9>5)=95i8IhxȈUِP i(0pi  Rs"=& &psYxvtٗw{)~ |9I)9ə!혉Q LȎ V\К8Z9P>p.P6@"pyي~FnI ҩTYO9NT`hـȀ._h=jing89E((Ȍٟ ؝ xQɋjID@AꀤH IlY8 *x *hYyh_j=}y,ڤN&\0Bz#8Yb`pOZNjHWi\ ^E=G:IڜȤf|Ryl)s W#J_|:zj5:H䈧(:J:ڂz*xxZ:to:Zx|vpj:z za_ZÊ釽 m ڮZ⊪ZΪyʮﺯzF*準j~گ i纨t$֭ K;$[ )K%2;[Hz;b";>mF*q; L۴ x6{ Wh%Xڪ+kl9š|\XaWgwКy)kWmC);9Mwg;)`y1}P *EKjRWb=W W{ʫc˨kKK۸++TqY`{ۀW+7Krfb+ ˀkkj˛pǜtZy`뽩ˬr˳K{׻K˽jtj[ kk˼RKK+}U{ K 7Vv黿{";{u[ -ܽe嵗ھ=2 k'6܄Bܵ l 57Ă!|K'P+<Kþd XZ/1<3u5|W.%,lc~ <m"&]|,ԴEzIVX-Ll $}|jT]X΍|<ܺ +7mxm^̲`'^2ݲM.ON)n+K^BY[M-na~FHnJL&(\>Mާ}EG^N`9N䍽 5`lNNvzn]~.~֓ͭ/1`^7.荎ُӂX c_甎x~@îŎ>~]霈.M6n>e^g.Cܒ ݟ:<]\ꑞ`bd®COnyLN>{ {e@^="$2_*,,j><57?uF߯?.sN~P߮I_~MEZ_SUWdo]/a?A^n uofzslxU>MJ4_g.?+E5 NKo//1/_^v?ON^`_Amo߰hj/ѿoԿn%_Ey<;:@iO_/qH G $xat0bÄ.XF3^hcŗ/L0@J-]SL5_2SΝeծeƵORJm.W" S֭6~VK_0!bChղmmڵsƭۼ|{ׯݾ .,ذÍ? yqdʓ-+ƜX3bΎ+g,˛Ѿ X֭];OBETQVUō_YVr͝?]tխ_Ǟ]vݽZuJvСE&ʹTVjM~00@$@4pkQw|D2H!.E-G"l7fSI+"2K-2̕\E ѩ1LĄ?/N;3N+d2F \SJԳPD4QE.OCw&eL358SO?uTS#m?+4C*sNha8 !>W_CmARaITt 9hav- h a_H`o3)7A1e\$ $b|g%Ɉt #|(K!̂.#6/߀/z Z7d 5RmPy$IV8A^zi Zh#x ,F`Pg`⠇ndBb%iGF;mJ~8Ty7ʹmj!*,,-@ .Nm `8A;` mO|x8@&^x)\m_m]{4yCVU7g|Xޭih (ˈ X`>m}` jPnr~x^mgIoR}ag}TѬ4뎿?s`Ah# \`6 ?U!3 @ΔSB}'|XJV ô\ o(`@/^ܫC _ZYj Wt gje[xg$TNhVX 0thlK'`PY98 Y<,mZc'sY"+8-@`ICv\dL7̍] %r3A ŹP}^5iHE:RbӣgNA'hIe:ST0IJ%L/ũXl:Tը *K2GIQ:UV\d=u4?WJU:VX&ek\ϳk[g2Wկ٬NE6@իLX66ͪJӮ50qlf5W(.dF]dEX;ۢm[K;[ֶkShiַn,{– nrV>x$s;]ӹ42.o]vnUSv]׼q/k׽Ozw;^׾pWp ]vKN7UhKWҍO{awx=rm'Y:}2e~2yέٝ6n΅_)ѕњSA_zԙԵf֪9Է>v$Q7ٕvms]u#9xcq/w<½&W|5yw}E?zҗ:vnzַ}e?{At~?|_Ӎ|7χ~_&:꫟~}wG̓G4xo)?~~4=9 /q2;PtP;0L=uQl $ܾ 4JY|@@D50A[<@`pܑAY KH=`()<6DR¢@@`TF73D4T9FT` h(]@5>C@](9@DGH?Ѓ : ARCAhIPkApC-$:; C@YZ>@BxA/ %CCD[4cDF{C8E\C<FHlmt FQTRDYJ`CnyGx`JTpFhC%jDA;6X5P4p4(ȉȊȋȌȍȎȏɐɑ$ɒ4ɓDɔTɕdɖtɗI|4`5`9=DGOBBIJLtDQЄH`=;@9ʨʩʪʫʬʭʮʯ˰˱$˲4˳D˴T˵d˶t˷J=胝dHqdAr#|qHD` JRMLD0CtDŽȔɤʴ$4DTdM\LDHMI~"rDH;u$Q(KÌDTdt$4,KMQ ~'Gs XMW@TNQNO%5EUeu ePdtYxO"tUV\e(܅ߔYp !%"5#E$U%e&u'()*R#Q݅,qPQhhHDQ5e6u789:;<=>?@A%B5CEDS``bgңK1S2lhE3}MNOPQ%R5SETUUeVuWXYZ[\U3OT\Q$TL5cEdUeefughijklmnopq%WmS0mtVJeWttsyz{|}~؀؁%؂5؃E؄U؅e؆u؇5XxUum׊uW`X؍؎؏ِّ%ْ5ٓEٔUٕeٖuٗ٘ٙٚY؜ V%ٟڠڡ%ڢ5ڣEڤUڥeڦuڧڨکڪګZ٭ڮگ۰۱%۲5۳E۴U۵e۶u۷۸۹ۺۻۼ۽۾ۿ%5EUeuDžȕɥʵܻ !,|###<=>:;<767/01)+-(*,')+#############""#"")""3 >L,-N3_ 5c 5c 5c 5c 5c 6d 7e>jBk2KeCLTLNOLNOMOPMOPQPQ`PPhQQeTT_XX_]]bdegmphounsvsvxy|}ԲfKFPas濇ÖƥɳǼ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#}eϿ(h& 6F(Vh  sFZE} 3/(,0(4h8<@)DiH&#}tȎV8"~'H ,\`)dihlp)tix|&ТbOF)Δ\d8.[b)b)'f馜v駠*ꨤjꩨꪬ*무j뭥Z (.g(Y 4%B pr %HȴVkfv+k覫+Rr '0lx(38>(J! 2G,Wlgw ,$l(,;\!8"ɽl&*l (8H!qGtPG-TWmXg\w`-dmhl6MqR%sf5_ ”NhcD.Wngw砇.褗n騧ꬷ[+wԝ%3| n '$2 ( G/Wogw/o觯>@ H‰<MЅ GL:D "X7K( GH(L ']wȟc pD!f@ HD͠-!-EX̢.H ao>c($Q;pH::w($@BÌ `?L"EI4.HNz ٠1b88Ji,gIZڒ{i!(; X:L24A 20h[ fz g@AQgG5Ee9f7Iz# tҚs'< 3MBw8@ ͨF7Ά>4d'EzQr(M)(=J$)ƠҚqC["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*exͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_ॼ@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`[ ؀8Xx;p؀ "$X2`-P +ww~'j̖rƂ8:<؃"@(ACȂ.I&GG TXVxVxLU ZU XMЅWEh؅_KD1s؀1~VGaX8XfȀLKJIXXHJrXޣy~Hk~v8ȃXLMPm耞XȀx Ky0Ȅ$vUH̸PMHX J@(8XȀQ*=Hx'|Ra HB ?P<9iiI!)%I yi)ْ*+# 6Y8ɒ9I:ɒ1 xLJ츌@ɀH訉OYX I)2`C= dPyha Pmrq9p)vYnyxIxz}ɗyY ɘ٘9YyYxNF9P造T50PQ'/ rbل|8xfQs!9ʹWXU[ӈHkX Ț_Gـh y iSȜyDX hhN L؀QPZ xc=eYn7Hɉ$ZXPЉy(x1:*S[~6"jJ[5*NZPڣJI=}ڐLZfڀSJ#jiʔHX@ڞxIh)jIgڧeڦܹʢq (EإG:[p~:ZJj:y)tɧ:YZר {kJ:ZʩGg : ǚBڇwʪy! *z؊C:xΊ:Zzdk:[ʡڪG7b9*ګ ۱J読ݚ,{j"{${"ְ-:[1k3 k;{HK/[?[I[V !ka_&8XX{ Z4{qD z(zդKKMOu$.W+|pWrkf[a[_|+@x5+0o[wu ո;@Yh+5t K w%=;0[번˴gDZxF V0Kp`_ 00400=[ +Kʭ캶 [;{ ۺzF;Y0+K[v SK2 ,őÌEk:Q\5LƉȪ@kVSlplXr\N]yo~ 02 VVuVwĄuz Hɇetv~j n˦sz^\`|ȬXMx;reb¹W²_ƪ̥\Ŋžv¼Ǔ?K˳xɆެ l\l\\@B>라N_^;~ KM~3Y} v>n!~'KH?A^`=nJLqs4f}]tހ,.N0n2bNm[]^_Q~S_U.^=eE.ӱ^=]ݏ=ް>5^y.{}>Z.\N}P^R~/ᑞΞ輾ҘyNnO:YNn8.iNamn>n|خھܮ.u~w>^~.NnN~h`j(*g.&~:*p̎;_G۳/F-ԙ\R,\OT,o]]mrjlOs^ʢ.!#%>r?_֌?_BONOuwM/0M쟟J?LN??Nn^oq_///`?OOޯˏP|?@Y?F>_@@'Va?-RN6eXqr%Kf@RJ-]SL[laSN=}TPEETRM>MUUV\M~=paÇ'^DqcǏ!GU\uu G Bp߿{ 5,8qVdȎ#?^<3̕5_mYўMwFYf֡I>u֥SsUu}+B B6FA$)$p k{ۻ'?z׻oo>~z˷?>}/o@ D?cP?)OG4RIړQK3H4PLsM|RQWTSOEJG3S \0Fy9v:u[[C-9Lz)pA-g{mbt_Yؗc}z  QAk)s۔N6Q] > kBN@!!bf&m\]{X…>p- ?kO};lBUvէP9hxa0 a,d{ۦT@oϽ!+7)At{uQW}Zue!`@t0-@cDS٤q[` 8V 6ADg/\_ >yl:X;22^ F"o' 0"!} Y!GoC,bl7 f:B27 ԕ0Z-LmhN|E;1| :Qz2)ڤXx/l-#)2V`gLԈ0moeʤu Fvq @R)B :R PR;(d0Pd2\N֭bR)S@9,H!fLlʤ(lӜt8SMSu9Oz,l ;R@:PԠEhBP6ԡhD%:ц3/$eϢhHE:RԤ'EiJQљ蓍 'ɤP-{YpִEM=YiKZֶ`EgStVV0mp;܀vLek[ąntkk(!wA ]v׻n[Vоqnzջ^Ůy^׾qa(Y.wpVZpbIE0%ٿQrT+[s*YgFs(ӫovߚ ;Z83l{g>Uy>Wrn'/W4eϘͬuƜυ^*ѕg+_z/tW=NTz֫Zvd͵w:cw{wg,Ƚ.rokt0;^.y1_wo>|Az`7鳞zWat起q{:_x?|G~|7χ~?}7V4}w?~_8֏Ϳ~?_@c4DT\\<8 t3ZP ()D!IX0|8B5IB%B43D4TCK:(,_( .BCXJ0;05?@,4CQÇD_SD6JKD8ЃDS_L&h]0LTUdE)0]P\`Bh^_A70Hftg4NDD9l GBhpGہBXOEl.ľ[?@AS1R]0aP~,m.a/ELMNOPQ%R5SETUUeVuWXYZ[=KJ]Th-e|TH-mEUeefughijklmnopq%r5sEom`,}K -5c{|}~؀؁%؂5؃E؄U؅e؆u؇؈؉X׈pW:֍}H VIّؐ%ْ5ٓEٔUٕeٖuُٜٗ٘ٙٚٛٝٞXءEŽXEڤUڥeڦuڧڨکڪګڬڭڮگ۰۱%[5Z%ڴ%ֳe۶u۷۸۹ۺۻۼ۽۾ۿ%5\UUeuDžȕɥʵ%?5EUeuׅؕ٥ڵ%5EUe !,|###=>?;<=767/01)+-(*,')+#############""#"")""3 >L,-N3_ 5c 5c 5c 5c 5c 5c 6d9fAl!Fn6LeFMTLNOLNOMOPMOPRQQaQQjSSfXXa]]beefmqfowksxquww{}ֲQFN]mŜǩȳǽ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#}eϿ(h& 6F(Vh  sFZE}C/(,0(4h8<@)DiH&#}tȎV8"~'XK,\`)dihlp)tix|&ԢbOF)Δ\d8.[r)bi'f馜v駠*ꨤjꩨꪬ*무j뭥Z(.g(Y 4%B tI%HȴVkfv+k覫+VI'+1lx(38>(J! 2G,Wlgw ,$l(,;L!<"ɽl&*|J(<!qGtPG-TWmXg\w`-dmhl6MqR3&sf5_ •NhcD.Wngw砇.褗n騧ꬷ[tQ"t2CV"+)H 40G/Wogw/o觯 ("I'zܷC3t-C+\NL:`NPDgaby!GH(L WӅǐ#A@ H"xjA o!k/=t>Gud|Oo^k4WwM|#oһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W ؀8Xx' ؀"8 X&(&*Ȃwyl*ga-:<؃>(90:C(9H-91j3x5yb?XVxXxHML`hN@bVK`FimL(D1 u2fWaYXxg؀LJaxf( hHcY{&H+}=&aXx= H]Hh]H0 JP(tz؊y7蘎XPLXI`VˈXX ܨ0N臄dQyxa HB ?P<ّ " &Y$+9!ɒ/')*) 3y45-@BiCDi1` LGMHN()&0RА)2D=H(>'b Puzyyx~ٗvɗ9iIy٘9IYٙ٘ѸMP)HY)N50`Q'/ z9jhTYX؎WI8 Țـ؝Nx۸x(FӖfpiyOـމ(PNp؎OHEȜ4i0zi ٕ8bɡ$%6P]SZX*Y` XHۣǟ,.*jJo(%O8Yyiʦp:YڂZ x$Ƥu9ZZ%ha_Nz:*Hȩ{ꖱC'Z:: x|~ZZƊ]*x|gڢJZJثъzZ՚'ڭkx9:Jʩʖ( HGbZ;lzʬ:pJh몰eʰ *Hza_ (Y( +6{Ujʮ +q:b'D[Ȳ<>@tFRۦ$?qA ]X Vka%_CK`ˇxF-;/ۮ֟,W+UI+K:0y5+l+W۬Wg:ʸWW~P':KJڰw۱ظ0.30WC{ =dM+u%W0KŻXPe[ag_+0=]P{KgkX˴ZBDRP~ Ws'O]P>0W;Lrfb{‘T@30TP.\~0\2ܳ WP5Y# ۜԺ ŕ`+_9܄uq,|uӫ^Yc̻JĚ̵Ȃƃkl~ȎXv||ɍɓLJODF>)^km f._Kj${ΜX޲}nmsu斍έq>h~᧮ ˒^+؟-> ^"NZo]?>Ӗ^95 ^`NӾ.gnH~ᆴY>>_NNt2 />6U o_ҚS;F!/NN/wN%o-OD>$o&뮮d\/^^ngԮ?OxOqI1/{= ?O?^^_Kmo>@S< ۪?/i}/oOOخ0|ٚBoFrtovѿ>w俎ޯb!?s̟/W?E@ DPB >QD-^Ęa |i@H%MDRJ-]TSL4S+]ʰcPEc'2]:=gLMT_^ŚU֓s!eÎ5{Yk˶M -ڹnƥ6ݺxw/`ذ ?Ydċ-;\3eƐ)%$HMnYƯ9wH2u Uը}Nr` Pʙ+7sҫ7_n=;ܩo{x⿏N^}zۣw/|ߏ>CD .AWMt'(ۖjꩨjЪ?1%F$DOD1EWd1 1F:$B.MhIF!?lH#D2I%!, Hp u1) sPʙ5&$L3D!'d35/c1( u6*M?4PA/ZOCIzN*oˣD8R4SM75PK4N+s;LSUGUW_5#OW3EQ;QOZ-5Xa%v_5\IqWJZk5VG:5WhP^h`Q^N-Cn͵N5|D(bpaV€"!^p^/f߁wo+tVT7 `.\P*dx)"aa:Yi)`( Fdgƺ"IQ?-dT}IPV.(P  (v`*>n"A)p\8 :tںӼ}vR~!nF^d) a , -8}X[Tc"߃)AWn/ztG/uPoTWE&[>aQFa6( 8#*xT ~K\1  9 >X7«O~Ya_/qB H @|Ġ  A) {Da|HB.b̄/|I M``\A@ȶ ̺` *8A@bpx`9L.^$ ȋ},#f# *CAmH0!|=iPJA^ "刐#/H.)Dd`L\6Xf%ԹY4}LnvL&y f8MvETg/%lOyQ>O~ӟh@:PԠEhBЂ+=wFhF5QvԣhE8Nes2%Eԥ/iLe:ӅMof"RtȦ,iP:Tը)H-P)MgRKsTVժW*B˥ka;'0}UdլgEkZJ֓@ԩ<kغծwūBZU^_WְH ֿBuua%;Y^dX]ٖTֳK/U=+g;ZֵֶZM,fͪV%mnu├) ۲dEk{륜ЯOE-q{V׺]̞vnx;^n& n Y^׽yVo~ Jr]~<`S= `7lΒw` W•$ wQWa'FCu)qe,brX7Ʊi`?nIc 2tۛ#7Mmd*WyQe.W-q<]~D&sլ>36ks7fǦVw3]Ӟ@Zsя6 iJW--0[ӟoK:gӠF5%TЫ>egXyַsk^You i&.'[̆hWf]dlzqnrX*wO| fw=o Kwyx~-\_Sj/o!>qJW1jA>r'QK/?a>ۿ7isɜ??ρ>O GMhV#l2ӥ.ORIMmw=YOu/eGVΞvTLUtwin۽z~숇|[N_0>_yͳGAOvя7QzNw{qs~>)G~|7χ~?}W]:߭}w?~+~?;?aws?4DTd<~9: 3x@0 $T@̻@@tCAƋA4 4AA |XIA()$AP+$&B43D4TK: ,_.TY0J0;P5?@D>5CQCA_SE6JKD8ES_7h ]8 LTUd)8] P\hBBh^_A7 ҪCHftg<NDD9ԻA l G| BhpB OEl $EQA`qzGCA-TZdGtSEǃDȄEPDkI[ԅX8EIPAɐ@GISGG,w섁$?:5H4@33ʠʡ$ʢ4ʣDʤTʥdʦtʧʨʩʪʫʬʭʮJ34P8;BpN@DY$cDƋ?@A%T:S*Յ$.PqRRhE/R0MLMNOPQ%R5SETUUeVuWXYZ[=_UbX~lTXMHTIuTlFJdUeefughijklmnopq%r5sVJП.R-Vcz{|}~؀؁%؂5؃E؄U؅e؆u؇؈XuTGeuWa$‹؊ِّ%ْ5ٓEٔUٕeٖuٜٗ٘ٙٚٛٝٞY،ءXw5ZEڤUڥeڦuڧڨکڪګڬڭڮگ۰۱%[5ڳ}W%e۶u۷۸۹ۺۻۼ۽۾ۿ%5\UUeuDžȕɥʵ%?5EUeuׅؕ٥ڵ%5EUe !,|mUU]WWRRSMOPLNOLNOBDE<>?<=>;<=;<=8:;:89:45534023-/0*,-*,-(*,(*,')+')+()+()+7&'A2 #""#"""##"##"## 5c 5c 5c 5c 5c 5c 6d 6d 7e>iBm!Gp'Lt8XxPez`lvkorpstswxz~øuLFN]n罂’ŠȭȻ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#}eϿ(h& 6F(Vh  sFZE} 3/(,0(4h8<@)DiH&#}tȎV8"~'H ,\`)dihlp)tix|&ТbOF)Δ\d8.[r)bi'f馜v駠*ꨤjꩨꪬ*무j뭥Z(.g(Y 4%B t%PɴVkfv+k覫+ZI'0lx(38>(J"R G,Wlgw ,$l(,;|"@Bɽl&*|J(@!GxaPG-TWmXg\w`-dmhl6M籇Rs&sf5_ –-NFm8iD.Wngw砇.褗n騧ꬷ[8xQ"t2CV"+)PH!;G/Wogw/o觯? ,BI'zܷC34-C+L:`DgebyGH(L W=/Ӆǐk$@ H"Oh1 o!k/<AЈX̢.z|"` 1+BACy_H:с!( Vgl=t>Gud|Oo^k4WwM|#oAһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W& ؀8Xx ؀"8 X&0&*Ȃwyl*ga-:<؃>(>@:C(9H-91j3x5yb?XVxXxHT SU@b[0^@hk(L(J 8 +2fWaY8Xg؀RQaHf O؆c`s0{=燵a8X= O b@O`X8(Ȇ hQP8h`NȇdQyxa8XȌbpO0b`To(P@8Ҹ`W yW2ӊy7ZI`;IYّ!I%' +9*))0ْ12i38/ɓ7ٓ/yVQ﨏88U()X`X1 ÐRs">  0otYrIpswziɗxٗYi ٘阊YyY 0͘TIɀLXQJ)VP[ pL`*(7` ЗFHcfpሃ ؜Ή舉eؚxWP`(U xxـHHcއ)YYY ̩ٝ OUO)VțWɖùhT89$Ʌ9TZ Jh )"ZFzbП-zO Lȁȣ%Hڥ^ڤ-(XxOؐ Etdڔajcc٣ek8)(Xڨڨ%X:aZ_\jHHjmzCsک *xxnpz!*wڪګڤvzȚi*|q*ʧzZ κ}X*J՚zڭ>jt$fZjx 7:;*:'#Fk+FI ⊫rZ* ڮj*+{8/13밨8;Dۚ.;ʰ(Y~ )jl)旁:*u&4K1W0'Z; + tڵKW9qR -kd˯ \+K)1*-x.W.+G˭w{y{+u%Hx5)`BPB)Yxp K`@~+G{IK˷뷳KpZ@0)]Y* ɫ}ۼ%+0:@.B b/ )pm]B0ٷeۡi*ܛKX-.0.l} }+X_/xDym{՗뺺㞮0<2[[Y:Z)BX*@gX-Ֆ.z^X>ݡn棾ik^mfN~BNɋFr0.ܹ~.ߠ^T~ž3]̞큾뱽<%nےN睌 Y. .N᝞k- Fy%{^~^>ea"n=$N&w(O=e݅ގ&ˎV,_$oN^^ .g֧jO~^6?k_cgioxd.h;MO_WY/QH l ޠ*^Z蜮﫬خ[ZM!?_Gړ -?П%*}ߥܿޯ<_a_ o*?#Z} 7!F@ DPB >QĂ>Qc=~RH%MD)J-]LcGNt)&=}쉏8ltI4/:MUTUCHQV]~VXe^QUmݞ|e̙5o T?D"UTܷ xbƍ?Y2¬irDYsqS'Ͻ?=tiP7=RdڵmƝk˘c>gyj]$FvIu۱k={s?/{yۯO^yӇ__/@@?40A@o@.d9dѐKn([MÖ0D񭁂H &_QFetFoQk ̑HQ"4R#I)*2K,|K'l2)R+K0[R%8HQcm8J7A%PC%PE?s%BĠD\FAX4SSO?D5G=<.OJ,QR_(TYgVF5ESCI$9?sVcE6Yp%]5UeOTYmTfsUVCp]w߭uaN_UUa7Nx8Wz-4j-u8b"=\I:K-F!h,Â-_yA~`TkV:py$*Aph䘟z{c^oX}ե(ŠA1mRc 1P$&h 1fmae1|0pfxꮩY_;fɥ  @ k0 @] 1p@n0\ A# doYwM1A 0ŰwȯwWͣ"rG@rђCFi) s;&8"`Ql#AL 60@ "7 *UdA`DzD&&$Jʉp\× 3Jfg!$p`10\ ͅ(Q YxӸMpbav ,XB2)LV3ę^.-]BƒP| gDj4kZA vԣ(3-ZJkGU҅T#fI3*77iNuSԧ?jP:TըGEjRZ)c4MQEWjVUvի_jU9L{u2g%(Mխok\:פu$0Ll`;X$+.JiR)ld%;YV֨eTbIS}3eE;ZҖv^=ة(1mle;["=+kg¶np{[ZCaoK"\6׹%SfܼvV|]s]5&zW+~nzջޢ~WC5@z󊄽o~o^7@(} pXzv0Ɂ%<&5 &u awtGHU FqUaW&̊eXַ5h ׿ͮ lb"~xsKhFϾkqͼEk׾mn[v}Pܸ&cϽntK[.f6=N7|/n~hw7tm qY1x|^kkS>>05$GM+e^dh7qYsX+yUsL &ѕ/<{?}mյn׬o5^Ѩ3{=] gj{wSmuǻwk}<x/jG~\P}OI}wy?7џ}wם;/$4DTd0; 2{7<$4DTd|@؋A|݋ !$ i7b"d&t'(<@6:ZB)-. # +T/43D4T dʫB6X:;?@A%BS20eXU҈҉TeX`NOPQ%R5SETUUeVuWXYZ[\]UVTFJRqThGKTL hijklmnopq%r5sEtUuevuw_a֚,V OdMVe5VlHfԀ؁%؂5؃E؄U؅e؆u؇؈؉؊؋،؍؎؏XfҜJTW4YvPYcWuٜٟٗ٘ٙٚٛٝٞڠڡ%ڢ5ڣEڤUڥeZeY|%ɓWUVҧڬڭڮگ۰۱%۲5۳E۴U۵e۶u۷۸۹[ZZ}[Zۿ%5EUeuDžȕɥʵmܾ۽]%ݓ5EUeuׅؕ٥ڵ%5EUeu%5EUeu& !,|*!!A*!!#""#"#######')+(*,(*,(*,(*,)+-+-.-/13237344484L 5c 5c 6d 6d 6d 6d 6d;h@kCn$Iq(Lr)Ik0CV6=C8;=8:<:;<;<=;<=<=><>?ABCGIJLNOMOPMOPZQQeSSeXWb^_djngqymsxuy{~mQl•Ȳ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#,)Jۈ"r!G,Wlgw ,$l(,;"HRɽl&*(H"zPG-TWmXg\w`-dmhl6M R&sf5_" —MHNokD.Wngw砇.褗n騧ꬷ[+}%3| '4r2HpG/Wogw/o觯>Ȱ!T<mЅB GL:D_ "X78M( GH(L iwȟc"t@ HD-!-FAX̢./h eo>s(*>@pH::}@D%DFÌ `' BL"EID.H Nz 1b8K(p,gIZڒ{p".; ׈=L2H8A 20h[!fz gߠCQgG5e9f7Iz#!tҚs'< 7MB<@ ͨF7Ά>4d'EzQr(M)(=J$)֠Қq\C["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*axͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`b ؀8Xx@؀ "$X:5P +ww~'j̖rƂ8:<؃.@(ACȂ.I&GG TXVxVxKP ZN \`X]h2( WoOj5}8Xh JJȅJЈ؆K0Y*80 /z=ׇwa< (LMl耋e؋ pMЅ(8 xȄ$v%ڸH(MddxȀ_x(؉8Yx؀N 30h#O`F0<I YِI  9)ɑ ّ!"i#(ɒ'ْ稀X8 8x=7I08*03 Y xݓy'>(P(0_dYbI`cgjiqɖhrٖwYsit xٗyz|Y{yY$L8ߨ<XKY@ 09p%09MdPyha\؛yߨـș _(hXJـxT=Vi~=bYɛJМɛ)A(LHBE)9hSi:X)IX X xDEӝVpyZ6ڞȞi4ZǙ:ʟٹZ :߉Z3zP;ʂ=W*&E*WyY[RzRZXJl]*,=.}0ڤ'|ڧ~ڧ$: a_ZGHj`ꝫB'fڨ* xxaczjYکڪvz** |wdꤪz ʇZL:yZZJ̊JƊx*zZ6*Z/p*jj:ܙzt#fk*F -ʯъe: [ʮvK&{fګ"(;4*;*{:eՇ Ky8ر8Gg:Z@xEYBK}H!LN;Px^ Yc}e[7{VנZ q;wa{W|Uwj+-;1۶(w%A@AOwUaKEp?`d;xKUMn E0O4Q+˺ˀ'b%ͫAbA^Q0APӻk"˳Z[s[л{g`_ +p{W4 :W W|z=p^{OP;w̄<|^WH}< }_5`7^tvxmnԒL<mmm m ֋W׏ݷBMDMٕ}Wkԛ֝mW}`{^}rڌ]U=(]֚}!ݳ\ʩM4-؎ּۯmɞۺ-IK-MOܕ-T6]}۹=y-]6P܍yͽ݋ޓݦzݜm_^M_.^_ ^ ^===q.`}ެߓߦgsWINlⅎn䉎߭y-_^M݅N!^ o.O}-]e^{1N إ. 䤎C~ܼ>>>Ν7nNkSN 2`~ƞNm^=]^ҎN^뇭G0n.-M5|^5+#EGOKCQᮧ/]`DNn.^a&kzv߰n_pOr[w0o|s.//~%o'?iOƪegIJ3_ 3t}[ϴߨ˟`=ߛ/?)_/_P49N6/ߏq@@ CH'We?-Rn\e\}D%Kb@RJ-]SLblpaSN=}TPEETRM>U.UV\M~P!C%^D/ƎC \uU^}XWEXqܯb6|qbZk9z)$㓊={3`ҥMFZ†%璎F>K嶙.([pxGF\r̓6L\gmC6;YZjNpxxWշw}zÿ?@4PD@ pA 0A  3 'pFs5V*z,ɶèEwK P@ H!,"rI%lJ$)I+K.RK/L21dsM7ń3L96SM<|sN e5E1x GC 4ҚRK/t%4GM;7;EuLWeUW/4H?h䎭F3TV__6Xa+V_yu,fTr TZkXiWL6Pqu^[sESmɝ[[]]MH7_}O[]VQ-%t_f~ DE,\^Oņ73mg Ycn  !<9g@N9o-YׂQyP!HB0#r8 t:kgxq& lBf1lA (txe,(h6hz~H\t&k+6Y\1ͦ(t`Aվm'AClh] '$Y ^artӍ`]ZvC'dvamʷrеҜ,CAz[ wAX 1Pll`fbHF"H ßw' 'ڶx!w\XHsn;- :)\C P9v"v*pa'y9ܡ zh!4j:a[OhvX#\!LL4saLxD}j n8Eȝ pw9#F1,v,BsN@zђ#gc(P-^a 5!n0mԥ9jL:UVժWjVլF%+HKչpլgEkZպVtQ'&~oJ[Wկ)^W8u `X6ֱAEpvLdXvֳf9Yoy7]E+жֵPEKvlIlu[v}>z٧f1mr6J,QYnv{VGm*qk]l׼EP[25=#yU^׾2]/pkٺm}<`6pr ;s K5aw8s]*/<^'0mk$ۢ/WU87q7cX?rcRGFrwDdWOrXWr]B[Y_3`ge4'Vf:yqKg>ϩ5n=h &tmQCc+t2KҗٌiNwUvE=jd8&IjVG$CCjZzʯ6-] G'ё*O,iC+tյcZ3zɖu=cd7{مvWlw{9w׽ϥ,OKީ[5 oxb?]=KlcsyE_B3}ꁍ {Geݞ}X`yۋ;Ǘ, G~yOշ~䱟o~۝v_wϟ[$4DT c @p$4DT@S<|  Aӛ.7$T%d&tBt; $|+,-(̻\sRB.$243DC[6ۻCЃ7H9:;7ЃC`C|YJC9CDDTCQ?T @`J@=EOP@8DxDYȘD\KRENZ[>PK T|6dh[@Mh@\dftg@hM@[hpኆbT =xpqLD= T؅bFahhAD>rz,>0EQ1V L$d$ǂ4ȃ iTÊɈlFc DȌDXjTlJ=w܅WDC ȍdɖtIC`H\u$[bKh'@ʠGq(hKjtlH0IYJhCʬJ JTIIWDB=87x7K7X˼˽˾˿$4DTdtDŽȔɤʴLK7K=B@_L)?@A%8(SW*MȐ ,-eXb8.LuMNOPQ%R5SETUUeVuWXYZ[\UUE-JqxvЈwTK_hijklmnopq%r5sEtUuevuWox-`a4V͈d\VJ؀؁%؂5؃E؄U؅e؆u؇؈؉؊؋،؍؎؊]V+{DH=}MVbוeٖuٜٟٗ٘ٙٚٛٝٞڠڡ%ڢ5ڣEڤZb%VdIגEYdeڪګڬڭڮگ۰۱%۲5۳E۴U۵e۶u۷۸۴uZd|,Z۾ۿ%5EUeuDžȕɥʵ]ܽۼܓ%ݐ5EUeuׅؕ٥ڵݽ%5EUeu%5EUeu !,|###+-/(*,')+####""#""*!!@/ 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 6d 7e:;9;<;<=>@@JLMLNONPQYSTfSSkWWa]]bhmanyhqxouxs{{[Mf߼ʽ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#,)JH"b G,Wlgw ,$l(,;H"DRɽl&*(D"GwQPG-TWmXg\w`-dmhl6MG R&sf5_" —-NlgD.Wngw砇.褗n騧ꬷ[+^z%3| '0bH ,G/Wogw/o觯>#P!T<mЅcB GL:D_ "X78M( GH(L iwȟc"j@ HD-!-F!X̢./` eo>s(*=pH::0z@D%DFÌ ` AL"EAID.H`Nz 1b8K$Bm,gIZڒ{mC".; ׈4d'EzQr(M)(=J$iΠҚq'O~o~~g~Wquo?_?X}x`^ ؀8Xx<؀ "$X5 0P +ww~'j̖rƂ8:<؃(@(ACȂ.I&GG TXVxVx@K ZJ 8]?XAX-qWoOj5}8Xh ?>l\?PȀJ >K jr*+p/z=ׇwa8X< X?k( ]8`MdPyha8^pdȋdxh^ ؆ph?P(U(xI0`( ӊy'JPE07 Iy i!I#ɑ'&%,-.)/Y4y+3+IIh Dɏ* x)%-Py h܃Qs!9$0$lpٖo9rtYqYxy{yi| ~}9Iiy阔Y9?KɎA A؀B*4 #E#Vpx hXƙyN8LAX9pؙPI؝V x=}ɐ8yٞDـi9 hɚ(C09W^ Ȋ~6f9z鏒؜ iMOIɋyZ^0Swڐ.8XyYY=ʀȠ$v?PAZO*UJJآ٠3HވxQzg:hW(ȥ_a kH'~&^ zaL_N*GNݸ5 ڦ,8bBwf*J7 eک>jըº |XJڬںzҺjʭ蚮RZ_:cYcʫ{z:jZ:wt#Z;jEK ۯiԊ*"۱+ *I*VJJiZj,z3[l اZ|H*JL7*G[|ȳ {q'1W.eF`1ķL[ZPu$ƱFW5Gw嶽.ksu`;ȷ~ .kxŹW3` |[r۳tF6۫Jw ;;`HwehF+2`G 2{yJBLpkKhW.x7۵ '[ʷ0{5;;H`|5H q;R˽{۷ G`LP@G VkW\к1* bd]L׬ޱwMnٴ(_^>>m=mL^]`n-Mκ]7 *,.N .`F.#|՗ l MmmoNqS-z!N~3} .؛$.ڀ^kЍ^ݏ 1_3^5v|nnԌͭ݊䰎ڲN.勾=X^׽-h痾Nn~.=`_.^ .N^پ|ͩt.nx.ĮnՎ^^Nk v_>/>9^=;w}p=Un>+-OI `>TL?,R/JO:n G?ᨾV0?o4:ϧq.PO˵WYe.I߱쒯f?BDocoj(?*" _a>˪¿J=m_ _~Ooi'og?A!@ DPB >L@D+#('We?%Mn\e\y$5kBęSŃPEETRM>UTU^Ś5jП VX6NĨG"Ore˗1g=KV^HU`… FXb]Y2Cfn1H&t S&7'Fp(ŭ][lCNo隗jv۹g[Bi?]tKm^=,1,|JuGs]=ݿ_>m9'.4H?sjo>d@ O"2O<+n 0DՀrAOD1ũ J ?P*t1GV3!n!}rH"G$T&,%t2J&R*|J.2K/$SK3a+8OF9e6.920veU9Yy3qi{rZY'CJ`ŐAfkzl-nɐ"!b!Ċ@R`;"c]*(~"ZbLO'#f /a!<ϋ`Wuב8("g!jvɗOrJG<=x->wEgh'_sQr4/Dza>'˜7/q?tPG_ϑ Ds>*WG߼8C_>j]DɾvQW6exq{{&vc|{x>P7|Cx7^Pux/㾻+i]~;Ef WܟϼGzCsѥzPk{0lj?|>p=_s]_~"|/'|o~~֏79~Wwxz@+ATdt @üX$4DT@ ̳; !$"4BhAK#d&t'($DY),-.5CB/43D4TC'A+2\9:;671;`<A$B4ĉ`;0=Q? CKLtD4JY|8;G `J@cmn>`M@[hCbT;wx;P T؅b0Gh|h ID=yȁ$H4 =EQ4]$^FAHȉH&LAfE ȆtF@Ȓ4ɓA@G|Etl Y k4Dɜɝ@0eǘidK`$dʦt &`Kqs]vF0`xʲ4˲O@JCuIO@JD=:h6`6X\3 4DTdtDŽȔɤʴLL\:=DJOh{<=lHDQЄH`=ȃ;:$4DTdttN=dHЄūI]qG`MRMLD0C%5EUeu PODHMͷ܅[4GĖGu$QK !%"5#E$U%e&u'()*+#K0QQ ٌKXQW@TQQQO9:;<=>?@A%B5CEDUEeFuGHS86uY[Ԍ(Ue(܅YpTUUeVuWXYZ[\]^_`a%b5cEV\=UL݅-eJT<ԏ bURmnopq%r5sEtUuevuwxyz{|}]W`WmhqjVl Uhu؇؈؉؊؋،؍؎؏ِّ%ْ5ٓEٔUٕeِ1vNVQOU؅ٟٝٞڠڡ%ڢ5ڣEڤUڥeڦuڧڨکڪګZ%Xٙ=د Zڲ5۳E۴U۵e۶u۷۸۹ۺۻۼ۽۾ۿܼۙY)U\Q,uDžȕɥʵ%ݥ5E]]EEՅؕ٥ڵ%5EU^uu%5EUeu&6FVfv !,|###+-/(*,')+####""#""*!!@/ 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 5c 6d 6d 7e>@AKMNLNOMOPZQQfSSgWW][\]di]lydnwiqvpuxs{{ë[L\|׾ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#dϿ(h& 6F(Vh sFZE} 3/(,0(4h8<@)DiH&#}tȎV8"~'H ,\`)dihlp)tix|&ТbOF)Δ\d8K.[r)bi'f馜v駠*ꨤjꩨꪬ*무j뭥Z(h.g(Yy3%B t%PɴVkfv+k覫+ZI'0lx(38>(J"R G,Wlgw ,$l(,;|"@Bɽl&*|J(@!GxaPG-TWmXg\w`-dmhl6M籇Rs&sf5_ –-NFm8hD.Wngw砇.褗n騧ꬷ[8+n{Խ%3| ~J',R,G/Wogw/o觯>!C!P҉<MЅ GL:D "X7L( GH(L ewȟc !l@ HD-!-E!X̢.X ao>cE((q=pH::0{8%B>Ì ` AL"EAI4d'EzQr(M)(=J$РҚqDC["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*mxͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_<@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`_ ؀8Xx?؀ "$X6 1P +ww~'j̖rƂ8:<؃(@(ACȂ.I&GG TXVxVxhEPP ZO` ]`X]-qWoOj5}8Xh XC@h8eȆg8؈?H Wr*+p/z=ׇwa8< xCP_pxȀ(C(D@H@MdPyhah hX_`(ȈxxM1@xhs!NJ9 ɐ 9ِyYɑ) #"!I(Y)i*+09'I/Y'YXD먀8x(x"%-` ݳy';$`$hlknypmt9wiu)x~ɗzٗy )9~;YhF0AـCEGIHbCЋ (AZ,P4lY\=^~9 ؙ8Yؓ_ɀHip 88ٕؔ̉9ɍb YٞCyBɞD)Yə왙E`MpMṕ㩛ɊDxF Ω칔Ȕ$ *RเG۳Vpx:(zFh=*W X13_acgz|"$Zl7*; x@Y77{ǵEN{'5[KW7L~%G/۴ձK.k{WV빙|.@KXXǸ=^=%K'u$&w5@> >pNK.3;3pL03ຩvOkQ˷7{LN@.` X뻔ڻ~>W{W˽罎Ka_ ,6>KhR>W˿ٻ {,wZd˻L`R`LVkWµ7:oPq,X|WkxuV{YL (Cڸ$ x[wDkۻ;N7п:WM`\W^lM gZ {Kn^p~(aw>慞\Nlm܃ߏ ޡ⣾^_їNm荍.njOlMn~^ꤽѪ~8> _$%^='[ؚǾl nn܀8X?Sr~_F?/m.Dk/HO~n߀#0?vn]/|;;:?pNϲf.6~⟟> _gz(Bo )_r߬͏IK/M_xoNΎО/oq@@\ɮcD'c'Z\:AdMA%n@RJ-]SL_l@aSN=}TPEETRM>MU UV\M~5P!C%ZDQ#G EU\uuNW^}80ޫVF׃ 6|mڌ;~ 9rqĝ=ShҥMF)X?rsc!lQ嶙g\8K?XF\r͇zο ~\Vrnb~k} uC;G^zKY.F>n5ӗ/@k@DpoʾB䰦DK0DG$Q 0E4㯷Tƕ,1GwĪlEïB 7dG2|tIijƻLP~P"2TsM444lN8ټSN7s>̓O=s? PA QEM4RD'=RC/eQM%4SN1RLk*ÌK۴2.3\#ZoUW^uW`Ub{d YbVfuVg=Zmn6\pǽ\kϭ6m5Wo]\tk= SeOU")$g_$`UW`E.Kv`/X)a-|UI9/cOFyIcVAK Y.9eo>ye?s Cxùh yh lcud;jsLzza$#Yk&[.vjN6离9jV'Zۧb&k"'lnieu~"adq`R(b%Alxo.A H ` 7ܦ%j/A'$!(ߕ QWy8$g!lzo]i}J=oX0%|%٤_  ؃ 0jt0&4A(0 4N}/d췒k~A D@ hwC,w,Hg }<1!M`D95GY0#¤C.KN!o0'5:n =̠ @t!H]r1$qD$m&I/GVF}яX J` O'TM%s ,F0AP .uiM241m2 kI3Jrtenز+;A`B E@'-%Oy$H=w RӢ97ᰝO湓z$ A ' C$KEy MA@ؤ %*.:Tdt,Cf)Np!_:,4aDj_j?ꦣLSUnjE6ΤoYw|m助Wկl`;XְElbX,E욹jlf5YvֳleU#)գaeAZֵֶmlqU>#QDnp;\]+e{ 6׹υnt{\m]]v׻pKV=b+׽liq,o~_W<:M VޗEp\ZM*z xpWkqJd&'Xp!y$WWKbf_v.gFF[Kis<1ssl:O{t->σFt5Lv.%=iJזst Jw{.uiRzΡ%SMjVջEme=*:uu-FQʛ=lw_!vKѼt=mj+ov-kz7pϽnv&ݵlw単wzƷV|Kfuw|' WG5^n#{vMrg[)yʕ}rfyA^>\ǜ 4ym>;?υ^iohG_tёtD+Ouv+gѾ媝o>ʝwG! b[g1kh܀G?o~㏟7w~ߞz?@#dD@St+: $(+<`3dtA( !$"@3%d&t'(B$A+,-.\>C/6243D4TCd0ܻл!2\9:;6t0CӒC<@A$DC{úB6(GHI6B@D|@XJ`DGQ$R4E-BPMN_DJ89;<@BCLNOLNOPPQ[QQhSSeXX_]^^di^lyeowlswrx|u~ZMa㻃Ծ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#)J#$!G,Wlgw ,$l(,;#Prɽl&* )P"G{PG-TWmXg\w`-dmhl6MG (R3'sf5_2 ™mNo4G.Wngw砇.褗n騧ꬷ;pđ~H&3| J(&C"\<mЅB GL:D_ "X78N(/ GH(L qwȟc"n@ HD@-!-FaX̢.x go>{).?pH::0~P%HFÌ `G CL"EIT/H` Nz 1b8L0q,gIZڒ{q#2; ר>L2H8A 20h[c"pLfz ᰇDQgG5e9f7Iz#g"tҚs'< 7MB<@2 ͨF7Ά>4d'EzQr(M)(=J$i֠Қq\C["%(MqJԢՇ:KCӑZtGT"SJծz*DϬ6**XֶbO -kS*qxͫ^׾ `KMb:d'KV6|p%OT{ hGKҚMjW;kO-I״յͭnw]-l:ۉ r:Ѝ.`{Ur֩uMtz /^+[xE,yYtvꍯ~,mы_NL [uy{r G\ [׾صIŗ_flVx0αwcXq"!q} r_ {PɠVٸBF.L2y_3p&mkV19xγ;Y޳M9 F(p~MJOLN1?lN{Ș&HO,@FEհjEk/ָ5gZOZzy .5m%v%mh[ڸv=l{•qlk:szNEDz7M Gzv;j &[X'oZ8-[k[[7crnaNsT˜5Ϲo^j<χ^蠻H=yv nOf:u.k]^=dOpϱۥxѳEwﲦwu|owϰ{LS{扏<<[^ߛ_<@OSzcO{ξsl/`6<{O|7.ЗONؗ{>'O~o~~g~Wquo?_?X}x`a ؀8Xx?؀ "$X702P +ww~'j̖rƂ8:<؃)@(ACȂ.I&GG TXVxVxHC@O ZOP \HQEh؅_KJ@s؀1~VGaX8XfȀD Cl\(BPmx؈Cnxhr ޣy~Hk~vȃV?X ȈH[(؈qN0ddy@Ȅ$v5ڸHO D̸q؈V@N20h#OIp9)i9ِ ) i ɑ I!y&%iN ((xX6=,`HBy/7pL=Hx'|Ra P&[Y`ٕ_^ d9\Yf)fhɖkgٖp9qrtuwx9Yv9&(D NP(؀O)-`5`aYQ=SdPyhaYXȓhȈkgX_耢(҈ GӚVpy ٝI()ܙiX ȓ؈și,P(ل|8xQs!6ߙ )BЛ‰h蠌 zˉh 8~6: ,XYii0ڟC) !*#: W)ڢFzʂ3ZK*Zϸ;ԩ=}9YHڥHڤhOʤc:QSZs' x^:tZtJz:v:y{}TVD`J:Exyx*tʥ:Zh"ڇʊ ( ejZ>ʪB[*:Jʚ?֊ZZ:zx |mp Jh*ɭ *zjXG7b گH|Jʫq*b WZڪʰ ka~_ "; {t*$0V:*\z걊zŖ=[Z"00X?[zhDGgn @x#@Y88ൂ7'[)+u$8KW85L 8m6[Wvz#0a{W#p" W50Py[ʩFݚ;{Wy548 a K404@ M wa8Pc{yE8Pz Q۸W0yUכa[#C;E'-u{>a0#5"쫿WKw,y̶[#Wv,fлLQ0L0!"P"`|k&Q?Lp~WJW#pc;Q‘ת(X]lWÀWM,#,U<La_6̏ɉ n+闵ˮ[xܱl$Zx&ʳX([V7_ YL|΍X|=l~΀XLl%̥JXͳ,[G΁ з `_W ц } `')҄ SL+}#ͬG;<X6`8^:mG>7=jWLO]Q }̤Ǧ|[4jş{loa|g{B"W+lv]lnpMwrmԃWI`K^M XkT ,ؗW}`^W_^ͣWMe=ce}Wy|=UY'xۓWLdmкmy w%Ee}܅{ }mӁ߭ͺ=̇]̉]6t,Jָܸ@$&]\S;Է`^]%^ amޝޟ&0M2M}޵Al=㋝w -'*PR^D]FH:.}۞[>lTVn,?.Զk K׆e~>iN UNgnk.^]]_a^}L$վ|t%~KWNrnd0^ޅ>^>n">ѥ汞~!Yڗz>^jn߄>fNhNu^ˎX~.â`nW-nN>_?>/~ڎ 郞`z^no^캾^n;-$O~C/E]L=7]]5?'ONTc2 蟟U?WOYĮVz/!/{}O/On^oׯaOo13`֝jmßů|A@ DPB <Q5y Q ײlRȐڍ˶׫P Y3Q̆5męaUTU:sVt~L+^̸GmDKeˊśWAYX`… >+׽״K,F=t;nʕ-_Lh DZj֭]xҵ99eEn.nl͝?=r_KEj/}#bGtݿUm7}Zw 4#0C0Adn ҭ2+I?T@G$Dr1?BzOC˛pE Dw; #o12J~DH}CR8ͳržPH~H"2TsM444lN8ټSN7s>̓O=s? PA QEM4RD'=RC/eQM%4SN1RLm@m*xKR;T?LL [mU]{\acMVfeeijնh[q%[sEZuuurӵՅ5 ]d÷^|!`ԁSu?#ckF%\>/8cضl5.94F9ed>~1dFqI[9gUnf`HIgvF:i{6Z//:bXnhDn_"ce庴F;]:~,mnmqr{K+f Ě l8ou{pȼǓh*n KbxRᆦ q!,`xO"o}o߅]zׇ|U!h(n@O Є.XBnpzp\N A_= [`Oc Pp8Tah_[G6~% 58 t08 G@% CN< GO~ :B|^=1rR\ lDO([b  ;;D-XB$pP4V /0AOH"fo2h"G`'KfrlMs=hBfthF[hJWҤ}4#D_ӟ),ePru|jVʩt]=kZ֜Vjuιu=K_'5FvqJvԫVvbS6v;VmnW]aиInv6̾ lw]˲gOB ഄwM;c>vG\-Nw<oEmWv'vɯrG[my̑ro漦osZ<z}>CѕniOdOtӋuZz|uhzC>vT'|ª8N{hor>w*˝wOw?xz /C#3%OvVmwi|zї>L7}y.P{_v=k{͞/?wyigȇ~}˻Ϗ~sb|w_!~ ~}?w?ϟ÷@L4C ds@ɣ t $4DTd|拵2ꋷ>$"4#D$T%d|A~s&)*+, T8B-01$2,'xz3d6t78<7³5<=>C:2;B4CDDlA@Է I=EJKD=HF’RHLDTTUMLK Y|ķŔQESd_`F>PX` ZY0L`?akl8FSHFpą`UG:vtw<GU`ïh(^Pxȁ$HUP^(|Gۊ EE(Ȋȋc$jFESHGCh4ɓDI0lCGpTFA~<ǖPHɜɝ<PDž̆ET hLD(IdʦttDhIY\,G4̄Ghxʯ˰GRǗǘ̃$4DTdt>@GN@KYTKlF4PKhNFPDHC %5EUeuPUFKL^P|\K،G[KS ̈́K`PJ`&u'()*+,-./01%253E4U5R%̈́%SL[(he8P"}UP#%# B5CEDUEeFuGHIJKLMNOPQ%H8W:UF˸ jBm Fp&Ks,Pv,Hf09C54643474579:9;<;<=;<=<=>=?@DFGLNOLNOMOPZQReSSdXXa_a\ivcp{ksxry}x[KYyݾ H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'GӨS^ͺװc˞M۸sͻ ~;0W[ƼУKNسkνOӫ_߭#}eϿ(h& 6F(Vh  sFZE}C/(,0(4h8<@)DiH&#}tȎV8"~'X,\`)dihlp)tix|&ԢbOF)Δ\d8.[J*b)(f馜v駠*ꨤjꩨꪬ*무j뭥Z:J).g(Y 4%B* &X2ɴVkfv+k覫+b (+1lx(38>l)J" H!G,Wlgw ,$l(,;"Lbɽl&*(LH"{PG-TWmXg\w`-dmhl6MG $R&sf5_" ˜]NplD.Wngw砇.褗n騧ꬷ[!G{Q#RCV"+x*X"@PG/Wogw/o觯P8b (zܷC3t-C+dL:`dpDgmby0GH(L Woۅǐ$@ H"jA o!k/=t>Gud|Oo^k4WwM|#oһwOxQw=ta//Ͻoqk#⯖Oi拖Ώd/;|?`a/5o_ןs߯俿}8W6 ؀8Xxh4 ؀"8 X& &*Ȃwyl*ga-:<؃>(C:C(9H-91j3x5yb?XVxXxHLK`hL@bVLah^Ȅ5pH(goPj6~Xxx I؆fh@h hIs8ȇ߳yXk~X؃]K0LUȈȈrUeezG ؄%vX8؋8ȉ8(o((ȎʘX٨Hb_ xPNFp ɐɐI ّɑ9 I!Yi,.)/0)<`ȋJ帀XȈ3pTԸ.P5 9 Ȋ㊅y7C,`,pcYhfdٖgk)nu l9v{wxI|})~9ɘ(`cPH8HX xG0V4;gW}HxL'9XXIؓ <lhH`xO hՈX=Z(>'b)깞9 ̙iI\ȋ]Ux ي~FI 9z=I)Yɀ ـ؝޹9=inǛ8Y8IGxz]Уɢ#F7^96R:>ڂjWJ@Z'HZ[]_TTXnF _ J幛Oڛ jZ P6բڨꉄj* 9t艦ک(w7yZ c>:za_*ʀ:j7NjPڪ[ګʺ ꇨzj|̚ڨJ) g5z誣Zjz{Zzjڮyt$Z{ZvE ˯0 պgj۱k: Fȩ,! ڤ㪱Z-;1ۯ*'#v*Jе*=[?KAtFkZ! XH+yȳj@qB[pye"@YRh[XW[x]ڴ$Zc˪*RW>%SPcp |=LQ+u%H"p6kW"!@W`; 6`Ykx67ฐ+Gc`_+Wgj:W;P7P>@+kkR?~;@!WTXxvI^` b+CK(W Lh{X оW6Pzk[ xyk jxʼ Hp!EPc"`;!5\LW x5˵zz&+䚲eK h;6WXS p!!|UFXc +7L+2Kaq;Yϫ^"pxuNpI+zU_,Gĩ{y|0>;ۿ:@opE SWnW">0s{b {'{KNk KW,bX zWR~n._p.^rNw.]}}>.NN{n[N_^.ꙎW.8_^cn>~KMO~Q~鈞꽾>XZNV^nf4j9=ט?L.Jχݳ^]Z޼nCY.)^[7M.gi+ɭ5m MmJn܎F#O/_c,=eoo~ރ &M?'n35JȮ5L?X_:k_boVMnM fl_.R?z.|^n=nwfg`Ƙoim2/JO_o4$O;_|`~o:,p=Ο9վ׮/ޟo{d?eM.W&B :QD-^ĘQF|H%MDRJ-]SL5mę3fȏ |\QPE>DMB@ڥ 8v]Ś*>v4PaR EVH:ݾW\um Z}-ӨSjEׁ&Ld|يYfΝ=ƙWoʥM[TSRZMuWbS#=[wˠ}\pF>Մ]uv㰐7Ltoݽ~qްykð+JO]~&o/̨+K@ = .0C l4&Q@9G E*0Fg0@\p[>T@ԏF#D28?>b-AK6kő!I/..{-K=r>0Η<446W2O 崳PC StG6C/D)ݍQ5DR+!S,4UU1< KWCuU]w[+G_c((]YhuZf}iVnv[o7o[r-W]sE7s煗wu7ve_y_~_{<`RT7mx-Y`!Lc93xdC.e=NI~dMnfcYfUg蝃&zhuF:gqf碓nZh>zik ){x͈֭slpl PTl;nK{nuVT&p-eMVIKM2'.#?m(uok s*'.]tS_tg u7Z}Xh>xlܿGKx';Ǘ(t!'G_賗zϭ'{Bzb>A`πcP4ŊumH?#,_p0+;G xa S Hp>#ĂPp B@ЄT HPchxNp"k\"#r#X Zp`Th jP)-ЁOX)t xp@Qc pPaj 4qD8.~lFư & vЃTXF -` x0'`$EA rBJZ҄qB PGRcW1h$eL(}Ԋ#ɋDd&.slb0"H.&&8ࠎR'9Wp$, ;LęكlH&R#cbF*< Q)(NGvAzR$k׾o~뮷>m[FFp`/׿0y[X" p5aS׉[a k'FqU/'ĺZ7qۼy1+`h\cG>2SF*e%hR8%vs™,fIla>mj@;=t%MD+e eNw%.7iR }jVՂKuFjZpu|k^5kbئ v|zlf7CIljW"b/h47dɄvnv{ꞵ=H{w9Xw9{zG 7Z>qg)x5#~3:ӲE>rdwMIrݲUr׼#_us|<zΝ3zIi+4g:>GTǺƭw[Ǎp?dg?x!v[i{v즻Ywp>||x;{G|xƋۮxz/y{z=?z{wG}si] O}yz~B%{%{N9|V>6O q~_Go<_}_B~׿@$W+DdT  ksS@ <6$ $DT<t |$B!̿"4C$%dBs' (B*+B-\.B!243D4T5d6t789:dC@A=>?@A$B48C@;FtGHIJ\C<ԽMNOPEL SDTTUdVD4<7pZ[\E8|L1Za$b4cT^_,EFdhiF:TE`TD8opqd8DFf |YKFo$yzGU4GDQv_pGKH>{TȅdȆ,8DGYRE9pȐɑ$I4?XL( u,EhZHMp@ɜɝ̃@pMHZhȃb؅T=ɦtʧ=Tb Jt+hhwL?0˰b4?Q7,,˸˹DE(\ɊT l8J4D $4LH$DpJl 4ʁHD<̤@GeJØTJLp*HTdq+pLˢl]G@ h$,δPH MCHP0D(?؃<88x|6$4DTdt=O|9ȃ=BHJ T˵TMG@>؃̃%5EUeuP>@`PGM~@ -PKEHD@B/01%253E4U5e6u789:;<=>]S.ME@RK؄m]HM DZR+KILMNOPQ%R5SETUUeVuWXYZ[5UKńBRhZPhh蜕` eRHuTHIIhijklmnopq%r5sEtUuevuwn}^eVW`Hx beԮ Tb]TYW@؄U؅e؆u؇؈؉؊؋،؍؎؏ِّ%ْ5ٓX]@TbPМ}}R6 K%Xٟٞڠڡ%ڢ5ڣEڤUڥeڦuڧڨکڪګڬZٗapҙٚ[lY}ٶu۷۸۹ۺۻۼ۽۾ۿ%5EUܰ֘eיǚ״U%5EUeuׅؕ٥ڵmݱȕ\]Ũ\ލ\%5EUeu^hGeu&6_UeU &6F.fv !&"6#F$V%f&v6'()*+,-./01&263F4V5f6v7F!),|##################$$$---BBBggg H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJիiʵׯ`ÊKٳhӪ]˶۷pʝKݻx)߿ LÈ+^̸ǐ#KL˘3k̹sb'FӨS^ͺװc˞M۸sͻ ~;0W[FƼУKNسkνOӫ_߭#ͼ}ZϿ(h& 6F(Vh sFZE}((r,0(4h8<@)DiH&#}tHV8"~'X#\&`)dihlp)tix|&TbOFI͔\d%57 '[&" |af馜v駠*ꨤjꩨꪬ*무j뭥Z&('g(YL/%BxeŴVkfv+k覫+Ѣ *lx(35>,JQ8G,Wlgw ,$l(,;X|Qƽ>l&*,m|EJ 9PPG-TWmXg\w`-dmhl6MJDQsxsf5_phN (x D.Wngw砇.褗n騧ꬷ[x B 9P7hCV"+ s $pG/Wogw/o觯N`QzܷC3t%C+L:`DgayGH(L W=ԯ ǐk@ H"oQ* o!k(<%ЈX̢.z| P` 1^"P(y_H:сAʀDȰgl/(!wL"F22JP 8@08Gz (I!QL( >P(gIZSep5( L2LE{Ci%)D+4 8}8:&5(҂!79z_9D?;i MB s(PxP eF7Qq:d;+JP(MJC~pfIk8!hAkӢ?)H_*R4ETJU*0LJԪz`UU#TjQBhWֶ5{c Yաxͫ׾n4eA:ӵzOyb{OuU]WZtlVN hg٦v e+;ֺVi#URͭNyJRjuKb15aY!r!YͮvG8V7vKv7ߕu[}=@۾-_|9;_~O ٦`XUo'La= o7aVp8z&H%|s8Y1]LX 2nw#<} d!Dd&Mv|%KʮrY.gÁg2cVi%׬X4{p^:ug.RMh ʅN4JFߔю[ђδ=!N#Ӟu=A-RԦNu2QVӒծ5(a-Z3ֶεq^Ǒ׾-bN6fΎv -jG6n7|)Y_Z6M a(^l]ex;ྷ}{83\#.wS8íw6C^푓<&?ySd~9̃-65s^<>y.T>:CwN4ԣSVzuBs:,׹f3Ӿ浳n{.+ӽQ;ޙ~2Ɔ?|x3~<7,S򖷱Cb%gؘC/==ꑼy1ӫ7c_~[s^ֹyo ė?w7 '}J+u񓿵?hӯ3c_n˿/icX?6uXd WnW} h7mUV_XUS"T$XFu(S*fւ2R4X3XvBQDX uHPJԄNX@؃QdPXtXNZ6煂(5օfLhlhKnF'Gtfy/Ƈ~懔&IHk{z:8o<`A(w`]Ԋ8lHWXGXH^XDt(Dʸ6hՌԸBx)xB؍6h\8"Tn{Ę'[ 4h^xe@؏ ?9㶎ŇyYӐ>3_x|e)>ّ ="9S&=(,g.T2I=4Y8yT7Г; 8)5I2i/,)ɔ&# )Iiɕ )IiȖ(Hh㈗ਗȗ(HhˈȨȘ蘹(Hhș虙(HhȚ~z(vHrholițfb^([HXhUQNȜKHE(BH>h852ȝ.+((%H"hȞ ( Hhǟ'GgᇠۧǠ'Ggǡ'h'^Rȋx`!*TE vP**U,fHPUOȉ*&LhM8JUNLN@AmtLC:UMTMKO*UKDM< 0 IGU4 T tQFIDIoF3PFvSyM-*H5E|AV8Fc¨\sC9C=Ĩ6DJDA-r//"<Ǻ(")r)PR;T[V{XZ+""(Nr);0ǂ%,]'npr;t[v{xzk&"(/E3f{M$b")$۸;[;#L$/"h{,$![{ۺ[ !!!C챻ۻ;[{!A;[z{؛ڻa$1[{蛾껾۾1[{ۿ<X1\| <\| "<$\/;cursive-tabs-0.8.0/assets/demo.yml000064400000000000000000000200421046102023000152070ustar 00000000000000# The configurations that used for the recording, feel free to edit them config: # Specify a command to be executed # like `/bin/bash -l`, `ls`, or any other commands # the default is bash for Linux # or powershell.exe for Windows command: ../target/debug/examples/simple # Specify the current working directory path # the default is the current working directory path cwd: null # Export additional ENV variables env: recording: true # Explicitly set the number of columns # or use `auto` to take the current # number of columns of your shell cols: 80 # Explicitly set the number of rows # or use `auto` to take the current # number of rows of your shell rows: 24 # Amount of times to repeat GIF # If value is -1, play once # If value is 0, loop indefinitely # If value is a positive number, loop n times repeat: 0 # Quality # 1 - 100 quality: 100 # Delay between frames in ms # If the value is `auto` use the actual recording delays frameDelay: auto # Maximum delay between frames in ms # Ignored if the `frameDelay` isn't set to `auto` # Set to `auto` to prevent limiting the max idle time maxIdleTime: 1000 # The surrounding frame box # The `type` can be null, window, floating, or solid` # To hide the title use the value null # Don't forget to add a backgroundColor style with a null as type frameBox: type: solid title: null style: border: 0px black solid # boxShadow: none # margin: 0px # Add a watermark image to the rendered gif # You need to specify an absolute path for # the image on your machine or a URL, and you can also # add your own CSS styles watermark: imagePath: null style: position: absolute right: 15px bottom: 15px width: 100px opacity: 0.9 # Cursor style can be one of # `block`, `underline`, or `bar` cursorStyle: block # Font family # You can use any font that is installed on your machine # in CSS-like syntax fontFamily: "Roboto Mono" # The size of the font fontSize: 12 # The height of lines lineHeight: 1 # The spacing between letters letterSpacing: 0 # Theme theme: background: "transparent" foreground: "#afafaf" cursor: "#c7c7c7" black: "#232628" red: "#fc4384" green: "#b3e33b" yellow: "#ffa727" blue: "#003366" magenta: "#ae89fe" cyan: "#708387" white: "#d5d5d0" brightBlack: "#626566" brightRed: "#8b0000" brightGreen: "#ffa727" brightYellow: "#ffa727" brightBlue: "#75dff2" brightMagenta: "#ae89fe" brightCyan: "#b1c6ca" brightWhite: "#f9f9f4" # Records, feel free to edit them records: - delay: 413 content: "\e[?1049h\e[22;0;0t\e[1;24r\e(B\e[m\e[4l\e[?7h\e[?1h\e=\e[?1006;1000h\e[39;49m\e[?25l\e[?1002h\e[39;49m\e[34m\e[44m\e[H\e[2J\e[19d\e[J\e[H\e[K\n\e[K\n\e[K\n\e[K\n\e[K\n\e[K\e[7;15H\e[1K \e[30m\e[47m┌───────────────────────────┨\e(B\e[0;1m\e[31m\e[47m 0 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[33m\e[47m 1 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 2 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 3 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[37m\e[41m 4 \e(B\e[0m\e[30m\e[47m├┐\e[34m\e[44m\e[K\e[8;15H\e[1K \e[30m\e[47m│With using the TabPanel you get a TabView and │\e[30m\e[40m \e[34m\e[44m\e[K\e[9;15H\e[1K \e[30m\e[47m│TabBar, preconfigured for you to use! │\e[30m\e[40m \e[34m\e[44m\e[K\e[10;15H\e[1K \e[30m\e[47m│Simply create it with:\e[26X\e[10;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[11;15H\e[1K \e[30m\e[47m│\e[48X\e[11;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[12;15H\e[1K \e[30m\e[47m│`cursive_tabs::TabPanel::new()`\e[17X\e[12;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[13;15H\e[1K \e[30m\e[47m│\e[48X\e[13;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[14;15H\e[1K \e[30m\e[47m│\e[48X\e[14;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[15;15H\e[1K \e[30m\e[47m│\e[48X\e[15;65H│\e[30m\e[40m \e[34m\e[44m\e[K\e[16;15H\e[1K \e[30m\e[47m└────────────────────────────────────────────────┘\e[30m\e[40m \e[34m\e[44m\e[K\e[17;15H\e[1K \e[30m\e[47m\e[38X\e[17;66H\e[30m\e[40m \e[34m\e[44m\e[K\e[18;16H\e[1K \e[30m\e[40m\e[50X\e[18;67H\e[34m\e[44m\e[K\e[17;28H\e[39;49m\e(B\e[m" - delay: 1050 content: "\e[7;57H\e(B\e[0;1m\e[37m\e[41m 3 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 4 \e[17;28H\e[39;49m\e(B\e[m" - delay: 153 content: "\e[7;53H\e(B\e[0;1m\e[37m\e[41m 2 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 3 \e[17;28H\e[39;49m\e(B\e[m" - delay: 154 content: "\e[7;49H\e(B\e[0;1m\e[37m\e[41m 1 \e(B\e[0m\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 2 \e[17;28H\e[39;49m\e(B\e[m" - delay: 666 content: "\e[7;44H\e[30m\e[47m┤\e(B\e[0;1m\e[33m\e[47m 0 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 1 \e(B\e[0m\e[30m\e[47m┃\e[8;17HYou then can add views and configure your panel.\e[9;17H\e[37X\n\e[22X\n\n\e[31X\e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;49H\e(B\e[0;1m\e[31m\e[47m 1 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 2 \e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;48H\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 1 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 2 \e(B\e[0m\e[30m\e[47m┃\e[8;17HOfcourse you can also use the provided TabView \e[9;17Hwithout the panel, simply create it with:\e[11;17H`cursive_tabs::TabView::new()`\e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;53H\e(B\e[0;1m\e[31m\e[47m 2 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 3 \e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;52H\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 2 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 3 \e(B\e[0m\e[30m\e[47m┃\e[8;17HAll you have to do is add:\e[20X\e[9;17H\e[41X\ncursive-tabs = \"0^\"\e[11;17H\e[30X\nto your Cargo.toml!\e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;57H\e(B\e[0;1m\e[31m\e[47m 3 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 4 \e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;56H\e[30m\e[47m│\e(B\e[0;1m\e[33m\e[47m 3 \e(B\e[0m\e[30m\e[47m┃\e(B\e[0;1m\e[37m\e[41m 4 \e(B\e[0m\e[30m\e[47m┠\e[8;17H\e[26X\n \e(B\e[0;7m\e[34m\e[47m \e[10;17H\e(B\e[0m\e[30m\e[47m \e(B\e[0;7m\e[34m\e[47m \e[11;19H \e[12;17H\e(B\e[0m\e[30m\e[47m \e(B\e[0;7m\e[34m\e[47m \e[13;19H \e[14;19H \e[17;28H\e[39;49m\e(B\e[m" - delay: 1000 content: "\e[7;61H\e(B\e[0;1m\e[31m\e[47m 4 \e[9;19H\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;19H\e(B\e[0;7m\e[34m\e[47mH\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;20H\e(B\e[0;7m\e[34m\e[47ma\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;21H\e(B\e[0;7m\e[34m\e[47mv\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;22H\e(B\e[0;7m\e[34m\e[47me\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;23H\e(B\e[0;7m\e[34m\e[47m \e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;24H\e(B\e[0;7m\e[34m\e[47mf\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;25H\e(B\e[0;7m\e[34m\e[47mu\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;26H\e(B\e[0;7m\e[34m\e[47mn\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 50 content: "\e[9;27H\e(B\e[0;7m\e[34m\e[47m!\e(B\e[0m\e[34m\e[47m_\e[17;28H\e[39;49m\e(B\e[m" - delay: 2204 content: "\e[?1002l\e[?1006;1000l\e[24;1H\e[?12l\e[?25h\e[?1049l\e[23;0;0t\r\e[?1l\e>" cursive-tabs-0.8.0/examples/bottom.rs000064400000000000000000000045751046102023000157430ustar 00000000000000use cursive::view::{Nameable, Resizable}; use cursive::views::{Button, LinearLayout, PaddedView, TextArea, TextView}; use cursive_tabs::{Align, Placement, TabPanel}; const TAB_0: &str = "With using the TabPanel you get a TabView and TabBar, preconfigured for you to use! Simply create it with: `cursive_tabs::TabPanel::new()`"; const TAB_1: &str = "You then can add views and configure your panel."; const TAB_2: &str = "Ofcourse you can also use the provided TabView without the panel, simply create it with: `cursive_tabs::TabView::new()`"; const TAB_3: &str = "All you have to do is add: cursive-tabs = \"^0\" to your Cargo.toml! "; fn main() { let mut siv = cursive::default(); let panel = TabPanel::new() .with_tab(TextView::new(TAB_0).with_name("0")) .with_tab(TextView::new(TAB_1).with_name("1")) .with_tab(TextView::new(TAB_2).with_name("2")) .with_tab(TextView::new(TAB_3).with_name("3")) .with_tab(PaddedView::lrtb(2, 2, 1, 1, TextArea::new()).with_name("4")) .with_bar_alignment(Align::End) .with_bar_placement(Placement::HorizontalBottom) .with_active_tab("0") .unwrap_or_else(|_| { panic!("Could not set the first tab as active tab! This is probably an issue with the implementation in the lib. Please report!"); }); siv.add_layer( LinearLayout::vertical() .child(panel.with_name("Tabs").fixed_size((50, 10))) .child( LinearLayout::horizontal() .child(Button::new("Prev", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.prev(); })) .child(Button::new("Next", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.next(); })) .child(Button::new("Switch", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.swap_tabs("1", "2"); })), ), ); siv.add_global_callback('q', |siv| siv.quit()); siv.run(); } cursive-tabs-0.8.0/examples/fullscreen.rs000064400000000000000000000040461046102023000165720ustar 00000000000000use cursive::view::{Nameable, Resizable}; use cursive::views::{Button, LinearLayout, PaddedView, TextArea, TextView}; use cursive_tabs::{Align, TabPanel}; const TAB_0: &str = "With using the TabPanel you get a TabView and TabBar, preconfigured for you to use! Simply create it with: `cursive_tabs::TabPanel::new()`"; const TAB_1: &str = "You then can add views and configure your panel."; const TAB_2: &str = "Ofcourse you can also use the provided TabView without the panel, simply create it with: `cursive_tabs::TabView::new()`"; const TAB_3: &str = "All you have to do is add: cursive-tabs = \"^0\" to your Cargo.toml! "; fn main() { let mut siv = cursive::default(); let panel = TabPanel::new() .with_tab(TextView::new(TAB_0).with_name("0")) .with_tab(TextView::new(TAB_1).with_name("1")) .with_tab(TextView::new(TAB_2).with_name("2")) .with_tab(TextView::new(TAB_3).with_name("3")) .with_tab(PaddedView::lrtb(2, 2, 1, 1, TextArea::new()).with_name("4")) .with_bar_alignment(Align::End) .with_active_tab("0") .unwrap_or_else(|_| { panic!("Could not set the first tab as active tab! This is probably an issue with the implementation in the lib. Please report!"); }); siv.add_fullscreen_layer( LinearLayout::vertical() .child(panel.with_name("Tabs").full_screen()) .child( LinearLayout::horizontal() .child(Button::new("Prev", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.prev(); })) .child(Button::new("Next", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.next(); })), ), ); siv.add_global_callback('q', |siv| siv.quit()); siv.run(); } cursive-tabs-0.8.0/examples/simple.rs000064400000000000000000000046751046102023000157310ustar 00000000000000use cursive::view::{Nameable, Resizable}; use cursive::views::{Button, LinearLayout, NamedView, PaddedView, TextArea, TextView}; use cursive_tabs::{Align, TabPanel}; const TAB_0: &str = "With using the TabPanel you get a TabView and TabBar, preconfigured for you to use! Simply create it with: `cursive_tabs::TabPanel::new()`"; const TAB_1: &str = "You then can add views and configure your panel."; const TAB_2: &str = "Ofcourse you can also use the provided TabView without the panel, simply create it with: `cursive_tabs::TabView::new()`"; const TAB_3: &str = "All you have to do is add: cursive-tabs = \"^0\" to your Cargo.toml! "; fn main() { let mut siv = cursive::default(); let mut panel = TabPanel::new() .with_tab(TextView::new(TAB_0).with_name("0")) .with_tab(TextView::new(TAB_1).with_name("1")) .with_tab(TextView::new(TAB_2).with_name("2")) .with_tab(TextView::new(TAB_3).with_name("3")) .with_tab(PaddedView::lrtb(2, 2, 1, 1, TextArea::new()).with_name("4")) .with_bar_alignment(Align::End); let view = panel .active_view_mut() .unwrap() .downcast_mut::>>() .unwrap(); view.get_mut() .get_inner_mut() .set_content("This is additional text, set after the creation of the view!"); panel.set_active_tab("0").expect("View not found"); siv.add_layer( LinearLayout::vertical() .child(panel.with_name("Tabs").fixed_size((50, 10))) .child( LinearLayout::horizontal() .child(Button::new("Prev", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.prev(); })) .child(Button::new("Next", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.next(); })) .child(Button::new("Switch", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.swap_tabs("1", "2"); })), ), ); siv.add_global_callback('q', |siv| siv.quit()); siv.run(); } cursive-tabs-0.8.0/examples/vertical.rs000064400000000000000000000045711046102023000162440ustar 00000000000000use cursive::view::{Nameable, Resizable}; use cursive::views::{Button, LinearLayout, PaddedView, TextArea, TextView}; use cursive_tabs::{Align, Placement, TabPanel}; const TAB_0: &str = "With using the TabPanel you get a TabView and TabBar, preconfigured for you to use! Simply create it with: `cursive_tabs::TabPanel::new()`"; const TAB_1: &str = "You then can add views and configure your panel."; const TAB_2: &str = "Ofcourse you can also use the provided TabView without the panel, simply create it with: `cursive_tabs::TabView::new()`"; const TAB_3: &str = "All you have to do is add: cursive-tabs = \"^0\" to your Cargo.toml! "; fn main() { let mut siv = cursive::default(); let panel = TabPanel::new() .with_tab(TextView::new(TAB_0).with_name("0")) .with_tab(TextView::new(TAB_1).with_name("1")) .with_tab(TextView::new(TAB_2).with_name("2")) .with_tab(TextView::new(TAB_3).with_name("3")) .with_tab(PaddedView::lrtb(2, 2, 1, 1, TextArea::new()).with_name("4")) .with_bar_alignment(Align::End) .with_bar_placement(Placement::VerticalLeft) .with_active_tab("0") .unwrap_or_else(|_| { panic!("Could not set the first tab as active tab! This is probably an issue with the implementation in the lib. Please report!"); }); siv.add_layer( LinearLayout::vertical() .child(panel.with_name("Tabs").fixed_size((50, 30))) .child( LinearLayout::horizontal() .child(Button::new("Prev", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.prev(); })) .child(Button::new("Next", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.next(); })) .child(Button::new("Switch", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.swap_tabs("1", "2"); })), ), ); siv.add_global_callback('q', |siv| siv.quit()); siv.run(); } cursive-tabs-0.8.0/examples/vertical_right.rs000064400000000000000000000045561046102023000174440ustar 00000000000000use cursive::view::{Nameable, Resizable}; use cursive::views::{Button, LinearLayout, PaddedView, TextArea, TextView}; use cursive_tabs::{Align, Placement, TabPanel}; const TAB_0: &str = "With using the TabPanel you get a TabView and TabBar, preconfigured for you to use! Simply create it with: `cursive_tabs::TabPanel::new()`"; const TAB_1: &str = "You then can add views and configure your panel."; const TAB_2: &str = "Ofcourse you can also use the provided TabView without the panel, simply create it with: `cursive_tabs::TabView::new()`"; const TAB_3: &str = "All you have to do is add: cursive-tabs = \"^0\" to your Cargo.toml! "; fn main() { let mut siv = cursive::default(); let panel = TabPanel::new() .with_tab(TextView::new(TAB_0).with_name("0")) .with_tab(TextView::new(TAB_1).with_name("1")) .with_tab(TextView::new(TAB_2).with_name("2")) .with_tab(TextView::new(TAB_3).with_name("3")) .with_tab(PaddedView::lrtb(2, 2, 1, 1, TextArea::new()).with_name("4")) .with_bar_alignment(Align::End) .with_bar_placement(Placement::VerticalRight) .with_active_tab("0") .unwrap_or_else(|_| { panic!("Could not set the first tab as active tab! This is probably an issue with the implementation in the lib. Please report!"); }); siv.add_layer( LinearLayout::vertical() .child(panel.with_name("Tabs").fixed_size((50, 30))) .child( LinearLayout::horizontal() .child(Button::new("Prev", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.prev(); })) .child(Button::new("Next", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.next(); })) .child(Button::new("Switch", |siv| { let mut tabs: cursive::views::ViewRef = siv.find_name("Tabs").expect("id not found"); tabs.swap_tabs("1", "2"); })), ), ); siv.add_global_callback('q', |siv| siv.quit()); siv.run(); } cursive-tabs-0.8.0/scripts/ci-build.sh000075500000000000000000000021731046102023000157610ustar 00000000000000#! /bin/sh die() { printf "\e[31:1mError: %s\e[0m\n" "$1" >&2 exit 1 } if [ -z "$RUST_CHAIN" ] then die "RUST_CHAIN environment variable is not set! RUST_CHAIN={stable,nightly}" fi ( cd "$(git rev-parse --show-toplevel)" || die "cannot find project root" # Badges! mkdir -p ./target/shields if cargo "+${RUST_CHAIN}" --color=always build --all-targets; then cat < "./target/shields/$RUST_CHAIN-build.json" { "color": "brightgreen", "isError": true, "label": "$RUST_CHAIN build", "message": "passing", "schemaVersion": 1 } EOF else PRV_EXIT=$? cat < "./target/shields/$RUST_CHAIN-build.json" { "color": "red", "isError": true, "label": "$RUST_CHAIN build", "message": "failed", "schemaVersion": 1 } EOF exit $PRV_EXIT fi cargo "+${RUST_CHAIN}" --color=always test --no-fail-fast exitcode=$? # create badge for `cargo test` cargo "+${RUST_CHAIN}" test --no-fail-fast -- -Z unstable-options --format json | \ jq -s -f ./scripts/shields-from-tests.jq > ./target/shields/cargo-test.json exit $exitcode ) cursive-tabs-0.8.0/scripts/deploy-badges.sh000075500000000000000000000026661046102023000170170ustar 00000000000000#! /bin/sh die() { printf "\e[31:1mError: %s\e[0m\n" "$1" >&2 exit 1 } if [ -z "$GITHUB_ACTOR" ] then die "the GITHUB_ACTOR environment variable is not set" fi if [ -z "$GITHUB_TOKEN" ] then die "the GITHUB_TOKEN environment variable is not set" fi if [ -z "$GITHUB_REPOSITORY" ] then die "the GITHUB_REPOSITORY environment variable is not set" fi ( cd "$(git rev-parse --show-toplevel)/target/shields" || die "cannot find project root!" repo="https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" tmp_dir=$(mktemp -d -t cursive-multiplex-deploy-XXXXXXXX) git config --global user.email "runner@ci" git config --global user.name "Github CI Runner" try=0 while :; do if ! git clone --branch gh-pages "$repo" "$tmp_dir" then ( cd "$tmp_dir" || die "failed to enter temporary directory" git init git remote add origin "$repo" git checkout -b gh-pages ) fi cp -ar ./* "$tmp_dir" ( cd "$tmp_dir" || die "failed to enter temporary directory" git add -A git commit -m "Github CI badge deployment" git push origin gh-pages:gh-pages ) result=$? if [ "$result" -eq 0 ] || [ "$try" -ge 5 ] then break fi try=$((try + 1)) done rm -rf "$tmp_dir" ) cursive-tabs-0.8.0/scripts/shields-from-tests.jq000064400000000000000000000014411046102023000200170ustar 00000000000000{ # filter for "passed" and "test_count" in input objects with `"type": "suite"` # and accumulate stats from all tests "passed": map(select(.type == "suite" and has("passed")) | .passed) | add, "total": map(select(.type == "suite" and has("test_count")) | .test_count) | add } | . + { # calculate ratio of passed tests "factor": (.passed / .total) } | { # calculate color from test factor "color": ( if .factor < 0.33 then "red" elif .factor < 0.66 then "orange" elif .factor < 1.0 then "yellow" else "brightgreen" end ), "isError": true, "label": "cargo test", # interpolate the shield label "message": "\(.passed) / \(.total) tests", "schemaVersion": 1 } cursive-tabs-0.8.0/src/bar.rs000064400000000000000000000476051046102023000141550ustar 00000000000000use crossbeam::channel::{Receiver, Sender}; use cursive::event::{Event, EventResult, Key, MouseButton, MouseEvent}; use cursive::theme::{Effect, PaletteColor}; use cursive::view::{View, ViewWrapper}; use cursive::views::Button; use cursive::{wrap_impl, Printer, Vec2}; use log::debug; use crate::panel::{Align, Placement}; /// Trait which defines which basic action a tab bar should be able to handle pub trait Bar { fn add_button(&mut self, tx: Sender, key: &str); fn remove_button(&mut self, key: &str); fn swap_button(&mut self, left: &str, right: &str); fn add_button_at(&mut self, tx: Sender, key: &str, pos: usize); } // Quick Wrapper around Views to be able to set their positon struct PositionWrap { view: T, pub pos: Vec2, pub key: String, } impl ViewWrapper for PositionWrap { wrap_impl!(self.view: T); } impl PositionWrap { pub fn new(view: T, key: String) -> Self { Self { view, pos: Vec2::zero(), key, } } } pub struct TabBar { children: Vec>, bar_size: Vec2, align: Align, last_rendered_size: Vec2, // List of accumulated sizes of prev buttons sizes: Vec, placement: Placement, cursor: Option, active: Option, rx: Receiver, invalidated: bool, } impl TabBar { pub fn new(rx: Receiver) -> Self { Self { children: Vec::new(), sizes: Vec::new(), cursor: None, active: None, align: Align::Start, placement: Placement::HorizontalTop, bar_size: Vec2::zero(), last_rendered_size: Vec2::zero(), rx, invalidated: true, } } pub fn with_alignment(mut self, align: Align) -> Self { self.align = align; self.invalidated = true; self } pub fn set_alignment(&mut self, align: Align) { self.align = align; self.invalidated = true; } pub fn with_placement(mut self, placement: Placement) -> Self { self.placement = placement; self.invalidated = true; self } pub fn set_placement(&mut self, placement: Placement) { self.placement = placement; self.invalidated = true; } fn decrement_idx(&mut self) -> EventResult { if let Some(index) = self.cursor { if index > 0 { self.cursor = Some(index - 1); self.invalidated = true; EventResult::Consumed(None) } else { EventResult::Ignored } } else { EventResult::Ignored } } fn increment_idx(&mut self) -> EventResult { if let Some(index) = self.cursor { if (index + 1) < self.children.len() { self.cursor = Some(index + 1); self.invalidated = true; EventResult::Consumed(None) } else { EventResult::Ignored } } else { EventResult::Ignored } } } impl Bar for TabBar { fn add_button(&mut self, tx: Sender, key: &str) { let k = key.to_owned(); let button = Button::new_raw(format!(" {} ", key), move |_| { debug!("send {}", k); match tx.send(k.clone()) { Ok(_) => {} Err(err) => { debug!("button could not send key: {:?}", err); } } }); self.children .push(PositionWrap::new(button, key.to_owned())); self.cursor = Some(self.children.len() - 1); self.active = Some(self.children.len() - 1); self.invalidated = true; } fn remove_button(&mut self, key: &str) { if let Some(pos) = self .children .iter() .enumerate() .filter_map( |(pos, button)| { if button.key == *key { Some(pos) } else { None } }, ) .next() { if let Some(idx) = self.cursor { if idx == pos { self.cursor = None; self.active = None; } } self.children.remove(pos); } self.invalidated = true; } fn swap_button(&mut self, first: &str, second: &str) { let pos: Vec = self .children .iter() .enumerate() .filter_map(|(pos, button)| { if button.key == *first || button.key == *second { Some(pos) } else { None } }) .collect(); if let [pos1, pos2] = pos[..] { let child2 = self.children.remove(pos2); let child1 = self.children.remove(pos1); self.children.insert(pos1, child2); self.children.insert(pos2, child1); } self.invalidated = true; } fn add_button_at(&mut self, tx: Sender, key: &str, pos: usize) { let k = key.to_owned(); let button = Button::new_raw(format!(" {} ", key), move |_| { debug!("send {}", k); match tx.send(k.clone()) { Ok(_) => {} Err(err) => { debug!("button could not send key: {:?}", err); } } }); self.cursor = Some(pos); self.active = Some(pos); self.children .insert(pos, PositionWrap::new(button, key.to_owned())); self.invalidated = true; } } impl View for TabBar { fn draw(&self, printer: &Printer) { match self.placement { Placement::HorizontalBottom | Placement::HorizontalTop => { // First draw the complete horizontal line printer.print_hline((0, 0), printer.size.x, "─"); // Spacing for padding & crop end let inner_printer = printer // Alignment .offset(( self.align .get_offset(self.bar_size.x + self.children.len() + 1, printer.size.x), 0, )); for (idx, child) in self.children.iter().enumerate() { // There is no chainable api... let mut rel_sizes = self.sizes.clone(); rel_sizes.truncate(idx); let mut print = inner_printer .offset( rel_sizes .iter() .fold(Vec2::new(0, 0), |acc, x| acc.stack_horizontal(x)) .keep_x(), ) // Spacing for first character .offset((idx, 0)) .cropped({ if idx == 0 || idx == self.children.len() - 1 { self.sizes[idx].stack_horizontal(&Vec2::new(2, 1)) } else { self.sizes[idx].stack_horizontal(&Vec2::new(1, 1)) } }); let mut theme = printer.theme.clone(); if !self.active.map_or(false, |active| idx == active) { let color = theme.palette[PaletteColor::TitleSecondary]; theme.palette[PaletteColor::Primary] = color; } else { let color = theme.palette[PaletteColor::TitlePrimary]; theme.palette[PaletteColor::Primary] = color; } if let Some(focus) = self.cursor { print = print.focused(focus == idx); } print.with_theme(&theme, |printer| { if idx > 0 { if self.active.map_or(false, |active| idx == active) || self.active.map_or(false, |active| active == (idx - 1)) { printer.print((0, 0), "┃") } else { printer.print((0, 0), "│"); } } else if self.active.map_or(false, |active| idx == active) { printer.print((0, 0), "┨") } else { printer.print((0, 0), "┤"); } printer.with_effect(Effect::Bold, |printer| { child.draw(&printer.offset((1, 0))) }); if idx == self.children.len() - 1 { if self.active.map_or(false, |active| idx == active) { printer.offset((1, 0)).print(self.sizes[idx].keep_x(), "┠"); } else { printer.offset((1, 0)).print(self.sizes[idx].keep_x(), "├"); } } }); } } Placement::VerticalLeft | Placement::VerticalRight => { // First draw the complete vertical line let horizontal_offset = match self.placement { Placement::VerticalLeft => printer.size.x - 1, _ => 0, }; printer.print_vline((horizontal_offset, 0), printer.size.y, "│"); // Spacing for padding & crop end let inner_printer = printer // Alignment .offset(( 0, self.align .get_offset(self.bar_size.y + self.children.len() + 1, printer.size.y), )); for (idx, child) in self.children.iter().enumerate() { // There is no chainable api... let mut rel_sizes = self.sizes.clone(); rel_sizes.truncate(idx); let mut print = inner_printer // Move the printer to the position of the child, respecting the height of all previous ones .offset( rel_sizes .iter() .fold(Vec2::new(0, 0), |acc, x| acc.stack_vertical(x)) .keep_y(), ) // Spacing for first character of the current one and all previous ones .offset((0, idx)) // Offset so that the right side when aligned to the left is on the panel border .offset(( if self.placement == Placement::VerticalLeft { self.bar_size.x - self.sizes[idx].x } else { 0 }, 0, )) // Crop to size including the delimiters .cropped({ if idx == 0 || idx == self.children.len() - 1 { self.sizes[idx].stack_vertical(&Vec2::new(1, 2)) } else { self.sizes[idx].stack_vertical(&Vec2::new(1, 1)) } }); let mut theme = printer.theme.clone(); if !self.active.map_or(false, |active| idx == active) { let color = theme.palette[PaletteColor::TitleSecondary]; theme.palette[PaletteColor::Primary] = color; } else { let color = theme.palette[PaletteColor::TitlePrimary]; theme.palette[PaletteColor::Primary] = color; } if let Some(focus) = self.cursor { print = print.focused(focus == idx); } print.with_theme(&theme, |printer| { if idx > 0 { if self.active.map_or(false, |active| idx == active) || self.active.map_or(false, |active| active == (idx - 1)) { printer.print_hline((0, 0), printer.size.x, "━"); } else { printer.print_hline((0, 0), printer.size.x, "─"); } } else if self.active.map_or(false, |active| idx == active) { printer.print_hline((0, 0), printer.size.x, "━"); printer.print((horizontal_offset, 0), "┷") } else { printer.print_hline((0, 0), printer.size.x, "─"); printer.print((horizontal_offset, 0), "┴"); } printer.with_effect(Effect::Bold, |printer| { child.draw(&printer.offset((0, 1))) }); if idx == self.children.len() - 1 { let (delim, connector) = if self.active.map_or(false, |active| idx == active) { ("━", "┯") } else { ("─", "┬") }; printer.print_hline((0, printer.size.y - 1), printer.size.x, delim); printer.print( self.sizes[idx].keep_y() + Vec2::new(horizontal_offset, 1), connector, ); } }); } } } } fn layout(&mut self, vec: Vec2) { self.invalidated = false; for (child, size) in self.children.iter_mut().zip(self.sizes.iter()) { child.layout(*size); } self.last_rendered_size = vec; } fn needs_relayout(&self) -> bool { self.invalidated } fn required_size(&mut self, cst: Vec2) -> Vec2 { while self.rx.len() > 1 { // Discard old messages // This may happen if more than one view gets added to before the event loop of cursive gets started, resulting // in an incorrect start state match self.rx.try_recv() { Ok(_) => debug!("Got too many requests dropping some..."), Err(e) => debug!("Other side got dropped {:?}, ignoring this error", e), } } if let Ok(new_active) = self.rx.try_recv() { self.invalidated = true; for (idx, child) in self.children.iter().enumerate() { if new_active == child.key { self.active = Some(idx); } } } self.sizes.clear(); let sizes = &mut self.sizes; let placement = self.placement; if self.children.is_empty() { return Vec2::new(1, 1); } let total_size = self .children .iter_mut() .fold(Vec2::zero(), |mut acc, child| { let size = child.required_size(cst); match placement { Placement::HorizontalBottom | Placement::HorizontalTop => { acc = acc.stack_horizontal(&size); } Placement::VerticalLeft | Placement::VerticalRight => { acc = acc.stack_vertical(&size); } } child.pos = acc; sizes.push(size); acc }); // Total size of bar self.bar_size = total_size; // Return max width and maximum height of child // We need the max size of every side here so try again match self.placement { Placement::HorizontalTop | Placement::HorizontalBottom => { (total_size.x * 2, total_size.y).into() } Placement::VerticalLeft | Placement::VerticalRight => { (total_size.x, total_size.y * 2).into() } } } fn on_event(&mut self, evt: Event) -> EventResult { if let Event::Mouse { offset, position, event, } = evt { for (idx, child) in self.children.iter().peekable().enumerate() { if position.checked_sub(offset).is_some() && (match self.placement { Placement::HorizontalBottom | Placement::HorizontalTop => { child.pos + Vec2::new(idx + 1, 0) + Vec2::new( self.align.get_offset( // Length of buttons and delimiting characters self.bar_size.x + self.children.len() + 1, self.last_rendered_size.x, ), 0, ) } Placement::VerticalLeft | Placement::VerticalRight => { child.pos + Vec2::new(0, idx + 1) + Vec2::new( 0, self.align.get_offset( // Length of buttons and delimiting characters self.bar_size.y + self.children.len() + 1, self.last_rendered_size.y, ), ) } }) .fits(position - offset) { if let MouseEvent::Release(MouseButton::Left) = event { self.invalidated = true; self.cursor = Some(idx); return self.children[idx].on_event(Event::Key(Key::Enter)); } } } } if let Some(focus) = self.cursor { let pos = self.children[focus].pos; if let EventResult::Consumed(any) = self.children[focus].on_event(evt.relativized(pos)) { self.invalidated = true; return EventResult::Consumed(any); } } match evt { Event::Key(Key::Left) if self.placement == Placement::HorizontalBottom || self.placement == Placement::HorizontalTop => { self.decrement_idx() } Event::Key(Key::Up) if self.placement == Placement::VerticalLeft || self.placement == Placement::VerticalRight => { self.decrement_idx() } Event::Key(Key::Right) if self.placement == Placement::HorizontalBottom || self.placement == Placement::HorizontalTop => { self.increment_idx() } Event::Key(Key::Down) if self.placement == Placement::VerticalLeft || self.placement == Placement::VerticalRight => { self.increment_idx() } _ => EventResult::Ignored, } } } cursive-tabs-0.8.0/src/error.rs000064400000000000000000000004501046102023000145250ustar 00000000000000#[derive(Debug, Clone, Eq, PartialEq)] pub struct IdNotFound { pub id: String, } impl std::error::Error for IdNotFound {} impl std::fmt::Display for IdNotFound { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Id not found: {}", self.id) } } cursive-tabs-0.8.0/src/lib.rs000064400000000000000000000361331046102023000141510ustar 00000000000000//! This crate provides a tabbing view for //! [gyscos/cursive](https://github.com/gyscos/cursive) views. It is build to //! be as simple as possible. //! //! The behaviour is oriented to be similar to [`StackView`](https://docs.rs/cursive/0.13.0/cursive/views/struct.StackView.html) of cursive, but with the advantage of selectively displaying //! views without needing to delete foremost one. //! //! # Example //! All you need to do to create a new `TabView` is: //! ``` //! # use cursive::{view::Nameable, views::{TextView, Dialog}}; //! # use cursive_tabs::TabView; //! # let mut siv = cursive::default(); //! let mut tabs = TabView::new(); //! # // That is all what is needed to display an empty TabView, but of course //! # // you can add your own tabs now and switch them around as you want! //! # tabs.add_tab(TextView::new("Our first view!").with_name("First")); //! # siv.add_layer(Dialog::around(tabs)); //! # // When your done setting run cursive //! # // siv.run(); //! ``` //! You can then use the provided methods to modify the content of the `TabView` //! Consuming and non-consuming are both provided. //! //! # Full Example //! ``` //! use cursive::{view::Nameable, views::{TextView, Dialog}}; //! use cursive_tabs::TabView; //! //! let mut siv = cursive::default(); //! let mut tabs = TabView::new(); //! // That is all what is needed to display an empty TabView, but of course //! // you can add your own tabs now and switch them around as you want! //! tabs.add_tab(TextView::new("Our first view!").with_name("First")); //! siv.add_layer(Dialog::around(tabs)); //! // When your done setting run cursive //! // siv.run(); //! ``` extern crate cursive_core as cursive; use crossbeam::channel::{Receiver, Sender}; use cursive::direction::Direction; use cursive::event::{AnyCb, Event, EventResult}; use cursive::view::{CannotFocus, Selector, View, ViewNotFound}; use cursive::views::NamedView; use cursive::{Printer, Rect, Vec2}; use log::debug; use std::collections::HashMap; mod bar; mod error; mod panel; // Reexports use bar::{Bar, TabBar}; pub use panel::{Align, Placement, TabPanel}; /// Main struct which manages views pub struct TabView { current_id: Option, // Version 0.6 changes this to only contain NamedViews, in the map this remains the same type though // as NamedViews cannot be sized properly due to their enclosed view trait object map: HashMap>, key_order: Vec, bar_rx: Option>, active_key_tx: Option>, invalidated: bool, } impl Default for TabView { fn default() -> Self { Self::new() } } impl TabView { /// Returns a new TabView /// # Example /// ``` /// # use cursive::{view::Nameable, views::{TextView, Dialog}}; /// # use cursive_tabs::TabView; /// # let mut siv = cursive::default(); /// let mut tabs = TabView::new(); /// # // That is all what is needed to display an empty TabView, but of course /// # // you can add your own tabs now and switch them around as you want! /// # tabs.add_tab(TextView::new("Our first view!").with_name("First")); /// # siv.add_layer(Dialog::around(tabs)); /// # // When your done setting run cursive /// # // siv.run(); /// ``` pub fn new() -> Self { Self { current_id: None, map: HashMap::new(), key_order: Vec::new(), bar_rx: None, active_key_tx: None, invalidated: true, } } /// Returns the currently active tab Id. pub fn active_tab(&self) -> Option<&str> { self.current_id.as_deref() } /// Returns a reference to the underlying view. pub fn active_view(&self) -> Option<&dyn View> { self.active_tab() .and_then(|k| self.map.get(k).map(|v| &**v)) } /// Returns a mutable reference to the underlying view. pub fn active_view_mut(&mut self) -> Option<&mut dyn View> { if let Some(k) = self.current_id.as_ref() { self.map.get_mut(k).map(|v| &mut **v) } else { None } } pub fn views(&self) -> Vec<&dyn View> { self.map.values().map(|v| &**v).collect() } // Mutable references to all mutable views. pub fn views_mut(&mut self) -> Vec<&mut dyn View> { self.map.values_mut().map(|v| &mut **v).collect() } /// Set the currently active (visible) tab. /// If the tab id is not known, an error is returned and no action is performed. pub fn set_active_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> { if self.map.contains_key(id) { if let Some(sender) = &self.active_key_tx { match sender.send(id.to_owned()) { Ok(_) => {} Err(e) => debug!( "error occured while trying to send new active key to sender: {}", e ), } } self.current_id = Some(id.to_owned()); self.invalidated = true; Ok(()) } else { Err(error::IdNotFound { id: id.to_owned() }) } } /// Set the currently active (visible) tab. /// If the tab id is not known, an error is returned and no action is performed. /// /// This is the consumable variant. pub fn with_active_tab(mut self, id: &str) -> Result { match self.set_active_tab(id) { Ok(_) => Ok(self), Err(_) => Err(self), } } /// Add a new tab to the tab view. /// The new tab will be set active and will be the visible tab for this tab view. pub fn add_tab(&mut self, view: NamedView) { let id = view.name().to_owned(); self.map.insert(id.clone(), Box::new(view)); self.key_order.push(id.clone()); self.current_id = Some(id); } /// Add a new tab to the tab view. /// The new tab will be set active and will be the visible tab for this tab view. /// /// This is the consumable variant. pub fn with_tab(mut self, view: NamedView) -> Self { self.add_tab(view); self } /// Add a new tab at a given position. /// The new tab will be set active and will be the visible tab for this tab view. /// /// This is designed to not fail, if the given position is greater than the number of current tabs, it simply will be appended. pub fn add_tab_at(&mut self, view: NamedView, pos: usize) { let id = view.name().to_owned(); self.map.insert(id.clone(), Box::new(view)); if let Some(sender) = &self.active_key_tx { match sender.send(id.clone()) { Ok(_) => {} Err(send_err) => debug!( "Could not send new key to receiver in TabBar, has it been dropped? {}", send_err ), } } self.current_id = Some(id.clone()); if self.key_order.len() > pos { self.key_order.insert(pos, id) } else { self.key_order.push(id); } } /// Add a new tab at a given position. /// The new tab will be set active and will be the visible tab for this tab view. /// /// It is designed to be fail-safe, if the given position is greater than the number of current tabs, it simply will be appended. /// /// This is the consumable variant. pub fn with_tab_at(mut self, view: NamedView, pos: usize) -> Self { self.add_tab_at(view, pos); self } /// Swap the tabs position. /// If one of the given key cannot be found, then no operation is performed. pub fn swap_tabs(&mut self, fst: &str, snd: &str) { let mut fst_pos: Option = None; let mut snd_pos: Option = None; for (pos, key) in self.tab_order().into_iter().enumerate() { match key { val if val == *fst => fst_pos = Some(pos), val if val == *snd => snd_pos = Some(pos), _ => {} } } if let (Some(fst_pos), Some(snd_pos)) = (fst_pos, snd_pos) { if let Some(cur) = self.current_id.as_ref() { if self.active_key_tx.is_some() && (fst == cur || snd == cur) { self.active_key_tx .as_mut() .unwrap() .send(cur.to_owned()) .expect("Sending failed."); } } self.key_order.swap(fst_pos, snd_pos); } } /// Removes a tab with the given id from the `TabView`. /// If the removed tab is active at the moment, the `TabView` will unfocus it and /// the focus needs to be set manually afterwards, or a new view has to be inserted. pub fn remove_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> { if self.map.remove(id).is_some() { if let Some(key) = &self.current_id { if key == id { // Current id no longer valid self.current_id = None; } } // remove_key experimental self.key_order.retain(|k| k != id); self.invalidated = true; Ok(()) } else { Err(error::IdNotFound { id: id.to_owned() }) } } /// Returns the current order of keys in a vector. /// When you're implementing your own tab bar, be aware that this is the current /// tab bar and is only a copy of the original order, modification will not be /// transferred and future updates in the original not displayed. pub fn tab_order(&self) -> Vec { self.key_order.clone() } // Returns the index of the key, length of the vector if the key is not included // This can be done with out sorting fn index_key(cur_key: &str, key_order: &[String]) -> usize { for (idx, key) in key_order.iter().enumerate() { if *key == *cur_key { return idx; } } key_order.len() } /// Set the active tab to the next tab in order. pub fn next(&mut self) { if let Some(cur_key) = &self.current_id { let idx = (Self::index_key(&cur_key, &self.key_order) + 1) % self.key_order.len(); let key = &self.key_order[idx].clone(); self.set_active_tab(key) .expect("Key content changed during operation, this should not happen"); } } /// Set the active tab to the previous tab in order. pub fn prev(&mut self) { if let Some(cur_key) = self.current_id.as_ref().cloned() { let idx_key = Self::index_key(&cur_key, &self.key_order); let idx = (self.key_order.len() + idx_key - 1) % self.key_order.len(); let key = &self.key_order[idx].clone(); self.set_active_tab(key) .expect("Key content changed during operation, this should not happen"); } } /// Set the receiver for keys to be changed to pub fn set_bar_rx(&mut self, rx: Receiver) { self.bar_rx = Some(rx); } /// Set the sender for the key switched to pub fn set_active_key_tx(&mut self, tx: Sender) { self.active_key_tx = Some(tx); } } impl View for TabView { fn draw(&self, printer: &Printer) { if let Some(key) = &self.current_id { if let Some(view) = self.map.get(key) { view.draw(printer); } } } fn layout(&mut self, size: Vec2) { self.invalidated = false; if let Some(key) = &self.current_id { if let Some(view) = self.map.get_mut(key) { view.layout(size); } } } fn required_size(&mut self, req: Vec2) -> Vec2 { if let Some(rx) = &self.bar_rx { if let Ok(evt) = rx.try_recv() { match self.set_active_tab(&evt) { Ok(_) => {} Err(err) => debug!("could not accept tab bar event: {:?}", err), } } } if let Some(key) = &self.current_id { if let Some(view) = self.map.get_mut(key) { view.required_size(req) } else { (1, 1).into() } } else { (1, 1).into() } } fn on_event(&mut self, evt: Event) -> EventResult { if let Some(key) = &self.current_id { if let Some(view) = self.map.get_mut(key) { view.on_event(evt) } else { EventResult::Ignored } } else { EventResult::Ignored } } fn take_focus(&mut self, src: Direction) -> Result { if let Some(key) = &self.current_id { if let Some(view) = self.map.get_mut(key) { view.take_focus(src) } else { Err(CannotFocus) } } else { Err(CannotFocus) } } fn call_on_any<'a>(&mut self, slt: &Selector, cb: AnyCb<'a>) { for (_, view) in self.map.iter_mut() { view.call_on_any(slt, cb); } } fn focus_view(&mut self, slt: &Selector) -> Result { if let Some(key) = &self.current_id { if let Some(view) = self.map.get_mut(key) { view.focus_view(slt) } else { Err(ViewNotFound) } } else { Err(ViewNotFound) } } fn needs_relayout(&self) -> bool { self.invalidated || { if let Some(key) = &self.current_id { if let Some(view) = self.map.get(key) { view.needs_relayout() } else { false } } else { false } } } fn important_area(&self, size: Vec2) -> Rect { if let Some(key) = &self.current_id { if let Some(view) = self.map.get(key) { view.important_area(size) } else { Rect::from_point((1, 1)) } } else { Rect::from_point((1, 1)) } } } #[cfg(test)] mod test { use super::TabView; use cursive::{traits::Nameable, views::DummyView}; #[test] fn smoke() { let _ = TabView::new(); } #[test] fn insert() { let mut tabs = TabView::new().with_tab(DummyView {}.with_name("0")); tabs.add_tab(DummyView {}.with_name("1")); } #[test] fn switch() { let mut tabs = TabView::new(); tabs.add_tab(DummyView {}.with_name("0")); tabs.add_tab(DummyView {}.with_name("1")); assert_eq!(tabs.active_tab().expect("Id not correct"), "1"); tabs.set_active_tab("0").expect("Id not taken"); assert_eq!(tabs.active_tab().expect("Id not correct"), "0"); } #[test] fn remove() { let mut tabs = TabView::new(); tabs.add_tab(DummyView {}.with_name("0")); tabs.add_tab(DummyView {}.with_name("1")); assert_eq!(tabs.remove_tab("1"), Ok(())); assert!(tabs.active_tab().is_none()); } } cursive-tabs-0.8.0/src/panel.rs000064400000000000000000000534651046102023000145110ustar 00000000000000use crossbeam::channel::{unbounded, Sender}; use cursive::direction::{Absolute, Direction}; use cursive::event::{AnyCb, Event, EventResult, Key}; use cursive::view::{CannotFocus, Selector, View, ViewNotFound}; use cursive::views::NamedView; use cursive::{Printer, Vec2}; use log::debug; use num::clamp; use crate::error; use crate::Bar; use crate::TabBar; use crate::TabView; #[derive(Clone, Copy, Debug)] pub enum Align { Start, Center, End, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Placement { VerticalLeft, VerticalRight, HorizontalTop, HorizontalBottom, } impl Align { pub fn get_offset(self, content: usize, container: usize) -> usize { if container < content { 0 } else { match self { Align::Start => 0, Align::Center => (container - content) / 2, Align::End => container - content, } } } } /// The `TabPanel` is an ease of use wrapper around a `TabView` and its `TabBar`. /// Additionally the TabBar in the Panel can be horizontally aligned, by default it is set to be left aligned. /// /// # Example /// ``` /// use cursive_tabs::{Align, TabPanel}; /// use cursive::views::TextView; /// use cursive::view::Nameable; /// /// let mut tabs = TabPanel::new() /// .with_tab(TextView::new("First").with_name("First")) /// .with_tab(TextView::new("Second").with_name("Second")) /// .with_bar_alignment(Align::Center); /// ``` /// /// A TabView is also usable separately, so if you prefer the tabs without the TabBar and Panel around have a look at `TabView`. pub struct TabPanel { bar: TabBar, bar_size: Vec2, tab_size: Vec2, tx: Sender, tabs: TabView, bar_focused: bool, bar_align: Align, bar_placement: Placement, } impl Default for TabPanel { fn default() -> Self { Self::new() } } impl TabPanel { /// Returns a new instance of a TabPanel. /// Alignment is set by default to left, to change this use `set_bar_alignment` to change to any other `HAlign` provided by `cursive`. pub fn new() -> Self { let mut tabs = TabView::new(); let (tx, rx) = unbounded(); let (active_tx, active_rx) = unbounded(); tabs.set_bar_rx(rx); tabs.set_active_key_tx(active_tx); Self { bar: TabBar::new(active_rx) .with_placement(Placement::HorizontalTop) .with_alignment(Align::Start), bar_size: Vec2::new(1, 1), tab_size: Vec2::new(1, 1), tabs, tx, bar_focused: true, bar_align: Align::Start, bar_placement: Placement::HorizontalTop, } } /// Returns the current active tab of the `TabView`. /// Note: Calls `active_tab` on the enclosed `TabView`. pub fn active_tab(&self) -> Option<&str> { self.tabs.active_tab() } /// Returns a reference to the underlying view. pub fn active_view(&self) -> Option<&dyn View> { self.tabs.active_view() } /// Returns a mutable reference to the underlying view. pub fn active_view_mut(&mut self) -> Option<&mut dyn View> { self.tabs.active_view_mut() } pub fn views(&self) -> Vec<&dyn View> { self.tabs.views() } pub fn views_mut(&mut self) -> Vec<&mut dyn View> { self.tabs.views_mut() } /// Non-consuming variant to set the active tab in the `TabView`. /// Note: Calls `set_active_tab` on the enclosed `TabView`. pub fn set_active_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> { self.tabs.set_active_tab(id) } /// Consuming & Chainable variant to set the active tab in the `TabView`. /// Note: Calls `set_active_tab` on the enclosed `TabView`. /// pub fn with_active_tab(mut self, id: &str) -> Result { match self.tabs.set_active_tab(id) { Ok(_) => Ok(self), Err(_) => Err(self), } } /// Non-consuming variant to add new tabs to the `TabView`. /// Note: Calls `add_tab` on the enclosed `TabView`. pub fn add_tab(&mut self, view: NamedView) { let id = view.name(); self.bar.add_button(self.tx.clone(), id); self.tabs.add_tab(view); } /// Consuming & Chainable variant to add a new tab. /// Note: Calls `add_tab` on the enclosed `TabView`. pub fn with_tab(mut self, view: NamedView) -> Self { let id = view.name(); self.bar.add_button(self.tx.clone(), id); self.tabs.add_tab(view); self } /// Swaps the given tab keys. /// If at least one of them cannot be found then no operation is performed pub fn swap_tabs(&mut self, fst: &str, snd: &str) { self.tabs.swap_tabs(fst, snd); self.bar.swap_button(fst, snd); } /// Non-consuming variant to add new tabs to the `TabView` at a certain position. /// It is fail-safe, if the postion is greater than the amount of tabs, it is appended to the end. /// Note: Calls `add_tab_at` on the enclosed `TabView`. pub fn add_tab_at(&mut self, view: NamedView, pos: usize) { let id = view.name(); self.bar.add_button_at(self.tx.clone(), id, pos); self.tabs.add_tab_at(view, pos); } /// Consuming & Chainable variant to add a new tab at a certain position. /// It is fail-safe, if the postion is greater than the amount of tabs, it is appended to the end. /// Note: Calls `add_tab_at` on the enclosed `TabView`. pub fn with_tab_at(mut self, view: NamedView, pos: usize) -> Self { let id = view.name(); self.bar.add_button_at(self.tx.clone(), id, pos); self.tabs.add_tab_at(view, pos); self } /// Remove a tab of the enclosed `TabView`. pub fn remove_tab(&mut self, id: &str) -> Result<(), error::IdNotFound> { self.bar.remove_button(id); self.tabs.remove_tab(id) } /// Proceeds to the next view in order of addition. pub fn next(&mut self) { self.tabs.next() } /// Go back to the previous view in order of addition. pub fn prev(&mut self) { self.tabs.prev() } /// Consumable & Chainable variant to set the bar alignment. pub fn with_bar_alignment(mut self, align: Align) -> Self { self.set_bar_alignment(align); self } /// Non-consuming variant to set the bar alignment. pub fn set_bar_alignment(&mut self, align: Align) { self.bar_align = align; self.bar.set_alignment(align); } pub fn with_bar_placement(mut self, placement: Placement) -> Self { self.set_bar_placement(placement); self } pub fn set_bar_placement(&mut self, placement: Placement) { self.bar_placement = placement; self.bar.set_placement(placement); } /// Returns the current order of tabs as an Vector with the keys of the views. pub fn tab_order(&self) -> Vec { self.tabs.tab_order() } // Print lines corresponding to the current placement fn draw_outer_panel(&self, printer: &Printer) { match self.bar_placement { Placement::HorizontalTop => { // Side bars printer.print_vline((0, 0), printer.size.y, "│"); printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│"); // Bottom line printer.print_hline((0, printer.size.y - 1), printer.size.x, "─"); printer.print((0, self.bar_size.y - 1), "┌"); printer.print((printer.size.x - 1, self.bar_size.y - 1), "┐"); printer.print((0, printer.size.y - 1), "└"); printer.print((printer.size.x - 1, printer.size.y - 1), "┘"); } Placement::HorizontalBottom => { // Side bars printer.print_vline((0, 0), printer.size.y, "│"); printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│"); // Top line let lowest = clamp(printer.size.y - self.bar_size.y, 0, printer.size.y - 1); printer.print_hline((0, 0), printer.size.x, "─"); printer.print((0, 0), "┌"); printer.print((printer.size.x - 1, 0), "┐"); printer.print((0, lowest), "└"); printer.print((printer.size.x - 1, lowest), "┘"); } Placement::VerticalLeft => { // Side bar printer.print_vline((printer.size.x - 1, 0), printer.size.y, "│"); // Top lines printer.print_hline((self.bar_size.x - 1, 0), printer.size.x, "─"); printer.print_hline( (self.bar_size.x - 1, printer.size.y - 1), printer.size.x, "─", ); printer.print((self.bar_size.x - 1, 0), "┌"); printer.print((printer.size.x - 1, 0), "┐"); printer.print((self.bar_size.x - 1, printer.size.y - 1), "└"); printer.print((printer.size.x - 1, printer.size.y - 1), "┘"); } Placement::VerticalRight => { // Side bar printer.print_vline((0, 0), printer.size.y, "│"); // Top lines printer.print_hline((0, 0), printer.size.x, "─"); // Line draws too far here, needs to be overwritten with blanks printer.print_hline((0, printer.size.y - 1), printer.size.x, "─"); let right = clamp(printer.size.x - self.bar_size.x, 0, printer.size.x - 1); printer.print((0, 0), "┌"); printer.print((right, 0), "┐"); printer.print_hline((right + 1, 0), printer.size.x, " "); printer.print((0, printer.size.y - 1), "└"); printer.print((right, printer.size.y - 1), "┘"); printer.print_hline((right + 1, printer.size.y - 1), printer.size.x, " "); } } } fn on_event_focused(&mut self, evt: Event) -> EventResult { match self.bar.on_event(evt.relativized(match self.bar_placement { Placement::HorizontalTop | Placement::VerticalLeft => Vec2::new(0, 0), Placement::HorizontalBottom => self.tab_size.keep_y() + Vec2::new(0, 1), Placement::VerticalRight => self.tab_size.keep_x() + Vec2::new(1, 0), })) { EventResult::Consumed(cb) => EventResult::Consumed(cb), EventResult::Ignored => match evt { Event::Key(Key::Down) if self.bar_placement == Placement::HorizontalTop => { if let Ok(result) = self.tabs.take_focus(Direction::up()) { self.bar_focused = false; result.and(EventResult::consumed()) } else { EventResult::Ignored } } Event::Key(Key::Up) if self.bar_placement == Placement::HorizontalBottom => { if let Ok(result) = self.tabs.take_focus(Direction::down()) { self.bar_focused = false; result.and(EventResult::consumed()) } else { EventResult::Ignored } } Event::Key(Key::Left) if self.bar_placement == Placement::VerticalRight => { if let Ok(result) = self.tabs.take_focus(Direction::right()) { self.bar_focused = false; result.and(EventResult::consumed()) } else { EventResult::Ignored } } Event::Key(Key::Right) if self.bar_placement == Placement::VerticalLeft => { if let Ok(result) = self.tabs.take_focus(Direction::left()) { self.bar_focused = false; result.and(EventResult::consumed()) } else { EventResult::Ignored } } _ => EventResult::Ignored, }, } } fn on_event_unfocused(&mut self, evt: Event) -> EventResult { match self .tabs .on_event(evt.relativized(match self.bar_placement { Placement::HorizontalTop => Vec2::new(1, self.bar_size.y), Placement::VerticalLeft => Vec2::new(self.bar_size.x, 1), Placement::HorizontalBottom | Placement::VerticalRight => Vec2::new(1, 1), })) { EventResult::Consumed(cb) => EventResult::Consumed(cb), EventResult::Ignored => match evt { Event::Key(Key::Up) if self.bar_placement == Placement::HorizontalTop => { self.bar_focused = true; EventResult::Consumed(None) } Event::Key(Key::Down) if self.bar_placement == Placement::HorizontalBottom => { self.bar_focused = true; EventResult::Consumed(None) } Event::Key(Key::Left) if self.bar_placement == Placement::VerticalLeft => { self.bar_focused = true; EventResult::Consumed(None) } Event::Key(Key::Right) if self.bar_placement == Placement::VerticalRight => { self.bar_focused = true; EventResult::Consumed(None) } _ => EventResult::Ignored, }, } } fn check_focus_grab(&mut self, event: &Event) -> EventResult { if let Event::Mouse { offset, position, event, } = *event { debug!( "mouse event: offset: {:?} , position: {:?}", offset, position ); if !event.grabs_focus() { return EventResult::Ignored; } match self.bar_placement { Placement::VerticalRight | Placement::HorizontalBottom => { if position > offset && self.tab_size.fits(position - offset) { if let Ok(res) = self.tabs.take_focus(Direction::none()) { self.bar_focused = false; return res; } } else { self.bar_focused = true; } } Placement::HorizontalTop | Placement::VerticalLeft => { // Here we want conceptually position >= offset, which is what Vec2::fits does. // (The actual >= means strictly > or strictly equal, which is not _quite_ what we want in 2D.) if position.fits(offset) && (self.bar_size - Vec2::new(1, 1)).fits(position - offset) { self.bar_focused = true; } else if let Ok(res) = self.tabs.take_focus(Direction::none()) { self.bar_focused = false; return res; } } } } EventResult::Ignored } } impl View for TabPanel { fn draw(&self, printer: &Printer) { self.draw_outer_panel(printer); let printer_bar = printer .offset(match self.bar_placement { Placement::HorizontalTop => (1, 0), Placement::HorizontalBottom => ( 1, clamp(printer.size.y - self.bar_size.y, 0, printer.size.y - 1), ), Placement::VerticalLeft => (0, 1), Placement::VerticalRight => ( clamp(printer.size.x - self.bar_size.x, 0, printer.size.x - 1), 1, ), }) .cropped(match self.bar_placement { Placement::HorizontalTop | Placement::HorizontalBottom => { (printer.size.x - 2, self.bar_size.y) } Placement::VerticalRight | Placement::VerticalLeft => { (self.bar_size.x, printer.size.y - 2) } }) .focused(self.bar_focused); let printer_tab = printer .offset(match self.bar_placement { Placement::VerticalLeft => (self.bar_size.x, 1), Placement::VerticalRight => (1, 1), Placement::HorizontalBottom => (1, 1), Placement::HorizontalTop => (1, self.bar_size.y), }) // Inner area .cropped(match self.bar_placement { Placement::VerticalLeft | Placement::VerticalRight => { (printer.size.x - self.bar_size.x - 1, printer.size.y - 2) } Placement::HorizontalBottom | Placement::HorizontalTop => { (printer.size.x - 2, printer.size.y - self.bar_size.y - 1) } }) .focused(!self.bar_focused); self.bar.draw(&printer_bar); self.tabs.draw(&printer_tab); } fn layout(&mut self, vec: Vec2) { self.bar.layout(match self.bar_placement { Placement::VerticalRight | Placement::VerticalLeft => { Vec2::new(self.bar_size.x, vec.y - 2) } Placement::HorizontalBottom | Placement::HorizontalTop => { Vec2::new(vec.x - 2, self.bar_size.y) } }); self.tabs.layout(match self.bar_placement { Placement::VerticalRight | Placement::VerticalLeft => { self.tab_size = Vec2::new(vec.x - self.bar_size.x - 1, vec.y - 2); self.tab_size } Placement::HorizontalBottom | Placement::HorizontalTop => { self.tab_size = Vec2::new(vec.x - 2, vec.y - self.bar_size.y - 1); self.tab_size } }); } fn needs_relayout(&self) -> bool { self.bar.needs_relayout() || self.tabs.needs_relayout() } fn required_size(&mut self, cst: Vec2) -> Vec2 { let tab_size = self.tabs.required_size(cst); self.bar_size = self.bar.required_size(cst); match self.bar_placement { Placement::HorizontalTop | Placement::HorizontalBottom => self .bar_size .stack_vertical(&tab_size) .stack_vertical(&Vec2::new(tab_size.x + 2, 1)), Placement::VerticalLeft | Placement::VerticalRight => self .bar_size .stack_horizontal(&tab_size) .stack_vertical(&Vec2::new(1, tab_size.y + 2)), } } fn on_event(&mut self, evt: Event) -> EventResult { let result = self.check_focus_grab(&evt); result.and(if self.bar_focused { self.on_event_focused(evt) } else { self.on_event_unfocused(evt) }) } fn take_focus(&mut self, d: Direction) -> Result { let tabs_take_focus = |panel: &mut TabPanel, d: Direction| { let result = panel.tabs.take_focus(d); if result.is_ok() { panel.bar_focused = false; } else { panel.bar_focused = true; } result }; let mut result = Ok(EventResult::consumed()); match self.bar_placement { Placement::HorizontalBottom => match d { Direction::Abs(Absolute::Up) => { result = tabs_take_focus(self, d); } Direction::Abs(Absolute::Left) | Direction::Abs(Absolute::Right) => { if !self.bar_focused { result = tabs_take_focus(self, d); } } Direction::Abs(Absolute::Down) => { self.bar_focused = true; } _ => (), }, Placement::HorizontalTop => match d { Direction::Abs(Absolute::Down) => { result = tabs_take_focus(self, d); } Direction::Abs(Absolute::Left) | Direction::Abs(Absolute::Right) => { if !self.bar_focused { result = tabs_take_focus(self, d); } } Direction::Abs(Absolute::Up) => { self.bar_focused = true; } _ => (), }, Placement::VerticalLeft => match d { Direction::Abs(Absolute::Right) => { result = tabs_take_focus(self, d); } Direction::Abs(Absolute::Up) | Direction::Abs(Absolute::Down) => { if !self.bar_focused { result = tabs_take_focus(self, d); } } Direction::Abs(Absolute::Left) => self.bar_focused = true, _ => {} }, Placement::VerticalRight => match d { Direction::Abs(Absolute::Left) => { result = tabs_take_focus(self, d); } Direction::Abs(Absolute::Up) | Direction::Abs(Absolute::Down) => { if !self.bar_focused { result = tabs_take_focus(self, d) } } Direction::Abs(Absolute::Right) => self.bar_focused = true, _ => {} }, } return Ok(result.unwrap_or(EventResult::Ignored)); } fn focus_view(&mut self, slt: &Selector) -> Result { self.tabs.focus_view(slt) } fn call_on_any<'a>(&mut self, slt: &Selector, cb: AnyCb<'a>) { self.bar.call_on_any(slt, cb); self.tabs.call_on_any(slt, cb); } } cursive-tabs-0.8.0/tests/end2end.rs000064400000000000000000000176071046102023000153020ustar 00000000000000use crossbeam::channel::{Receiver, Sender}; use cursive::backends::puppet::observed::ObservedScreen; use cursive::backends::puppet::Backend; use cursive::event::{Event, Key}; use cursive::view::Nameable; use cursive::views::TextView; use cursive::Vec2; use cursive_tabs::{Align, Placement, TabPanel, TabView}; use insta::assert_display_snapshot; fn setup_test_environment(cb: F) -> (Receiver, Sender>) where F: FnOnce(&mut cursive::Cursive), { let backend = Backend::init(Some(Vec2::new(80, 24))); let frames = backend.stream(); let input = backend.input(); let mut siv = cursive::Cursive::new().into_runner(backend); cb(&mut siv); input .send(Some(Event::Refresh)) .expect("Refresh not accepted, backend not valid"); siv.step(); (frames, input) } struct TestCursive { siv: cursive::CursiveRunner, frames: Receiver, input: Sender>, } impl TestCursive { fn new(cb: F) -> Self where F: FnOnce(&mut cursive::Cursive), { let backend = Backend::init(Some(Vec2::new(80, 24))); let frames = backend.stream(); let input = backend.input(); let mut siv = cursive::Cursive::new().into_runner(backend); cb(&mut siv); input .send(Some(Event::Refresh)) .expect("Refresh not accepted, backend not valid"); siv.step(); Self { siv, frames, input } } fn _call_on(&mut self, cb: F) where F: FnOnce(&mut cursive::Cursive), { cb(&mut self.siv); } fn input(&mut self, event: Event) { self.input .send(Some(event)) .expect("Refresh not accepted, backend could not react"); self.step(); } fn step(&mut self) { self.input .send(Some(Event::Refresh)) .expect("Refresh not accepted, backend could not react"); self.siv.step(); } fn last_screen(&mut self) -> ObservedScreen { self.frames.try_iter().last().unwrap() } } #[test] fn test_puppet_screen() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { siv.add_fullscreen_layer(TextView::new( "This is a smoke test for the puppet cursive backend.", )) }); assert_display_snapshot!(frames.try_iter().last().unwrap()) } #[test] fn end2end_add_at() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabView::new() .with_tab_at(TextView::new("Third").with_name("0"), 0) .with_tab_at(TextView::new("First").with_name("1"), 0) .with_tab_at(TextView::new("Second").with_name("2"), 1); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_add_at_action_change_tab() { let mut tsiv = TestCursive::new(|siv: &mut cursive::Cursive| { let tabs = TabView::new() .with_tab_at(TextView::new("Third").with_name("0"), 0) .with_tab_at(TextView::new("First").with_name("1"), 0) .with_tab_at(TextView::new("Second").with_name("2"), 1); siv.add_layer(tabs); }); tsiv.input(Event::Key(Key::Up)); assert_display_snapshot!(tsiv.last_screen()); } #[test] fn end2end_add_at_panel() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stonks")) .with_tab_at(TextView::new("Fooooo").with_name("So"), 0) .with_tab_at(TextView::new("Ahhhhh").with_name("Much"), 1) .with_bar_alignment(Align::Center); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_panel_smoke() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stronk test")) .with_active_tab("Stronk test") .unwrap_or_else(|_| panic!("Setting active tab has failed")) .with_bar_alignment(Align::Center); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_remove_active() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let mut tabs = TabView::new() .with_tab(TextView::new("First").with_name("0")) .with_tab(TextView::new("Second").with_name("1")); tabs.remove_tab("1").expect("Removal of active tab failed"); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_remove_inactive() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let mut tabs = TabView::new() .with_tab(TextView::new("First").with_name("0")) .with_tab(TextView::new("Second").with_name("1")); tabs.remove_tab("0").expect("Removal failed."); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_swap() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let mut tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stonks")) .with_tab_at(TextView::new("Fooooo").with_name("So"), 0) .with_tab_at(TextView::new("Ahhhhh").with_name("Much"), 1) .with_bar_alignment(Align::Center); tabs.swap_tabs("So", "Stonks"); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_switch() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabView::new() .with_tab(TextView::new("First").with_name("0")) .with_tab(TextView::new("Second").with_name("1")) .with_active_tab("0") .unwrap_or_else(|_| panic!("Setting active tab has failed")); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_vertical_left() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stronk test")) .with_tab(TextView::new("Pshhhh").with_name("Stronker test")) .with_active_tab("Stronk test") .unwrap_or_else(|_| panic!("Setting active tab has failed")) .with_bar_alignment(Align::Center) .with_bar_placement(Placement::VerticalLeft); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } #[test] fn end2end_vertical_left_with_action_change_tab() { let mut tsiv = TestCursive::new(|siv: &mut cursive::Cursive| { let tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stronk test")) .with_tab(TextView::new("Pshhhh").with_name("Stronker test")) .with_active_tab("Stronk test") .unwrap_or_else(|_| panic!("Setting active tab has failed")) .with_bar_alignment(Align::Center) .with_bar_placement(Placement::VerticalLeft); siv.add_layer(tabs); }); tsiv.input(Event::Key(Key::Up)); assert_display_snapshot!(tsiv.last_screen()); } #[test] fn end2end_vertical_right() { let (frames, _) = setup_test_environment(|siv: &mut cursive::Cursive| { let tabs = TabPanel::new() .with_tab(TextView::new("Pshhhh").with_name("Stronk test")) .with_active_tab("Stronk test") .unwrap_or_else(|_| panic!("Setting active tab has failed")) .with_bar_alignment(Align::Center) .with_bar_placement(Placement::VerticalRight); siv.add_layer(tabs); }); assert_display_snapshot!(frames.try_iter().last().unwrap()); } cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_add_at.snap000064400000000000000000000043161046102023000226640ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 Second | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_add_at_action_change_tab.snap000064400000000000000000000043221046102023000263510ustar 00000000000000--- source: tests/end2end.rs assertion_line: 107 expression: tsiv.last_screen() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 Second | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_add_at_panel.snap000064400000000000000000000044761046102023000240520ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 ┌──────┤ So ┃ Much ┃ Stonks ├──────┐ | 1 │Ahhhhh │ | 2 └──────────────────────────────────┘ | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_panel_smoke.snap000064400000000000000000000044401046102023000237430ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 ┌────┨ Stronk test ┠─────┐ | 1 │Pshhhh │ | 2 └────────────────────────┘ | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_remove_active.snap000064400000000000000000000043161046102023000243000ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_remove_inactive.snap000064400000000000000000000043161046102023000246270ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 Second | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_swap.snap000064400000000000000000000044761046102023000224310ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 ┌──────┤ Stonks ┃ Much ┃ So ├──────┐ | 1 │Ahhhhh │ | 2 └──────────────────────────────────┘ | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_switch.snap000064400000000000000000000043161046102023000227510ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 First | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_vertical_left.snap000064400000000000000000000045121046102023000242710ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ┌─────┐ | 9 ━━━━━━━━━━━━━Pshhh│ | 0 Stronk test h │ | 1 ━━━━━━━━━━━━━━━ │ | 2 Stronker test │ | 3 ──────────────┬ │ | 4 └─────┘ | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_vertical_left_with_action_change_tab.snap000064400000000000000000000045161046102023000310200ustar 00000000000000--- source: tests/end2end.rs assertion_line: 215 expression: tsiv.last_screen() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ┌─────┐ | 9 ━━━━━━━━━━━━━Pshhh│ | 0 Stronk test h │ | 1 ━━━━━━━━━━━━━━━ │ | 2 Stronker test │ | 3 ──────────────┬ │ | 4 └─────┘ | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__end2end_vertical_right.snap000064400000000000000000000044441046102023000244600ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ┌─────┐ | 0 │Pshhh┷━━━━━━━━━━━━ | 1 │h Stronk test | 2 │ ┯━━━━━━━━━━━━ | 3 └─────┘ | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x cursive-tabs-0.8.0/tests/snapshots/end2end__puppet_screen.snap000064400000000000000000000043161046102023000227250ustar 00000000000000--- source: tests/end2end.rs expression: frames.try_iter().last().unwrap() --- captured piece: x01234567890123456789012345678901234567890123456789012345678901234567890123456789x 0This is a smoke test for the puppet cursive backend. | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | 1 | 2 | 3 | x--------------------------------------------------------------------------------x