diff --git a/code/.gitignore b/code/.gitignore deleted file mode 100644 index c47965c25..000000000 --- a/code/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/vendor -/node_modules -.env diff --git a/code/vendor/chumper/datatable/.gitattributes b/code/vendor/chumper/datatable/.gitattributes new file mode 100644 index 000000000..412eeda78 --- /dev/null +++ b/code/vendor/chumper/datatable/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/code/vendor/chumper/datatable/.gitignore b/code/vendor/chumper/datatable/.gitignore new file mode 100644 index 000000000..343ad7744 --- /dev/null +++ b/code/vendor/chumper/datatable/.gitignore @@ -0,0 +1,6 @@ +/vendor +composer.phar +composer.lock +datatable.sublime-project +.DS_Store +.idea diff --git a/code/vendor/chumper/datatable/.travis.yml b/code/vendor/chumper/datatable/.travis.yml new file mode 100644 index 000000000..0a1c1cb2e --- /dev/null +++ b/code/vendor/chumper/datatable/.travis.yml @@ -0,0 +1,12 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + +before_script: + - curl -s http://getcomposer.org/installer | php + - php composer.phar install --dev + +script: phpunit \ No newline at end of file diff --git a/code/vendor/chumper/datatable/README.md b/code/vendor/chumper/datatable/README.md new file mode 100644 index 000000000..62a951ccd --- /dev/null +++ b/code/vendor/chumper/datatable/README.md @@ -0,0 +1,614 @@ +Datatable +========= + +This is a __Laravel 5 package__ for the server and client side of datatables at http://datatables.net/ + +![Image](https://raw.githubusercontent.com/Chumper/Datatable/master/datatable.jpg) + +##Known Issues + +* none i know of so far + +##TODO + +* fix incoming bugs +* code documentaion + +##Features + +This package supports: + +* Support for Collections and Query Builder +* Easy to add and order columns +* Includes a simple helper for the HTML side +* Use your own functions and presenters in your columns +* Search in your custom defined columns ( Collection only!!! ) +* Define your specific fields for searching and ordering +* Add custom javascript values for the table +* Tested! (Ok, maybe not fully, but I did my best :) ) + +## Please note! + +There are some differences between the collection part and the query part of this package. +The differences are: + +| Difference | Collection | Query | +| --- |:----------:| :----:| +|Speed | - | + | +Custom fields | + | + +Search in custom fields | + | - +Order by custom fields | + | - +Search outside the shown data (e.g.) database | - | + + +For a detailed explanation please see the video below. +http://www.youtube.com/watch?v=c9fao_5Jo3Y + +Please let me know any issues or features you want to have in the issues section. +I would be really thankful if you can provide a test that points to the issue. + +##Installation + +This package is available on http://packagist.org, just add it to your composer.json + + "chumper/datatable": "dev-develop" + +Alternatively, you can install it using the `composer` command: + + composer require chumper/datatable "dev-develop" + + +In Config/App.php: Add line Provider: + +```php + 'Chumper\Datatable\DatatableServiceProvider', +``` + +__If using Laravel 5.1:__ you will want to add: +```php + Chumper\Datatable\DatatableServiceProvider::class, +``` + +Add line Alias: +```php + //new + 'Datatable' => 'Chumper\Datatable\Facades\DatatableFacade', +``` + +You can then access it under the `Datatable` alias. + +##Basic Usage + +There are two ways you can use the plugin, within one route or within two routes: + +###Two routes + +* Create two routes: One to deliver the view to the user, the other for datatable data, eg: + +```php + Route::resource('users', 'UsersController'); + Route::get('api/users', array('as'=>'api.users', 'uses'=>'UsersController@getDatatable')); +``` + +* Your main route will deliver a view to the user. This view should include references to your local copy of [datatables](http://datatables.net/). In the example below, files were copied from the datatables/media directories and written to public/assets. Please note that the scripts must be located above the call to Datatable: + +```php + + + + + {!! Datatable::table() + ->addColumn('id','Name') // these are the column headings to be shown + ->setUrl(route('api.users')) // this is the route where data will be retrieved + ->render() !!} +``` + +* Create a controller function to return your data in a way that can be read by Datatables: + +```php + public function getDatatable() + { + return Datatable::collection(User::all(array('id','name'))) + ->showColumns('id', 'name') + ->searchColumns('name') + ->orderColumns('id','name') + ->make(); + } +``` + +You should now have a working datatable on your page. + +###One route + +In your route you should use the Datatable::shouldHandle method which will check whether the plugin should handle the request or not. + +```php + if(Datatable::shouldHandle()) + { + return Datatable::collection(User::all(array('id','name'))) + ->showColumns('id', 'name') + ->searchColumns('name') + ->orderColumns('id','name') + ->make(); + } +``` + +The plugin will then query the same url for information. The shouldHandle method just checks for an ajax request and if sEcho is set. + +##HTML Example + +```php + Datatable::table() + ->addColumn('id',Lang::get('user.lastname')) + ->setUrl(URL::to('auth/users/table')) + ->render(); +``` + +With seperate table and script: +```php + $table = Datatable::table() + ->addColumn('Email2','Email', "Test") + ->noScript(); + + // to render the table: + $table->render() + + // later in the view you can render the javascript: + $table->script(); +``` + +This will generate a HTML table with two columns (id, lastname -> your translation) and will set the URL for the ajax request. + +> Note: This package will **NOT** include the `datatable.js`, that is your work to do. +> The reason is that for example i use Basset and everybody wants to do it their way... + +If you want to provide your own template for the table just provide the path to the view in laravel style. + +```php + Datatable::table() + ->addColumn('id',Lang::get('user.lastname')) + ->setUrl(URL::to('auth/users/table')) + ->render('views.templates.datatable'); +``` +##Server Example + +```php + Datatable::collection(User::all()) + ->showColumns('id') + ->addColumn('name',function($model) + { + return $model->getPresenter()->yourProperty; + } + )->make(); +``` + +This will generate a server side datatable handler from the collection `User::all()`. +It will add the `id` column to the result and also a custom column called `name`. +Please note that we need to pass a function as a second parameter, it will **always** be called +with the object the collection holds. In this case it would be the `User` model. + +You could now also access all relationship, so it would be easy for a book model to show the author relationship. + +```php + Datatable::collection(User::all()) + ->showColumns('id') + ->addColumn('name',function($model) + { + return $model->author->name; + } + )->make(); +``` + +> Note: If you pass a collection of arrays to the `collection` method you will have an array in the function, not a model. + +The order of the columns is always defined by the user and will be the same order the user adds the columns to the Datatable. + +##Query or Collection? + +There is a difference between query() and collection(). +A collection will be compiled before any operation - like search or order - will be performed so that it can also include your custom fields. +This said the collection method is not as performing as the query method where the search and order will be tackled before we query the database. + +So if you have a lot of Entries (100k+) a collection will not perform well because we need to compile the whole amount of entries to provide accurate sets. +A query on the other side is not able to perform a search or orderBy correctly on your custom field functions. + +> TLTR: If you have no custom fields, then use query() it will be much faster +> If you have custom fields but don't want to provide search and/or order on the fields use query(). +> Collection is the choice if you have data from somewhere else, just wrap it into a collection and you are good to go. +> If you have custom fields and want to provide search and/or order on these, you need to use a collection. + +Please also note that there is a significant difference betweeen the search and order functionality if you use query compared to collections. +Please see the following video for more details. + +http://www.youtube.com/watch?v=c9fao_5Jo3Y + +##Available functions + +This package is separated into two smaller parts: + +1. Datatable::table() +2. Datatable::collection() +3. Datatable::query() + +The second and third one is for the server side, the first one is a helper to generate the needed table and javascript calls. + +###Collection & Query + +**collection($collection)** + +Will set the internal engine to the collection. +For further performance improvement you can limit the number of columns and rows, i.e.: + + $users = User::activeOnly()->get('id','name'); + Datatable::collection($users)->... + +**query($query)** + +This will set the internal engine to a Eloquent query... +E.g.: + + $users = DB::table('users'); + Datatable::query($users)->... + +The query engine is much faster than the collection engine but also lacks some functionality, +for further information look at the table above. + +**showColumns(...$columns)** + +This will add the named columns to the result. +> Note: You need to pass the name in the format you would access it on the model or array. +> example: in the db: `last_name`, on the model `lastname` -> showColumns('lastname') + +You can provide as many names as you like + +**searchColumns(..$fields)** + +Will enable the table to allow search only in the given columns. +Please note that a collection behaves different to a builder object. + +Note: If you want to search on number columns with the query engine, then you can also pass a search column like this + ``` + //mysql + ->searchColumns(array('id:char:255', 'first_name', 'last_name', 'address', 'email', 'age:char:255')) + + //postgree + ->searchColumns(array('id:text', 'first_name', 'last_name', 'address', 'email', 'age:text')) + ``` + + This will cast the columns int the given types when searching on this columns + +**orderColumns(..$fields)** + +Will enable the table to allow ordering only in the given columns. +Please note that a collection behaves different to a builder object. + +**addColumn($name, $function)** + +Will add a custom field to the result set, in the function you will get the whole model or array for that row +E.g.: +```php + Datatable::collection(User::all()) + ->addColumn('name',function($model) + { + return $model->author->name; + } + )->make(); +``` +You can also just add a predefined Column, like a DateColumn, a FunctionColumn, or a TextColumn +E.g.: + +```php + $column = new TextColumn('foo', 'bar'); // Will always return the text bar + //$column = new FunctionColumn('foo', function($model){return $model->bar}); // Will return the bar column + //$column = new DateColumn('foo', DateColumn::TIME); // Will return the foo date object as toTimeString() representation + //$column = new DateColumn('foo', DateColumn::CUSTOM, 'd.M.Y H:m:i'); // Will return the foo date object as custom representation + + Datatable::collection(User::all()) + ->addColumn($column) + ->make(); +``` + +You can also overwrite the results returned by the QueryMethod by using addColumn in combination with showColumns. +You must name the column exactly like the database column that you're displaying using showColumns in order for this to work. + +```php + $column = new FunctionColumn('foo', function ($row) { return strtolower($row->foo); } + Datatable::query(DB::table('table')->lists('foo')) + ->showColumns('foo') + ->addColumn($column) + ->orderColumns('foo') + ->searchColumns('foo') + ->make() +``` +This will allow you to have sortable and searchable columns using the QueryEngine while also allowing you to modify the return value of that database column entry. + +Eg: linking an user_id column to it's page listing +```php + $column = new FunctionColumn('user_id', function ($row) { return link_to('users/'.$row->user_id, $row->username) } + Datatable::query(DB::table('table')->lists('user_id', 'username')) + ->showColumns('user_id') + ->addColumn($column) + ->orderColumns('user_id') + ->searchColumns('user_id') +``` + + +Please look into the specific Columns for further information. + +**setAliasMapping()** + +Will advise the Datatable to return the data mapped with the column name. +So instead of +```javascript + { + "aaData":[ + [3,"name","2014-02-02 23:28:14"] + ], + "sEcho":9, + "iTotalRecords":2, + "iTotalDisplayRecords":1 + } +``` +you will get something like this as response +```javascript +{ + "aaData":[ + {"id":2,"name":"Datatable","created_at":"Sun, Feb 2, 2014 7:17 PM"} + ], + "sEcho":2, + "iTotalRecords":2, + "iTotalDisplayRecords":1 +} +``` + +**make()** + +This will handle the input data of the request and provides the result set. +> Without this command no response will be returned. + +**clearColumns()** + +This will reset all columns, mainly used for testing and debugging, not really useful for you. +> If you don't provide any column with `showColumn` or `addColumn` then no column will be shown. +> The columns in the query or collection do not have any influence which column will be shown. + +**getOrder()** + +This will return an array with the columns that will be shown, mainly used for testing and debugging, not really useful for you. + +**getColumn($name)** + +Will get a column by its name, mainly used for testing and debugging, not really useful for you. + +###Specific QueryEngine methods + +**setSearchWithAlias()** + +If you want to use an alias column on the query engine and you don't get the correct results back while searching then you should try this flag. +E.g.: +```php + Datatable::from(DB::table("users")->select('firstname', "users.email as email2")->join('partners','users.partner_id','=','partners.id')) + ->showColumns('firstname','email2') + ->setSearchWithAlias() + ->searchColumns("email2") +``` + +In SQL it is not allowed to have an alias in the where part (used for searching) and therefore the results will not counted correctly. + +With this flag you enable aliases in the search part (email2 in searchColumns). + +Please be aware that this flag will slow down your application, since we are getting the results back twice to count them manually. + + +**setSearchOperator($value = "LIKE")** + +With this method you can set the operator on searches like "ILIKE" on PostgreSQL for case insensitivity. + +**setExactWordSearch** + +Will advice the engines only to search for the exact given search string. + +###Specific CollectionEngine methods + +**setSearchStrip() & setOrderStrip()** + +If you use the search functionality then you can advice +all columns to strip any HTML and PHP tags before searching this column. + +This can be useful if you return a link to the model detail but still want to provide search ability in this column. + +**setCaseSensitive($boolean = 'false')** + +Set the search method to case sensitive or not, default is false + +###Table + +**noScript()** + +With setting this property the Table will not render the javascript part. + +You can render it manually with + +**script($view = null)** + +Will render the javascript if no view is given or the default one and will pass the class name, the options and the callbacks. + +Example: + +```php + $table = Datatable::table() + ->addColumn('Email2','Email', "Test") + ->noScript(); + + // to render the table: + $table->render() + + // later in the view you can render the javascript: + $table->script(); +``` + +**setUrl($url)** + +Will set the URL and options for fetching the content via ajax. + +**setOptions($name, $value) OR setOptions($array)** + +Will set a single option or an array of options for the jquery call. + +You can pass as paramater something like this ('MyOption', 'ValueMyOption') or an Array with parameters, but some values in DataTable is a JSON so how can i pass a JSON in values? Use another array, like that: +setOptions(array("MyOption"=> array('MyAnotherOption'=> 'MyAnotherValue', 'MyAnotherOption2'=> 'MyAnotherValue2'))); + +```js + +//GENERATE + +jQuery(.Myclass).DataTable({ + MyOption: { + MyAnotherOption: MyAnotherValue, + MyAnotherOption2: MyAnotherValue2, + } +}); +``` + +As a sugestion, take a look at this 2 files javascript.blade.php && template.blade.php in vendor/Chumper/datatable/src/views. You'll understand all the logic and see why it's important to pass the parameter like an array (json_encode and others stuffs). + +**setCallbacks($name, $value) OR setCallbacks($array)** + +Will set a single callback function or an array of callbacks for the jquery call. DataTables callback functions are described at https://datatables.net/usage/callbacks. For example, + +```php + ->setCallbacks( + 'fnServerParams', 'function ( aoData ) { + aoData.push( { "name": "more_data", "value": "my_value" } ); + }' + ) + +``` + +**addColumn($name)** + +Will add a column to the table, where the name will be rendered on the table head. +So you can provide the string that should be shown. + +if you want to use the alias mapping feature of the server side table then you need to add an array like this + +```php +Datatable::table() + ->addColumn(array( + 'id' => 'ID', + 'name' => 'Name', + 'created_at' => 'Erstellt' + )) + ->render(); +``` +Please note that passing an assosiative array to the addColumn function will automatically enable the alias function on the table + +**setAliasMapping(boolean)** + +Here you can explicitly set if the table should be aliased or not. + +**countColumns()** + +This will return the number of columns that will be rendered later. Mainly for testing and debugging. + +**getData()** + +Will return the data that will be rendered into the table as an array. + +**getOptions()** + +Get all options as an array. + +**render($view = template.blade)** + +Renders the table. You can customize this by passing a view name. +Please see the template inside the package for further information of how the data is passed to the view. + +**setData($data)** + +Expects an array of arrays and will render this data when the table is shown. + +**setCustomValues($name, $value) OR setCustomValues($array)** + +Will set a single custom value, or an array of custom values, which will be passed to the view. You can access these values in your custom view file. For example, if you wanted to click anywhere on a row to go to a record (where the record id is in the first column of the table): + +In the calling view: + +```php +{{ DataTable::table() + ->addColumn($columns) + ->setUrl($ajaxRouteToTableData) + ->setCustomValues('table-url', $pathToTableDataLinks) + ->render('my.datatable.template') }} +``` + +In the datatable view (eg, 'my.datatable.template'): + +```js + @if (isset($values['table-url'])) + $('.{{$class}}').hover(function() { + $(this).css('cursor', 'pointer'); + }); + $('.{{$class}}').on('click', 'tbody tr', function(e) { + $id = e.currentTarget.children[0].innerHTML; + $url = e.currentTarget.baseURI; + document.location.href = "{{ $values['table-url'] }}/" + $id; + }); + @endif +``` + + +**setOrder(array $order)** + +Defines the order that a datatable will be ordered by on first page load. +```php +{{ DataTable::table() + ->addColumn('ID', 'First Name', 'Last Name') + ->setUrl($ajaxRouteToTableData) + ->setOrder(array(2=>'asc', 1=>'asc')) // sort by last name then first name + ->render('my.datatable.template') }} +``` +##Extras +Some extras features, using the Datatables api. + +### - TableTools + +To use TableTools you will need to add some files in your project (https://datatables.net/extensions/tabletools/), if you want some help download the datatable's package and inside the extension folder go to /tabletools and study the examples. After, all the files include, don't forget to pass the parameters like this: + +```js +//In view: + +{{ + Datatable::table() + ->addColumn('your columns here separated by comma') + ->setUrl('your URL for server side') + ->setOptions(array( + 'dom' =>"T<'clear'>lfrtip", + 'tabletools' => array( + "aSwfPath" => "your/path/to/swf/copy_csv_cls_pdf.swf", + "aButtons" => array("copy", "pdf", "xls") + ) + )) +}} + +``` +If you want to get some properties like "which row did i click?", see the javascript.blade.php and the variable $values. + +##Contributors + +* [jgoux](https://github.com/jgoux) for helping with searching on number columns in the database +* [jijoel](https://github.com/jijoel) for helping with callback options and documentation + +##Changelog + +* 2.0.0: + * Seperated Query and Collection Engine + * Added single column search + * Code cleanup + +##Applications + +https://github.com/hillelcoren/invoice-ninja (by Hillel Coren) + +##License + +This package is licensed under the MIT License diff --git a/code/vendor/chumper/datatable/composer.json b/code/vendor/chumper/datatable/composer.json new file mode 100644 index 000000000..1d1bf8fb9 --- /dev/null +++ b/code/vendor/chumper/datatable/composer.json @@ -0,0 +1,38 @@ +{ + "name": "chumper/datatable", + "description": "This is a laravel 4 package for the server and client side of datatablaes at http://datatables.net/", + "keywords" : ["laravel","datatables", "ajax", "jquery"], + "license": "MIT", + "homepage": "http://github.com/Chumper/datatable", + "authors": [ + { + "name": "Nils Plaschke", + "email": "github@nilsplaschke.de", + "homepage": "http://nilsplaschke.de", + "role": "Developer" + } + ], + "require": { + "php": ">=5.3.0", + "illuminate/support": "~5.0", + "illuminate/view": "~5.0", + "illuminate/config": "~5.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*", + "mockery/mockery": "dev-master", + "orchestra/testbench": "3.1.*" + }, + "autoload": { + "psr-0": { + "Chumper\\Datatable": "src/" + } + }, + "minimum-stability": "dev", + "repositories": [ + { + "type": "vcs", + "url": "git://github.com/orchestral/phpseclib.git" + } + ] +} diff --git a/code/vendor/chumper/datatable/datatable.jpg b/code/vendor/chumper/datatable/datatable.jpg new file mode 100644 index 000000000..3bdbcf223 Binary files /dev/null and b/code/vendor/chumper/datatable/datatable.jpg differ diff --git a/code/vendor/chumper/datatable/phpunit.xml b/code/vendor/chumper/datatable/phpunit.xml new file mode 100644 index 000000000..e89ac6d80 --- /dev/null +++ b/code/vendor/chumper/datatable/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ./tests/ + + + \ No newline at end of file diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/BaseColumn.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/BaseColumn.php new file mode 100644 index 000000000..414a5a2ad --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/BaseColumn.php @@ -0,0 +1,37 @@ +name = $name; + } + + /** + * @param mixed $model The data to pass to the column, + * could be a model or an array + * @return mixed the return value of the implementation, + * should be text in most of the cases + */ + public abstract function run($model); + + /** + * @return String The name of the column + */ + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/DateColumn.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/DateColumn.php new file mode 100644 index 000000000..2c650364a --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/DateColumn.php @@ -0,0 +1,69 @@ +format = $format; + $this->custom = $custom; + } + + /** + * @param mixed $model The data to pass to the column, + * could be a model or an array + * @return mixed the return value of the implementation, + * should be text in most of the cases + */ + public function run($model) + { + + if(is_string(is_array($model) ? $model[$this->name]: $model->{$this->name})) + { + return is_array($model) ? $model[$this->name]: $model->{$this->name}; + } + + switch($this->format) + { + case DateColumn::DATE: + return is_array($model) ? $model[$this->name]->toDateString(): $model->{$this->name}->toDateString(); + break; + case DateColumn::TIME: + return is_array($model) ? $model[$this->name]->toTimeString(): $model->{$this->name}->toTimeString(); + break; + case DateColumn::DATE_TIME: + return is_array($model) ? $model[$this->name]->toDateTimeString(): $model->{$this->name}->toDateTimeString(); + break; + case DateColumn::CUSTOM: + return is_array($model) ? $model[$this->name]->format($this->custom): $model->{$this->name}->format($this->custom); + break; + case DateColumn::FORMATTED_DATE: + return is_array($model) ? $model[$this->name]->toFormattedDateString(): $model->{$this->name}->toFormattedDateString(); + break; + case DateColumn::DAY_DATE: + return is_array($model) ? $model[$this->name]->toDayDateTimeString(): $model->{$this->name}->toDayDateTimeString(); + break; + + } + } +} \ No newline at end of file diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/FunctionColumn.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/FunctionColumn.php new file mode 100644 index 000000000..53b7b956c --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/FunctionColumn.php @@ -0,0 +1,17 @@ +callable = $callable; + } + + public function run($model) + { + return call_user_func($this->callable,$model); + } +} \ No newline at end of file diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/TextColumn.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/TextColumn.php new file mode 100644 index 000000000..20c9458cb --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Columns/TextColumn.php @@ -0,0 +1,17 @@ +text = $text; + } + + public function run($model) + { + return $this->text; + } +} \ No newline at end of file diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Datatable.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Datatable.php new file mode 100644 index 000000000..a3d2507be --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Datatable.php @@ -0,0 +1,57 @@ +publishes([ + __DIR__.'/../../config/config.php' => config_path('chumper.datatable.php'), + __DIR__.'/../../views' => base_path('resources/views/vendor/chumper.datatable'), + ]); + + $this->loadViewsFrom(__DIR__ . '/../../views', 'chumper.datatable'); + + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + + $this->mergeConfigFrom(__DIR__.'/../../config/config.php', 'chumper.datatable'); + + $this->app['datatable'] = $this->app->share(function($app) + { + return new Datatable; + }); + + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array('datatable'); + } + +} diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/BaseEngine.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/BaseEngine.php new file mode 100644 index 000000000..d2cfb1b62 --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/BaseEngine.php @@ -0,0 +1,553 @@ + column + * 1 => name:cast:length + * ) + */ + protected $orderColumn = null; + + /** + * @var string + */ + protected $orderDirection = BaseEngine::ORDER_ASC; + + /** + * @var boolean If the return should be alias mapped + */ + protected $aliasMapping = false; + + /** + * @var bool If the search should be done with exact matching + */ + protected $exactWordSearch = false; + + + function __construct() + { + $this->columns = new Collection(); + $this->config = Config::get('chumper.datatable.engine'); + $this->setExactWordSearch( $this->config['exactWordSearch'] ); + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function addColumn() + { + if(func_num_args() != 2 && func_num_args() != 1) + throw new Exception('Invalid number of arguments'); + + if(func_num_args() == 1) + { + //add a predefined column + $this->columns->put(func_get_arg(0)->getName(), func_get_arg(0)); + } + else if(is_callable(func_get_arg(1))) + { + $this->columns->put(func_get_arg(0), new FunctionColumn(func_get_arg(0), func_get_arg(1))); + } + else + { + $this->columns->put(func_get_arg(0), new TextColumn(func_get_arg(0),func_get_arg(1))); + } + return $this; + } + + /** + * @param $name + * @return mixed + */ + public function getColumn($name) + { + return $this->columns->get($name,null); + } + + /** + * @return array + */ + public function getOrder() + { + return array_keys($this->columns->toArray()); + } + + /** + * @return array + */ + public function getOrderingColumns() + { + return $this->orderColumns; + } + + /** + * @return array + */ + public function getSearchingColumns() + { + return $this->searchColumns; + } + + /** + * @return $this + */ + public function clearColumns() + { + $this->columns = new Collection(); + return $this; + } + + /** + * @param $cols + * @return $this + */ + public function showColumns($cols) + { + if ( ! is_array($cols)) { + $cols = func_get_args(); + } + + foreach ($cols as $property) { + //quick fix for created_at and updated_at columns + if(in_array($property, array('created_at', 'updated_at'))) + { + $this->columns->put($property, new DateColumn($property, DateColumn::DAY_DATE)); + } + else + { + $this->columns->put($property, new FunctionColumn($property, function($model) use($property){ + try{return is_array($model)?$model[$property]:$model->$property;}catch(Exception $e){return null;} + })); + } + $this->showColumns[] = $property; + } + return $this; + } + + /** + * @return \Illuminate\Http\JsonResponse + */ + public function make() + { + //TODO Handle all inputs + $this->handleInputs(); + $this->prepareSearchColumns(); + + $output = array( + "aaData" => $this->internalMake($this->columns, $this->searchColumns)->toArray(), + "sEcho" => intval($this->sEcho), + "iTotalRecords" => $this->totalCount(), + "iTotalDisplayRecords" => $this->count(), + ); + return Response::json($output); + } + + /** + * @param $cols + * @return $this + */ + public function searchColumns($cols) + { + if ( ! is_array($cols)) { + $cols = func_get_args(); + } + + $this->searchColumns = $cols; + + return $this; + } + + /** + * @param $cols + * @return $this + */ + public function orderColumns($cols) + { + if ( ! is_array($cols)) { + $cols = func_get_args(); + } + + if (count($cols) == 1 && $cols[0] == '*') + $cols = $this->showColumns; + + $this->orderColumns = $cols; + return $this; + } + + /** + * @param $function Set a function for a dynamic row class + * @return $this + */ + public function setRowClass($function) + { + $this->rowClass = $function; + return $this; + } + + /** + * @param $function Set a function for a dynamic row id + * @return $this + */ + public function setRowId($function) + { + $this->rowId = $function; + return $this; + } + + /** + * @param $function Set a function for dynamic html5 data attributes + * @return $this + */ + public function setRowData($function) + { + $this->rowData = $function; + return $this; + } + + public function setAliasMapping($value = true) + { + $this->aliasMapping = $value; + return $this; + } + + public function setExactWordSearch($value = true) + { + $this->exactWordSearch = $value; + return $this; + } + + /** + * @param $columnNames Sets up a lookup table for which columns should use exact matching -sburkett + * @return $this + */ + public function setExactMatchColumns($columnNames) + { + foreach($columnNames as $columnIndex) + $this->columnSearchExact[ $columnIndex ] = true; + + return $this; + } + + public function getRowClass() + { + return $this->rowClass; + } + + public function getRowId() + { + return $this->rowId; + } + + public function getRowData() + { + return $this->rowData; + } + + public function getAliasMapping() + { + return $this->aliasMapping; + } + //-------------protected functionS------------------- + + /** + * @param $value + */ + protected function handleiDisplayStart($value) + { + //skip + $this->skip($value); + } + + /** + * @param $value + */ + protected function handleiDisplayLength($value) + { + //limit nicht am query, sondern den ganzen + //holen und dann dynamisch in der Collection taken und skippen + $this->take($value); + } + + /** + * @param $value + */ + protected function handlesEcho($value) + { + $this->sEcho = $value; + } + + /** + * @param $value + */ + protected function handlesSearch($value) + { + //handle search on columns sSearch, bRegex + $this->search($value); + } + + + /** + * @param $value + */ + protected function handleiSortCol_0($value) + { + if(Input::get('sSortDir_0') == 'desc') + $direction = BaseEngine::ORDER_DESC; + else + $direction = BaseEngine::ORDER_ASC; + + //check if order is allowed + if(empty($this->orderColumns)) + { + $this->order(array(0 => $value, 1 => $this->getNameByIndex($value)), $direction); + return; + } + + //prepare order array + $cleanNames = array(); + foreach($this->orderColumns as $c) + { + if(strpos($c,':') !== FALSE) + { + $cleanNames[] = substr($c, 0, strpos($c,':')); + } + else + { + $cleanNames[] = $c; + } + } + + $i = 0; + foreach($this->columns as $name => $column) + { + if($i == $value && in_array($name, $cleanNames)) + { + $this->order(array(0 => $value, 1 => $this->orderColumns[array_search($name,$cleanNames)]), $direction); + return; + } + $i++; + } + } + + /** + * @param int $columnIndex + * @param string $searchValue + * + * @return void + */ + protected function handleSingleColumnSearch($columnIndex, $searchValue) + { + //dd($columnIndex, $searchValue, $this->searchColumns); + if (!isset($this->searchColumns[$columnIndex])) return; + if (empty($searchValue) && $searchValue !== '0') return; + + $columnName = $this->searchColumns[$columnIndex]; + $this->searchOnColumn($columnName, $searchValue); + } + + /** + * + */ + protected function handleInputs() + { + //Handle all inputs magically + foreach (Input::all() as $key => $input) { + + // handle single column search + if ($this->isParameterForSingleColumnSearch($key)) + { + $columnIndex = str_replace('sSearch_','',$key); + $this->handleSingleColumnSearch($columnIndex, $input); + continue; + } + + if(method_exists($this, $function = 'handle'.$key)) + $this->$function($input); + } + } + + /** + * @param $parameterName + * + * @return bool + */ + protected function isParameterForSingleColumnSearch($parameterName) + { + static $parameterNamePrefix = 'sSearch_'; + return str_contains($parameterName, $parameterNamePrefix); + } + + protected function prepareSearchColumns() + { + if(count($this->searchColumns) == 0 || empty($this->searchColumns)) + $this->searchColumns = $this->showColumns; + } + + /** + * @param $column + * @param $order + */ + protected function order($column, $order = BaseEngine::ORDER_ASC) + { + $this->orderColumn = $column; + $this->orderDirection = $order; + } + + /** + * @param $value + */ + protected function search($value) + { + $this->search = $value; + } + + /** + * @param string $columnName + * @param mixed $value + */ + protected function searchOnColumn($columnName, $value) + { + $this->fieldSearches[] = $columnName; + $this->columnSearches[] = $value; + } + + /** + * @param $value + */ + protected function skip($value) + { + $this->skip = $value; + } + + /** + * @param $value + */ + protected function take($value) + { + $this->limit = $value; + } + + public function getNameByIndex($index) + { + $i = 0; + foreach($this->columns as $name => $col) + { + if($index == $i) + { + return $name; + } + $i++; + } + } + + public function getExactWordSearch() + { + return $this->exactWordSearch; + } + + abstract protected function totalCount(); + abstract protected function count(); + abstract protected function internalMake(Collection $columns, array $searchColumns = array()); +} diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/CollectionEngine.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/CollectionEngine.php new file mode 100644 index 000000000..e3dbfcaa1 --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/CollectionEngine.php @@ -0,0 +1,309 @@ + false, + 'stripSearch' => false, + 'caseSensitive' => false, + ); + + /** + * @param Collection $collection + */ + function __construct(Collection $collection) + { + parent::__construct(); + $this->collection = $collection; + $this->workingCollection = $collection; + } + + /** + * @return int + */ + public function count() + { + return $this->workingCollection->count(); + } + + /** + * @return int + */ + public function totalCount() + { + return $this->collection->count(); + } + + /** + * @return array + */ + public function getArray() + { + $this->handleInputs(); + $this->compileArray($this->columns); + $this->doInternalSearch(new Collection(), array()); + $this->doInternalOrder(); + + return array_values($this->workingCollection + ->slice($this->skip,$this->limit) + ->toArray() + ); + } + + /** + * Resets all operations performed on the collection + */ + public function reset() + { + $this->workingCollection = $this->collection; + return $this; + } + + public function stripSearch() + { + $this->options['stripSearch'] = true; + return $this; + } + + public function stripOrder($callback = true) + { + $this->options['stripOrder'] = $callback; + return $this; + } + + public function setSearchStrip() + { + $this->options['stripSearch'] = true; + return $this; + } + + public function setOrderStrip($callback = true) + { + return $this->stripOrder($callback); + } + + public function setCaseSensitive($value) + { + $this->options['caseSensitive'] = $value; + return $this; + } + + public function getOption($value) + { + return $this->options[$value]; + } + //--------------PRIVATE FUNCTIONS----------------- + + protected function internalMake(Collection $columns, array $searchColumns = array()) + { + $this->compileArray($columns); + $this->doInternalSearch($columns, $searchColumns); + $this->doInternalOrder(); + + return $this->workingCollection->slice($this->skip,$this->limit); + } + + /** + * Filter a collection based on the DataTables search parameters (sSearch_0 etc) + * See http://legacy.datatables.net/usage/server-side + * + * @param Collection $columns All the columns in the DataTable + * @param array $searchColumns Columns to search on - values are case-sensitive (must match definition from $columns) + */ + private function doInternalSearch(Collection $columns, array $searchColumns) + { + if((is_null($this->search) || empty($this->search)) && empty($this->fieldSearches)) + return; + + $value = $this->search; + $caseSensitive = $this->options['caseSensitive']; + + $toSearch = array(); + $searchType = self::AND_CONDITION; + // Map the searchColumns to the real columns + $ii = 0; + foreach($columns as $i => $col) + { + if(in_array($columns->get($i)->getName(), $searchColumns) || in_array($columns->get($i)->getName(), $this->fieldSearches)) + { + // map values to columns, where there is no value use the global value + if(($field = array_search($columns->get($i)->getName(), $this->fieldSearches)) !== FALSE) + { + $toSearch[$ii] = $this->columnSearches[$field]; + } + else + { + if($value) + $searchType = self::OR_CONDITION; + $toSearch[$ii] = $value; + } + } + $ii++; + } + + $self = $this; + $this->workingCollection = $this->workingCollection->filter(function($row) use ($toSearch, $caseSensitive, $self, $searchType) + { + for($i=0, $stack=array(), $nb=count($row); $i<$nb; $i++) + { + if(!array_key_exists($i, $toSearch)) + continue; + + $column = $i; + if($self->getAliasMapping()) + { + $column = $self->getNameByIndex($i); + } + + if($self->getOption('stripSearch')) + { + $search = strip_tags($row[$column]); + } + else + { + $search = $row[$column]; + } + if($caseSensitive) + { + if($self->exactWordSearch) + { + if($toSearch[$i] === $search) + $stack[$i] = true; + } + else + { + if(str_contains($search,$toSearch[$i])) + $stack[$i] = true; + } + } + else + { + if($self->getExactWordSearch()) + { + if(mb_strtolower($toSearch[$i]) === mb_strtolower($search)) + $stack[$i] = true; + } + else + { + if(str_contains(mb_strtolower($search),mb_strtolower($toSearch[$i]))) + $stack[$i] = true; + } + } + } + if($searchType == $self::AND_CONDITION) + { + $result = array_diff_key(array_filter($toSearch), $stack); + if(empty($result)) + return true; + } + else + { + if(!empty($stack)) + return true; + } + }); + } + + private function doInternalOrder() + { + if(is_null($this->orderColumn)) + return; + + $column = $this->orderColumn[0]; + $stripOrder = $this->options['stripOrder']; + $self = $this; + $this->workingCollection = $this->workingCollection->sortBy(function($row) use ($column,$stripOrder,$self) { + + if($self->getAliasMapping()) + { + $column = $self->getNameByIndex($column); + } + if($stripOrder) + { + if(is_callable($stripOrder)){ + return $stripOrder($row, $column); + }else{ + return strip_tags($row[$column]); + } + } + else + { + return $row[$column]; + } + }, SORT_NATURAL); + + if($this->orderDirection == BaseEngine::ORDER_DESC) + $this->workingCollection = $this->workingCollection->reverse(); + } + + private function compileArray($columns) + { + $self = $this; + $this->workingCollection = $this->collection->map(function($row) use ($columns, $self) { + $entry = array(); + + // add class and id if needed + if(!is_null($self->getRowClass()) && is_callable($self->getRowClass())) + { + $entry['DT_RowClass'] = call_user_func($self->getRowClass(),$row); + } + if(!is_null($self->getRowId()) && is_callable($self->getRowId())) + { + $entry['DT_RowId'] = call_user_func($self->getRowId(),$row); + } + if(!is_null($self->getRowData()) && is_callable($self->getRowData())) + { + $entry['DT_RowData'] = call_user_func($self->getRowData(),$row); + } + $i=0; + foreach ($columns as $col) + { + if($self->getAliasMapping()) + { + $entry[$col->getName()] = $col->run($row); + } + else + { + $entry[$i] = $col->run($row); + } + + $i++; + } + return $entry; + }); + } +} diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/QueryEngine.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/QueryEngine.php new file mode 100644 index 000000000..5a23fc79f --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Engines/QueryEngine.php @@ -0,0 +1,306 @@ + 'LIKE', + 'searchWithAlias' => false, + 'orderOrder' => null, + 'counter' => 0, + 'noGroupByOnCount' => false, + ); + + function __construct($builder) + { + parent::__construct(); + if($builder instanceof Relation) + { + $this->builder = $builder->getBaseQuery(); + $this->originalBuilder = clone $builder->getBaseQuery(); + } + else + { + $this->builder = $builder; + $this->originalBuilder = clone $builder; + } + } + + public function count() + { + return $this->options['counter']; + } + + public function totalCount() + { + // Store temporary copy as we may modify it, we'd be stupid to modify + // the actual "original" copy... + $originalBuilder = $this->originalBuilder; + + if ($this->options['noGroupByOnCount']) { + $originalBuilder = $this->removeGroupBy($originalBuilder); + } + + return $originalBuilder->count(); + } + + public function getArray() + { + return $this->getCollection($this->builder)->toArray(); + } + + public function reset() + { + $this->builder = $this->originalBuilder; + return $this; + } + + + public function setSearchOperator($value = "LIKE") + { + $this->options['searchOperator'] = $value; + return $this; + } + + public function setSearchWithAlias() + { + $this->options['searchWithAlias'] = true; + return $this; + } + + public function setNoGroupByOnCount() + { + $this->options['noGroupByOnCount'] = true; + return $this; + } + + //--------PRIVATE FUNCTIONS + + protected function internalMake(Collection $columns, array $searchColumns = array()) + { + $builder = clone $this->builder; + $countBuilder = clone $this->builder; + + $builder = $this->doInternalSearch($builder, $searchColumns); + $countBuilder = $this->doInternalSearch($countBuilder, $searchColumns); + + if($this->options['searchWithAlias']) + { + $this->options['counter'] = count($countBuilder->get()); + } + else + { + // Remove the GROUP BY clause for the count + if ($this->options['noGroupByOnCount']) { + $countBuilder = $this->removeGroupBy($countBuilder); + } + $this->options['counter'] = $countBuilder->count(); + } + + $builder = $this->doInternalOrder($builder, $columns); + $collection = $this->compile($builder, $columns); + + return $collection; + } + + /** + * Remove the GROUP BY clause from a builder. + * + * @param Builder|QueryBuilder $builder + * @return Builder|QueryBuilder $builder with the groups property set to null. + */ + private function removeGroupBy($builder) + { + // Handle \Illuminate\Database\Eloquent\Builder + if ($builder instanceof Builder) { + $query = $builder->getQuery(); + $query->groups = null; + $builder->setQuery($query); + } + // Handle \Illuminate\Database\Query\Builder + else { + $builder->groups = null; + } + + return $builder; + } + + /** + * @param $builder + * @return Collection + */ + private function getCollection($builder) + { + if($this->collection == null) + { + if($this->skip > 0) + { + $builder = $builder->skip($this->skip); + } + if($this->limit > 0) + { + $builder = $builder->take($this->limit); + } + //dd($this->builder->toSql()); + $this->collection = $builder->get(); + + if(is_array($this->collection)) + $this->collection = new Collection($this->collection); + } + return $this->collection; + } + + private function doInternalSearch($builder, $columns) + { + if (!empty($this->search)) { + $this->buildSearchQuery($builder, $columns); + } + + if (!empty($this->columnSearches)) { + $this->buildSingleColumnSearches($builder); + } + + return $builder; + } + protected function buildSearchQuery($builder, $columns) + { + $like = $this->options['searchOperator']; + $search = $this->search; + $exact = $this->exactWordSearch; + $builder = $builder->where(function($query) use ($columns, $search, $like, $exact) { + foreach ($columns as $c) { + //column to search within relationships : relatedModel::column + if(strrpos($c, '::')) { + $c = explode('::', $c); + $query->orWhereHas($c[0], function($q) use($c, $like, $exact, $search){ + $q->where($c[1], $like, $exact ? $search : '%' . $search . '%'); + }); + } + //column to CAST following the pattern column:newType:[maxlength] + elseif(strrpos($c, ':')){ + $c = explode(':', $c); + if(isset($c[2])) + $c[1] .= "($c[2])"; + $query->orWhereRaw("cast($c[0] as $c[1]) ".$like." ?", array($exact ? "$search" : "%$search%")); + } + else + $query->orWhere($c, $like, $exact ? $search : '%' . $search . '%'); + } + }); + return $builder; + } + + /** + * @param $builder + * Modified by sburkett to facilitate individual exact match searching on individual columns (rather than for all columns) + */ + + private function buildSingleColumnSearches($builder) + { + foreach ($this->columnSearches as $index => $searchValue) { + if(@$this->columnSearchExact[ $this->fieldSearches[$index] ] == 1) { + $builder->where($this->fieldSearches[$index], '=', $searchValue ); + } else { + $builder->where($this->fieldSearches[$index], $this->options['searchOperator'], '%' . $searchValue . '%'); + } + } + + } + + private function compile($builder, $columns) + { + $this->resultCollection = $this->getCollection($builder); + + $self = $this; + $this->resultCollection = $this->resultCollection->map(function($row) use ($columns,$self) { + $entry = array(); + // add class and id if needed + if(!is_null($self->getRowClass()) && is_callable($self->getRowClass())) + { + $entry['DT_RowClass'] = call_user_func($self->getRowClass(),$row); + } + if(!is_null($self->getRowId()) && is_callable($self->getRowId())) + { + $entry['DT_RowId'] = call_user_func($self->getRowId(),$row); + } + if(!is_null($self->getRowData()) && is_callable($self->getRowData())) + { + $entry['DT_RowData'] = call_user_func($self->getRowData(),$row); + } + $i = 0; + foreach ($columns as $col) + { + if($self->getAliasMapping()) + { + $entry[$col->getName()] = $col->run($row); + } + else + { + $entry[$i] = $col->run($row); + } + $i++; + } + return $entry; + }); + return $this->resultCollection; + } + + private function doInternalOrder($builder, $columns) + { + //var_dump($this->orderColumn); + if(!is_null($this->orderColumn)) + { + $i = 0; + foreach($columns as $col) + { + + if($i === (int) $this->orderColumn[0]) + { + if(strrpos($this->orderColumn[1], ':')){ + $c = explode(':', $this->orderColumn[1]); + if(isset($c[2])) + $c[1] .= "($c[2])"; + $builder = $builder->orderByRaw("cast($c[0] as $c[1]) ".$this->orderDirection); + } + else + $builder = $builder->orderBy($col->getName(), $this->orderDirection); + return $builder; + } + $i++; + } + } + return $builder; + } +} diff --git a/code/vendor/chumper/datatable/src/Chumper/Datatable/Facades/DatatableFacade.php b/code/vendor/chumper/datatable/src/Chumper/Datatable/Facades/DatatableFacade.php new file mode 100644 index 000000000..9eaf29dad --- /dev/null +++ b/code/vendor/chumper/datatable/src/Chumper/Datatable/Facades/DatatableFacade.php @@ -0,0 +1,14 @@ +config = Config::get('chumper.datatable.table'); + + $this->setId( $this->config['id'] ); + $this->setClass( $this->config['class'] ); + $this->setOptions( $this->config['options'] ); + $this->setCallbacks( $this->config['callbacks'] ); + + $this->noScript = $this->config['noScript']; + $this->table_view = $this->config['table_view']; + $this->script_view = $this->config['script_view']; + } + + + /** + * @return $this + */ + public function addColumn() + { + foreach (func_get_args() as $title) + { + if(is_array($title)) + { + foreach ($title as $mapping => $arrayTitle) + { + $this->columns[] = $arrayTitle; + $this->aliasColumns[] = $mapping; + if(is_string($mapping)) + { + $this->createdMapping = false; + } + } + } + else + { + $this->columns[] = $title; + $this->aliasColumns[] = count($this->aliasColumns)+1; + } + } + return $this; + } + + /** + * @return int + */ + public function countColumns() + { + return count($this->columns); + } + + /** + * @return $this + */ + public function removeOption($key) + { + if(isset($this->options[$key])) unset($this->options[$key]); + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function setOptions() + { + if(func_num_args() == 2) + { + $this->options[func_get_arg(0)] =func_get_arg(1); + } + else if(func_num_args() == 1 && is_array(func_get_arg(0))) + { + foreach (func_get_arg(0) as $key => $option) + { + $this->options[$key] = $option; + } + } + else + throw new Exception('Invalid number of options provided for the method "setOptions"'); + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function setOrder($order = array()) + { + $_orders = array(); + foreach ($order as $number => $sort) + { + $_orders[] = [$number, $sort]; + } + + $this->callbacks['aaSorting'] = $_orders; + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function setCallbacks() + { + if(func_num_args() == 2) + { + $this->callbacks[func_get_arg(0)] = func_get_arg(1); + } + else if(func_num_args() == 1 && is_array(func_get_arg(0))) + { + foreach (func_get_arg(0) as $key => $value) + { + $this->callbacks[$key] = $value; + } + } + else + throw new Exception('Invalid number of callbacks provided for the method "setCallbacks"'); + + return $this; + } + + /** + * @return $this + * @throws \Exception + */ + public function setCustomValues() + { + if(func_num_args() == 2) + { + $this->customValues[func_get_arg(0)] = func_get_arg(1); + } + else if(func_num_args() == 1 && is_array(func_get_arg(0))) + { + foreach (func_get_arg(0) as $key => $value) + { + $this->customValues[$key] = $value; + } + } + else + throw new Exception('Invalid number of custom values provided for the method "setCustomValues"'); + + return $this; + } + + /** + * @param array $data + * @return $this + */ + public function setData(array $data) + { + $this->data = $data; + return $this; + } + + /** + * @param $url + * @return $this + */ + public function setUrl($url) + { + $this->options['sAjaxSource'] = $url; + $this->options['bServerSide'] = true; + return $this; + } + + /** + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * @return array + */ + public function getCallbacks() + { + return $this->callbacks; + } + + /** + * @return array + */ + public function getCustomValues() + { + return $this->customValues; + } + + /** + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * @param null $view + * @return mixed + */ + public function render($view = null) + { + if( ! is_null($view)) + $this->table_view = $view; + + return View::make($this->table_view, $this->getViewParameters()); + } + + /** + * returns an array with the parameters that will be passed to the view when it's rendered + * @return array + */ + public function getViewParameters() + { + if(!isset($this->options['sAjaxSource'])) + { + $this->setUrl(Request::url()); + } + + // create mapping for frontend + if(!$this->createdMapping) + { + $this->createMapping(); + } + return array( + 'options' => $this->convertData(array_merge($this->options, $this->callbacks)), + 'values' => $this->customValues, + 'data' => $this->data, + 'columns' => array_combine($this->aliasColumns,$this->columns), + 'noScript' => $this->noScript, + 'id' => $this->idName, + 'class' => $this->className, + ); + } + + /** + * Instructs the table not to echo the javascript + * + * @return $this + */ + public function noScript() + { + $this->noScript = true; + return $this; + } + + private function convertData($options) { + $is_obj = false; + $first = true; + $data = ""; + foreach ($options as $k => $o) { + if ($first == true) { + if (!is_numeric($k)) { + $is_obj = true; + } + $first = false; + } else { + $data .= ",\n"; + } + if (!is_numeric($k)) { + $data .= json_encode($k) . ":"; + } + if (is_string($o)) { + if (@preg_match("#^\s*function\s*\([^\)]*#", $o)) { + $data .= $o; + } else { + $data .= json_encode($o); + } + } else { + if (is_array($o)) { + $data .= $this->convertData($o); + } else { + $data .= json_encode($o); + } + } + } + + if ($is_obj) { + $data = "{ $data }"; + } else { + $data = "[ $data ]"; + } + + return $data; + } + + public function script($view = null) + { + if( ! is_null($view)) + $this->script_view = $view; + + // create mapping for frontend + if(!$this->createdMapping) + { + $this->createMapping(); + } + + return View::make($this->script_view,array( + 'options' => $this->convertData(array_merge($this->options, $this->callbacks)), + 'id' => $this->idName, + )); + } + + public function getId() + { + return $this->idName; + } + + public function setId($id = '') + { + $this->idName = empty($id)? str_random(8) : $id; + return $this; + } + + public function getClass() + { + return $this->className; + } + + public function setClass($class) + { + $this->className = $class; + return $this; + } + + public function setAliasMapping($value) + { + $this->createdMapping = !$value; + return $this; + } + + //--------------------PRIVATE FUNCTIONS + + private function createMapping() + { + // set options for better handling + // merge with existing options + if(!array_key_exists('aoColumns', $this->options)) + { + $this->options['aoColumns'] = array(); + } + $matching = array(); + $i = 0; + foreach($this->aliasColumns as $name) + { + if(array_key_exists($i,$this->options['aoColumns'])) + { + $this->options['aoColumns'][$i] = array_merge_recursive($this->options['aoColumns'][$i],array('mData' => $name)); + } + else + { + $this->options['aoColumns'][$i] = array('mData' => $name); + } + $i++; + } + $this->createdMapping = true; + //dd($matching); + return $matching; + } +} diff --git a/code/vendor/chumper/datatable/src/config/.gitkeep b/code/vendor/chumper/datatable/src/config/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/code/vendor/chumper/datatable/src/config/config.php b/code/vendor/chumper/datatable/src/config/config.php new file mode 100644 index 000000000..013421802 --- /dev/null +++ b/code/vendor/chumper/datatable/src/config/config.php @@ -0,0 +1,146 @@ + array( + + /* + |-------------------------------------------------------------------------- + | Table class + |-------------------------------------------------------------------------- + | + | Class(es) added to the table + | Supported: string + | + */ + + 'class' => 'table table-bordered', + + /* + |-------------------------------------------------------------------------- + | Table ID + |-------------------------------------------------------------------------- + | + | ID given to the table. Used for connecting the table and the Datatables + | jQuery plugin. If left empty a random ID will be generated. + | Supported: string + | + */ + + 'id' => '', + + /* + |-------------------------------------------------------------------------- + | DataTable options + |-------------------------------------------------------------------------- + | + | jQuery dataTable plugin options. The array will be json_encoded and + | passed through to the plugin. See https://datatables.net/usage/options + | for more information. + | Supported: array + | + */ + + 'options' => array( + + "sPaginationType" => "full_numbers", + + "bProcessing" => false + + ), + + /* + |-------------------------------------------------------------------------- + | DataTable callbacks + |-------------------------------------------------------------------------- + | + | jQuery dataTable plugin callbacks. The array will be json_encoded and + | passed through to the plugin. See https://datatables.net/usage/callbacks + | for more information. + | Supported: array + | + */ + + 'callbacks' => array(), + + /* + |-------------------------------------------------------------------------- + | Skip javascript in table template + |-------------------------------------------------------------------------- + | + | Determines if the template should echo the javascript + | Supported: boolean + | + */ + + 'noScript' => false, + + + /* + |-------------------------------------------------------------------------- + | Table view + |-------------------------------------------------------------------------- + | + | Template used to render the table + | Supported: string + | + */ + + 'table_view' => 'chumper.datatable::template', + + + /* + |-------------------------------------------------------------------------- + | Script view + |-------------------------------------------------------------------------- + | + | Template used to render the javascript + | Supported: string + | + */ + + 'script_view' => 'chumper.datatable::javascript', + ), + + + /* + |-------------------------------------------------------------------------- + | Engine specific configuration options. + |-------------------------------------------------------------------------- + | + */ + + 'engine' => array( + + /* + |-------------------------------------------------------------------------- + | Search for exact words + |-------------------------------------------------------------------------- + | + | If the search should be done with exact matching + | Supported: boolean + | + */ + + 'exactWordSearch' => false, + + ), + /* + |-------------------------------------------------------------------------- + | Allow overrides Datatable core classes + |-------------------------------------------------------------------------- + | + */ + 'classmap' => array( + 'CollectionEngine' => 'Chumper\Datatable\Engines\CollectionEngine', + 'QueryEngine' => 'Chumper\Datatable\Engines\QueryEngine', + 'Table' => 'Chumper\Datatable\Table', + ) +); diff --git a/code/vendor/chumper/datatable/src/views/javascript.blade.php b/code/vendor/chumper/datatable/src/views/javascript.blade.php new file mode 100644 index 000000000..8e216c848 --- /dev/null +++ b/code/vendor/chumper/datatable/src/views/javascript.blade.php @@ -0,0 +1,8 @@ + diff --git a/code/vendor/chumper/datatable/src/views/template.blade.php b/code/vendor/chumper/datatable/src/views/template.blade.php new file mode 100644 index 000000000..e432196fa --- /dev/null +++ b/code/vendor/chumper/datatable/src/views/template.blade.php @@ -0,0 +1,63 @@ + + + @for ($i = 0; $i < count($columns); $i++) + + @endfor + + + + @foreach($columns as $i => $c) + + @endforeach + + + + @foreach($data as $d) + + @foreach($d as $dd) + + @endforeach + + @endforeach + +
{!! $c !!}
{!! $dd !!}
+ + + +@if (!$noScript) + @include(Config::get('chumper.datatable.table.script_view'), array('id' => $id, 'options' => $options)) +@endif diff --git a/code/vendor/chumper/datatable/tests/.gitkeep b/code/vendor/chumper/datatable/tests/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/code/vendor/chumper/datatable/tests/Columns/DateColumnTest.php b/code/vendor/chumper/datatable/tests/Columns/DateColumnTest.php new file mode 100644 index 000000000..a5df9601a --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Columns/DateColumnTest.php @@ -0,0 +1,56 @@ +shouldReceive('toDateString') + ->withNoArgs()->once() + ->andReturn('fooBar'); + + $column2 = new DateColumn('foo', DateColumn::TIME, 'foo'); + $c->shouldReceive('toTimeString') + ->withNoArgs()->once() + ->andReturn('fooBar'); + + $column3 = new DateColumn('foo', DateColumn::DATE_TIME, 'foo'); + $c->shouldReceive('toDateTimeString') + ->withNoArgs()->once() + ->andReturn('fooBar'); + + $column4 = new DateColumn('foo', DateColumn::CUSTOM, 'foo'); + $c->shouldReceive('format') + ->with('foo')->once() + ->andReturn('fooBar'); + + $column5 = new DateColumn('foo', DateColumn::FORMATTED_DATE, 'foo'); + $c->shouldReceive('toFormattedDateString') + ->withNoArgs()->once() + ->andReturn('fooBar'); + + $column6 = new DateColumn('foo', DateColumn::DAY_DATE, 'foo'); + $c->shouldReceive('toDayDateTimeString') + ->withNoArgs()->once() + ->andReturn('fooBar'); + + //now test + $this->assertEquals('fooBar', $column1->run(array('foo' => $c))); + $this->assertEquals('fooBar', $column2->run(array('foo' => $c))); + $this->assertEquals('fooBar', $column3->run(array('foo' => $c))); + $this->assertEquals('fooBar', $column4->run(array('foo' => $c))); + $this->assertEquals('fooBar', $column5->run(array('foo' => $c))); + $this->assertEquals('fooBar', $column6->run(array('foo' => $c))); + } + + protected function tearDown() + { + Mockery::close(); + } +} + \ No newline at end of file diff --git a/code/vendor/chumper/datatable/tests/Columns/FunctionColumnTest.php b/code/vendor/chumper/datatable/tests/Columns/FunctionColumnTest.php new file mode 100644 index 000000000..68ed7562b --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Columns/FunctionColumnTest.php @@ -0,0 +1,31 @@ +assertEquals('FooBar', $column->run(array())); + } + + public function testAdvanced() + { + $column = new FunctionColumn('foo',function($model){ + return $model['text']; + }); + $this->assertEquals('FooBar', $column->run(array('text' => 'FooBar'))); + } + + public function testAdvanced2() + { + $column = new FunctionColumn('foo',function($model){ + return $model['text'].'Bar'; + }); + $this->assertEquals('FooBar', $column->run(array('text' => 'Foo'))); + } + +} diff --git a/code/vendor/chumper/datatable/tests/Columns/TextColumnTest.php b/code/vendor/chumper/datatable/tests/Columns/TextColumnTest.php new file mode 100644 index 000000000..f6a617f46 --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Columns/TextColumnTest.php @@ -0,0 +1,13 @@ +assertEquals('FooBar', $column->run(array())); + } + +} diff --git a/code/vendor/chumper/datatable/tests/DatatableTest.php b/code/vendor/chumper/datatable/tests/DatatableTest.php new file mode 100644 index 000000000..e1af22ddd --- /dev/null +++ b/code/vendor/chumper/datatable/tests/DatatableTest.php @@ -0,0 +1,61 @@ +zeroOrMoreTimes()->with("chumper.datatable.engine")->andReturn( + array( + 'exactWordSearch' => false, + ) + ); + Config::shouldReceive('get')->zeroOrMoreTimes()->with("chumper.datatable.classmap.QueryEngine",NULL)->andReturn('Chumper\Datatable\Engines\QueryEngine'); + Config::shouldReceive('get')->zeroOrMoreTimes()->with("chumper.datatable.classmap.CollectionEngine",NULL)->andReturn('Chumper\Datatable\Engines\CollectionEngine'); + Config::shouldReceive('get')->zeroOrMoreTimes()->with("chumper.datatable.classmap.Table",NULL)->andReturn('Chumper\Datatable\Table'); + + Config::shouldReceive('get')->zeroOrMoreTimes()->with("chumper.datatable.table")->andReturn( + array( + 'class' => 'table table-bordered', + 'id' => '', + 'options' => array( + "sPaginationType" => "full_numbers", + "bProcessing" => false + ), + 'callbacks' => array(), + 'noScript' => false, + 'table_view' => 'datatable::template', + 'script_view' => 'datatable::javascript', + ) + ); + + $this->dt = new Datatable; + $this->mock = Mockery::mock('Illuminate\Database\Query\Builder'); + } + + public function testReturnInstances() + { + $api = $this->dt->query($this->mock); + + $this->assertInstanceOf('Chumper\Datatable\Engines\QueryEngine', $api); + + $api = $this->dt->collection(new Collection()); + + $this->assertInstanceOf('Chumper\Datatable\Engines\CollectionEngine', $api); + + $table = $this->dt->table(); + + $this->assertInstanceOf('Chumper\Datatable\Table', $table); + } + +} diff --git a/code/vendor/chumper/datatable/tests/Engines/BaseEngineTest.php b/code/vendor/chumper/datatable/tests/Engines/BaseEngineTest.php new file mode 100644 index 000000000..5db48e8fe --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Engines/BaseEngineTest.php @@ -0,0 +1,113 @@ +zeroOrMoreTimes()->with("chumper.datatable.engine")->andReturn( + array( + 'exactWordSearch' => false, + ) + ); + + $this->collection = new Collection(); + $this->engine = new CollectionEngine($this->collection); + } + + + /** + * @expectedException Exception + */ + public function testAddColumn() + { + $this->engine->addColumn('foo', 'bar'); + + $this->assertInstanceOf( + 'Chumper\Datatable\Columns\TextColumn', + $this->engine->getColumn('foo') + ); + + $this->engine->addColumn('foo2', function($model){return $model->fooBar;}); + + $this->assertInstanceOf( + 'Chumper\Datatable\Columns\FunctionColumn', + $this->engine->getColumn('foo2') + ); + + $this->assertEquals(array(1 => 'foo2', 0 => 'foo'), $this->engine->getOrder()); + + $this->engine->addColumn(); + } + + public function testClearColumns() + { + $this->engine->addColumn('foo','Bar'); + $this->assertInstanceOf( + 'Chumper\Datatable\Columns\TextColumn', + $this->engine->getColumn('foo') + ); + + $this->engine->clearColumns(); + $this->assertEquals(array(), $this->engine->getOrder()); + } + + public function testSearchColumns() + { + $this->engine->searchColumns('id'); + + $this->assertEquals(array('id'), $this->engine->getSearchingColumns()); + + $this->engine->searchColumns('name', 'email'); + + $this->assertEquals(array('name','email'), $this->engine->getSearchingColumns()); + + $this->engine->searchColumns(array('foo', 'bar')); + + $this->assertEquals(array('foo', 'bar'), $this->engine->getSearchingColumns()); + } + + public function testOrderColumns() + { + $this->engine->orderColumns('id'); + + $this->assertEquals(array('id'), $this->engine->getOrderingColumns()); + + $this->engine->orderColumns('name', 'email'); + + $this->assertEquals(array('name','email'), $this->engine->getOrderingColumns()); + + $this->engine->orderColumns(array('foo', 'bar')); + + $this->assertEquals(array('foo', 'bar'), $this->engine->getOrderingColumns()); + } + + public function testShowColumns() + { + $this->engine->showColumns('id'); + + $this->assertEquals(array('id'), $this->engine->getOrder()); + + $this->engine->showColumns('name', 'email'); + + $this->assertEquals(array('id','name','email'), $this->engine->getOrder()); + + $this->engine->showColumns(array('foo', 'bar')); + + $this->assertEquals(array('id','name','email', 'foo', 'bar'), $this->engine->getOrder()); + } +} + \ No newline at end of file diff --git a/code/vendor/chumper/datatable/tests/Engines/CollectionEngineTest.php b/code/vendor/chumper/datatable/tests/Engines/CollectionEngineTest.php new file mode 100644 index 000000000..c9cbaaa41 --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Engines/CollectionEngineTest.php @@ -0,0 +1,328 @@ +set('chumper.datatable.engine', array( + 'exactWordSearch' => false, + )); + } + + public function setUp() + { + + parent::setUp(); + + $this->collection = Mockery::mock('Illuminate\Support\Collection'); + $this->c = new CollectionEngine($this->collection); + } + + public function testOrder() + { + $should = array( + array( + 'id' => 'eoo' + ), + array( + 'id' => 'foo' + ) + ); + + Input::replace( + array( + 'iSortCol_0' => 0, + 'sSortDir_0' => 'asc', + ) + ); + + $engine = new CollectionEngine(new Collection($this->getTestArray())); + $engine->addColumn(new FunctionColumn('id', function($model){return $model['id'];})); + $engine->setAliasMapping(); + $this->assertEquals($should, $engine->getArray()); + + Input::merge( + array( + 'iSortCol_0' => 0, + 'sSortDir_0' => 'desc' + ) + ); + + $should2 = array( + array( + 'id' => 'foo' + ), + array( + 'id' => 'eoo' + ) + ); + + $this->assertEquals($should2, $engine->getArray()); + + } + + public function testSearch() + { + // Facade expection + Input::replace( + array( + 'sSearch' => 'eoo' + ) + ); + + $engine = new CollectionEngine(new Collection($this->getTestArray())); + $engine->addColumn($this->getTestColumns()); + $engine->searchColumns('id'); + $engine->setAliasMapping(); + + $should = '{"aaData":[{"id":"eoo"}],"sEcho":0,"iTotalRecords":2,"iTotalDisplayRecords":1}'; + $actual = $engine->make()->getContent(); + + $this->assertEquals($should,$actual); + //------------------TEST 2----------------- + // search in outputed data + $engine = new CollectionEngine(new Collection(array(array('foo', 'foo2', 'foo3'),array('bar', 'bar2', 'bar3')))); + $engine->addColumn(new FunctionColumn('bla', function($row){return $row[0]." - ".$row[1];})); + $engine->addColumn(new FunctionColumn('1', function($row){return $row[2];})); + $engine->addColumn(new FunctionColumn('bla3', function($row){return $row[0]." - ".$row[2];})); + $engine->searchColumns("bla",1); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'foo2' + ) + ); + + $should = array( + array( + 'bla' => 'foo - foo2', + '1' => 'foo3', + 'bla3' => 'foo - foo3' + ) + ); + + $response = json_decode($engine->make()->getContent()); + $this->assertEquals(json_encode($should), json_encode((array)($response->aaData))); + + //------------------TEST 3----------------- + // search in initial data + // TODO: Search in initial data columns? + + $engine = new CollectionEngine(new Collection(array(array('foo', 'foo2', 'foo3'),array('bar', 'bar2', 'bar3')))); + $engine->addColumn(new FunctionColumn('bla3', function($row){return $row[0]." - ".$row[2];})); + $engine->addColumn(new FunctionColumn('1', function($row){return $row[1];})); + $engine->searchColumns("bla3",1); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'foo2' + ) + ); + + $should = array( + array( + 'bla3' => 'foo - foo3', + '1' => 'foo2' + ) + ); + + $response = json_decode($engine->make()->getContent()); + $this->assertEquals(json_encode($should), json_encode($response->aaData)); + } + + public function testSkip() + { + $engine = new CollectionEngine(new Collection($this->getTestArray())); + + $engine->addColumn($this->getTestColumns()); + $engine->setAliasMapping(); + + Input::replace( + array( + 'iDisplayStart' => 1 + ) + ); + + $should = array( + array( + 'id' => 'eoo', + ) + ); + $this->assertEquals($should, $engine->getArray()); + } + + public function testTake() + { + Input::replace( + array( + 'iDisplayLength' => 1 + ) + ); + + $engine = new CollectionEngine(new Collection($this->getTestArray())); + $engine->addColumn($this->getTestColumns()); + $engine->setAliasMapping(); + $engine->make(); + + $should = array( + array( + 'id' => 'foo', + ) + ); + $this->assertEquals($should, $engine->getArray()); + } + + public function testComplex() + { + $engine = new CollectionEngine(new Collection($this->getRealArray())); + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 't' + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',(array) $test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',(array) $test)); + + //Test2 + $engine = new CollectionEngine(new Collection($this->getRealArray())); + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'plasch' + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',(array) $test)); + $this->assertFalse($this->arrayHasKeyValue('foo','Taylor',(array) $test)); + + //test3 + $engine = new CollectionEngine(new Collection($this->getRealArray())); + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'tay' + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + + + $this->assertFalse($this->arrayHasKeyValue('foo','Nils',(array) $test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',(array) $test)); + + //test4 + $engine = new CollectionEngine(new Collection($this->getRealArray())); + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'O' + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertFalse($this->arrayHasKeyValue('foo','Nils',(array) $test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',(array) $test)); + + } + + public function tearDown() + { + Mockery::close(); + } + + private function getTestArray() + { + return array( + array( + 'id' => 'foo' + ), + array( + 'id' => 'eoo' + ) + ); + } + private function getRealArray() + { + return array( + array( + 'name' => 'Nils Plaschke', + 'email'=> 'github@nilsplaschke.de' + ), + array( + 'name' => 'Taylor Otwell', + 'email'=> 'taylorotwell@gmail.com' + ) + ); + } + + private function addRealColumns($engine) + { + $engine->addColumn(new FunctionColumn('foo', function($m){return $m['name'];})); + $engine->addColumn(new FunctionColumn('bar', function($m){return $m['email'];})); + } + + private function getTestColumns() + { + return new FunctionColumn('id', function($row){return $row['id'];}); + } + + private function arrayHasKeyValue($key,$value,$array) + { + $array = array_pluck($array,$key); + foreach ($array as $val) + { + if(str_contains($val, $value)) + return true; + } + return false; + + } +} \ No newline at end of file diff --git a/code/vendor/chumper/datatable/tests/Engines/QueryEngineTest.php b/code/vendor/chumper/datatable/tests/Engines/QueryEngineTest.php new file mode 100644 index 000000000..6798a2b2e --- /dev/null +++ b/code/vendor/chumper/datatable/tests/Engines/QueryEngineTest.php @@ -0,0 +1,251 @@ +zeroOrMoreTimes()->with("chumper.datatable.engine")->andReturn( + array( + 'exactWordSearch' => false, + ) + ); + + $this->builder = Mockery::mock('Illuminate\Database\Query\Builder'); + + $this->c = new QueryEngine($this->builder); + } + + public function testOrder() + { + $this->builder->shouldReceive('orderBy')->with('id', BaseEngine::ORDER_ASC); + + Input::merge( + array( + 'iSortCol_0' => 0, + 'sSortDir_0' => 'asc' + ) + ); + + //-- + + $this->builder->shouldReceive('orderBy')->with('id', BaseEngine::ORDER_DESC); + + Input::merge( + array( + 'iSortCol_0' => 0, + 'sSortDir_0' => 'desc' + ) + ); + + } + + public function testSearch() + { + $this->builder->shouldReceive('where')->withAnyArgs()->andReturn($this->builder); + $this->builder->shouldReceive('get')->once()->andReturn(new Collection($this->getRealArray())); + $this->builder->shouldReceive('count')->twice()->andReturn(10); + $this->builder->shouldReceive('orderBy')->withAnyArgs()->andReturn($this->builder); + + $this->c = new QueryEngine($this->builder); + + $this->addRealColumns($this->c); + $this->c->searchColumns('foo'); + + Input::merge( + array( + 'sSearch' => 'test' + ) + ); + + $test = json_decode($this->c->make()->getContent()); + $test = $test->aaData; + } + + public function testSkip() + { + $this->builder->shouldReceive('skip')->once()->with(1)->andReturn($this->builder); + $this->builder->shouldReceive('get')->once()->andReturn(new Collection($this->getRealArray())); + $this->builder->shouldReceive('count')->twice()->andReturn(10); + $this->builder->shouldReceive('orderBy')->withAnyArgs()->andReturn($this->builder); + + $this->c = new QueryEngine($this->builder); + + $this->addRealColumns($this->c); + + Input::merge( + array( + 'iDisplayStart' => 1, + 'sSearch' => null + ) + ); + + $this->c->searchColumns('foo'); + + $test = json_decode($this->c->make()->getContent()); + $test = $test->aaData; + } + + public function testTake() + { + $this->builder->shouldReceive('take')->once()->with(1)->andReturn($this->builder); + $this->builder->shouldReceive('get')->once()->andReturn(new Collection($this->getRealArray())); + $this->builder->shouldReceive('count')->twice()->andReturn(10); + $this->builder->shouldReceive('orderBy')->withAnyArgs()->andReturn($this->builder); + + $this->c = new QueryEngine($this->builder); + + $this->addRealColumns($this->c); + + Input::merge( + array( + 'iDisplayLength' => 1, + 'sSearch' => null, + 'iDisplayStart' => null + ) + ); + + $this->c->searchColumns('foo'); + + $test = json_decode($this->c->make()->getContent()); + $test = $test->aaData; + } + + public function testComplex() + { + + + $this->builder->shouldReceive('get')->andReturn(new Collection($this->getRealArray())); + $this->builder->shouldReceive('where')->withAnyArgs()->andReturn($this->builder); + $this->builder->shouldReceive('count')->times(8)->andReturn(10); + + $engine = new QueryEngine($this->builder); + + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 't', + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',$test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',$test)); + + //Test2 + $engine = new QueryEngine($this->builder); + + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'plasch', + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',$test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',$test)); + + //test3 + $engine = new QueryEngine($this->builder); + + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => 'tay', + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',$test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',$test)); + + //test4 + $engine = new QueryEngine($this->builder); + + $this->addRealColumns($engine); + $engine->searchColumns('foo','bar'); + $engine->setAliasMapping(); + + Input::replace( + array( + 'sSearch' => '0', + ) + ); + + $test = json_decode($engine->make()->getContent()); + $test = $test->aaData; + + $this->assertTrue($this->arrayHasKeyValue('foo','Nils',$test)); + $this->assertTrue($this->arrayHasKeyValue('foo','Taylor',$test)); + } + + protected function tearDown() + { + Mockery::close(); + } + + private function getRealArray() + { + return array( + array( + 'name' => 'Nils Plaschke', + 'email'=> 'github@nilsplaschke.de' + ), + array( + 'name' => 'Taylor Otwell', + 'email'=> 'taylorotwell@gmail.com' + ) + ); + } + + private function addRealColumns($engine) + { + $engine->addColumn(new FunctionColumn('foo', function($m){return $m['name'];})); + $engine->addColumn(new FunctionColumn('bar', function($m){return $m['email'];})); + } + + private function arrayHasKeyValue($key,$value,$array) + { + $array = array_pluck($array,$key); + foreach ($array as $val) + { + if(str_contains($val, $value)) + return true; + } + return false; + + } + +} diff --git a/code/vendor/chumper/datatable/tests/TableTest.php b/code/vendor/chumper/datatable/tests/TableTest.php new file mode 100644 index 000000000..84a0f48a7 --- /dev/null +++ b/code/vendor/chumper/datatable/tests/TableTest.php @@ -0,0 +1,179 @@ +set('chumper.datatable.table', array( + 'class' => 'table table-bordered', + 'id' => '', + 'options' => array( + "sPaginationType" => "full_numbers", + "bProcessing" => false + ), + 'callbacks' => array(), + 'noScript' => false, + 'table_view' => 'datatable::template', + 'script_view' => 'datatable::javascript', + )); + } + + public function setUp() + { + parent::setUp(); + + $this->table = new Table(); + } + + /** + * @expectedException Exception + */ + public function testSetOptions() + { + $this->table->setOptions('foo','bar'); + + $this->table->setOptions(array( + 'foo2' => 'bar2', + 'foo3' => 'bar3' + )); + + $this->table->setOptions('foo', 'bar', 'baz'); + } + + /** + * @expectedException Exception + */ + public function testSetCallbacks() + { + $this->table->setCallbacks('foo', 'bar'); + $this->assertArrayHasKey('foo', $this->table->getCallbacks()); + + $this->table->setCallbacks(array( + 'foo2' => 'bar2', + 'foo3' => 'bar3' + )); + $this->assertArrayHasKey('foo2', $this->table->getCallbacks()); + $this->assertArrayHasKey('foo3', $this->table->getCallbacks()); + + $this->table->setCallbacks('foo', 'bar', 'baz'); + $this->assertTrue(False); // should throw exception before here + } + + public function testSetNamedFunctionAsCallback() + { + //set an anonymous function + $this->table->setCallbacks(['foo'=>'function(){ return foo; }']); + //set a named function + $this->table->setCallbacks(['bar'=>'myBar']); + $parameters = $this->table->getViewParameters(); + + //an anonymous function should be included as it is. + $this->assertThat($parameters['options'],$this->stringContains('"foo":function(){ return foo; }') ); + //the callback it's a function name, it shouldn't be quoted + $this->assertThat($parameters['options'],$this->stringContains('"bar":myBar') ); + } + + /** + * @expectedException Exception + */ + public function testSetCustomValues() + { + $this->table->setCustomValues('foo', 'bar'); + $this->assertArrayHasKey('foo', $this->table->getCustomValues()); + + $this->table->setCustomValues(array( + 'foo2' => 'bar2', + 'foo3' => 'bar3' + )); + $this->assertArrayHasKey('foo2', $this->table->getCustomValues()); + $this->assertArrayHasKey('foo3', $this->table->getCustomValues()); + + $this->table->setCustomValues('foo', 'bar', 'baz'); + $this->assertTrue(False); // should throw exception before here + } + + public function testAddColumn() + { + $this->table->addColumn('foo'); + + $this->assertEquals(1, $this->table->countColumns()); + + $this->table->addColumn('foo1','foo2'); + + $this->assertEquals(3, $this->table->countColumns()); + + $this->table->addColumn(array('foo3','foo4')); + + $this->assertEquals(5, $this->table->countColumns()); + } + + public function testRender() + { + + View::shouldReceive('make')->once() + ->with('datatable::template', \Mockery::any())->andReturn(true); + + $this->table->setUrl('fooBar'); + $table1 = $this->table->addColumn('foo')->render(); + $this->assertEquals(array( + 'options' => '{ "sPaginationType":"full_numbers",'.PHP_EOL + . '"bProcessing":false,'.PHP_EOL + . '"sAjaxSource":"fooBar",'.PHP_EOL + . '"bServerSide":true }', + 'values' => array(), + 'data' => array(), + 'columns' => array(1=>'foo'), + 'noScript' => false, + 'class' => $this->table->getClass(), + 'id' => $this->table->getId(), + + ), $this->table->getViewParameters()); + $this->assertTrue($table1); + } + + public function testSetData() + { + $data = array( + array( + 'foo', + 'bar' + ), + array( + 'foo2', + 'bar2' + ), + ); + + $this->table->setData($data); + $this->assertEquals($data,$this->table->getData()); + + } + + public function testSetUrl() + { + $this->table->setUrl('foo/url'); + + $this->assertArrayHasKey('bServerSide',$this->table->getOptions()); + $this->assertArrayHasKey('sAjaxSource',$this->table->getOptions()); + + $return = $this->table->getOptions(); + + $this->assertEquals('foo/url',$return['sAjaxSource']); + } + + public function tearDown() + { + Mockery::close(); + } +} \ No newline at end of file diff --git a/code/vendor/phpunit/phpunit/build/bin/phpab b/code/vendor/phpunit/phpunit/build/bin/phpab new file mode 100644 index 000000000..9dcc3bc86 --- /dev/null +++ b/code/vendor/phpunit/phpunit/build/bin/phpab @@ -0,0 +1,897 @@ +#!/usr/bin/env php + '/vendor/zetacomponents/base/src/base.php', + 'ezcbaseconfigurationinitializer' => '/vendor/zetacomponents/base/src/interfaces/configuration_initializer.php', + 'ezcbasedoubleclassrepositoryprefixexception' => '/vendor/zetacomponents/base/src/exceptions/double_class_repository_prefix.php', + 'ezcbaseexception' => '/vendor/zetacomponents/base/src/exceptions/exception.php', + 'ezcbaseexportable' => '/vendor/zetacomponents/base/src/interfaces/exportable.php', + 'ezcbaseextensionnotfoundexception' => '/vendor/zetacomponents/base/src/exceptions/extension_not_found.php', + 'ezcbasefeatures' => '/vendor/zetacomponents/base/src/features.php', + 'ezcbasefile' => '/vendor/zetacomponents/base/src/file.php', + 'ezcbasefileexception' => '/vendor/zetacomponents/base/src/exceptions/file_exception.php', + 'ezcbasefilefindcontext' => '/vendor/zetacomponents/base/src/structs/file_find_context.php', + 'ezcbasefileioexception' => '/vendor/zetacomponents/base/src/exceptions/file_io.php', + 'ezcbasefilenotfoundexception' => '/vendor/zetacomponents/base/src/exceptions/file_not_found.php', + 'ezcbasefilepermissionexception' => '/vendor/zetacomponents/base/src/exceptions/file_permission.php', + 'ezcbasefunctionalitynotsupportedexception' => '/vendor/zetacomponents/base/src/exceptions/functionality_not_supported.php', + 'ezcbaseinit' => '/vendor/zetacomponents/base/src/init.php', + 'ezcbaseinitcallbackconfiguredexception' => '/vendor/zetacomponents/base/src/exceptions/init_callback_configured.php', + 'ezcbaseinitinvalidcallbackclassexception' => '/vendor/zetacomponents/base/src/exceptions/invalid_callback_class.php', + 'ezcbaseinvalidparentclassexception' => '/vendor/zetacomponents/base/src/exceptions/invalid_parent_class.php', + 'ezcbasemetadata' => '/vendor/zetacomponents/base/src/metadata.php', + 'ezcbasemetadatapearreader' => '/vendor/zetacomponents/base/src/metadata/pear.php', + 'ezcbasemetadatatarballreader' => '/vendor/zetacomponents/base/src/metadata/tarball.php', + 'ezcbaseoptions' => '/vendor/zetacomponents/base/src/options.php', + 'ezcbasepersistable' => '/vendor/zetacomponents/base/src/interfaces/persistable.php', + 'ezcbasepropertynotfoundexception' => '/vendor/zetacomponents/base/src/exceptions/property_not_found.php', + 'ezcbasepropertypermissionexception' => '/vendor/zetacomponents/base/src/exceptions/property_permission.php', + 'ezcbaserepositorydirectory' => '/vendor/zetacomponents/base/src/structs/repository_directory.php', + 'ezcbasesettingnotfoundexception' => '/vendor/zetacomponents/base/src/exceptions/setting_not_found.php', + 'ezcbasesettingvalueexception' => '/vendor/zetacomponents/base/src/exceptions/setting_value.php', + 'ezcbasestruct' => '/vendor/zetacomponents/base/src/struct.php', + 'ezcbasevalueexception' => '/vendor/zetacomponents/base/src/exceptions/value.php', + 'ezcbasewhateverexception' => '/vendor/zetacomponents/base/src/exceptions/whatever.php', + 'ezcconsoleargument' => '/vendor/zetacomponents/console-tools/src/input/argument.php', + 'ezcconsoleargumentalreadyregisteredexception' => '/vendor/zetacomponents/console-tools/src/exceptions/argument_already_registered.php', + 'ezcconsoleargumentexception' => '/vendor/zetacomponents/console-tools/src/exceptions/argument.php', + 'ezcconsoleargumentmandatoryviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/argument_mandatory_violation.php', + 'ezcconsolearguments' => '/vendor/zetacomponents/console-tools/src/input/arguments.php', + 'ezcconsoleargumenttypeviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/argument_type_violation.php', + 'ezcconsoledialog' => '/vendor/zetacomponents/console-tools/src/interfaces/dialog.php', + 'ezcconsoledialogabortexception' => '/vendor/zetacomponents/console-tools/src/exceptions/dialog_abort.php', + 'ezcconsoledialogoptions' => '/vendor/zetacomponents/console-tools/src/options/dialog.php', + 'ezcconsoledialogvalidator' => '/vendor/zetacomponents/console-tools/src/interfaces/dialog_validator.php', + 'ezcconsoledialogviewer' => '/vendor/zetacomponents/console-tools/src/dialog_viewer.php', + 'ezcconsoleexception' => '/vendor/zetacomponents/console-tools/src/exceptions/exception.php', + 'ezcconsoleinput' => '/vendor/zetacomponents/console-tools/src/input.php', + 'ezcconsoleinputhelpgenerator' => '/vendor/zetacomponents/console-tools/src/interfaces/input_help_generator.php', + 'ezcconsoleinputstandardhelpgenerator' => '/vendor/zetacomponents/console-tools/src/input/help_generators/standard.php', + 'ezcconsoleinputvalidator' => '/vendor/zetacomponents/console-tools/src/interfaces/input_validator.php', + 'ezcconsoleinvalidoptionnameexception' => '/vendor/zetacomponents/console-tools/src/exceptions/invalid_option_name.php', + 'ezcconsoleinvalidoutputtargetexception' => '/vendor/zetacomponents/console-tools/src/exceptions/invalid_output_target.php', + 'ezcconsolemenudialog' => '/vendor/zetacomponents/console-tools/src/dialog/menu_dialog.php', + 'ezcconsolemenudialogdefaultvalidator' => '/vendor/zetacomponents/console-tools/src/dialog/validators/menu_dialog_default.php', + 'ezcconsolemenudialogoptions' => '/vendor/zetacomponents/console-tools/src/options/menu_dialog.php', + 'ezcconsolemenudialogvalidator' => '/vendor/zetacomponents/console-tools/src/interfaces/menu_dialog_validator.php', + 'ezcconsolenopositionstoredexception' => '/vendor/zetacomponents/console-tools/src/exceptions/no_position_stored.php', + 'ezcconsolenovaliddialogresultexception' => '/vendor/zetacomponents/console-tools/src/exceptions/no_valid_dialog_result.php', + 'ezcconsoleoption' => '/vendor/zetacomponents/console-tools/src/input/option.php', + 'ezcconsoleoptionalreadyregisteredexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_already_registered.php', + 'ezcconsoleoptionargumentsviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_arguments_violation.php', + 'ezcconsoleoptiondependencyviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_dependency_violation.php', + 'ezcconsoleoptionexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option.php', + 'ezcconsoleoptionexclusionviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_exclusion_violation.php', + 'ezcconsoleoptionmandatoryviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_mandatory_violation.php', + 'ezcconsoleoptionmissingvalueexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_missing_value.php', + 'ezcconsoleoptionnoaliasexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_no_alias.php', + 'ezcconsoleoptionnotexistsexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_not_exists.php', + 'ezcconsoleoptionrule' => '/vendor/zetacomponents/console-tools/src/structs/option_rule.php', + 'ezcconsoleoptionstringnotwellformedexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_string_not_wellformed.php', + 'ezcconsoleoptiontoomanyvaluesexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_too_many_values.php', + 'ezcconsoleoptiontypeviolationexception' => '/vendor/zetacomponents/console-tools/src/exceptions/option_type_violation.php', + 'ezcconsoleoutput' => '/vendor/zetacomponents/console-tools/src/output.php', + 'ezcconsoleoutputformat' => '/vendor/zetacomponents/console-tools/src/structs/output_format.php', + 'ezcconsoleoutputformats' => '/vendor/zetacomponents/console-tools/src/structs/output_formats.php', + 'ezcconsoleoutputoptions' => '/vendor/zetacomponents/console-tools/src/options/output.php', + 'ezcconsoleprogressbar' => '/vendor/zetacomponents/console-tools/src/progressbar.php', + 'ezcconsoleprogressbaroptions' => '/vendor/zetacomponents/console-tools/src/options/progressbar.php', + 'ezcconsoleprogressmonitor' => '/vendor/zetacomponents/console-tools/src/progressmonitor.php', + 'ezcconsoleprogressmonitoroptions' => '/vendor/zetacomponents/console-tools/src/options/progressmonitor.php', + 'ezcconsolequestiondialog' => '/vendor/zetacomponents/console-tools/src/dialog/question_dialog.php', + 'ezcconsolequestiondialogcollectionvalidator' => '/vendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_collection.php', + 'ezcconsolequestiondialogmappingvalidator' => '/vendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_mapping.php', + 'ezcconsolequestiondialogoptions' => '/vendor/zetacomponents/console-tools/src/options/question_dialog.php', + 'ezcconsolequestiondialogregexvalidator' => '/vendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_regex.php', + 'ezcconsolequestiondialogtypevalidator' => '/vendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_type.php', + 'ezcconsolequestiondialogvalidator' => '/vendor/zetacomponents/console-tools/src/interfaces/question_dialog_validator.php', + 'ezcconsolestandardinputvalidator' => '/vendor/zetacomponents/console-tools/src/input/validators/standard.php', + 'ezcconsolestatusbar' => '/vendor/zetacomponents/console-tools/src/statusbar.php', + 'ezcconsolestatusbaroptions' => '/vendor/zetacomponents/console-tools/src/options/statusbar.php', + 'ezcconsolestringtool' => '/vendor/zetacomponents/console-tools/src/tools/string.php', + 'ezcconsoletable' => '/vendor/zetacomponents/console-tools/src/table.php', + 'ezcconsoletablecell' => '/vendor/zetacomponents/console-tools/src/table/cell.php', + 'ezcconsoletableoptions' => '/vendor/zetacomponents/console-tools/src/options/table.php', + 'ezcconsoletablerow' => '/vendor/zetacomponents/console-tools/src/table/row.php', + 'ezcconsoletoomanyargumentsexception' => '/vendor/zetacomponents/console-tools/src/exceptions/argument_too_many.php', + 'theseer\\autoload\\application' => '/phpab/Application.php', + 'theseer\\autoload\\applicationexception' => '/phpab/Application.php', + 'theseer\\autoload\\autoloadbuilderexception' => '/phpab/AutoloadRenderer.php', + 'theseer\\autoload\\autoloadrenderer' => '/phpab/AutoloadRenderer.php', + 'theseer\\autoload\\cache' => '/phpab/Cache.php', + 'theseer\\autoload\\cacheentry' => '/phpab/CacheEntry.php', + 'theseer\\autoload\\cacheexception' => '/phpab/Cache.php', + 'theseer\\autoload\\cachingparser' => '/phpab/CachingParser.php', + 'theseer\\autoload\\classdependencysorter' => '/phpab/DependencySorter.php', + 'theseer\\autoload\\classdependencysorterexception' => '/phpab/DependencySorter.php', + 'theseer\\autoload\\cli' => '/phpab/CLI.php', + 'theseer\\autoload\\clienvironmentexception' => '/phpab/CLI.php', + 'theseer\\autoload\\collector' => '/phpab/Collector.php', + 'theseer\\autoload\\collectorexception' => '/phpab/Collector.php', + 'theseer\\autoload\\collectorresult' => '/phpab/CollectorResult.php', + 'theseer\\autoload\\collectorresultexception' => '/phpab/CollectorResult.php', + 'theseer\\autoload\\composeriterator' => '/phpab/ComposerIterator.php', + 'theseer\\autoload\\composeriteratorexception' => '/phpab/ComposerIterator.php', + 'theseer\\autoload\\config' => '/phpab/Config.php', + 'theseer\\autoload\\factory' => '/phpab/Factory.php', + 'theseer\\autoload\\logger' => '/phpab/Logger.php', + 'theseer\\autoload\\parser' => '/phpab/Parser.php', + 'theseer\\autoload\\parseresult' => '/phpab/ParseResult.php', + 'theseer\\autoload\\parserexception' => '/phpab/Parser.php', + 'theseer\\autoload\\parserinterface' => '/phpab/ParserInterface.php', + 'theseer\\autoload\\pathcomparator' => '/phpab/PathComparator.php', + 'theseer\\autoload\\pharbuilder' => '/phpab/PharBuilder.php', + 'theseer\\autoload\\sourcefile' => '/phpab/SourceFile.php', + 'theseer\\autoload\\staticrenderer' => '/phpab/StaticRenderer.php', + 'theseer\\autoload\\version' => '/phpab/Version.php', + 'theseer\\directoryscanner\\directoryscanner' => '/vendor/theseer/directoryscanner/src/directoryscanner.php', + 'theseer\\directoryscanner\\exception' => '/vendor/theseer/directoryscanner/src/directoryscanner.php', + 'theseer\\directoryscanner\\filesonlyfilteriterator' => '/vendor/theseer/directoryscanner/src/filesonlyfilter.php', + 'theseer\\directoryscanner\\includeexcludefilteriterator' => '/vendor/theseer/directoryscanner/src/includeexcludefilter.php', + 'theseer\\directoryscanner\\phpfilteriterator' => '/vendor/theseer/directoryscanner/src/phpfilter.php' + ); + } + + $class = strtolower($class); + + if (isset($classes[$class])) { + require 'phar://phpab.phar' . $classes[$class]; + } + } +); + +Phar::mapPhar('phpab.phar'); +define('PHPAB_VERSION', '1.20.3'); +$factory = new \TheSeer\Autoload\Factory(); +$factory->getCLI()->run(); +exit(0); + +__HALT_COMPILER(); ?> +) +phpab.phar8vendor/theseer/directoryscanner/src/directoryscanner.php"VA P7vendor/theseer/directoryscanner/src/filesonlyfilter.php +VAf<vendor/theseer/directoryscanner/src/includeexcludefilter.php^VTBl1vendor/theseer/directoryscanner/src/phpfilter.php VFA'vendor/zetacomponents/base/src/base.phpYV\0vendor/zetacomponents/base/src/base_autoload.phpNVH¬Lvendor/zetacomponents/base/src/exceptions/double_class_repository_prefix.phpVV6w7vendor/zetacomponents/base/src/exceptions/exception.phpV CTsAvendor/zetacomponents/base/src/exceptions/extension_not_found.php6V~9 <vendor/zetacomponents/base/src/exceptions/file_exception.php-V5vendor/zetacomponents/base/src/exceptions/file_io.phpVO;<vendor/zetacomponents/base/src/exceptions/file_not_found.phpJV,T]DX=vendor/zetacomponents/base/src/exceptions/file_permission.php VDg7Ivendor/zetacomponents/base/src/exceptions/functionality_not_supported.php>V V&JFvendor/zetacomponents/base/src/exceptions/init_callback_configured.phpV: Dvendor/zetacomponents/base/src/exceptions/invalid_callback_class.php_V +Z»Bvendor/zetacomponents/base/src/exceptions/invalid_parent_class.phpEV@vendor/zetacomponents/base/src/exceptions/property_not_found.phpV"yAAvendor/zetacomponents/base/src/exceptions/property_permission.phpfVW>D?vendor/zetacomponents/base/src/exceptions/setting_not_found.phpTVH[Y;vendor/zetacomponents/base/src/exceptions/setting_value.php[V3vendor/zetacomponents/base/src/exceptions/value.phpV.Ѷ6vendor/zetacomponents/base/src/exceptions/whatever.php V8K0vendor/zetacomponents/base/src/ezc_bootstrap.php|VMɶ+vendor/zetacomponents/base/src/features.php.V +''vendor/zetacomponents/base/src/file.php_HV ^'vendor/zetacomponents/base/src/init.phpVVukoGvendor/zetacomponents/base/src/interfaces/configuration_initializer.phpVE8vendor/zetacomponents/base/src/interfaces/exportable.phpVB59vendor/zetacomponents/base/src/interfaces/persistable.phpV9J +vendor/zetacomponents/base/src/metadata.phpVb0vendor/zetacomponents/base/src/metadata/pear.phpV<3vendor/zetacomponents/base/src/metadata/tarball.phpV g^*vendor/zetacomponents/base/src/options.phpVy*Ll)vendor/zetacomponents/base/src/struct.php?Vt<vendor/zetacomponents/base/src/structs/file_find_context.php V-њ?vendor/zetacomponents/base/src/structs/repository_directory.php VL8'U<vendor/zetacomponents/console-tools/src/console_autoload.phprVi>vendor/zetacomponents/console-tools/src/dialog/menu_dialog.phpV1wԶBvendor/zetacomponents/console-tools/src/dialog/question_dialog.php"V tQvendor/zetacomponents/console-tools/src/dialog/validators/menu_dialog_default.phpV+Xvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_collection.phpVONUvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_mapping.phpVFSvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_regex.phpV{Rvendor/zetacomponents/console-tools/src/dialog/validators/question_dialog_type.phpCV(9vendor/zetacomponents/console-tools/src/dialog_viewer.php" +V?R28?vendor/zetacomponents/console-tools/src/exceptions/argument.phpVX ζRvendor/zetacomponents/console-tools/src/exceptions/argument_already_registered.phpE V/wm'Svendor/zetacomponents/console-tools/src/exceptions/argument_mandatory_violation.phpV Hvendor/zetacomponents/console-tools/src/exceptions/argument_too_many.phpV*Y۶Nvendor/zetacomponents/console-tools/src/exceptions/argument_type_violation.phpsVu[:MCvendor/zetacomponents/console-tools/src/exceptions/dialog_abort.phpV"@vendor/zetacomponents/console-tools/src/exceptions/exception.phpVP[ Jvendor/zetacomponents/console-tools/src/exceptions/invalid_option_name.phpVdLvendor/zetacomponents/console-tools/src/exceptions/invalid_output_target.phpV]vmwIvendor/zetacomponents/console-tools/src/exceptions/no_position_stored.phpVRGyMvendor/zetacomponents/console-tools/src/exceptions/no_valid_dialog_result.phpVX=vendor/zetacomponents/console-tools/src/exceptions/option.phpV}YPvendor/zetacomponents/console-tools/src/exceptions/option_already_registered.phpV-/߶Qvendor/zetacomponents/console-tools/src/exceptions/option_arguments_violation.phpV~xRvendor/zetacomponents/console-tools/src/exceptions/option_dependency_violation.phpVrQvendor/zetacomponents/console-tools/src/exceptions/option_exclusion_violation.phpVhVmQvendor/zetacomponents/console-tools/src/exceptions/option_mandatory_violation.phphVYXpAKvendor/zetacomponents/console-tools/src/exceptions/option_missing_value.phpVF^Fvendor/zetacomponents/console-tools/src/exceptions/option_no_alias.php VHvendor/zetacomponents/console-tools/src/exceptions/option_not_exists.php$V6ESvendor/zetacomponents/console-tools/src/exceptions/option_string_not_wellformed.php V0Mvendor/zetacomponents/console-tools/src/exceptions/option_too_many_values.phpwVLvendor/zetacomponents/console-tools/src/exceptions/option_type_violation.phpVD/1vendor/zetacomponents/console-tools/src/input.phpKV&}h:vendor/zetacomponents/console-tools/src/input/argument.phpVh";vendor/zetacomponents/console-tools/src/input/arguments.phpb!VJESJvendor/zetacomponents/console-tools/src/input/help_generators/standard.php9VBj8vendor/zetacomponents/console-tools/src/input/option.phpOVS~Evendor/zetacomponents/console-tools/src/input/validators/standard.phpV x=vendor/zetacomponents/console-tools/src/interfaces/dialog.phpT V*/Z;Gvendor/zetacomponents/console-tools/src/interfaces/dialog_validator.phpV 5Kvendor/zetacomponents/console-tools/src/interfaces/input_help_generator.phpVtӁFvendor/zetacomponents/console-tools/src/interfaces/input_validator.phpyVeutovLvendor/zetacomponents/console-tools/src/interfaces/menu_dialog_validator.phpVTPvendor/zetacomponents/console-tools/src/interfaces/question_dialog_validator.phpV&cֶ:vendor/zetacomponents/console-tools/src/options/dialog.php2 V3Y?vendor/zetacomponents/console-tools/src/options/menu_dialog.phpV1vf:vendor/zetacomponents/console-tools/src/options/output.phpV0ِI?vendor/zetacomponents/console-tools/src/options/progressbar.phpVte%Cvendor/zetacomponents/console-tools/src/options/progressmonitor.phpF V Cvendor/zetacomponents/console-tools/src/options/question_dialog.phpVWia=vendor/zetacomponents/console-tools/src/options/statusbar.php Vp~[9vendor/zetacomponents/console-tools/src/options/table.phpL"VseK2vendor/zetacomponents/console-tools/src/output.phpMVW?7vendor/zetacomponents/console-tools/src/progressbar.php:Vdm|;vendor/zetacomponents/console-tools/src/progressmonitor.phpZVq5vendor/zetacomponents/console-tools/src/statusbar.php Vj rM?vendor/zetacomponents/console-tools/src/structs/option_rule.phpV Avendor/zetacomponents/console-tools/src/structs/output_format.phpkVh+-Bvendor/zetacomponents/console-tools/src/structs/output_formats.php+VD1vendor/zetacomponents/console-tools/src/table.php)sVsB16vendor/zetacomponents/console-tools/src/table/cell.phpV+(Կ5vendor/zetacomponents/console-tools/src/table/row.phpw/V +h%ڶ8vendor/zetacomponents/console-tools/src/tools/string.phpVF)phpab/Application.php@ VD +p4^phpab/AutoloadRenderer.phpb#V +B" phpab/CLI.phpVVPʶphpab/Cache.php@V "9phpab/CacheEntry.phpVжphpab/CachingParser.phpVPI!phpab/Collector.phpe V*gLqphpab/CollectorResult.php +V8[ phpab/ComposerIterator.phpyVRphpab/Config.php8-V +;s9phpab/DependencySorter.phpmVo0&_phpab/Factory.php;V+Զphpab/Logger.phpV!phpab/ParseResult.phpgVWߪphpab/Parser.php CV +lPphpab/ParserInterface.phpVphpab/PathComparator.phpfVhaphpab/PharBuilder.php,Vbζphpab/SourceFile.phpV%phpab/StaticRenderer.phpV,phpab/Version.php +V?"phpab/templates/ci/default.php.tplV/]iphpab/templates/ci/phar.php.tplV~ phpab/templates/ci/php52.php.tplV ^@N"phpab/templates/cs/default.php.tplVBw#phpab/templates/cs/phar.php.tplV2q$ phpab/templates/cs/php52.php.tplV&N˶phpab/templates/static.php.tplVp휺"phpab/templates/staticphar.php.tplWVT.ֶYsbk CL^_j?[!QI4F6ۻ{:&)nwo{{_~[<,Nޝ;ċURhLÇ??DB/zI*?3JZp'{4 4#"CɄ7 Ld.Z'La4x$E2Â' M$~ +|^8 9f0#? >!<=Wzni$z)Rēz,M喊4V& $@S\8KZ᪓ <9; ZK (~;7]U{kҊ=\Rfzghh6I 0 ]g3TvL0*#"\xPY`r۬ͨvD%8i0D!det8]]#F@%됓:CIqGepmY]iyٷz9`X4a-\Z4Rj8㫡Hn̶W&-jݕLe$!]тLd8;nWE{`kCffѬEt5ehٺC4z+ t ܒZV'3bh]ue|g|ZxGoi[uOpӮE<Z"]ijpU"bf邑=C.lۘINDf)}}-?NNHdJ  &x!%f8e+9,>H~5 L$kr}_i*M M(PW_bMm/~-jΆ{e1iFܱv@mD,Æ9,34hMs-{ ՁT5+*CP¶TB0eȅ[/ q~C?˝^&|AK4NX~ !͋-ߪoڭfy ^ _a)l1; @ԑWπOØ2(A{cȬY[y>?Yaݍ޽-%U,%`U~Tvͣ藡M~-³')`Bה ugpoVn(@'G5Jq[ 5a-yrD1G߯Uq=5Ks|8z#9^_̕ŜoϺf,痽*kXR6hB,B3]Tv̓gN(1bTЮ ~kK)[4m'էԠd}8snע8cd=ƠpL/ ~Pu@Q<PŽCqS kW|<1ɪ2jwɡz r 7CSHתVci9UPc7.AZ +T(CK)QbSϟ"},,#l_+YQfڑOGKٶꧥ#CE1?|_RQ⯺H6b]:` ;/HUVjz+ނs>Aeme<8q* %G'yvkʞxyU`e9اL:ݓSkaB+ +WIJ/O fe6M|Wq +woĮ;ɭĐ睌`}kVhgtDLUIRz mHsFͽ1>k =xN#V&gvS\ґ鈵Eeڎfw2jr:kui=g[ |ٹ__oůUK#_ͫ_&X yxʡf4Rt~_U|ƫK yi.ٚ3I֌ dEI{stg UxɷVao6_q(V.ܤ+eiE蘀,y4@蘈,,wGɋKlKwGeٍ߾[ݽכ> 2I?Qʒ$[<&(*L=qjFpI SX0\m(&Ht [vG|͆uIJRZ4++-*+ryJu))˩,jHrhUmimVPYm4[e,S(I;YUɔvRTkQោͦkJ<ʹ]iz{J*VH]jbYhQT^TY"A%+iO?pBd#TG?@=^:4676״H+CzǨKE[QIM/)%Oї:J b/.[BԍL +y +=,ۢ)@iAHY[tCkr'gt)^yhe$`_!#̥`~iLsYX㐏qgvgf@fW>CEgssY}[?> ALq`5LhBgO{=_:!ma̝g|΃N|#@\b̏)ڞ`Hv8fk=քC.oxHϢh_d[x? AN=ϑvtEfx0Ѯ8ykXxDE,mKA"?1 peySy,?g n=uJNkKj}H:ʍD +oE:6O\eIɍ50"?LKxR M|V@_{{T%d1c/HP⮷zwB,['OCi~J$fHk&J_N !JlTiv^ڬH_wPTC҇ӫoL}V}p4BOP\ӯwNW/O +{:/v;uT?ujCI=fJ1ZQ 2ZЖDJaP$;Bɲ(pb9ZGf~w{RҴH/jj`>O2y c]S%kYZ,*_MJJʌZR^R*dIHUjfK2_6SYDX3$e˛Ff}al$pB=rmWl>q_bImA*v[7ȧIM^LSiuhg̞Ei;YC4: ZD?D}J۝,CvI#<) 0g SenlNYI\wXGZK]HD,3IJS&eH:FjiP C{:mUvV׃L$Ytm^wK9&EDsY(]Qȧq-`<lUȄ $\yxp~ęܟ[ fǗC\,'mi9PXOc5 xk0HmX79 Ѹ s]/qiŚ8[!@ 5|Fdgsϙ0- 45lbJ+B ϱN\b떃r^q|lJ|p*bgѳx$IlB%9OZ<(O],@tY#h*\-D1YRB+<4)KtG#mj$6 4>ߤѽJQr&[z!@['Ia>-3Nu/+.DM:5_̈be5>;I\J`ZyfP@ym& zLmUUz͡%z[a*\ˁ]lhӖiO &/7Ωtk6goK{{U; ]&z2_ٌ'wY%Vx2mONΞпuDm5(Ȳ.o±39yBNN*Hx,II,Z'ׯT$I*I+hC,r.< +q-%9?)'aU˜0$"*LB\:DsB'rl,i8Ee݆ XI~^h]'KEEbFG@HOpRC=m_yqe& B~EbEf+G$D@djAB[V݀&鬧I^\i=],n2Lǣ8QB HU +gi%T0Mmɨqr-ίדp~w7`prqs޿qzyqv~s~y^8og!eH4DʉO%l!4yl̤%w2m + mS]$˒qdL@(p~TA52\M^ًr"8 pdjZk8@5,}U/Q{PFi y!9RƬ!8L̗):t 1=bDUDۃ1Fl=1?aL0R]Ntƛ``M4iMk PDWIv9Veb(I"wCl5[6eY*B\ dOR (79W=Y 0;pqZ3Æ+d|c@d +}yq(Obdq6+gaQΧ舕Z<~th ,}nxC/VRljo//|;^E+vDFu&Pמ6ȍyB̕SF‰5=$dфj,+ a0pAfɎ lK4z|o>CqTlooܳm'/Zw2z~zDv-dڲۦacFrX]TCMB˶e"/Lh>=|?, l ~n3bT5El\?=\`='ǻi}G`&Pz @x};I{%8I=ARSӞ7 vmQy0p$JffT@@8`SWv{Tt;Xղn!%=`@tORlwN247E@8Q2)SfmX8D3q Th y@REl%e0=ú]-fs>{}ּ%oác1MT]`kaM{I?ڦ`{~b.֑)e-pw+n^Aec2ֳa[ K/jL*{?T[LAbĥ7hL|7öPƆO + 8>c#@W`XYƀ\}yv1!@hb@µ瀭/v% 0J%'B)̆ꈒ>0dlMѾX?)P*[UUi8鬾Z]5u5ѦqIF>k]͞,z͎S$kD / І0b;5yo>8d]t=U*DZ>% pwtFbrNiFVH T{5l:<,]2V=b;ebFBr!Uo*FN ;|)H0b3bp} +J=e>4ތK|-6xh3U~s]h6l"Cȧ&a.sj*~3YePd^Tk{.ÈoS dr=`z +o9C>AV Mx[:w9?ˎP] J\`بab}P +5t +>swD<>>;)v$Zxo]վL}c~'S`ڌ{>A.e7f1ƤݱzMh/ĺd%1 6E4)6zY:5q&Ӝ69njc[Ud;sR$yL"EȤYΆۘ}]D&oMU0>aRL`m!ec0Unwi?;07`5;[AcEMAqL@w&@ Zczꕌh`2{Vo@ ѺFUgS VVb6/H W + >J/rcR9AYg7>54?ҵp' pJ6u~ vե)ͮ2Y<wv L/s'+ /ZʌJcϨ*1ip!.CZwhk 6|\wDK]jXZ$UG8 T]1UgNR[RzK}~tEW ġ3\Z t@ٵ1pÚEyV!淪Aag[>r<#l)jbUb$~>f '+Q(x~o)o S]qӉ̚u] +Tﱷ&Wv/4 @BVχjlZ:y|eaԡD1 ~V`pT@%:ƈ'EDu+|V$;f>rZ^]OtwtJ" .u8 99/ +H@t;ıՖu }&ޖ^A6%ly{zoNuQmaVJv ?9 +TVhhkóÛWMpqS{cmZ&NYZxMRq[Ft[oJNͯi\.W|6/!rS?8Ƈ䔅m{[#([Ә{/=-_zس%< Ey[&Qa{%IM$]h@vkNnjPK%^Ias\);lF+פ]UEp m=BcNT0n"fj՞}[깛k*wɒd׈U].X &wi_a mQfAEoU\ 5=x6*&כ ` ʘZPFme==A,xHwh@_ndLA2?<Km#LSj\QJ9Xe :ȝD+" U,)ަiҹ`AӅ}qDUt썎;LDZ@.Ṟ|geo+6Bq;|eDf!&[ٛgtkS7yyx!+K68pC+Ԑ=\l#z&}7Yy9eqrQ_!aHE{s],@ iȒ2?~{\%˩,WQ[< 9YۀzenZ ~=.O4U6+nZ*7_R]~ebJ0JV|6 Z2ZKJKkF=%ЩnEg#^dU8J.0ATyfC؞֪*; +(( @"De>h5a hb1&M*ȃVY2+|-|U?o:zk_ܙ%ZNL7ub7^K(5 " ҌrI+,*-K8u*ӯ93xu| Ju{fQd}" ,O@/vFb: rnWmP| +YNwK/Fak #炙I#KU;jkȒJ9+{/ 0*ZGrH+qL |?|^N'7x΄к]$Vi[p˯4x.n5wt0^|*^=~ƦN `L靃́x`Y> 2:4\:ףxN~^"eQМn-+ieVTxw els.\,QpdYASСM_Te">I}=ͮ#c}#9L\RzPY v-QVfnn8DnrZ+l6,A ZDeRKRQܧ!%JCQm$>́!}q,FÈ i\Kv@5LЕXj g jP1}DH%76M b2*3t]%ˎTGVI%Zܺ\ϢlRdcL ȤwrFt|{sƪOLФTpx-u(c@AS6"Үڨ/!G? Ô6Vv-~à[gRXJ&Ƈ3Fƚʬ](E%<jLע!b],kt3Hݐ(1#vڅd^]^%?j>&%CW\HFLE!SC6bYI3 xM OLUdqb62WC?ȧ}P7nt[<H.%YJzak[{K<~}W]&ރZ\Aʚ;8C89jgp}Ӈk)w{v:*3W(]C(E``/ش19:Jbx% U ;3%fjar8(sOF= Pb"{p;*x_tm披zrU^ts| wsIkoFߍTo0_qBZ*F}luZSCWSe#X vf;K;' ؾw޽dzrYvHh/V:D_\84ռB8wqfq$^ D0oΰR#SLa:}M1V+E"+ R?W )QK:>‚D)N- +PV 1yiʵU҃5ZT%1tq pLK\צjnwbpPNCJAgt2KB -CtnjoA̜ "P=s4ޗ'IR@I\cnuΑZ?*eIDIUIA.BШ` v,Z, DQw80}ϾLogp7Nf7p1\g鄾F0sHQ*|*-J+َb 얶ODJ=W"GOaRJ+帵+僩x )Lr?2XX:$I>G$a={/d-%jbe]SѲhnZXUp +"[Qdb@s$߬Si>y6fݡـ?pAl`PEO QFd4%n֥|~n}R̊?llƜlԞJ2{xQ ߃^hD懒ҥ~rR/='}r{9=H:j"ߴVVY5=F~9twxl:٧TaO0_q(Ui}+jնV"eO\lMݤM#Rƽw޻;*0W2!_2'J/1# +wo4U>z\1N_Jm4T2aG#WԠ$:P*)E\[:(6eDi Bj)tD 4# 4J?@JXW $j̘NDYnA5EENJ4ȘM,i]5c?(q@ѩGl RY d6]"VV`{tn[8޷ITK>I6M3bH,$y49&Vh <*bYL\ ָF &e6th n^L(̢\Y4v w㛛|9Dz-6!C@JSb*SMK'S!$Of52CƵɄƣq=mݖ|&\*Iౝ0d405|2v{ow-;N*ρּ6ڸɤ$b4TݾWo:ϸ*$ɢL lO2xQW/Tt1V!N\բo@۴W$[gfɃj f%yIZKӭV+"nDo*qHt !/auvϋVVړ9?oaa=r[~Xsn +spqTao0_qB +iumAEj~LrNSVwgv"!{wtR, vHfVNH%U +n?SaqPYD.U2|pq&%8g1aN{1Ez*a@Oě[RPi J%DriT{" 4 +X+#ӥ])4v) 7c)Ѩ!c7uZʞ=FXhSZVȣsw}.ˋL +{tn[8IK?saVUp_4l$I4|O̍ZR| VLT +4?RAs_?ؒ}n@Dv-O:Y#{wy_0ً?.~pFpV'&7"! 9[/Cmo@^57Bs(na/JǍjoWTe]t|M h_ ]|xvk/ =]Eex̱v <7-m+7,e]/:X'TQo0~ϯ8iJ&eKYJMG7&;ݤ bBPU>)?:BȤ0 . +0%~ΙF{>e.dӚt3s0eȵiBˍș +@ +jvU_m ]F`BQ=H]xFsw8"rS֔9Y\PvD,PaTnUf*^d+P7T/RyOFeIVn:)՝cDV5 uéCl Bh|\&Dn*DН} x%+1`N +0cPӼm'HUD17BɭoXC2"km]V[`ئgz$}lcG (yDrq]\q ,0[Q-b:!-*/ƀd§FYĔ[G1SNK'`<#yذJ4jmk5izUa8_1*-8RGzktT~Lbk;8j8- {3ooka!J )doXi+VCk#dM) ( kWvcB2c6 +kPZib\(ryťi 6r38$ S`hSveY BBePtkT'n +Qy,OE{RTŘߘ$:} # ?262EbU] +&S=?IԖzJ;fM˜z]M% 8]y]^M%Z||o.?xvKGZd*<;@n<;<,琫\ۡDCmdy^t?.| ]svݹ{Ô ,]EV +T [m<[sfǨ`UkjD9}r0\f'OWm V =0NݦwgEL1RLH_ +ȗ(Ԇ[A!4Rc`mBf/PYYg.3:y6[G!:D#&xj.\UQCb/lf7{6ӧԵqW~sr +d[04xPZhiBO.(Nm;o+ЖZO ;W_-+t[QJ?i\{Ji]]Vzhɥb[gOD/lv wzчq{^Un@}WJ\DqǤmJ.T1ih14; J3s̙Yʽc"DZY!T EhX*ZG[D( +B2P8ED^JO42Zȴ(-uFAPb"|rqKX5TҮ(FPiKJ%XriTt9" 4s7F&+ Rh̩ޜ[ -Nܖ^7lZ9c?(~ yN:PBY>;S,OPC7jǻ&^@V@/@XF`em~UU #<& o4zK[̍J(H4b"'VXTTeC)%\C85@*0^IjAJ2$)yGlwۢ֍Z){d #"~>. zSPb2EbyIC{ msR@?bu]#<&;{, +CRe,1[V`W`l\3VS]Em0*L_]:(}yAȃA4p5]_.*X,rF0_d>;.~C0L!Ln%n +C"xOrK[\%Gy*X" _qQlD1#֙GmK`):>x!aHk_4$SCGBa4},)c՞fպyčtDjJPF&¤Q^)ν;ǡMV%?nfvgYh0`SAȴrB* +pk ZeNj%6mҺqO3 damH# + ADl"zd/&"ϥH@#ݚH 6"*ro@09{t5X;ЍBcײl%vb잸ۖnuZ9rc?-? -`Pzt)+;/3%aeBeݺ;A[>V@pڹjEMӄ )btMMGanj/ *R%i݈;@*CWŐѶ q^I$ l7N!N{p>Ntwjv|>N$.feg =Ma3{\d>WMR(ONK'[a&W2#{EP'4~P*4ZK"sG)e_޺)F~d*1MH J}*.oGQM]S.9f5r8hi s\%FX{-G?Oޡ 8|^ +9x| L9(l޾IRtLƈ8%qI{R1(,gD{fy0yd}x:'¾_JghwXn<0L#xi9waq삳oTn@|+VRcR5%Tb(Oq^){>ǡ&D !rQl^0`s!HPZKQb,[ 05zb2{X8e4L%q0O%Q3bRI?YZXt"QGj6TJ}5Q$Q|@iz{! +oiUƁ5r +:oVy+ǒ׭+M3F'8sqPLrIX^dJhэ!1+!o̺[1犳 z,౱iZ ~RgxDaYR~W"-TI"y~P>q]!iD- ?$0$܆%N'2Ű" +"9L;F §² Rtj4s* j$i%R};"jzSus/[];L0{!;;{Ekù;鎶3F_4#;apvTak0_qAc۱.Mf6ӕ~*|v:'u;N Fg0ƒ{ZUQ|r LU Bi +|aO(U6e~\ +&WF_BaJv7dT IajjvL|FSn9[2e +7eTSǁP\IZy@ƤBVKdFV>4چ|!,,<ǂ@a_Gql_YjzP9⅙M7Vz=2+ b[DysU6h;7uoDOu:m-= 46o6'V:ڵ8|ZːNv<~8%kULowud/ipCc6A/DK6=j}R##j6=Fst9 TaO0_qk}iVډ!>!qlC9I MR{~?Y9F`!s\+Ǥ* 9"T,frY;rQ:pzE a_1GUw̃L |3'Yuh`Z['Tߺ0x`x۠fXog* ]A6kn2ڑ3 +@b0>c.{2ϤRܫ7/H+ΓSi482Tao0ί8EDY6*2tU?UUmJ}giIݻ. LB\I˄2[ ~ ~ժFmw lX[$CJp3'G5tKƶL#Tӎt1zD JV*;ZKe,׈Jkf)Ob3a<" +[P0*bY&\iVtPy".QctsU M!jrR@tCYҺSM/@uߌ)|# 'SKoGS]He1LtXUI{uQk7=`^ +a0> ömg):Dzȹ%CM=^Ċ5q-Y& @,ZMeczi@Pۘ(J!NGpq:xyy:JV<5\,x/zZ@ܹ/qr9Q)ډ u? [9Ndް!WzBT¸"=*a>mo'jr{!#(ПY|Z rM[m!yJ xhsE3Ω42}AŦƬ5ӻ:Wl<dt5!Rp"Ej}dIf8?|x?.ܗ`#:)Փ_BGbGEy7տRFݚJsn?[78TSMT-qMW2 X$C Ԃ|KSj!sNNOQϠh;*ۇ_Z EΤ `S)orB)6ud$(O䷃}(B]V݈x i}?D2 r|Y?F8.BJo4?tbUWp{v{ A[>E|b4z4^_2{,_69X,- ! m 6g}p\ T]k@|ׯXL!qǤ4u"!O,#ҝzwDдc3;WA80,RJgbQJ*[>X9vNhO],f4H+;X"YMУRVA2 b9@z/aG=>őF=H 6w#*eҧHE% @0w`dweE6JؖOK^ \wŘ"??Q+z\=JbSy~[?%OB%SI$I\gʤgg#@͖ CA6IaU;#nt^v_㬜% +Vn1_gAT~O + ΂2nQTIҫȃ^VBO #7mryW;ZwdЧ'K@ +V+ i\Ex|0a袏4?`85PEaD5c۷{$7ɢwxO.iK2uQYi%׋rPi{prEoh^9v1m 'vLDt0&: +Oϱ{xKiJNF!]$7 @Gv#t[#-| y.(A*<4X3L{#*4v+kb*,]PuS9a݋1O) +cA~u8y+4Uu)|tP0~5 zs h:W_q۶#m8P?b>%m!{5ĚFy֐⪘r &8mQn ل0Ia])W-?>Km +;Y.W傾f,>s) IF6LJV? 얾OLndFTш;4Ph*i@dJ:o*ˡ?p O 'vCX Tq{~f>JalmCևv +w=~Bwf'h2fԾ8tPۀ ?pC-p5$_frDQ$"FTÅEǞ^p _ >פ_cVWHg Ξh^'sFg+H\pXPr pݡ[{4Bs.On{Q{vwW1WS W蒗{ L5? 5b4*d^L{A5e9=w >x_3?QC &o[t=c~E0x5Fz!]:1R59$:VcIZʯ/:"lAGsuFOKftf[|ZY(=BOtl2g4~ $<8:h6:O(ĔFzN 4xcTnF|Wl>؆*}m5BKp>ȫ;h)=@E+&ٛٝݟ.egg3:QklAdO +|Wȏ쯟478KR!%wF St jcSiZ;Y֧W, yn澍P IfÒhǜook:st&1:T*ύ\*2DD sQ]{S\gه4o/RvLOBkQ c1;? 4O ]8UtAn*NQaLkra叨ye]-U"t&;Tv'{[q_}O+Rt05*,:b!0em/P6z4_hӛn[fV~s[^m-ִ~oՂ%UxF* ?M-cB<[`*Ƣa_ T2j[S d҇!Ym{|HM#~tԕl bGqmzb9 -a +M&Jqx(~HYxVUU4?)tfRhVpDЕqAH#́j,$Hrm q,nR'Yy|أYP${IU%.dȨ5F{2Xt Bd"Jki#BkY;Om|0{@ ;KT*VŠ,wGs.GWj1Fm$_ e?7h8N̆ц%׎)҇o?WEtϱeyhNQO;D/FA 5[~~|/}i ̿r޺w+_ѯXmU‚0aA >.~U]oF|~B0bGn2BԐ Qip:k;hE);{$m@`<-x]oArv63J,\FVZQUNHЧFˏUhjD)RЍjk ~ ƺRN 6Mj!M 6&A[C'QMA"(RUE)gez39?׾[hO;>2\siQ68"Nt.צ@zt dwF95-YJvד-p_Z1Ē/Ư4Obv%dlি,U@ĪȘݩ{]xú8!ԗI"[W$essޛRyn=^I`%\K AEΡqMp8!0mqFIFi6,F!]_҇b1-iFg2tG#gnG2Rk"TsGU~৞-!(I9fzaJh*ϩߒ7}=')pQD$;Ƶy큐;R;$%\; :.Vtz=<Mӛ|ʦ( c7tByJlpp@tNFt<X yn:9_y^`o_@tpLpx|2#Zc_dc~N@V_EJ}G%bU"'ᕊ;g `r{{(Pr>ڗkr+FhW:>_B4_#j-ȩRr+UVm4Y>娀`{|xfp"%G`v_>M3<3va%<9DQ)./9q'Zmo8_1kkMl\&En Zm"2%H%I?nu( _DӨ{/ZP&LH!'L9/oLkΒ4 +)4T܇$ԝ/"p,X:LJh_ 10Yc1Jl "IDukۻ+4ʌC"b`0F(f 3- >i*iBXMEݑ*k'2nZuVHWݗ4i6O[ Hс/=%(. +6G[9P?-H8Up\,`$IX,L Iϩ{\B1dBkm>{<QY j{mEU3e +Y+'1^a>nUD,Mxt^#%SWCȷWa-n '_Dޘ E].8mh} Ĩ5g+W?w_v_mu,o {:s7Mi_A;~0=T%R!ӻh B@$Ƞ Tܿq1.sزp0NG {>g1P?XYFBD@Apx™!:001䩄-TϤCj[E~Ɵ Y֜4^f"y7lƟ@\5rQv_4 s A8]-)s&On +cLք *&,.st. 2W6~E4@Gt/L! ׸[fwrƛ2OuAPnv5c t)f$"J3U^EЋIW7ۼC1O{R"\ϛ~p#Z|{0dsTF:l5|S1n {h"Bb^Lu1@`mǗ} ?c-]C.{{S$vDQг +Y㰴-:jTpAAK靬|el՛A*Kps˕ޗC=G)e`Q8!U,e3mR33mQ1kz:>ɤV儢<LR8̘br`+ +L1>iSa"W& چKgx! }7*31ZW%<7P#ͧ <5Qgq0Pz¡3hX ]bMJf)/>+KUմMrxQ'֙Ry*R4;Q,Y6wΔnTʚ .3#6e/ yU8/Pnl/&q/l %oZ|oc^TŕG_wozmFy 6JSYL$}d(lvr7u/~?`Sd,MN3HaV&yM +"LHr9dl,d(a$:@`sIJG6P< #ܚ,J,l\gltLΣ%wdlZiIq_o#և h_iY欐S 9 -qēVkOH:9D +K4s\36WՐ4D6kBJQ<_V1_IP*'3WKJtwjޞxu_뻫1e7n{v~; `l%FQ:dp@mrKD(Yg/"KPY"[DE+cT6hj+93J6>jLLzD:r=mC~BnΤE@f + +$?-D>OCR^UlZ$(_#8d")1ȵłA 3CuOsJ2a+cذ + Лp )}#O_| s"b _ yQ,&!r&ry΀F V3`0KßQ>M=ƳwvO0zlǚ +(8 G.)L ~( G$44iWZx}! %v;D^dͪ,"^ͣ`VR*Td\ѐ3q/>L{fLf?23}3ǯĴ|%eøZdg軔lO|@G~VNioD7Z:lȖ雋ޯ{2qhsR1`6CK#V- L0Hct jH2K +f-qoǒ?RaT  +|O(Vro ,|1 8r)|%0~l#MJc4O!V'ьTَ4M 4Lct%8| ]yև%|!h \W'a}Y8Kx8|Cl ZM` 's5F\{ +uefW쬤cNVƅJ "U,p*~%߆Ws84,%FSx)t;%O=\3{͎pttWG >{s.*JC)}ʱ?}WɆOZڏ[~6Jɾ=q GjFqZNҵ5n汌"̹ 2҅\s6'}sqt5l FĘڀy&来 DFi7`%u[V*BZB jT&Y(`@*-jR^h5Ã-x;d1H!Zg;@"rE^Pq1YP_ՀZsձ!jStu7vX!'K(sLA;IMqtϽR2;gИ꭫K _Ϥ{*PX]k,Kó"BU'Ř4K 3k&]<"Ti?,f, M/7QgG|+;-6z6m=%5 S3ENs"]RQ WO 8q=昩io0?؊q'a)Ә*}+2$Xaz%'*˫GZq҈5+#nxzC˫H>\DH],bRճ~TAx:u:-JսWt=Bd3D x% -0sՁioОXv ϔqn18)V_859}YhW{,gv[nA?&Qn'T%[NCXp}(ӄ CNrh#IoEQ78Ֆ,O]y3:ۇ|*Z#F +Ipu4RprkTuALQ'NK=rjЩ章..x +@R&75l;6w^*4˝bjauog/h}躥b&K}g 0۱f[R[`5~>K`ߐruĢgP3 /@3F> +pA9,ZןHrVV#-B$ (v&ibER,&xdjH%cQ_W)H'`Z'-P(5CV0 '1 +KSWDf GiP }Գ &FĠv6~j'Ͽmk'`W`qm[ $iucm:ϙ9^+U=Am2~3˜91{suV|-A5{F=I-E>VdO]%3_nݥt5}g+b7+J!t.pvx>|T6|9Й__T϶1fY4V,@SRI0[穞Zb /^40s^5'\qhؗs}-Sa?*ePA+Kգ"3(2BBF T +mN%M? J=OƠ!FiּҼO;ڟvll OwRf)J"MVRBuI5C<+]/q'hT)1Z1Ȃ1~r\ewB^Ť\dT~ZkYK[I47U\ H|5 +}.*!s€W APA",DK=^3%aÝ &}zO#$Sv?uΣ|*dJ):L^6У]6'!\ХVρSvwPa dS<ņm"mivC^ՙB駛 +#[-W)[r1OW$ 2z4_dKzq; yf1=j{$K 6@e Vo[`vܠm2UkjT<֧JW.Nkixc;jk>Ȅċ@ {.싛KPrM~|U31ށ},<S>*g)M"qD)C)í-cp)'ŹV-w\ Oٱ63;s,ba0{)1tIHk$IUxnfxFkی3]78nM^2f:WV #N< +_Ƌ:Ϳ&cλ2G.j!'-ł' z|4`mkuO< Pz8T؈)}}sоy3.dEK:uϊ$qވjާ=69$MJ\QG˨Hk:1]mL;.v-OGXɯ*^\_}{tXnF}WL  E,(NF&va9 0VچewޙP.vS\Μr^%ggGpע*iB`3|,`QߋK3 K⟉U%3fN?%9i+ iZL+ +oX9_piM`¹3s{?] ]$L0R 2AG )j3Qt*Zs j%6sQy::cx,ƺVUuHF~GCOtwϝA* S^Zt[`2u! s0T)`.P0K`nm9HjgyCLcfo&WQߕИX^llEtr@/V3.if6I.bMLs5:N`<9wx҃O_n?ç~|5;<k|&_7?c(Xj +=Q5} :b&R O9jɵk0TZNfTTwb]LQˀ$aa$qb|{LbgRdf n 2^5V +VoQjOm"CwvН2ٰb + >y}1xcx#]jOFs[i-$&~}ufV]VH56'}l᱗c4܎~tޚ-jҨ &-פl"t} FR*(ڢ1#Zn)#` i83!JkZB:> e,Z0d!TUNZ7vc׼WZ`;֍Ǟ?ދ WD۞qyd hpgZ5WEz +`uxvFO:kP*.Ve| U dB-JֹPnO:w6[˃tKcV\B '_xٹack:: +Dl .YQ⎍]2jL[/.nHxb,GXXC#cfrVՏXFQlw/f 7 +nqj޾X||9e{ܚ-3a,6s szDU/gW?tp'I`4X8Yޖ5R[ak𡷔]r~q\sK7Sq枖_S5"56-OGߙƨpc''B>D}a E6GIKcrO;Lq3b lHeB/T %im?oJY\#+{}n"=Y?٠k䛚'-X ,#Ͼ[m>T.;$v^&̷i/k#1DJ1W}LsnsW3OG T]o8|X}Huc[1Nha{EZb+*IEq߻Ka$ g|񪫻Yv}=kX@YPm*5}UӍ5{]&7:hlWrO%? +:MrY_uZ7 +pzЌ#j("zonaϪ_j?XC5`g3mCQNWu;r﷕VUG-zYדs始? t1^\=`lӉAQX. kFQ=uw܃5~HN0v^-:Y ð(xa]w캸E'{Ӑ֗^;xwXkmp*ǎj.hBp~L'ӒDnmxF^/Ç|>,m~[n676߬mGAo@loE&XG4}<Ǯ;l'mM\Of·$O/CwPF_%72ٷ٫Tao0_qNA|(i&׹$flgY9qWTE}{wgmN Jh/V_!(?zJ{.P %VHzd+\xe4LjF6c{uDi-2lOPYϕ@|Eg;(JEMi Db)l:iVkR-ݷe)*qpM7J9R=6c%^)࢓qd6T7bx>>Hl=%bM[+e 7#ٱg 01Py߾I"^[&QbrA]g/t5:G)K=AJ0ԢgQ!Ģq]ιԴH24;,pnlp\yKYNfMo+Xok<UZALwm: +:-  9enຖ ֐l*2(i01c2]3#@P 9dT+89iW%ΆL:1GFg`?}QGZ+i,A_yA\cMyBD FWyE;F*=kofe+a/<,jmGnoۀ2svk29{? Ua8_1ZDZG +lEjOrOM!lqnD]33~ϗML ]RWL5AiMAdkov^v&۫#itsNƔE2v:嘖5 +.匰dGHj:k6cERc?'JcfzsM{bt:ў:Tk9ZU:DDžrlsp(΀h*r㏅c`۞ʈu/#"@.1V26P:]M\J+؝}Tad˗ y$]UnfޮSЛjڬZbY2߯o! բ(#? -}|Ù LѪP4j sأ!Ky?% 7)%7n{$ 6I^=d>LߊDzJA[GW&+xeB8P `+PM tY +\VF3ce$}4wcS(8N6,$bZ~DEUgmݣUq)1k:w]MҢ$<<混i-8+#ZY!TK? +baLZ `3"^BdW:WR+ W]V:Dg>c&繥/b2CLPY"T&x?*6"i=,(CɩE R+3\,2,Baf"R21>pz]lu F@Q tȀvy'bJ[ n>Z* +KX +8ﲻusDyZ \+u3 6= Wp_gAO/_SѕϝB\f|"1ZcQݠ"#ղަ"A}LЪ&܌)mx?=4}d2FSb4݌ ǟHQ*|L3n*(5>U50[9X",fN)f40|mykR> lKdh WLnH51|pL`OH!cv5չwg*͚! +Ny5Qޮ6c V4GnU'THO;&5"]:Uy̮b\ԮzP#:ߝYyFђ{\|e5=w'~U*FTؔA?3VӃp` +$"!}rtxy*ؚeJ(8|adA?lݒ8B,bVrzfA#cD~x5]f{_̎XIa{+CdD66T8Z8Y+9 īdB&\%3.,պӼR9qɇݗТ lB! 7xJ[$gI'cpUM,o7obRܹ(u7:V˗w{<ȔK#D? +BKp@(5^[$5;a؁Yv "Bl9M3y{T"Iy=mr&|MBZSND9||aC|'p$v9?%w/X+RctDDݼ6 _wZل|.e!#GrFn;٭:"i_ 4 nP:W*of8WRxcO|٥iDBsRωY*UN‰A}ϫHs&B:5Xth l~-,pwQG ;,Ma}|lWGkH0YDZ'Jz, f[3H̺]i;ڮnlֲiGCMׄQTݱ/a)4;3'\%Nv(*kLW'ٵ}K#lKfǵ?:?tXo4+s<[xpا͠+07Mfn;d>omکQCɎ^>~S%m]CE>Xv5;7b#'woo'WmOH_1EIP+> mZ@ +mo&NY:tݙygf׿*t℃gY"o =W)6gb3 &d}{<kq<3dY^f>SqAo<;>u. E^ċRIX(8Oy0\^'oO!@dDzCXE'wB H52f rX+5aK.X +d< d-eZf!b H5`2ۃ7d6Os4O\\ۋd>g0~!&ӓp CBP4&FГ@j1u 0,,Y!̯нQpƒJ+HcE%t+7%䊺Jqm UYz6kХk[W l%I6W^DY6z"/N)Τ!}Olc!zK51j 5 +#}fR㎰P PߟWy'i$XsVęEW-D|4vC4_=Ee\]a8z}b>dKS &WW㘟f\3~h4Hj +*əE!zHv+KJarGM5Գ)EIճyd,5NjqE]U3td }0eG=¾z: +} eR/wht7Ի5jO5n,v%?bnkN]6VvϦFa@9덺Զ3åR +MK**h[ȑ@x՜w8Bzg#`;Iz 7 +GXW%8[YRV:7^Cp`LlYOVF֏v@^Zc5Bn4E~ysQqK c؛Yk^lv42_UKt]jEtK|yFZhyEU D[sƗ3jE*1UӟM}m_DߥuoU'ھ'HzJ;}V9q}" reD |_ C5@\l{oeTn.|ʊ|6np_mڒX7}V)+ͬv)qiv^wXmo6_q R{eIIgu8m C@KE&UJ#˶le6mlw{g$nZpW")2! !pzY¯T($bZJx\&܇Tygt4+I WAINJTi,"X9r&]Fvpq ZIH0׈JC ښE $05!>9xE8IA%Dĸ-2ʍI|[u2Jk1CE H=)[T)d /xh4v{5`3 +ˀ$M&i.3w{Wpteʈ' 11/hhkA(CbqtDtac&F F?뷷s.Gp}]A$ _Gp+kr-(+|m 8%1D /0S?P/˝cYh5E81k%EiԗBwS*RFd1hJRP̸,p.iSNI@No>LHZWe҉V"No,JM;4ȔK>X`D@B71F8Ѣaw,xhYz +V,"1;yA4 ?R%]H6, ]e3bzrY r1KpzN!QsHM r.w\H[:_sr h)UQZcԔ4$ + i 1PY3 *ZUYEB*Xml@ +\FT80& +Wd%?3`!}/MRZmf)Qnr9 ZK0a72n1\&BJ +}ŭ&%6AɛbٶM>榽*MyԤjjK0iKnkDA]T:1p|N6$ˈI9 =a&\V|QMIV%nT˹s[~x{FY.B:7%L1<.:B&[7Yϗ2Y3c7cM&BՄ`,o{kiMknVB*L proJk*7K1̙6ӧ_n)dys!j~/B'_n6גswK/ilX] 9}0)s<0ʫVbޙÊs-J ϵ&5<)X-]: WuF9ggTj@+Cl\ؔJbS i)DUwWQJJJ[Jh573A80,2Ja1u܌8\f,j_7a '(]^рV@MWuAB EraC8?#t[ZmnaCP"%%HE'‰ arVzgdu[neMV,%] dl<%;RTfL 7pH4꿎G>;PAc>]"Vե*ٽk/f? ll߆a۶3 ϴ)AbxN&r.U֒[_iDM2&hQ~EkqUL9Cߦg$}?lG(8I +(YpLULmQr͙l +HQ) |o<-}lȌ䩢B5Trk-i<*PYNI۰%[w/hEӰ߮_A\R~?__!/τ6\󈟍&+._҈dŧJ܉y$ EJ{n lKƢ;쀧= +}B'Aa;|>`|`h-K;VaoHίE>4 * +^U*؃]SͬR ;;3o޼*zE.`* D+'*#䝰ȦT5Yq )crb2Ae1Ϥ }zaVpR+L' +[(imDA,Q9;}b9 AbT֏4tGZh i*9(@*:(=v4 rvFf(46[r)4mj+ꎌI/a@鬳 _{R@iŇ+Gp XYR{wsƯ]榁^؛sի(jf,<6YJ>>U[P%bMX p}Ecq遴J?@ ߣI MY + ZdG㧢BSJ˭2%yyQYv}R[DyBH3Ȣțz~? +”ԉ&I)n &m70QQqLTM?jz~<`*ݛ[нz$ahAa{$! 1b7`ulY"Ykαhީ.׼~B*w2pBϝ괣y;rxQxeDibzHymYh;IEQ>^ #\>wtPv20SEljxVI>x] N'/Gm< +ms[wꋫ>l[tQjIn1-V%NODM;g羼c\^${Y(`?4ίB~Ro8jeѭCz܅'_7Jw/DjBhP$)% @EtQKt'w +^.d H/[Le?"LB|=!6cʱTϏڠ6ڟy`$/VaoHίE}l4!*"LMOZŬb8~3^0`{o|y+8;k\CaB +Ys?O3B7BcE]˂`,¯PL4+Uʘ$tU'נ$h!Sj,13.#Dsqi>@ȹM?#X!WE@%ψ*`X f)2K5OIfڀ$Z7')'SnTYKiу0I:xN'6:c@Y]v EbY +&#]b :ZRˀY)Vc Eژ]TUgp_$k$A>LyQ`)1,7rd%rMYE@ƊˤGх7AMy(ylN!4 a矧_u8 '( .|<+Nn)KP!$ +(~-uGb%"'%uϵLIhLkB)徣T4M} `mΒ ]S-?edZh 1_ f_?؉ +f.ǧo #}끝;f* !ԭ.pDphHI00-Ɠh6^޾oIK^2~q%أo[ߎiۜMng!6rg.#5, ]gx4LdR^.@q9E9DRk|)|I*MjI>q .yI yIme=enQ93ͲZxGxv䱂Jby%'M>7R >828luԴ7cN@TOwɈoo:eZ#úU.>546Ke=8giɻEO;:Qoˇ\icV +|4K.3av]E(ΚqV:)ڧI7o0L68m8WImtm-Jǡz{~j;umhGo[yzj}8oYr6}W`b;㚝>6m՗FXJEŞB͉l#"+0 WLFszn IM`kc-B#WotqE=^Ѭ6}ChZO!pGA=5D bQP6 IJ`{ UH`-F@S"(Z [`|EG\HQr<~Y.4#Y7G2K|iI tY 9 +[5H8߻a6>Ϥ&$6 +P =jw5 ȚkW=.lo7ܠzb >i3\Q(idf] 4`7u,1\Y:"BQX!xfz]Bb;o*rLjb{[X *xkomfX&79֍JWKg4~Q;Q?$ރ ?烞{ajwaumJ{ vmbH=f qII)<.(#ۥgpw)&73Y &WթM;^.s}!R*xRk!w=zluG?Xn#7}Wp?ȣ' "X  Zulځ}x{KfdNnҳѻwg #rŚ #Z&Ld&rMj=c&4[#81|b^fbI D}L)JKERc_d$N#kؖ Y(Ɂ{n6kꙬ@].9Mt>Gr0'S람?&a^RNe 'x uF׌))S[1\=XRim#|6ͥLǝghDk;]Fv)$vV%RLLYNB㸥"FJH- 22C El$kse:3MA&&*a.cw 7h—^%,FeBK/STԁ(ϭ!{X@n2ξARqf!iP. ['#*i*p oII`rR=t)M +3r[ӓ&"KAi13ifpoZx5\h;~˘T,Fȳ!'I:\2fdv{DR֙1r.s/玷z1oĕwl8Kr԰SCꅏ]+*NϲʄK]evim\Yv=kΠsU#SJ?X1#\Ω~mѢ _ X)vP\ +hNVȼ<)o9p(AĆXibK=>v (Y-G., .XEdT?' +01B r!MH…)vpp:xOąsg,l.γk))=t[mJؐKxkFg7f\>|Uq~4I QȒ[WpʗS@+b9G)fMXIy]~Xk},Q(uWmjJ3``2?)vi;iSd 36|V7/epGopAzJQyuӫ ^}085;0tNה2.Rȍ0&old}f8Cކ#f0\%>tq ^A:a0 {-^;?/BWw8<#ug +./ *%Cmxtn855rkz2n#>*rD$^|i2ߨ4r:&bs,$+U<%6OiQ>*vɢܮxWe1Wå +4,` K%o!KB&c~ bȯ-]9,3U 2;/(vUcr̃+3TTIܙOl=]}_ +1HjDaycNj8h-0 +Ba1m`)dZ@9;q=jv~{|^WStΌjG/DUﶲU)Ik1V#7t\ qĽj Oή;_kbqѺf")eۈDѬWy ee<ꇆXc辝퍰d@!\'oOm]-nJɾ6kYUsHYZ^1zJ:|^OmuoP`i?t }.يBo7 zqNv(0}_AxTz }j6?Ap}?Yo8'](>Ml9MQ,-6/8" I=-;-iEpyާh{>XH%D&KfVDW**ʅ6R%גjɢk=2$#h1gFe#L*OYrȘJRUf,7"_fBEb􀱉~|;]ݰȋs-f{f=ģyd/$fbɳ9t0IDW2F[XurJEkg#Tol@D[(r-JL<9 пpHK>^}8$105<_ l#׏'w yܤ<щ=l*`gd%L@Y# p_$" @(Xpmo[LE' ` 9m@. +Aدx`CX6] LwpUؙ0oSRe@P4cرJ @_u@w Xߒo}6X55 1Mc=<%1J=vGŧ)sOʉ|sfHAyǖmgk ɔgv1MKswM8_K8+dH\/^eMw,3TVk  +<*SҖ.򌯅A)(^cԸP.+\ oVj>hw5V\GL%Ieȓ d<2N rN(a}:{cV֎O1}O9 J +Z;dE>7OVuZRm;"M4 R1cN8JZ (NZJDlG.j*cs؍(MV¿Mv31];dDӑʠX"}]~|,e{2k عUDWo?̐uY_OO BH:+ 5-+%d<,vI# =W^luĂi l%<?qd:W Zv9SفpA+'jzz?B>aw.`gӫa^%O/ `э5-XyϠ̍N/ +'NhRjT@_[*arcq a*S +7nj!>XT0S3 fGxPcB,P[eVyV\xahTɊ+`DY/5Aϝ`|M;mx +x8QR5vlѪ)Q"ŬmAf* Aش+x{ Ej..}/h\v ;¡GsȂn^A o~_~! Q`qmb~"p]T06r{%|Wm*Ѥ;o >/*QM:sX5{A`mU橭mzCզd'OS8oW@{>s(k$gۮtdⴳכpW@"9I^M ݟ$ղU[}&Ah]JTƣHhgR'+C?mR>it49tݐ`Dc~ =gc/1AI#ā;zz IvNG}J(%rn'v eZj +>"` u!K7&~.~G `2VU;)^:Pæ50C'mTbS +Ay誰O]yU-L';𩛪s[|`;j} ?<;VplSc}dP͂\*?/73]W/G5ukrӅ.¡ڄaB iRu).Z-35Ze~0܇zwt/ Y[o~2T8vudT8XJ" jZ^-p 3Cr6DY/ _dt~~ٍ TbLd1L\$SM$[c+#eȍ,y AJy'd"dF5W+Z'&Uή7}B3iZ.s[GZHL6dl..ۑ?ҬFfl[(U~ؐ!ȨEun*i SDl-Sз@W7ޘ +jםʝ+]0#B;2^7|eXR:OH mX$ n]l QKL! +S*ckcҋh9ا jN]N<ݰ OF'Ra X-.OY*D9ԣ#aj302HCE!k7%o (uB8ORJ"O$Ay,L xx&"cHNTAq,]E=yrj8[ܟ#Af8FCXeU4 XB'CKuLbNM2¡T q a-G*`އu6ljݩ7 jf6ebi:ZS-b ]\ngv4_,_anH]eQE_|=A;]sۘA1~ȵ͘eDe#DӴd[ʳkgǯEPҸ;I8I9Līz,y۵Dl-M6u _e;HX5Yȩesg8Fl2DVZm0|@&EhSMHE'Jrf +Đ(!dܴLcC(̊#D2DIG+YxW +.ExՍ"Us* Zԃ)@UX,gRp !ͰU֤%PT',}_'`D Gp}ڕlZ\'Qɰx5[I@{=srg\t[ivW͚qGC>Z)_©KeM"8=*v]lIZbnUiǢ@m+-~/ktjZQ{Ǖf@з^3K~,7->ggf~(B#[Q射[>)jARfvy({4rGJ\'"|[DyM;<}Kr<^C3"*If!.X6'6zӊrQbm/ɤ3r<NoA@*t,Q,)؀hy)XgtZ0eK%ej5VKǖ * Bp_(_ds)#e45G#*ק>Z1-3e15:Vۘ!Kfe$tX 7UB\wv;^KP %k~ÄW4d+.-"QkJA==&|)XPw }X_'`('޸/5>. U".y掎.O/W)y}%=pz-߽;Zh9 =f`q~ɖM9$\*}uC˭fdB IJsfBƎwG<~);@#$!%jyyù|a#@Ĩ1W|;6<ޔ +ҶF'פn&`ǃ1J"H镊?$p@>n.z"cB#hhKDR2uAA\c4~`qp# +X0>Z8&1h![y軗w/` X\ l[5H[7 G"ڤչYzT֤UӇG1)jf cd gՕUYlzMcW]-EVm^O0^"4.x^', %]/l0 nQL0GQta{.L{s++f<`N̪^g߽i }ku@8hIP@R؜ѷY6y}@͒ 8`4uP!8rKOq{ (YڝKr<("Oqؕg(eV] ̔^KtɎ)lբQd#RPC2sDQx *g؉𡽑Ī2jY[سW}q@f`duUp; -Z{fZ0"2{\BFQkNt$t[}{9"GXyklK{Zms?E_e5G \ FZ!fүΩ4J3gɴ=Gm״ +(lkRګyڑtm)]*j _مmSE綾ddW"հk0AQ% +ZPaWq:[V0)3{+XGR7`9b M;pGw~;W4e%:A].HdM?wo[l~&X4^q'mI~r4 @GDZm9ӴGeO*Jw7B~ՄF +xpxI6 +{+wcRi D8#kN|om6WlžpmtEXmo9_*T!-)R rv{Y{~3c{ߒ gfe{]TH+˥*av)iet*-R7:gSssܘAMoe$1f<^5Ѕ1ƺӋ?Eδ( V:wr^id<ɅX è~r94NXK3ҰίT8hL*"GP0 c =&2V"7K28X7ԢCs;1I)mYaDHdVY*HGW?y%zedBazQ?ƸEih8\Nt C÷98dޫTVr<0@H95V +E/9d\%}62UI .B6N5NxaOi}|?cON']^ɛl|9.J=3)S61TbFE\Sd2Ʌ <<,7">DKk౒@ePt+% רwL$^x Пy2ң?_pH*bF9ajXX$rBH-֘Rj~h(MSO{ !x b%Fs+maPX,Ȧ5g:M0]F|b{^@1 +X2q>r @TisN r,RD`b|Ak$ qFk//W4@V4-A-n§/PΉH \j0lt_'J0zOe~OAG KW>.R1H琍L+X Wf ІQ1XIAtiY90,>/$U Rs5 bSl[&8@PNN&%)\%=_jR?h*][psZ޸ *9,M#[IiA ݇rx@Ѳ^BH]BE.SviЭ A)[ אNc.Fghhz/PeL BIJT'UY%mۙ|E1Ory؆Zd.=X\6(21?"2sثK_H^D[~Ј"M _GPt,k?pK1ې@ R0ʓ'>^yoGGvF wL*ڼ{EID\PuuHeG]g4V5ZZ$q@" ) 톡5^m8JfEJM<;"%TGtسgWxS^ͦqyfm +ߣ;F0LQ0XVЏ@ǏYEQk@OV%g L 4g 뱗;-:.y9eD E5td'0j M`h܋DQ%^jPD[~eڝ{=E6 Ia@'ߟbW.ǂ6Md`D>)mwqU=QQgD|u3a0qĂ}pQ^iݙGbQw?ўCmWMݞ~ %4jB4{y?XmoHίCUE" +wؔ4i^t* UUUb/fz!\~3]H^P3<3WJB<$㰫-/I3Dc>**kִyo"i/m;H7n =,+hçӻmx kCw)V@ A %rgq># q{-] +(vd^90i3U/787|e*1J[TU id\]ew]u:T y[#2Q|bz 8T%ϗEmxDžR*6)m +]ϛ 0NԽ2W:f41FdKxa;!]hXP"BrN6r1@^~h?ݲ_jjϩ:F%_s{pe{ 'Yq  wAӏeKE{9o8ۏ|"WWFk6U$+WXms6_udWާU'4ZKM'<I)%H).NLb}ؗgeGp +7*0*T_/47|'51 ʗSH4C\F?#=K"p +Z2JFXDXʥ Ifh4l)T2[6ݭ<H +JL7,r)%2*۩e Fܕ3bl?\Yo\s͓+ +זXFEY \PEi lxIi]R [_ٳ.[9ÿ/Q?ߜo_S[\[3.;ll-[ x${M-JN =(g{װcEX^} I5bq,!`B?1|+!eZ|EOSe@l Y t\H**γܭ搤GleR+Qf:YC\&l{c yZ?ݼ8SBr^>W>|L_|{f1!ْj?kuD&{uA/B6mq" Ґ.͖mYEf ׻;;n5YWN'WM^kts{K]ӷvP0|X@a:XAηne Kڜ)<]8<հׁ& \}RT.&8҆_4p֗Z +Oڱ8E%'ub4Ҳ]wi=M ^ޒ& irVQhpϰ{2m;(W`T">Τx2A:,۳#/7r§ v8v^f~;vů&Ī 8.*{;Ȫ"Y-*o\ z]#50/Ξ˛YQwnєЁ/_Vn6}W }*Mq58dDD$R%(E3#Rv.nօg̜3dξznT)!3 .o%ٕΔrDi%[i!+sVeR;7"ßl|+\xe4 c- m,TvZ7_]Dڻ @*%_ܭWװAϕpHU~kG`(R!9zgU`Z-۪󭨔&q]kݙ&rPuh07Gό89GHK%tP]9~ A̚D. M[$iv""%&Ez}#^9ߍD2FhIA ,ZŘ.P}"E,pMFG1MWݯt.VpuW>t1Hlϵ"O%jʰ<]4P'iy*ji+HZ$sG<]mqJ.ݏ*ʘO)I"]IŸ lCIsdޫR]7܋ϭi쪥pHPvMUI5y摺uGQc u^x!×tLOT9Z5'OYI)E9L./.I`%^n'G4p^{3gIbU(ji㼩B1U r8z_2alVK&^*;3?K?18tìkkCao,Mk]f E"*u c^g+In}>e*h! ہ#"ߺY{0M3>!8맜FStE<$륿kt =Pk\p%nFB=>E/^Y`rl80 e-) QU{S0#D[}oXͧ,/Э;߳dKj\#p amsÐet_䂟|\+hGDZQ8Tn0}+VYMkYlh]Gcn*6YߵiR5!{ιE[AxvlDLH!KWJUcˮAiO[+^3cVtQ,*Z+U{3Q,ǝD'jPZihkw"R#f"6ְ'_3@/lEw^S)Vµf5I'K EYYPDm*RII73ڒփF)3գKApBh18 ;T:fKĚLrս cR@׀Yv< _1OxtNr6I􄹕5Cn&Xqךn~P>Ģ,mt4mH6gR|8]]}fpvQ;&qo(wqr$˨>ډ 9,OqNE.DPGVm4x&es*) +'/;0`9Eq;dQ;K\|~U_o6ק8nhkmr[Y&5;R-ِm& +!0.(lN +-+LTPRK *'ֆKYŴTPhH*YFTe +a*3xk@ +RNAeV*DERD~X&P :GZn6tkhBAXs[Ud*3Y/7d+P RIu/Oڋ1R~5 o/.z F);ә]]q&2k>\[/9* 5`&81q۶C*@1~GKb> +&l85jB5aXktF "E9:4Aߦh"Q_ ٘"Ia^I:Kima2fK[gbNߦ?g IFV!VQ{0n>3^色a%B)PaQjS{qMm7$֦crSQ(؝)xgՄ2VNu 91S˕Cg78r>y?.Qi?Mőf=5mr!{>=n< NcTձd ?g,W`=@`뷈õ33c400EP)?+k RwY39$?< HV'!!d{z"b''a[G ER(98SY>|.B(g͚Fdη/_U7]DiR \>⺈>Xި[Ѣod/<=e0Ë7ti;7 gsq8EQ\P 9D/ߝT]O0}ϯ&L{ ȀjX+ mؙ{ݤtDBsνOEPh/V:@ohg +Ll^g3](SxXfM`I*!鑚oEII:ZBiZUb", R@?LGg0'ϔ[H@(0'*e(M/ sa3.M*_x0Fl%vbܚ;.MZr݆ч"boa60~t) ;`H䒰*2[w3H]Kbf\F +60 x`lwKJv^s tZ+Kϖ *R%Ō +B ug`L/uMFi>$(hqr=*OG)Ll2>MG1i4>Rdt>UMRʼnbONBJ="G#0'R9.#G|h*ПS Sc)q,sqwS5]eußPC#}Xohg3gd~݊URhFJQՎZʝA78z}Er=5fO"1>Y_f˲}AŊC EnMk~hN0)k4jբG]U=NyeEJTKK{Z1 M?{Lx(vV?8ퟚ{G9NUQO0~ϯ8U<$at@hHirkؙ:ߝӔڄbw܏a*KL+'B٩VVP) +Nj22Ce1=vR^^VnT.I2}@MGȴqPvAџ$>lOaIK@+݊bV[XR*璏%HE '@09zmdr[ƮdM-dړ]XuM);UowJ%4FG]5(+LtXUṚ7m 7$:^p}VՇQԶm()Lf7ƀ$"dE1Sρݲ铭1KQyhDP;4~:j4ZK$sG%7exLxTOsUz)Q$(g2!~+CV$5wjgs xhѰ)IEuۤT?m]^$7dBAJ-Ʋ~Tݹ`>v{<`Q(:Tnl {@vJ$iB7`DEBU1=–RH`O'&wߒtQpeYnRr ,y?~ХKĚ ;bc>v䃻=Wg9g! saDg^ڦ ?@Oȓx SUnF}W  E,IXGBTa2 d.E3+RAj9s\fmBRIU _VVWQl+V~N9 }9*8DAXV>\G' +[9b1r:Aq)@',Ú!яtmFZšBZT ];,){Ɓ ˘Jc!-q붧rc_(Sq\;޻{PAkLp XTR{N9CD <K3nƹf]M<զ 'R6IoYUh-g+ iڃhU.VW7 ) {ۡ .tmH/ H6k4Rq:qs_(y +{[&,^&kQ IF1LJV~0pu r-sVޡҠZYP{ 7r~2+awJ +Ua +fՄ2 H~ġl" +dUɒqy͊V<7ei ھFNsECUp ;Ѥ P]ի?0wS'0~%\~<4v{Z>׫ZRJ ՠkEgӮ/`ݪ|Js`N7>Q>W\D?øU[EqRA޼c>0|ra/Īil=>lRYQd%j ##fEc^1(OfCfxh (w긶ޚa<!8wߝT]k@|ׯXLbXIH,!O,k;d-=KPJ igwfgn]Q#X! *g6gR6ڸKZM;"Ga Y,@рVhmXMvI}᥀ޞpS;0lv"<6EK 勉d~N{̽*Z֏F&VR7h u\cF~NmzoZOPۄh%'%q28O!ZeXaZ4^-##ZF6,J('si|5fr+3FޣQek-i<*PYׄ_R9UR֥GI CA_ +Co{!ao*cAπE[[)^{:9ϕ(eNmAV + sykiSX+阐BVjJZrQ;$Y;' b N@\\ JudwKG4$z2*sjĶst+ b9@g"],aGT=#p5ze`GXY +ߚ5 $)JvzBSKW#{,<% RTf _]vr.[vtODLA"j',H;yqsexߑY|EG̽lZrk' y=Ċ-qmX'@,zCjv NIM|7I3xHO&Ɋtzuv:$٣G~N YFpALw˳<|Z9Y\'yXPg4a%4VX?ZK$KG+\ߴ[rMv?R2J5q<$%B|܂BH%hH9F>3$VGlKf?ޡ,ûfO/{t!Tmk0_qAc۱6KfVݕ~*}ڒ'qb.QƲruQ{tVDH0 .r0NRhYb(v6\5p{-yBc +F:f =")lD,h5zEREKTGoC "\!V("DW~2m)Gh)hJbiѬ.hrD,PaTj'+d+Pt^lD>%{tR;3f +Yɟ0 4꾎&]=iZ9LtXUġ;u/g9) 6`O +0> m}RA/1&gH[Q֯+xVmkZ 5h9.E 1S$ dsΣ0]p7pU5`jHQT++r(~9nr5&< yr\PQU\h5L=*n\Si K~,ruJ0E~ +ޟٿJ^?qՁg$"s#ҍ5r.5UF6LnVc0:MeMc<8KUמKJF禢 tXPH/vhOLIv/ge꿀F Ivk{͖C w'n㑷| 1,a'}8E`r]|Tak0_qAc۱KfVݕ~*}vڒ&qӒb,+޻{Ojp4 +`s^!dRX%5>gS)0V|Xؔ)Ce `^ +8 uhX[°m 'Ra/1.{"cnEP~6\SW[`XelE\+ֺ AyVSE9vhӛxLM)j3D DI.N.oSnnEXtx9D{/ƀ2*OJ;Ĕb~䧞sK7'0H(V"rB]sFkdN2~K.ݏ.UYG\n:!#(П[˄2 Oך ˔|mxo/ΎT +A*Yh&okhE~~/x >T# RLr3;'׬gBQQP?.5F HwgfE@%m2{z:{D"owu; w `x3`\|~TQo0~W&`c;eiU*tU*x%6Mi6@4iHY[^Xx or) る +L?Z6+R$F*,O9[_tذe9=Y)D,d3:)Тu:*!#J!P$.}MJbm蹩)kzRpY\@SvxU@kRJI6#}H<%{ R&3YpL4c{@5;s]p&rԽ wCsR@0`f@mL{}QbpI17AɭCeĵakb+r\TKLjHO6z4 |(Ym~ޤp^_q^j_Di鴁0ȯQ|$˨>ʊ :dFvZ>s^䉪cB%QhQDqJ[dM5U*eäP=RWhńh㠮-;7CY(Gw^ ]Rd0+a4Lx3c^xH[]`U[?ke DIB4\A_(ĢΧ&ʹ"Imh!GuG6bD7n$nm #G) YFGҲbQLsnT(U$y-r(TťdJqP7UпSrIv?qnc +y?p}( +.?f<|/sQnH0::0[P7EhhlALfaziVѰTw?dHo-.ўcχ10%hI-й{;>K_?Tn@#5UiՄ&vaɐF9.V]Q Tfo7otVmpQ#p%-RlFոmPr^3cVsQ,*Z+U{3Q,ǝD5(44J;KPX֬RD_>fD a腭0+{*ŊB֬!DPctr(+ M%Z9)f"cS[zP(ez4c ?aN-=aBgXLtXւIѣ~,r77`^ +0PY۞a+ .IbxC&0Fcȭq~+rZMXҡMIQ +q/QKo .JxvW:mBO{'K@ZSb*X4qpidZb/8ɓeJR=Ѣnq5Dx4PK۴%d+5TTcRQKaϧ-B*ӶE97zX}>ODZXi){ڿ"\5974͜duz3O(,ٱ?ml%!EEc&W)mժ@!Y- +7L|lRd|bvJp% XN1e7^dQKp9 Tak0_qAc۱KfVݕ~*}vڒ'u;N3,HzOݻz[{dDHB* +[εjtJҠvk,dc`xJ̘2E`V;)=bNnU&AEZ!J#7r0*bDGp3>#]G:itF6i9Q,|(A*Z,w띑ւf+k/a+r쉇kN#}18 CWbJ[h|et&$K)Tн':?>Il|뺙pg`ѿF=07Ħjl%TbCZKq.(3TqUL MpkdMFA a&Nh-<@T*3Z;@,C|hۅ`M;:$}&|zQ _xa|%Fd|.q2N##'W@—ʰb*Qqഴ}r.SZ^SR)Z"Qȸ1"r0]8=\AVG#чӫX|O@%ݚlJ{XQ($S aG0 3u52];ЕBcײ| MZ0ܦ%[]6Tz11WpJ4h^{\lAi}t@0%`yIbݰrƯM*T@f \[)sEVU5H4l),yA[O*CkI4r TXLT\A_(2Jm&i/Z H6k4G0~M!|.>?-n<[Lo"|v=]L3zx=?NgC@RCa!(&~j1p4ur%cRޠcRɥZP{zčDŽ_\֙4?E߯5! vǐt j +aDT<. Olb`IS+JҲ4Y-ncV03ADX# +[(i9mDA,Q9;H}t6>9gL֏4-FZhY&9(@*:(=v4 1TW7FKQhRVoTQc!-quGeu'F.)S~;x{dNmo. (x+Lp XYR{wr]="T@/6@P)sa7M3@<sRv&*ZRW- i<QT k!/oBR\} 6t/ZH7 H6k&0Nzi>|>Ob8^tr:zp='}@Rue!(f0ptur!SZ^SR)ZQ{ 8&8T7_3 q:%u*yoMH!(Pq .Z*aD4<.3O P05 "in zjN"BPa;T*{ҧn#u'.6\'n.܏\2זWKw tX/YG w;+QxA +}$6 +VZfYU=ERZ4uנ,#PuQOVX=PhwYgy{2m!zui]+ھҧlB0ҏnx&7<<ܪ]p?Tn@}WPDpǤJB -H(ObfЈb'TW$dyg3 ^/LdhTReV=ieuI +V\)Ai1Y&,ഏ1,EBH/]- DW p<&]W42Z(1rQQ2wAd@ Bgx:Ò3>v#t+#-ڬaIDJN-r +O3aRrcdrkƮdIbMZ2vMKZ7jn-bo.0|t<PAe5:w̥PG7^r&^pAx)@R+0z <6YJ ?h|B[̍Zr[% y؀(U"55W7 9>mez5H/mר3`u0F}71 x:`~ jO3zpvOU,TTAL%;^?[: SY%2L?SR)Z"R{ =c.5j+:ǦSPPgПS5]R0hG@!ZX UoL`N +6`@aL}mʃ^bpI1עDɭ Wr &V)[גQnEq-ZCۦWz$}w\a QjTHf47ni1/[KV"eݤQ[SkdHbh  L Gqh,IY+Ď)wLL%rqlגKKFm|D$ l٣D~F0Q Z;*CŪCgBv"ޒՖw_O>Wh%Q[ ܳn4jDjӅJjRswCPT9>=ءxANK)E.5f O9oygoTao0_qUA|[ZRiɘirkbvit$@L޻wwMtVBJZ&FUnP2QQ%Y>rstGi0<0jGcaZ3(]%ѡZ龺AgVh5sѧOYX–(;|.L#%T,υ+*jO5LN-WN:ڔzF2O<%;RT͘J$c +pp;99@* 쀾D \rk!8y)a@ims]͙'f5#{_IV}cX^'t iD>ٴr >}^PZVIRŊbv0O'WTk%k#m؍ +m[dFQ*q+nc:?I{.nUjL2I"Aӟpm{!Q[uޢ ‡f;x,iC$י-c ysj<h}x2vFԓ ~+B:{ro=h H!L- +O.@cs@Ѭc'X"F%i7OT0R%!{]}m5<gQxV|uepwG"?n BBH~l]wtyd"Y GO7&apzOT]o@|XE]6DthRtjDDQH.-*" X +[\փnZXJɸC,i릓2P5c _(K~;y'Qw::}е؃/C.M%SwA$z.R@oa |{smND <Ѷ{5u6毉tU:GHK=^Ab5qDRU9f`hKz$}@m4$idcKV+LU2`yezeJO Wc@j'cY1Q,si|rs9Se#JRІ51hkZG$ Z0Ti5Kj#lunRX8=R8wg[-7օmט#v=jiU1Ϣ21;EiG(l $zs?>+^+B;ti>Xم|D%]" +vZݻ8\M&`Ө<0yx/%jrr,xBjVE_GLXkLw :`ZB9.>D?T]k0}Ach ݖ>پvڒ'q+NRFY :sjYy`@H0 .r0KвEe?X߳F=yIН,tb =B)E,NpzEREKT[ǵbXKFBD~f dDS8"p;\C#3d)Y\AXœjNdV<_@|N;2zKKZײnn=?>)]^׿p蒭AH}t@0%beUp&nr6m9) kLW)1չ7M3bH$y89#N5<*btr @,E|hѺk2M( ds5!| Y8YuqCp{̣$-Y4[m +"7C@RKbʭSvK[']a3<,G + +Uɵ-&)Gɍk*mGd Վ U׫S|Q(|ߝwS )uSn)3L # +.hI]yiuL"wVօUwCEn7@4ԬcUۈJv-Z{I[ҠHnx +8oc +` 9)d{ݓ(Vev,#:݃Z XIg{VuLUDKNsv+Eq gzvYHoNv5@Ӻoiw2AxϻUnF}W  E,(ITGBTa2 d.E+3+R1 ^r̙/M5p Y Z9!T9 ~MoeVɾRo)vG}Vbv0 ],^\ݢR##W1" Ĉ>|L9 8g@#݆lF-)2ɩERуaG0לjodqndE.%^t`1pj-WuK>S .OpI4lnw)st@0%`eUHRVwA zKp]6U hf"<6yЕ|$fx@w>T[ǫ=PbEX p}Ecqv"陴"7 ڄpCYc&/?%evw7pnч0 -`ݳaa HQ*| AH%3YOOVK'[a*2T^!;4~@*4ZK 3G)e׳xL{{ˡJ.] Ug%B + )~lN$Ȣ9s:}HfE3+j21OXmϖi_/ -uukP:Tٹb4IOk5(MML S7ή +(2EK*R87]xMR>N5B$^><6j`e> UH@Ua><Swu;xW>ƑF>H¨V[E{ttszmIV.8o޲}e$>oPMhͧ/+rwgE-޼6O%?H#qzLÞNFO'r +È)Ey$:Fh 1i9(wY ޚa4ap{;=ks7+`*$-wU{rdG؉je)qmZՈRs8dm~moQ;[S3@4F7{~#W2|U%*[-Du̗jtJci*әr*|NI>n"jTYÓWCW) qb]n*xd"Yizr"IקG/^9gY۬2Y)n⃘d6˰d)&Db.b="[\U"]EyS+LɀU׻|#bZHO& +`>ɝXؔ.ҏt]z%)Ֆm@K-PWD>!jw{{;II^,T~=>y9U_W˴,ak0Ɨw"YVp]&84QDm#Zv&3h +E]-9H;:۷ǧG/ON^_߰_ J? `ሦ3H-ru:ZlE*MZrXuVԖ :JMa? (N|I5/ܓ}R 5!D+MA4Zu\\A/D0 MOņV(N2Sk,m)KUt$ eˌpX'Erx|.!BV{M^;m?x<а`;l[Eo{\bY.5)~(%_m#qd7KHE2ǿN?,lMCS4: pWWINW3Z93f_QS,!ggjgOY%PQXW fGǧB|SFCu Y! +A";r8ҏl6חiVN5*=LJD8OPPI h+XA>W5"(Jj9DTL T...\}Ahiw,3JU T +{5@I! Йuݠ YvN/`RO*FA]a!CBKW&6b/EP}p;z@6t@s*WhA-~a4ޥdB!+v(_nzv'.tllKq<$\B,V3t$et$̈:D +ʟ`LLmQ NҪ==琡Bx|y rr_Zҿ41m{mZ:?*3ȁB +,#K:Y/x@;Xx_)ͦb +rM|b\/.H^i5`XEgq@~ICC0T@ߟ5 dQM 54EV ƙ޸Nrex̋ 03@gHU,$_KfjrGP&h%dRfWD@SDnuj6+=E~}s j_HM +'H@;ۋu|)Π:\N,ӕi V- +B^VwAþY@;.)Ň#*;d*xC;qtւ`2t%ONrQEC_ٹ9Cwڝ^vBJ:sD2] )ě,8UgdZٝ}D.rKFwf ^벦]\V8G53+}3A5ڥc@KOgژX XjܟR@wdp j#_ 1,+2*Zy^;zxXb8-=0S3ik~FU'נMpqlp&fDtxGXUL6+z(>}1a-g|&ԑ~ףh7q.h=1{LmMp?_4 ڹK^$1~s8šbP\YwTᵫ(cy7[[94eU=>9uV*gO  +,u=,~jZK'clH+lQJ4̠uxjx ,wrFa9[?=>Oѯf݀*~P}A%V*-Kkܔpwg7rgɀ&8}gx߫a.^_.а?+LK):H}b$oGGKTH`pkz@20~(xІZa>@H4𽲱aIã?k][c2]ΝH-BtuwEhMσq W韶^B$Ct(ͻٸ^ {Kp:[` e\L*-uTڻ+R gwb^`ݤŝ`#Dzo]e6s{e/%1H8e:GM\ۙc\Qk_-fI@-/k +8ܬB#;|8ed )v+`hUOERcm67'))B0[lg3ҶogZ[st}'u<[djT!A+X{iL7 K,v? fӵ|KCOvʓ'Q9ZKDAGsuݟqw@1w=D̫&&qLMuCX7 B3XceM͘&۴w +0q^0 .޻(lƆ㩰p I PNULWdv:R0?[yĚ4H`V9w:P YHO;Skv~NVX[WGZؙTB=9ф4'%o*:褔| 1 }7\{IMnVoYmPpfR7TϨ5FNipɷME v4G7eta(*&Kr+P,+jJ-gvDT&g$o>,Y `m+Xjдi²mQoSn2Gna;.N^۳>Kr?-I~0rŵRwNJA8>9o>4Q,H>̆:j:8jS4d9|-VKrP+̣1ߤ(ڜrȝltt[Tڶڶ25m@~]p*)T~ +Z9(ܐv :͠=v G}XSOR2C[loI0ml=쬿9MY:=72n/A%Zt̅|ƂSF7l9~i^.tsj KTT<*JCGpdYSrrgY+g`T4Uq%#ydֲu8HdŮ!blTqB,_DwOIU=<}}ґ9U@ ڣZ6HbBogêA> *k8~= ZGR J휂_PYf ƒiENos<*Ne$*-NnJ#D6Cy*D|l;rji_~71$KucrY֙51gq?HF3Աn̴褏pSq{'E&MejꂫgU}Cv([v\B+)iR^jbREVA:XW.>&1xoDrq_,|??"u}Tm{j%yU]Gd +o z*hG Лo%feo4mh82DEStvgr"Urp3#LwÀ*cΘF8u|쐋2ePClYv `q R0`NcW}Ǚ-RT@PavIJbt۞) t3 ͱjgp*𳯈3DV0ӡtȣ؞\vE%g#YFJQIp? "RWk̇2MdwHZt 啷8aJJLo%Ε%dGJ>8BVӧSR^'bQ3SV5w gԽ^&w**uN]")s̩1R\TyU Y% %1[?Yg(G6TC&+G*2ᑝ0|v@'72dWѹM9]Rh/aaVkјF'IVV2zz7okX98#Fd\h"[d n7Pp$Βjm Xh+0FwѿoV]C#/u7zZEAzZ3OTCz?9=s26aGvTƘ v=+;࡛$["7:+8zq L|/G~/"Ei*KHHk On;212{> +&Ecաmzp:] l>`^Tx n7Z=5$~]BԈe{k"B~!=hrGZ~G:YT\dzLGwMNvI?L3l1ɶ +ޠ s\.:#9L _g1Pfv9ꣷcr F O6Eqõivl<;"=s*pӖ8[U.Ѭӫk_j_9[5nz*`>e3 TqI*O ,Y]WhǜG}45a`235ߏC?&0Q x2sJȱ%6j4m ȠfGnohYvK'یEyU"Dgp<z/{~8,8zLkXu +)'u["VcQ;_ն6җ-qaܚ%^veƾhwѻcjL%'z6|ʖڼ < +E%Qx a暹ÿg1VSr՜ϣIAMl $1O E !U_S}p '2@CϏZh \9•_4 +9{:{@G\.i\AĠ\XoF**QL y|"_f!d0v[=6}1DfVOo.^ ci0~v"Ys`9>EzN㛤^GޯzmsX:oEXδ7>BؘI{.ūa# 2zw@?6v<XW.cs6곝 -,mAQ0#1,n?[řqY +lnfEh֊l1Pe™ȥt#X<$A/ <9o +HO1BTOA2uLYYF.H{TmIhAD,il=I@'5&Z5BĚb_*R0j1+ HxkTڇZ9woh]! _d) 쬃ITU9ΫW=Skt8QKRfH-A25u8T*.x5j->΁Ƒ0iᨓvN3y+ePԛC橥 M|'(y3MǦ39 C'za:_߾P|2C+t+o "`u`D/cRz")k3ebor +ӷ;[K?;1)簘â?2֑e)NȴW v\(֌RƳo԰6.'Xkb2I6E *_ LdP f/]k*IVNM$,E7n财YߔL/5͌$v`*d +UF<+53z-L6v$V+HND`ЩD} r 6-|L>66ofi Km3 dMAC}^`^Ue؀S J awb,uC:܊u5xؼW#-[i.D8Oơ.4f5EIUu?8Rapzh9 -IxME U0۟~)V.vu\;˾L $w:/6hNBXx55=`Nmyx{yKI/?ARC2ob'X `ξz F?o Aօ{ɠZt{Qp1:mHnej =녺$>5_44}t|ک]G[4j"t٩ikfB".:r|ߦ|H mg3@='/įCw^Fcj8,wRf6`3_T^py NU N{Ocr][R6ʗ*EL{+tZwWaU8ym(uRZ%zX s\ݒ`W&[;M?!ٚk 5,$L&a]?9.l1_j;bx5Arۘ*55x(Ac3xΉDfPGyɜxSlW<:j<|مa P,hL.p}k G.reZH(޺Hco>ށ&& +īX>W)+sJY)?*!5>#7^YIJޒsԁ D*OQ1l5 +WGhByd8^}kLNTpyۅi(Q5.5bee+Jчnl㙐uda͋JB v^w%4\O֠JkՓ3yj X9 9+2tW,+=y՞>r$e|98 Of/tc5KLb)%C~hBж\!244^g*f{hMo-2=vѦ^S%ɥ& Cmvq=A#x,ҾHE&yHe+5p=SPu@9lQEUG]/߇/?wАCHh +VW-'~zb3иZTpNۏr2mgIu&ĺħijԀA:m?ܩ'2\4ҧmoi9=#<ǣI EfI}-?(>~!1ć, n;!lFXFZ,Y]˘W:;tJg%GnH)^pi2 P4~UomI|cSbnZNH~g|%O]_b.rha(]4qgl8x9N\ 5L[N:^ 57MAD`%RKQ.2-ڱ&ǚBe\ɲ)H<7[o8RZiSIyYmo"Gί ++r]6&J jEV34azvǘ߯_lKIdcfze۟EzՀWp-bL4H_{(~6ϗ<L*ί"SSHeeL2Vt}g N2졙/"y9:#΍x +f)OzJ+ MT,q3>gٔd|AH12(keB v`w4D!Z(@JMwyh/!W1Fwѱe DFE=} % ,I~Zj0pGf+";]FoI̕B"C'k`)z e$Xex2o$T]C5#s4`{W#7f?L 2y-.O*员K9sHyR)c)!"ZlJ.{2ji,el4.S]#ټ5|!C<4&bJT |1! Dhdu: * 7  JSl"s] "4pe@-x{֑VL O0ǡg A`MOB͖\{D?H9u>2q=87`3GVO-mLSthSJbȫ "ZP[sF$i/.|6_i&z;K0^0/ʹ䨿ŪΕFKnS[X^ΌdsZ S>X&s#*w0r%I#5iM[JTK !Xk bf> Til#^XWec3Φ' m22I6VٖU##alo:O\muIڠsMˡ+h8^%~pd[+]i,4%-{V5Mw$yf Ww`@iK&)5Ғ=004aOܦ9̘K-9Z촳9TgHc=dbumDYMHE3Hw` +yt5pQ+k:e(m*|Kf]z|E5b_!<`_(?78fR۲3n + טn\+{=K603reZ 7*ט-58e%p} *;4 dꔑ#3eRxTeCdyCʙȸγ[j> +fy&EC ܫ4 qiޑVFZ*r ӫ6im׭Bނ k97"]̓[fa^zɕ雈L)WyrSmq$q'̱M"bbaMacj5~S-n䋌jM\y)$ Z#7!O^ +49@LYv0b,*}Gv 4elT=$R3 nɅI ̼^q306AˋsXZwQ:@ +sVГd_ɁAJPU<1e>|1mPZ[F55|OYmo6_tcu!vVEQ-sIx["˖Zl-swϑ/y?}"OO0 .b挰?S)L@% aIp)HR{ oxĄf112*F.d.bj:L)JKER9~HFBŘ5#d̘U? O Fk'a פ@ciJT,*ƀ#-OB0<{ e|Nq0 .eCD_A]ҁ(wGϭ.䚭v̀"K9ѕ6w^b9.#Ԡ4#dnLvEѣTI?Ȏ s-R5{`<]W)-06QB"颴EPM +"^]Q`L6y=]v8zB|L.l8^ޡOY0 LLa)GDY\VϓXgX^l0#f٫eP w.8OGP=ݔMBs).HhGe fda5 0ٹ(vEt,g/\@gԕx=0hb3؞i;ʔ4aU[-S #զ+>N_ChN~ſ`拶8J|,{ÚqAyMT1zsy|="_oT 7(]R+ ^+?rapf6O[lխA 9B|Uc9t@[b!bǗWgWgu]b0͟2Rz^=ޝfpiPqz[sr-sp] +j43w؍ !( ++;A`[{=oq|63i?x3+5 +O+$Y ]h +; +.ϸu2)%fh7/hѨ"jF,ox|/Vjk?|Q )f!_>|ojOnZSiOMܽo|Zs6q+)գ}qb9nb7N@$B:ow $;1Hb]9[/G/ vFI0K?wIH\,_H!V"NՀDfrAv? lt cBŶ@f! cx"Ap  ˔%[P-5*K+҄-[!Ɍ*1z *|:0'V5^')˔ȩ3{ ) Qf7C$2Nd^x?c4] vIA"CgB9H(oJ5H)- -9H`x:AqrYA05jո~8_{׫O7&Řܲw7&W7pwίÙ~cLkJ!ZT +de@o1"aŋ/[$!i\ +V3pUS)Z`OHID4xp|1+_]~H$TI򩡍v%sp'*GXTUTXdER)3D6W<py2V 'S!<`+ .el +Db[fã+peia"'i޸?+^/fڿo7\ wCklÎC|L\b[tX"܈ذT]sWeh_nJ,hQ>~D" N|֥ڂxclSg|X1YP+$ thVYP¥eM0DLf6dOj$Z7wZdP>"d %WDaDQ-IqRM +07DctT~=oI$Z&2F J=oKo0jc31%\1M'WCGmGS{y8Z6dA οâ L&[B$DEѡ30BBrGJY H;20NS4MXTh%7Me3M֣FjT买;2DS@]g|.$#\'ڗ waUK tYPl(K)R&b\d!(wf@W"j73o$4AxxaD['+ tf/R>͍IO_S]{w(:3!^Pwxů#o]f +E= ,KNR@hPH:3`$2)9x?ds-!]`cn(H5/~a"X@ɦ8zpDt` !)iL-! hPL#?8\4aLX#|>`ŏUPaP1(5mWНALȎGrZDpڄy:> +O9zС@ǎ>k.7NSKȐͯxkxT@U_T+]\.2,Gp֟wj9l A TL=N 7UJq_zd +VcIy^9)GbK]i*΍ij}]W;¹Ob -xAc y75jUcÅ Sa] +tHON&9 *5"k +fTzky:}VTw=͸g7$'Yݪ̸&kQ9ؑ:Cw"GnIV6ƣv8D%/O\ &)BЀǸ%86j7 v[ +ZRuW쪇_'|ЦntR +xb!T*QΤeSR`l$ڪvKٖ~d]`oonj W:ty ٿ,<{gJaǰdCCGި&u|%'t*8h?YGpҭ a΢i;jz OX*-))6{zgaS}V(7򅨯^XJŊ"GLĦ%Jǯl yvda:yG(OkysanӽM +lk KF*S +T.*p[%d?)L}Nq|q])m~FT&V\\{})Fs'#V?L +αX/x.Ի˭@s2^:㊰Б[}k2Y}[ZS/lXLUm9 !f ?EC?U)hテ4 +s@gW(wgRN͙ÛCЅWmwuړu &g:^^ظBL/glטlO̦{ ;mjbFGks6vF~T$n\5ב3NP"$L:KowH;m#7v`Zϟ]r6ͳ2J$rM<236M#!0!~J<_7h8 V2&(Q[i@kQz+>MfU4laŋe"P-Py8%&8oĢI^`ae @0VU2h +G?6R~cE2]i 0 é88'R*YKYaXҰ@3iђ+GhA1AR$U)-$Id#h*ɻk'z*r0rs\(fh3ȋR:\{ڗ P`vpX$ͳy`%`52%{VnVEKVZ𾶦w|Ui5^z|>>U"`z.P'h|uh;O3ŕbb jCo](s i4cDpQ]6yĀg#Ezhjz,)ixJnRѐ@ Ývs^-d Ad^lZpd"x)]K&[i4 x$e%J76[k^UQeA%GLwLӊlJqJv '﨏w`{)"dɲ/;j~g1%X [Dj<^@m0F$&lԤ&h%yI艊[ zQEW2LDK+kPf5-,M"ɦ+q}ktW{iqD>er6vF&MC- TXT OԿ2 r{!<=U0Zb`o lRTtdF[.RWDbUXcM{ѱl,Ks<;R,ey{-*UE A8']'哬!}HVU +ȩۤ jTUq ^Y@I7MwvmzP-I,־ںe>;mX{cwJsIj̛VNu|e.hzz 5Q:$_xrocm^NsdOA‚4E3y0""NH@H]$Z#\D:ڈmڷ+V}ZDkt ~q + >A1"Ea͠V`;ZG~$}].|vX +h B8nOg!9D ڴ.CyY[Od" ,P"H|;2P+kltt=0gfS魣rS{30T\J2z'qP|׎'8\f` : DBw30|T 9^Ք|5w-C7H +z34%^ vdvH kNd X7 lfZF~^MVM ;ڹH]pǪ`(yA"WԂoDצ`U9#ņT w8xR誰T!Pfggf7}e o'&y&Ev#q `?VɅI ^VEn$4K:˜41^ئqV(џ@H^8 wpMm_/hG +zV.D0ԪS =}K[ T)[4fy'cmVѬ8GxZKbvYQ &C&m[,ߺ +>СZYX΄ +s}Hcu$t"!\tf[WQRN;qsԺͳ2_-^['uNSkT|P:uAxouMW3lK/%TVMVJK^Wwi&mg6-)?z?DPs Q|\\x=J6UK?m㼤 n(j'%>4r%8,]}ۀ}075NA_f2Eb +=)nvڀ#DU|6ɣ|31§t0Hw.{i~m WIsx3 PŇ,o|(Bdꪜ1y;hg lLܵW硲@ K3Q\q<*jlRNeӔ3>[xp6C 9~9 hf`P>p'67&Vnwk$:.u^ߍiVSzV%(?DV'?Gdv73hWnF}WL Y*NF2`) +ewb&ޙ]ީk(a[wng ۋx_ >p/B 0+JFZ|jX3口81s + T' #}0jbT.M{ #d+'mʅ"!,Pyd`ʹ5?y_&}_h¬PFhHK4|_kB {2* 4JDfez-b$RA%s4D] j"i Ѽcb`8,v1ˌrYfH~Vφ4M<*1뼉B5fD(b ,ƨF۞ŊĬV>9<^abmS9ik#yTX Fbs89Q\s *dV 6<]e;L>i6t$M$6Q f~sEɔ<@n5<*A,`KЍ- knY1=cº؄*妊a6#)J}E#"MHX1֨ض@@_`]a5i6'v.dh.B_cIɼ-LjQtvy5U"'z+E32FcXhV2 Vf#}he`$ 8G9 iZ%i C~'",ȳ6YXtבrO=kč ppЭ&)–,n^__Q駟c,i߫f"Sn/z1V&^ TAvvkRvc_ω]ݜIU2{ukU7ijp3 ?l-,V-R>eIʝVW^Ԡlb*t'J\݈łr\ǨAJdג&M\>; RYJ`As*AB<,lrGiG3IE9籿N6*ZcR9ę7mFv=?L=tL*@jJ}i":fmޝ1_T=@HqO̴Uzz +MnHmX߳@,n z5>}oi|)^lR?1Gqxdw0qÆ)px/E_0Lʝ`*5ݳa\IO\1Oϲf·/笟*7ovB8k;DDŰHjɫq[:e+ܶo-_ɋWMo6W =$ *zmq]@$ӂ2kTIʊw{5R87oҿ~ y~VVNH%Un+.ZB C)(> J5+pk ' h ڴ(ڌ rXrv0G駳V3i8H5BVJdEAEp\Lu32_;ЍBcײ\6m-պu(Wu cKe3: =KR%`eUHR _B䖁^pkI4Hx#m$|&ftWZKlSKC/w *B%a-D !U>hEoӞJ/ ڄx1OCx,/q|w7.&7slz=YLfSz G5^2 +*ERɌbSj }r%S*OrE=Q)Zݺ|T-΢5]Ր~'L0V*ܴ3Ok&ܴHOkMB Wt<~ UaoFίED^;5HT-lcﺻ8vw"yof]=JGtNWdJ Jm + +;&#ے/*mqJ`i* b%ZlFWjOjUKk=HfJ8T^B_h]~Vk񱛔$QM{g_B@&`1(ŁB T(^_e%a|ynjaV)9 [fTm0p&lV_Ōz~O苈:ҶޤJ,lo_k0oon"滗+郻 ȦOv qJ?Wt7h)"7h$?gL@l17&;>ҝ}YfCGEkma6FeagplP+}opdEˮ=؞17W)^ +1de:aԩU{?pK)/M.A+M [} .ñJ]7e\cJDv={(;g=,J sy. <:m $rhC o<" (k4oYmo6_q( +4.\{6flH]O-2QHʮ7HJFZ=w Uu6~ µ*%dF{whgJ9Ue +o,(]LP42T"?3[a%\Z+|2~$V-j2Q ++ZjF3)Y| ,79U~g`D+2-J ,T;E/JUhoN̮'ѕ1C?~s<@BG,;CdLV"uU*356㇨,(} 01?Ջxݎ[?077tyKFZYbBTX Rl)(&Z.$ ij 69z4t~̦!}7o'7ܾW7 ~}zzCʒ^;|J-1OZ E- + HQIVRdX+Ϥr$zǷT%?c?XYscJy2 dtx̯R!c'_I{Kaa0ʚ#0`FQX ,͖x|t\^5x/<}>Ʌ[ǥAZՓ{!䲒G^t1s%rIbj{~:Ɛc"~jc=>=}}٦qEVЏZ!u2YDd nWAcb82nbꓺDyPy'/!u0{RZG[ NkCVPhQLh2ipߴ)5u +c6E! "Wќ/ͯd;pP`iiS52hP~Gr4,NޓG+1Qܧ7qTWL!Hk[#>C&^1ؑǼCfxc)p0 v-8W ύHSu 1s7byn1I|¸/w|銍PMcC<\kX>lU3jL2r|<|xrFݩ +}q!q:w}{j>i~׉!5v;ܙgAiqѵ?ԧLi +7Ȝh'Emzwř`,Lv52::2?޵xa˜ iSue9l~os NSsRƝ[w'14V5AwIl~) 9S)G.mT]䭽,qADSGZ bxE!0R6ʝ /TMo8W =mIkۢGZID$%)nJ]-|4fVE(B*j ~)nrL޿,h+QR9,P9,됙Qп\W~a{ER++Z +9*vڎܢ+-bʻ%@ʯ7*tco(F:}JZ~NX [2BuA +k~["7mA3֓ xO0N^_N@iSuOp XgZ)T'vTDxt yxo^&0 K/H1yKʮ:S-:Gj}%wPbGX[1à`IqU/8E$ZHH6ftWwg>d6!}xH>nw6۬ +G7[-I2je^V3?E iN`!+Y=UFm GdIr%oHG.5mV6dONIAN+l1{< ť2HxǕwlВG{q"Tѣl|4H=^ybL0NXغo@2DQ{~n6gpg' HdgѴʓr#4.~n>5e}q0nyPQEiSL otAor$u} ~Í"\gq.%VRH,gjt8cpJ:LFVB#;$f*)n_[э'"|GӘ^GX'bWqr詗ǘ9 -޹VnAV0 h6# QvAB AW4Z3>V=Zm`GDK>ZD>T 9b@ +˚KYJخp,i=覗r7cT%1m`Шw49J@iO֎.PGgr@x)w@8Fw~m΅'<צGrv,^sJHCo jb-q-E !U1cBpڦg$t&|FQq2wQ'3&Z"֫8+z[Bgxu=$(| iidkNf$O( +H[kdN E0%dG+պ>)a((0߇))T2 &O SDZJAVuC?- _.|rr{sig-(o +t]a8v-ǕRW04ƣ;䘣:oJЯuQ +tf<9 ApqTQk0~8.{\ڬmYIXž8Jr]oN( 8ݧNΚ]%I +! *G~~m:ե.oE% qJ s-sT pCӒAVQ*Ah@h@+lB0rDikTN2DXӋ+y/NH 6%(QD0uYN;ykǒ^#C1NX[Q'x|kуZtGtXTRg^ pAx)a gsy$]M'<զL*Ȯs*JC5 b qD m0qE IqMų ,O,N.]^ެnZ* +.t.63@*ύa^(G~ +-Clʜ䩲%B!irk-,tTSPx|$U0ٿLHI&? 3LL$ +=O a%<2y%uk՗D/+.|v +{GD3o+t.;ZGshjVβe7ޝM%r,Q1|{4ػ"V}(@ɶ +s2t ΍m<įM!CUo]n3<FcVn8}W <$cu]`-"r["%Z&"ZD[Hd3g m*@̹s?'W7,٧vvg0JX+Bk 5ϘN0t"[YPڬDJ|ڱnT٤uCF>#J+8d6٥^ He4|S pغnkc /sZfɚV>sJgO1zN m>ʜl] +/6 +DbYEtr@Fe#kE[-i"{icF8I»q<{y2cqww|rf|2[O埓u8RS) D*Qc 4u2OR$Jq#גRpƸ6`2Eyu2dw~ WMC͕ʝQJ1TrgY-^й$UZS#$LQ.?1 ݎwGҚWΨ +ٲ' 5_Eٶw~@'uwzۗ{/' +jVMIdL>ɛ9iÇ:vlsZ7OϚ{"'9'E aIC<B%+s'ZC:!.7ސ4YzdSe-؆#"qXm$I7uL\{>C6k)}o5`͒5J0VYh%zVi'8 XA:ɹ|mHr%+_礥_AXcBp UHpfx|m[a2tcoIjQأ Ց6 ]k+tk'sWmo6_q0RR۾%uZI0cnb+ZdT)Jrd;݊}(0"swg* ;p W"0, +^L˄ʳBDƳA´$ 4H+8Y̚)WBFϯ\8IKTκi+S=snOgKevrZR}U0d% 2ZGHP񘩐 dQ"^+9[P(+vYu#2F%'Q?@PϬt6Ivw y"XX2T"9`6Q0C2&?YRC";_>F̛,Z#ZB!  +}Mؚ2he ^"'$= iA.b90wx>ٛ~=.&sd1M +$dzq!CS.Wz*Q6} y9D$ / s-W4rR-HTK*M|x+Y$!8[H4ա߲D`pnm7-O9M&/G#X9>(&B[9VZǖgFd|]qW.@7܌q"c"%SՉ"bm +3UG!$@qр-B򢜅77^Q5+i$N +^!vL:GJɵ+_)Jcv~MpXPC0P~ >=I?hpU +#%h )쟼63Ruϑjt;G%``"l^>b" londlV:G>짎8t]p[MC#^'`%% Բg4ۏՇzH;LS8,Y`dkcW]8W ׯ5c"[VpJ^xɻ6 |W񴙲/w -cj9SՊj(j?t*k ,[W{kس͔k{j#P[q: }4Xyl(-o\{-0 b XlǩCn? mBQzջ\|Rtwn__Z_Hg;oo;t_j= R0qB~׈ _&ֶG̅L} R j/;WmoH_1WU4BRPZ]t(9BE{f^Wg b{}y}:fq F"PZT'2ڙTs徟y¹j;Id ްR' υpcr Rzgp|m,̌-<[5=H +LjZ)|?^]%XB+?E`n-LДcEE +JBV&ƔidUԃkiTeoH nB00b d] uu q3mT:;Wk<^0H1T̩(F1Xq4IiY"*eܣxtM0O;azp>@46AbЕ,%*Wb }rDENrHH̝<31c<ʑVnaJ.ܷd1)k\ޕHi?A0Ooc8d1gr˘j#_ 6yJÔ3_jֹ`H(.f,1|햩u[5Xm@Py82S~NɴsJOmѥ$(J U{lg dyy B 4] qb T3?ODSaExX8 8'걜<&K"4e˥Ո6#yϭr3?7ҘV`@.ö]+oy`-p|]Vt/8KFi\ קk,BD|i 3L6PŎ7H`sG0BҨ0ť8:B8kg bQ'L˸!!4s%*IVÝQF~jͼ*e{m;NpRw"+ +=!̓0'tUcdGt1~d@~חOϻ}~*j4k2$P,[V\|] hqٲ^`0( ʕZLEwRK%?HQKsuC1p%Ow,yqd!cU /^ɓY;̻vgwV GQwd}k(">$I3#_?UV4{v Gvj\SHd\cH2`5b T.G4D)?6 +R8m<7[@,5d/x1\ ,JBbhٕ>0υ93 b4i\`y2 \xrA;qGDOcS5d9U4Fx<둶rMP-4"^@ؘQdI^LF|]9pN޿?_&pNǯG1~3i9GN)RArO.ꖢN*ų8Ol$ׁl7T$}S\o z)pa 5Y.AAr,O(PfH6xZ)="@f]bwi 8 ֌{L^̛;^13l҈Z]{3 l&[|~O,KGW[ ݔA(Yuk "2uZ?t=jGWsйӿH7bڸ^/ /=o?׶/$/o6sۺm^Umo6_q08 \kݧӸi"r[I&,IEI/uDEXKLλcA?wAҶ27fXSmJUāb*;KnA_ WTXMR$%km]EXoDߦ]Do@ecGqӤo4|ގgM[N&0}MgoT2JEmE1S`IטDO + +yGZ1`"#yT8Qiz-L{mCutWR☑ ;+]Am7VbZ6i*HjRQ;fSn7S7f hbx> J<.ZI*ـo޼ño1P' gsB@{\oUE 01 J˩j#`JYat _S88{s>IZi.je7B#\2Bo2}E *GV2}Z.KK/RD3̭bV6x;J`#H?\8Z szDPc"tGj-MڬeA\2$cjݪ*Quޑ#.c6Vg-Bip6RXVR䑳cP*'jJjnaٚgf +PdJ&dgGtyh 0^nAU$k*6A(Gb < $hiZHJon ؄Qw2 /&|溜_.z2[L/pu /fՌ]d-^ 2 +"(SɈbSȁRɕ<)En< +ԙ4ƍcG&#aVmaJܟU5X Rgbh$ ]$[ +m{%S̨R;N:A^C|;DvJf< <⼍V=xk=Sc!`w";TM^b D$vnD[R]pbjsLwHi6 "ho 1* #N}x?.^٫(H|<&;iZ|t8# ?V%qSSG-Ja[ {VPѲ&H[*@ڀ1X#[(64:(Ѡ!(OW2Ixg8ܮ8Xh@MՖPpJ?s4=Z<<[Za<߾1KR~jĿ?i+S^#~Q:=k}nn ox,j|sϷ}kr&YN+T*U%6ǯ_ .GUw6V;hGhKiB{wN:gwUmo6_q0ecۤq3VC(!LD"5ﻣD[Ӣ%(;y[(>9ejTRV_z&N.UN'H aLQYi7DJD/]# µU&1+ +[(i}(ڈ rXrv |ryKBh[hsK +%LrjQT@`.LŦZF+YQ9\0 Rk]w' +%>Ȁ.vP[F|HrU!JwW&a n_ +e coX9WiFiǡ1;MN t +8^AT* Z;@(C|6ߦ-i"7 ڄ`$q2Iy2cq77|r.gd6kO矓! QF2\!(f== OT.eJ婼9BɨДZ?Be$R:/*ˮ{) 8T7Ss qq)%?'JWp|EMd"[ .P?pLrq2bZ{Э~€w(aDX ?̓x&s-7]w`+ X5E"0AQuf>_̴!(ǤK:|)7U..!2ZcDU.ظڀрPm jH@L'fZsPAT:g`wkh/FH8M]p%~J/Zbdf(uuWhy|Eb!r(g AQ:ЊDAܑ 6Y,7h>a3{􆽻>~B q׿iGPKr-(w3dmh1͹#&A/f/|RgŜ˙",p1S0eO^aRb ae |TMٶ}?tF^nޏk?y‚96ܯ2 vn`M6^G{jRf^3e9 +Xx$\s6jy])FOp9y%&'PIɡ6G='|3' &jﱩd {2,xǾ+;gMv{J;!ku: ]C|ewQ9|I]Ysbt` mPbiX%ET̪1$//'inJҘJY,A!JTDRm%ĽxbVR?1:0dJS&=-D<F%%A<3pyy 7h ZЯgA޿j>5ZS[..f8SV'GD\FUSR"#YceM3շEƚ2՛) cR1:XZ:[gCx[m͎zLg=^`ݮWCt5P͹IֿKV[ÈsoO/Z<~&C)MQ@Dgi4ҍqF,s8_f FsD{! +3\CΖF)b9:Ǵ-m2-_a+ƯZ?@BsuV߻3f^@5DAtpBZOEyzS1)2;KYX~AQ~ppekh戃7!w9$]rMۖs`{2..k@vado_l?D۱w|$hnׯ ++v bn(!ȌE$f\,م֕Oh +y^{}W -\B (}O}m@Q ö[[gE9餷ݿ~C/ȻpǓT \{s7_djLʱJbőu$ĵ\$g= fDs}x 0/Ҋs[Ww9@w?4@}z>]F`aYcA @MOΖ \3J3"6N6߲6`o&yޜ~}~=e7ً&W7_g P.$PbEIEͣ, +lމa-U$Ѵ{JL<uGR:&iӌwSc޿w@l]~H$dZdS/0IKXU‚e)żYǤ5uE3z?E6@PX.>&Ih#vT#SY4 6|Ԟ'kV0/'Ld +ddmܘIE߆Lo1{ +R>D#v`0<1TnEN$TFŝٓ#CƜ¨M"uk@dAr D9=;bg<7 e}YFhxnk0 +*ȄsD @u}7*yP0*ۯJ@_m}7OVVW FRaªXPDɳ pA.vg(ZrZ #  +F1HO4n&+1qB+2ObyN~qy|~2eiEVEJ6Y;J$g?㙮Kg胘 =Q`z.BÒRGdS(%Ԕ6olzM{J`U3_QUD)ꛎr)8jZ[%)X:蹊UUC #>&ͥ Z&w`,lp6UKXY51hA}zƞi ܯ'+㭈tw?d<^q_9-O*Qb.5=ٜLMmhsufgm΀<>k|/w]A-'kfQ_-5 tP0- +q:RrauSZ^Ԟ6Qn{|'tmqHϰJ+!۾[Kgو嬥Մv$WS>ctff!8ۚB%vխ01ne) %R Ns:l4:\!iv\ rjvu&j&Q \pI"4:cILF3pB9#4ќ `4XqˊW!Vzs(ܗEg4 +߃D oQ[̃QUa_ձx&U- gN{ZIR5DydamQbgؚᙩlN{;& S4Es-d[_[N[rDP}ԽULګ57w +^M X* #^A$kA(/*^qX v¸2}>:2N֞fۥ#M3~޼zdFUYL T /I0D=0X|2<]j6*\mMh~QԳepRq nS,67[ZFfx'}r1 e).S)Bz4Vա!HXw-[w<#CIMWKÐN]7I#gFeP:Wװ¥,%"M}j}N!B,M0J>~w ֮27l@#u<7d\cIx[̬HK`EC;^UB{yuDbtf)*)Dv-s~ū~g> +GB$TmD/%+:njfgVn$ڡ̭\x1EULUKwid9ah &){nLTȜ(5$-R^?ig쨂D()R,u5ٯ{( *J%-hýAQI-@HI^uXj!`ˀjپ {Jә45V(Tcv?M2 +KLhQwG 5C] < +S X6.[9^ +}E'Mz፪Q3tusuvfЀ6=+[{(Ęʋ4NcvKW)cϫ4UZ`M%($:#G“|݁VHa@'$sj}|oSRƍr +jmۊ&fKF㘾#1\:D嘌VQ̻nv;y:i$WSN+ph"lC=i HZYE ͈}/$~< ]bI%ꆈs"$tZnU?C|[&p8\A:69 +n&`򢵲=Fh%fiwJGT\I-`<^Za +dh[&9K[<᥾{-rhROť:*M}ZߥRHr0^^O6&i3sUiࠇZw:7 Q'-B霠2s)N V9 )eKMNAdӣFxdEĮ(ގ79Û;PuV"EӾcnxS)Ielvg]ԧR6k՝ӳ$mtۣTֲ&ߪAοtoz]'- 0ŗ_D4Tl%4dHG$ȱA ~Ҋh/`bjҾb{/ۏ{t7].j<,3(m  =wOt@h`櫨px/dh mC%ւf-2nmڴh [/8Y4PR]7)94-m7Rk[lj$Pk^`rOrF׸kǟp/R{ - Mnև֣ȩﳛzFQp@%{iy|#ƙ +-7Ngܐp`owXYFp4MDfUo[uS 1UE&Dvb]ARKc}y'Jdf}6[|l<еʷ$|Kh:l  fO;.j-DUg*L{>u\ihy:wg[SG_ !RMlS˒J.jIu;~=3^Ié +#vgݿyh =ځGpz0H, Tq8R 8r(9$)gp(%KK8`*7 {6:)cI0|cw&A C QLj|PX?u,dc\0өKn|&r.)*ve cp#7&UFV [*L*1;"BФ]vw̳} L U2ΑQ`~"px.2aVYqf"Ip\ <Ъ8|8@휷NGk7ucd"B1AY=$8Pe}lݔ͊hl]lt1û˫cxw㋫K.|&CV.%I]@b"3Ay*ᭌ9)""*r 5l..'cS4mah ڬd(sAko+H -%f, ;wfN{f]K%,}VɄJUkln|Zv9ؙI{:6(^᮰1fD!k3 20Y + ʙHDWZORK`Àɣ u+%ia%[0kMK_N4+U# ({0Y2zK+1`_w-!Ak㺐-YaOe>AbV|%z_b%ŏF0gXKVzhfr@?2Kn)$E)hv6e(ٲpt7CE\, %"I:ضkd X{S{-''EzՁy,ypqYw}եS Ay cD|\02Z9 23r%V) ɲԒ""B+omhWwrXɨ%Ar%G/Ä^~ +b5]Eʔap \,(OΙzvӘu>FCҶN3|J,e\H#N|?D`B=l<^xd6x LEM7T[|?8e<)J*e;h6X"yJPeo\;ף..4->]Nݒ +TLج1mWEkR3ɔI٪ 莙zhyU(79,KB +i`3]ލ\]ej^;:-6o#Ew 42͈@.;GJ+\Z Joy^+:x +/ Âǖ +S!&d+`4K AZ:dcӎ|uӖލҕyQvשY{YMpFL]=J٤kS3ŕf}a}=ijMDЇζS,h.US-6VU2=[~4B?ጒ{a6:NOYn }P̩C9l5vo_ +O+ө/TEBt"Kxq@-n,.S% TUzv`k|-5K.!-}.>]kgڐgmT)}w[U{g*q\4%ٙͩpcJ}H|zovyBs0ң=:ˇН_nrvvI- FiHBNxeN,[v[+'2PaUW#PKmEPuH 8T=RraօmkvъJn'Ӫٳav+y+r`Uj+rߜ|Z~պP)U6ЇI3xGLȋ^&tT\X-'ӕR 0 ES.]/ejst$XyB)}+YR朋R$g +H6͞.?nKoO_ @ se~٪]v@Z,REQ_lkG] veo[ aX5 ݏۯ1Q8\PŴAtO־=R;.1C ?Ŵ M9q\al &n$%!#2qjop7l Oyϓ E6I$Xkjk]!u2K0m +Uu:˒7nGL+<*{t~{J;qGo >^%ϖkĶA( bq8$ԁiNE4tz9L=y<̇d~ߏfp3,&)7dn]SB%My<9-6Ni}>9 9+ WRhST2@xlDAi6%oʦBHS}H<,<a;C͒ \iLy'.?(SYNNѮ< El #ˀ_ӛi5h|]TY%y 0}[ՖL7TЇSmԼsP vq'dA_6|FBK 9S2>}~6~w6t")[p<6z_C#Sx8W D8fnL8I1>1;l`FEdyhnEO0J B#=0s[QsfOE'eܧpyҔu;<,C: +S^EU[ 6 @ 2AG`W +%շl~|F'V"K۰= % ֨LI06ߊ(툁"瑧G1PWjfee9vHKf벖#5;~8m[Oa2Ӵ-YEc2nL 4`Ôb>86ì=-sR~`~|GU4Klic;gߵA CkەU~@}Sb9wq]Ues/y¼ɦwYWKs+o>N%p14dB|SL +c_a#֟X҄Q +gVr,Np'V~L {TϏ(N}W`t!Gh Lq*6\G̺Mmz@E3Zbˋ"2tIs8RX-:'f^|@ҥнh%Pv`9\, $ Gp+J1+ה][]9vT +ˍbLtឡ + H?av\=i|[:=w.]Xhf>zA)GX{Uӥ;fVs387ġL/4'fl]r:=A;}-bz>/YmOHί(!$lHFvnѭŠdv[Izqܹv_UuNz^8HtjJU:; tDy6DdYwЦT$L`5o\ .p4VڗÛ4SI6QQ15RejPJ&?^]E>` ר <I8VZ$R|0gAhSab2ҋQәLfjF&9-ҹW7F@Bz hӡ{9sT[3YRɅEqQ"Q"x׮21y zR]nY8eO=mb7`x}B=DfZ2h +eMĒ<Ȏ@)-N; APuSi "^]f!v؅Ϸ_>`t{={r; \&CVyaH TEe\ ESKJ'i8%UFPc,UF[k,~$R>FZ'Ó~_`O1ŴW!^]q$)2A+#y X9FS>#%, -=r +k>ұ_>kkM;rRl–Ƌ.r@GCؠyre_ZYE0WU}^+Gm8b/.w:0Br"_$Zm̽Gq쉃9 hٟ]JADcHD'& Q(f"F'lZ$ EW4rͳ)xN:{6eśHpwRlb8ҹEOFٶ)Q(̧,C[x/"\fWMqr|Eוa_3X/xcf`BI  + +0Xf:Y>_xB cĪMN/Yq%QI`h7n%ir:-X;HS~ME>;9P'.mD܆P_ l?\O/8wKYAlEF%.gMF%x@Oƶ*Qf ֝wX߅_%Mi*Uc#0ShxxdwuBBwka#ʺ + UbgkH ɷT ov)MǑ|*bu p(W0sZLžFܤ $%N$d(2hˍ5w\p:ˌ{b"Pʨ)=Gto;7adv}]N gMcc%f,'NbB,GI +`͝N ϴ݈\ڙƐM:@ D3QZk`̷͔߬\GojcѨu h(["e]g<`_Wo7;}ÄB1)r\H,N#"șj6Ia78IkCHg:RVv`KΎ4SYG`˼Fﻤ*nWrHƶ=,pXuve׌PсZJֻBu1&.BoJԫvkSfzc5P/r[S;C8bbuLөo!  LNM24(:*8\TNڠݤQ_kci{߼7Td+0+V{DŽگS茌Si9I(,."P(t~~O't텒2𨽝yz=!g(Du +Yˉ[:iqVpG<kM<#.h*KEsC7JXl/+Zo|}_Sf{rcNhuM~mV )[禁wdh朖[?lԫ/B&?-ۘ4m[V69"V&A!T9tլTuT<>QjTIs$jqSlyՖIGxNAYib҅ +Í`}*"76Qh7z4Si1ڴ)}L2 w{y-w(kZ]~4|y?{_a5Ϟ:Au& upmRwػ[ƺe]46+l~lu#{~ś~ږ)n7TY:(ЩlE6 qQ+Ú!Dڅbn -&}HP %MB$ԅ KO~V?.AfebN./?_Xmo6_q {4qf fpmQ C@˔MT4;&]m{QN_f?:j\XB&FD%s0 "Mt̨4)N,IQLIY<!Ȕ"pL,tW]24$Lsk6WbK=~t=^\BLi+,P'P1X#$˹gff\2^ M(wF[,ƺJ J#jFQkg:p,+HRv -X$di]e}蔤SJҨy !i0&;˲ v/i/.ֈ߅ +D^bƢ rE#< iILS wCo^@| >|N~~777r 7pq=m8^>oH M, SEYOb˓d"bxɼs N\̗JSj5:9Cz,aRi݊W+r4IӘ%^9?GyGx*b:-rTKl"4)PQ +D.`L9fBdVoAb[tOTRʤ>/akL8$"}M;f! XkʓWxzW|Ną.uJ*x0q^TR83IL`*iK +&|GT"J(d)Rɠ?vڟT 6՝ ٪B Bא* EUJ.w74 $6FUNU,BjM'a3-.[[ؽJ#x[s_Ww;;ndHػj,lIA}b+mh,M CAEk\ +CD̾Jj$VʡSECA.3P.T@iC},%-Մ3z-z0?ocES,MbWh!K:x-Y,BKTՌT=c lNg#I>-mvئ>@4Lgr?r1ʻׁCk6T66Х,{j0i_p`5]Y|R2wpc)$ rfLoEp<WCG%9p!l I{BY@n0n +CF? *"P}EJZ6TF0=ʡTC:,\`Vii#){䝳NĹA"NL[JC~EyH&T%]E5^$.vQ{);&Z$d󷷴믫ʕdq.6>W*xp{uT8xn_u->lj~劻V+\̘z{nENUhZzQj  {/_CKf5eޞHC-$dy+s.Z7/Š9AI6IgֽB}^!Yqd^.z5Ӌb:Ǔs";Y.=Eh(R<A*mA( 3pjc˭)mK+EzjbSEiFr{L3q +(D;Nٴ ?e`}akͽf:.-__y=ZϖEQ[tn!|;L' +%;Oc7@]oމ+3|~*X>f#/E9'vC{w_~ԜevWnq$b-}Z/XoG_1, U?8vaːDQn9>n1uwfӶ"$v6U''8+rdfDpF22׹J)3L2GL<9SdAӿ>r2-Lr_N"Dq> >V|zн1ZKЎ 3Aa.QcAY +"[OHJ$rq'bJ*}]ܻF> r/PEb:_jg{IK">3h.6e:Ư^QҀYW@Wɀ⦿cffs>75!U .6?bd{S4:|R5F\(hlVEllN@+ +#%uiZ-`ؘQ:|4/Noн-\\>t>]A8>ԁcP)r-Q)@hy3н,Y!\٪q5R1ƂJoJaHxwl2DpgIi?Bx~Њ$i+f%ߨS|@Uqye=B;0J$n,@c@"nT#4 Q˾R<6c/|0쎜W>F3E RRp1Wɩ1Na,#wiAU>ѽcRJB0v+c 7̈{; O(-,+fpC_w i$l5ʲl~Nb \"ttZBz;a{ ,b/=y 0pGHGGi߈t̻^֠s`Ӡ?4ei>W ;q׷lBĠ$Ą& ֋ '6yO3UN|*x\M?&,%A3B%f޷q}IaVPx!VH=w s>*!SZk9(h>%qVqGZDzkVLHio+s˹,f?9*tmr%Z>po|"gF@eqyYdO!-TC,+)׆,Oi0}@X |8S {);#goe ކ`nbߞk*d1pUL=$tBdŧ2KqD,fmдv$,!UAO4 0J" ,)ٶ#.v2)]CK|[/ty6e-_D}i,ݔf&JC|oi +߈RÓ^tR.1t(}Gw(nUG*>\WA=v3PCg^%jaW~Ih#=w+^]3vkN%7J**Uv?m'ƙBXzP.2 +w6-Q6@6߿oz pznJ{yr;Xث@!-f2# Tn$-9W1#®XZq=Xmo6_q kǴI5fp]QE@KE5uﻣHE-mAֽ~:_0)PfLd wx%3-S~]0#hSHyyFZaB|ؔLq"2p: +dI[*XJUyVb^ ,KK=rnOgwc ]a0 J FS,f),;RTd)W!xcT!c)+QEY'mAlS|zSlSOt<38NfwSl|=w#N>o>p,$0RAQO>By(bbzYRC"ﹲȹZ +MdX +cAIu#7ϒ7X;252oR!;K>DؿmphMĴ,T"FS!6RLSZRmy/$kJ<5+ݵ > .*:RzZl=@^2WKE bnzƫd[t{/=o["5>3(q c>}vU*g^dO^Y,[hR3&Y!c/)V]*'`#n̙>BH%1GqlwRK7"G:6sdDthT?{3 pSLWI3l`{UϝnqB:lNV@,kiO}qR^6?U8xq,Bew{쳽;QmWҴ>fѣÕd{kcSoORRd7=uZhՂwxۛKm/\Ti#Ҕ.(Ug,lONjxqq1e|?grN7ژ.xtQqUG+{)㬲2"`̪Q=7HЅR:Dxm M›oPFp]/nq ]"rtCX>v#D n&n%o/g-ݨӄ-~$ $RO`ȟ.&>7 {ғ:w=x\m~AH%c@ Abp0oo;>.T|?aW-Xk0j+_[(FhzL۟=98Rҗ7fh?G9ϥsa] v/=ks8+km%dYŏ2vV&K( H IEqf߯&@J$3;[ڝ"7|:# g,-8 +/Ó,-N@k&#VfԸ7M6.QyHGQg)ݜs{g9e1HDMrg<-c7˫ ) ;6PhQ"1(!l~Ǔiɲeba>rs)`5,z-$)Ւm3Bw.4N[VzϢ{f%[@gK@͓8J[R.df,*7iYe'";Y>W$^ޜҪϻ4Ee=Ր!(A)`́餍  @z7f\ܴw}w}ݻ_ݰkvruyzѿY..Oی`(y#i#K-RNŜq<"p6>aY\h @r1KRz)++A)CʲzԔM2֤U9#e'9J2wkp64=w PC6l"#*X~8&'O H ;/p)\O$U9p-J.tDryT5?}Jw&ޱZ,z"$@UH$!@\ѕw *4K7Z$-HEp9#^f#u!qvI'a%X ʄp)s9#))3HPCA"BmJ^LR&㉚΀&(ۢcs_rRMыa6i]=$mc٢/J6 `Wlu$qr9&ۚhz˷bX;f v56-{@+vY2aHYh|z,xpljV6{PCOx##i4\A1@ԌJjZJ 5P${ h6C#OS33x;ޢ!> '6#H͸9*H\9LC/5#0VŃRO4Lbo!*w +& n- l*.0 a8sKDfS:9B;E{jGfLXa3~X84{AB{@G&9E/sƘDĴ [H' +[/,2e<*.3بsbM2bE7 PЏ p/JGhZAG$`8\D eGsV%Jmc0O:U$4 é>d{oo{W gGHfnУwjз?%Q>8aMOv󡘐y\?m_^]p#w ,`'$Bjw8xTO{o.~==;{{ώA VZogzӿ};u>nOl8&vm : ~E΁Bhp(`M@+^6BL"I>J1πKHH/No/vv*lgcZDb&&6b6M8M-2?6[q)MDβOQ.h\+,%Rm),UK7fA+WxKk( of('>r %p` L.8 +uop Oa*ŵY!!3Xwhn¯`grjt3hK %IqQb&o?W0bزfr;FyTrUpڿ +3P0$)#e殝$"x{K/n#GےMmC[%4AlcwLfG׎`ؚY&Tikt[u1ooZ"w\ޮ +bS {#|$fO0+xŭ\hZ1ԓ44PpmI[FUlGy\C xWk3^N0M NWnP?1gZ0ˬ-\="Z+U\=9.h$sry8b1 +0ZbȺVB`DK,IX + +!,f|չ)5{8,0jL9yFk8okԶDÄe׼\r=\乱cvF6,71JNޯ;}}ɑDu+Dڐn x>㠌DCb +bLLbIqGi&d4Dl>/pOg2UuA,(` 5*xL[2,˄E-m (*Q,iF - ֡{P ûN+ef)j^Ze_d^*Q&AR*w8)Vrٌ6)Y&"q*wL8'F؍8MEMxѪ Hzf`rBMyz + pzu )AEM4 㪍SڂtcTgw8 xVH:zCmG^Ln$;| s"u>Tm6cXט­W%um(!Hu375L}\CǭHeUS>՞p_ ƐeSI, 缡Lhm3UzYmG_v[5Wl _(6Ż7rXhڄBhyɑT=d^\+c3V>_{{,t1FH_V~Ted:6~(W84~&.͗!*Df员T;~G%36hQi|zKZWNE"t>^[c+Tm/C+)RC4=7, YUv|T(wCu ͹;jC֖J4dղUlW ?]SP/ +Vca̲ZVp~FKšz˫FvpfZ +tv}{U$Wn NrHHݩ!'ͦ?kDֹ#&Y [`0<#tp[GT3_%ul[BoٿA-Y>Rs5ZIx;{/C1ۆ_B/*b~ܵrU. M9,-ϬZ:|6 +xO0褻! ngfI5hz0䕳 +lK1kwS#eTO1r4F5W<5j=Rxi4+{TBj#yleehr ñ>P'\ ӭѣYE&lCqwWz:ȤۜyTѼQl-g Csh #y{=FQDp#dh V; oSG4 [R kHY{49c? ^lJ=t|c֍Jby rZ֎A_< v~BBNB"gW+aȲ0<z4s`v6Ձ3 JMJ[ ;lQj8[SǿB~zezh^Q̵T镗v/!*b!&-Hk,ѵt&E=P(YԷvtH2S}U{ԻkW [U3ve XB׿uQd-sXYYWN#nZBGjc򥪶vnqRw jY<[Lw9ޭ' M +:vּ*@wx|b ƍh%ll »'J}!E)\H-D)OT2x%}_EL~Ioj#&k-!c[&%&ٌ]}7sa~x=Z.;C2\*VXcFO.=.>:J2#;缦Tx>+7cWUO4](KcX @jI e!D> ~ڑ5դ=u_t7, xۖj}ٌ{,UxP#]%QkWr֬KqCSW`8bQ54:Ο^yMsLmEw_x6Vuj[JpnˉitV/΄ұIN)=xd1(hX1Y0_F7}dHgVw6V0!mIJxRIV|JkӪ~>c=3#AZ%n 5dcKXS"1 +n?lOgճʱk:,bEQ! |4g" e5Ğڊv[ox:!ls boI k%T鶮+oZZho/zp]np/"ѩUT/''.> +AX5ڧfaM˙N/eȵdAhw +\pXSqaג)ü*:$>pWqbJŒ;yp=ȷA!M0H)z(jvG_qmiLRQQ E$n DىZN7tc8ϗs 2tb`BDf%ch7vfMEV{PE'X|_ߍ5R[s7lGqJ""/b?TzKc,Kptэ#M˹ ==3:g5+ bGරzKѶaJ;'#2z@QFj2I$a;Kb^rpK +7\!UK2 &="- @X\m,\#7Wm$y~߹+Xʘ)ЋVo^{ +CoN + +w~5F3r'+Xfa[6lIT:%ua@Z̪ +qr'~A?n滅Z +.yQ/< I,(({ sܥvUQyDSdP0|b_%^j ᛦpNoM\)u*kQag"-"@EZzjlcVEe!%)}yr}yž߉ M-v~,y}){W1hn"y7r1AֲJYZb^Z@S^GY>9On}^@ISQ!OtC2Pi)bJ&ZdU/OUFXTu,9 +Oy[olϧ[.~-UgWj֜m'{aGy_u +Onx=n>Qn>5fGQj\V NƠ2G#(kWHQ]g~_h40)b ֞е e,UβZkBxs!,/ψwjTfw5n1iz~4n7I:`CX/^<u}L03arr|wv_jtӳlbS:';;o XpC49:xJM<% ~5Pt*&o 'A:\q,T#33fgR$a +ee##)Qp鴼^ҧnH42 -rb~̋ 6b5L%yr5|zꦜ@c$o]Dt!y` +c񗧧K kϽ<<y ycl`rŵ +bp*B1IXϟ> ˼}ͥLl^#ʡi݁~=2Khn$eӏe2A;꠸,DE\r/`QXDt]~WSy)ƱtnmaYݶt.Q8Ŧ" .L]0 +177q3X/ve)p>&󄖧ƀ*R 8秿Ɛiwrjr$טOɪG?ZdIy: ['߬?xF˼8vu~#bn5PYrOEl/IfXwی +=L5[p +Jdѓ/C5SQ)89Ō%g9LҕhI{L7UV͑hBGCmhT,0emaOt(mfG)v(T.~BtY.IQ) 8El4,ݴ(h#'*%N +Ҝ)kitZ0 LmgRP 1|V668GH-KbSov3 +p隩 + ~y%Geh{vZ Y|D !iQlyfAjAeክ̣ hZ(Z;p%ZL$s&1%@""W!︾?},fa7{SU_={@_m-V嵢(RhNTD**iZfI=Ctu1c;f<`.0R<P*/__;1BZyPsoLgYNNl`᣿Dm&!;`]?JD`\(]Mڼu>^+vQ OA&#nх[M~E)өnH5Ӝ2aUkѮך |*h>9՞ѤJﯲ=J U7od-{+G?|tޏƾ/erׇwekmmOR6NOE/d9ݜkCz\L]Y5\pmmq,Q:vxY V6j3=ncL/*5j}(]o"TZD>%),[;hmW;lt.[xh/\WP[ Ly{ nwsTwݼ[^5hil{;h_pWXmOG)IUӐj6"UB>qaܔޙ}3iE^Uݙy晗E`NA-8?Q(( + ]!sSP8?hVLp8pʔ{<:mrQI:1+IC`4 Η gct|uu<NFpq/Nx$?:24cAN R\>9 -6N2?=t/'laqk"bK +DSLtRI-}Kl5Qiw6Sz=?tg^O/BmCRLF9sojX.AzեIZa&^+L;C1jM XbQ@c1e<1 UK S;?dbfZt%X$ӵ[Ęle n˳Kh. ,1<<Ԅ/؝*"qjL>λL4z|zS +ZC/UqNWӦrNbcTE2uG79M܇. #_얃L`BqACX QK`r1VCsY%um) 4ݖ'^U"B+׌۵ c1P:j~:RRhku&e!WF|pw4;>DzzWdgg稕.3MQ}88$*)}:K^cIeVzܰUncQR10o؞86R q|؁Sij SJvҜv\gLT!8^*l:IT`\eIbnnD )lӏt?׵"K!)kl>|L @k!8'~-~xQ5^%J?rxR+dEN'dx+S*|{V+Rכ+vEIbKz6(hT#zYcѵeorF&xJ1`H^|6[J 6rkI$^U2%4i.¹XHpyxd_0dT=M ^j m//R1JWLw\u7Oɵ{mqv0נᛘMX&j,:([?qF6O8aBrS "9y voc0lmp%`urP:yZ,a4#)Xy3/*K,&b& Tl O?KXm˺kї1Tѥӥ^5@wΥ#eM0RdqO)0f3|LkUs6gK–Ae8zczZ~q̅6]ej^%_dt^\"d2Q(T_v۔,V RQIѦzJw9\Q觪u-4IL|.>1h5-epx̓c"r I]~v֏+BR _ud.P<ҫ㋙L5SCIan-Hd{A>,˒HSV-Qz?:iDd]⨞PW$g$,e4Wn1RY/K.5SmJ͗ˠ[fzJjj|(ry!b̲I'~ 3fR@ϙ5U"++V'ŗC/Riak|5MDXGDa毥eīH@~,%;z1VrVTK9^7 Rr6ayjL9.`7CV b_unu9q_pmdG 74&[d{/KT}snϩ]͟(=yM[^P{ٙQUC69)"5RkV[dq;/zV5YMuc63_{wNX93! .tV19&Md+ +"< ׇuMֈLjWő֦x[; nVϴQ'g+kbwYgT(uՆYk ~1,k<ࢉNjisk,=Hdu-m2o&.7SbAn/V\Fcu6% +b7ML=XBF#]oോu쒕@p?Jq_!JWқb$H*"?k~,F8v5utG7cT8["wu9[t4r%PtGV!/Ci>K?hMߠpN73@IU5 +kwyK]ݛ2Cб#M\HYCG;6bY}ִ:{5pKh?P`ҫ:J*FKi8O?_ YmsH_J-"l8VNI\%` I5߯GB L.Y>y4 o%Gq( Y^x?^2 N,7>~o[6:)R!ż d0ͳ 搧83+ #,bL[dK ua}58-E 9o_-pe9K}ۻf7 (҄xvw74;kسnߴ^@`}fb4l.1X[Jܳe.J[M' -2kik{=+m4*U!'uǎuEω;= .#Zk صZ(3[ j;c&=,<{8h"!Dc=й&/-\Gp&"]"^XX}t-z;$Fmjrh9K{l%d>; uSpnk>&f D.Wq@_gğ7Č c~9&9(UV]!V(e:n_|urt5U +WE+ =:"Gԧ,Fc휅9U +|²5TpЌ~¼N ~` KsaJ3MY {/%` q'vh# *+~KųzzF 31V;28+?a/#!%%RP +?L|3+)G$=yF6eE0čOii*ㄅã 20йśvVe:^a9LD:+qEF&6cn& 1SL(zM)\FpC]QA +`6h!@ -FbaN>]NTU: b#E S_Bj <Z64=Q4>4*N'g>OՠRh!_e,p]3Bw8TlKՎ]՜EptOtd$c'43z8NȣF:J.c2\UC ^,X 1joDyKU"Q$ &o@@wˏ[_Jf/Ǭ g +?J7|MY4q%9_u~/2J, яDN7PFiȴ04"!T7"a~$0U` *tS<`k:Le|'"tJ4@u-.<A}`p8<ROc9؜Uv֙x(h KS[4gxpJB `C]*kLVen|F; n! }hV%1/*K✪qwBAU@ť{s VMӋ?Z'G)MX]~8 s$kp-nE"qg>-o*NeyMX6i^u6^`*pnAXQrviSjtuàd͌.'۫{rŸVԨ%U_ LYh;'B78uSu+RCl n(㪏^o2tTܸx ]&soM&u x aoVWW;`Fծ[d\ccܢ lo^n{5F-onv=¸@9T=&7ϓkwLb)ͱ7t + rH?\#ԵGHmX zN|oX#kP%6 Nղ䫘15t$5u%lc}]6۪;5Oz)W9fk󴲧zz=/OW|?ksŜkSכG%zsk*lW.YBb56Iخ[~tw{Gjػoq_Y38;uM`LӍ~ht]FЂ\ۇvD87=fz6[ X8,fp!3q,p6 Cny;6Nw,߳4aC2 O4SoAfy֔燎)`. D3W4X:h^ Y{OAt< iX}41v p6{#i_G0sԫK֌`;gƠu_N{G]x$ڭq9@L E^4;\AfC5BZG`Sgþj 9J,`<gg'RPJ;hx`\"sD5GxdOi~h >[=􆤸ШQxF!vwd @ _@CiH׽. "^Tξ\ޥPk:(9>G aYi2-G{K{hȵ!iK_@yр7gdZ>gͳmd^N&uWOsӺ3o9.&X7mzn.`(?QDhb?=AXG~=12g)r\i*=v܂(Zwա +EEHjiԚ2T;am8z%eO*Uڐ]?Wr ^F`PdQw8iT|.DQkq,@zg ]fy-E0 L<͑؂U/JSt Sƣ&38ZPu@u1o\}K2b;-ݕ`]ySl$TϟtH@w|=:2PyNjW~6UP/`7b)ZMBiMyȃ2܈j37(B fGz..lρ8n?8 sY՘ruA,)\[Ofת3%)>S[xT#H[ ETp9Em$(k/b8gIuW,Z4#bK&f橆,FR.6 *eSZ{V`؃;QEip9sNS-Y{v9j SM9zQH5:v 0ckbfg5Fc6[>䳹G\hT\Ĉ7.éC4\H~|oZy\ޫDMKX0A:,WxL[Q@E" :Tjc`,^}ob\A:TC?ĖUUa^ .: +J9aqAz +˓WnWJF33쇼u,Dxי(𲕯ܨ]pw+>Sdu(OKP좊J)%Dg ȜHNՉ Ѩ7'0 PsEOQЂzOTqě!;ߣ) uA,Qs `c8~47Q<8UGa &>-ԧ,tjBvpC @0`nqKĎx2iUP~ +皪8 Ϫz. +8H08B>a#d:ؙO7rߎc[Tw@YRRI$@)[pu/8Ag'@N;0}CLTǎJuj~XbKj2y@WAuPl#o:ȘA;#F1>y \J|"q+;FêxSKE%"8z"ho"0{T('rh AsTN_uc<>r+ͥ)# ;1lUz9~WSXm/o=RY:V4hZ"wu?k&tGE7ߗD_T~t{_3yKGkwVv-t383ɼF^I(PW0YNϩNx^C_kȼa!m߻aW?mu[O]Ol13]GE#\YQ$sy_(P$ })@hZq>ժ+6YVrEw<ԓv@o~)5yf>^{һks]?^ЙQ,Vo{yK~=.HK`툕#.mT]O0}ϯ"TE04mo PeŚk[}iB'$Ͻğt Ih5ew-"kPEa0Ab2a_ـIg^6:2:B9Z5jQwH$8K渒X0%39Prxҵܞ_v=45t ߹79 :o$<)%D# +4um D痿tѿ!7@>qkё=c5.G'>):!ˎ.5`>jǓ'۹$֨yPǒ,er(/-GJL=HXgٓJKΑ|u})Pcq +I#^sbL'5Xn \ݢrRp{x+P{/-N АjOOj|\nöun0 y +8jM;H5RI#ABNk;X5_b%|39eqOޡ&d&{"/NJAU:Ga:>K!2,=ȇ\owl(Lv3EL@pF.|F.X-^8h <7Q#S5k=//H>] Lvo-wUXOQ@(֢~GcɀtKIg!y 2=+f  6Ơ7 sea^4-5A[!uS-|kl +~2a%~4W:LHNRE +]%2WB>{(;-23pln0Z?*8>Y>d:'{7j%I`nPNϒsF%j~ӏѨ>D)^\{bیLٳC9O(+4L0G v +jp{KFYj:(ı dLO浇KVŴMJvPI\Dq*R PM,U7gDwM;?9vǧy=q|2CD¯|CT*$4ζJDthQb. H(DmN g'Lkt1 ) ~uf0[mWvt&6b :5P|Q9N:WT*Ht%ܯ'kL9IJ"GŸOQ3&;gOU]8=>~ +9Zg}ru( xX,P+Rw[4ڸAײ]?XqUv[0T',˓k6 &ʾ۰ACukճxDacvbC,/p}V@w} t?l?Zuջ/Z]㫆aqvϱXr6}W3jI94/6θI:GXi]Q$H)RrDŽ7Y%6"ޚIS;8\]W4%%(}dPha44d )] ,z*wu*!Qi5,Bq;6̷&ugdZ5u2>ΞxUEe+͝A/lQWo|N`x?i:CQO)(@=Xjm3S%hȩOI<|amh6yf쑆pFxQGŸ@?%tawaA&Vulԯ1iDn = `!FsS)sp; M+^|.*{O o@7tM5Hu40qlEũܒ|klJe4 3n#tGx!b]+Vϲm (M- SCS`X18V(&Ul. [Oe@ 1(/moYȾՐ[v}+?*+]]Z_ڠcFZo=Ŵj\N[s[bc7UD`spfo ^0gfwggvj{r a} +U Ӆ_o~5mq.Vk#YG zQzw |}vχ(؅-woO Mԃ^ ] cC[n8f.ls%^1ǐz|{ph\mX.{"('pzŨOl#XbkԕYba QVPTgm{РT.! Huuga~̍1āg8/cz:*&O-("8eK4aR 2  -$$h M3H̄\ؑX$ +w\ lE[:ñ,(4.uغV }n>#m7ةp61K .gfNG: |aͰj|`:& ЯCZf@'>֜Y0ѯt )Y/Il W1O\ :֭)IHaD5|agH7UF}rAM-0/D1*!K 5OD*< mhf '=0P/ 5R!EΦFz:*UC4\87-ZX\f#nyS3>C| 0 +#v~KZa,>vh!DluFc͌KSуK qڧT2Z"%JE{Z0.&6j;#F׺u2thD>4Ė +˹ǧ1:O@Vs1LO[ym+w:]np|;Q>^ň^ #:7G0I>g' h֊_3 BExk#z@8cS{Oz!sЁyxҕСⱷ&flu$tەz)q}<;2GTF}Ō%C)3D(#6Jd"!f7I]\jO0ظth'Iltc!񴧓yQ$v:hz.?–Ƴnݼ(h,|5;M{;%N(<;RȀBd`|>D Y1be0€MBL!MAC˝x 0QT2dtѣRC]q"2L B3|{_`x]a`yVq?땈X|.nXkV輸%(RqSC]{;z%e-[gT/Xd%n#'ga"y?b۫h |2p<+Θ6fBeu#~iUFB*d/E +׳+M^ܱ+YԥS@ o%̋YU9˴guEu&'Fr$4oOAXBGF=$,fQ0a[ 21au= jZF{&{S~=.#/t5,;# [!!?IQe+L[wN qZY$}Zy}mU`*XKLN1JJLC9?8!57nB)‹؋/&[ +2gvӓp?Zl1yܒ2C}5BJGx7i}0OO)% t2{W|!CɼTX~)$~ubZW0&r>߼ +53JBl4Vsmi[G[I :B,գ$2GFf'%@@5D hޚ#aγTk0A ;qOW"y?fPȎ,=ɓjWh."!-K"g-6o`W\$\^E89%d7y-eqXڼ-a,2JJvWW[Kk2OKol +%} R yk[BT%\,B _ Mw$gJߕ&F]7y.dvm=mYqm~{M'T]s?+%ϖx\ܲD]b~1 }#ms:BU%&(Dس0H.f?hAy \S9g`r<\ vU\w*X}lOs-m +#|ʳ=JθVK+G~y4a;wN|d|ҭ J0\ZZ|KtVskEpYcK\젏LUB%Q=ƶƥ +RH+]-Yl_|iiQU9E]ݑU`po9yc5vH)fNz-O +Y!lQo3RNP 0)&RbD,N&b0QK|L/{7Ax^_Ի観!yR ƎUK 1ìv'*?^U u4^{TWm}RH~ t*C#juL+ZBՊEVZQ/4A7ޫ{<VmoH_1U:%/4`+aNUa]beq~3 8-Iw3<3;?EK.۬n܃7''9y"Ϣ(J#|?4G@fVP%[:m"IUYe<䲈^yTTse]Z@\0I:M Œ + QӲ , +y&P΢dKojqI?n|< +PkTmk=Wby \c]=I8P@+l[bT3l% .t%s-F]tk< ^61ECnc ]Z`WWƺ%P^ckPߜE :V&LF^x +}~F9ja컁<É+ͨTu3Q|nHߝ +dQ+%4ٲHp<3 XȢl":5>%sΚpℇ~4P;|3q+]%prw{mke/_y$뚒/MI@,hlq˶ GY6) {hUd=:vYwqjp^LLl_:oiRr0޽]~-b02st]ZU۟RSeo>͐K\ 'ig? ~#.\#W !mz`Re.Y! w:XQ{p¯킾.qAПaCp`<](0-{JFmO|vfÍ0hWQx +Bn+:jcv펬~VeZW˰u=:X[o~ϯEZ<\v󰓝`'lH6V0xiH6~!tW_Uץۿ}x8 =Z.xcFk{}1wlC9 l=;=;bh8dKjV2S9~5Gcfdq&M^`|]rqbcǴ/{vftzN%d͑cY<{yc3nmz#ӻV." uh4ԈmxYDܝ, \w"[ޥ=p\_n[C4CZG.h?²gyy` +#LMԞX33 bWmVL3)>p%*39 3̱ϬآUlld4V>y)Kȗ*%0"v>_ϫa=TV&VlHgXʭDzh M cGLb4'_KTA;sh)ѰkГ$xqR`/L-i a?He8y}F]C%s + +8$rf2f,nJGbUjKZƮ1w"|yU <溜;v/JHɱ& +%Ꮔd(Bon6qu<ˣrO] +*;-όp7xn4~ĎwBQ_P t7FtFT!ЎjrvGLB ?TyY [M9#+~P}Ȧ]zQA%;!# M/,H:Bri_ p+t uavbh4KfG!kCOdsu'rN~xK3o&_ހ0j馺nQQ c.4zP]5ڴԷ\+qt ꩲ_fcXS:zwG; "9CnKFW`XŨZmD˯Er/injII|m u}س +X6#IwpޱlT8ꚈrAĚ*֙ўtj**ۤ9<\5Yqq.ZԤZ;J3]CS9`;fع[^,^|)5s_+>-$i+t[ cc=e=.8Lߧe飄HrX[|8nQ-ӯIǝV5Yj+J'M55|P=J,R*W՝hE+؋s_[ta7F8U%D䲻kUIHTYxg*ޯ*xKbTqkVY3 QH>ϔ݃Ʀ9'v垡հՍFgݎ2m7x{f硾1&n]ZwIohwުvշ7g'!W#= Q~9wێ$ߍK f\7>A%r>U]14yY`*(wpl"TyYM-4cW 4Z4#_2a]^OEr7P5UQo6~8yp5 +aKׅh,y"BhG,z wYuۡ/Ӄ-atsu5+E%۟~k *?Kx.M +\-OukJdQF)U Y]@k$5\MYgJeJ6WE-x3R˦z* |i$Tz..Jg\^6yW mO*Wzz ZlWVMKCt) ThqfͫK}l0@ V["Z {Y7Nop_:hgeVsуB%Ft֧;ZHRD lm$D 4J3e LW IlGآSĨmЃmf2}>m۫͘A%b8x*V$$~` `F#?^6 a@$ +p5 "ƅ7cwxF5Џ˄rql aDQ0 X41-@O{5`?O2a!kuDd3N1%%IӐ$i&˘S%!a \# F7+"Z Ed.VvzQG$zg~XI֮BEdNHa3Ա ]XBt0)i| .$x֎(}rfX,4Iҥ`qt"P!dJ0:pZǑŊŵb`5Ql#VbYQO1(": ٌF>ƭ뒯fNmnӐ[:hgm-)Yk TWyϏ'埳NJTV3q4~ N8F@}v|oFn i Qr)=m{4A8Pv8k!.jKo0%!w'wFg{(Ucx >9KޗY7͋לJª?Ax#f6wfU.eݻS#kdXZR[obנ]3_([Ys8~@Rޑd󰶕 -1dI+Rɸ`eTnD0}efD/4nmqXzn#`q1iN[lOɁW4#}sF?aoF4MM#:s8tϖgdQ$ +ᔲ/gw<Fn!,c2f;u< nY;?x^pd3EӜƻ^[DTi0e="[,ƦS~SqnH܈xYA+:wNM6 UK X;[Il1sz:n@u(6p`hb/Μb9J缂0J;wb % ) 2bJ p$= DC, hAg 1|hQ$Xb7}d= ]KN dd~9Ѡ5F]ڷG=߲mzGeÞ @Hۦaiwz9I<6mW͐t&Crl:G=>aRMAN6;>BNh8 &vMc j\b|56^Oj1!  PV?\5GFF_#(ӈ54:&0~7"}t,2;:h']X66 *x3ǨYY6m/Ay2F_͎ab[Bl]v^f}0L219 C׃>5 _t +|;2; {A1:NGwX-RT-KƗw l oeXhL iL.h>4ЍYcCKCw<'f!1ָsx?ʹ8t^wg s,dӭ^FZZU"=@D?bX]д0TľTiv"ã!phpbuF]6ʴ'?44#pI edV ؒ);ID6ffq3SԹa Ɍ:Cg'|g<A!էhpNXsn|*8r+|q$z9 8gK5YhG;3dЙCd. 4ܫes G4ZzмUQɚen~z\%ճC D:W SM^1lCҍO4!*[;\d9ſ'I..L]ʒqD+ ߿/:3]!ez5VI^#`i=z_~ɸ@C=v12$%>NB#RZA>灲ұCRp.^R߸ _Ue]I +҉8`9~jP-#ZH|x$o`jb` IgE^gxd A8 +5H1zOha{!We$"ve24ɡ%M i!T5AT"J'{d)YXWƪ|!&r|ǦeK):P/*0Xz )WYj@[ +#bmE쓆-qc +bbdgv=q8i?i}*ex &k$da 360N/*zG%RmyN*$aL/9M"!IܨA1]1q%spX09Ǹ㶫:6_ Veĕ rR@|;wՃ\3[#i1*Fk)(D?uɢ3:j8Uk^5; R%ɪ*UsQb/C5U"bSЪ"f?_uR+ ( )]`kF-_t0CKﰴ%[C6\vA1;ԆkŭͷJqPee Tk𔝶ERҤy@Rjg%I_je=q8iY;ֲQ~*$+pBzN!}͜@K&՘b4ʆu璂]EZ%F$):(|gVjэG~?xuMS\ȮdEd'beR=,dy2~T1}RDBnTt)%\$U O^#kY*x@>/YEdHv\k<0Н"oBʺ/- "`7QF6JzD9+F@qCAƟA.^$\hvV:$'щ#*pJ|rlm&ҷ`TN[E4N} +Y[eHU{xY]gWsՙj(C0=z*L :s5_"lђ?j^wYg'_G/҈|Y'-v;3y;M +Z9}LE:_w$qAU GrVƋQ\;䝭8Tm.~ '> +~m_U֊Y-db#Mu%H4+qաei>5?}:aT0<^-YxH oٌ Ç]c 9a9ia>I#q:%n&>ke|"]=xCtˬnQ" Åa\E*XI#$qDj ]GǕ@{+6M1p+.M`'ȋ[~&>ٞ+[(?Dž[5k|*X| _p^C_tMOs|/bnm˩\r5q`U! Ma\dERR+3e <JU+iqۑy8!92Rep^~yYo߄P27"Q`B-H.=l56<%Xa#U1s5 x +QL7Qe0)D36=SX4F5,+P0ܐ@4WjZF׭+qF +bݖ  R(&1:iMCe{,O ''yuWAxƚ&;;"25\´K&Un\)SnhYkfp%NwyF0$V>Q= ni'wn+'6O\f^c_,3(N[ E>9fiG8E>EAއYgEQ-k;e?T"5*bw],x?X]o}_Qdg$M63m% 8hwM;F\*jCwթӧ :Ku4LU>. +ɿ>}9|JzKD"~YߧQ VzB)̞epL\yӲDPK +cʓ2I52 chd\a$SIY2 y8 U"l (͒0MH`EQ3iAȾrZ⬑c;Ϝy, Uj,gx1Myb0Np&",)(cՓFYw]F9tck<2{mz:u7Kw+;A\n#dO޵nYգz@2aQtUZyccht~[-3=-lhW /p7\sYy7qFj5<ý5wN)"'!k<$i&|Vx;c;0u y{LZ C3:ԡ_Ftp +'۸+<0؝[鹦v:;Lq}[KG&6 yaƛ 7+IS1{t(vN؟zz[K{G#t8W Y?΅B$ I]BEY͍LVLq:i٠ #t +/tge:+,YW\uNk؅&P6 +=H"T_ݜUnXN$x@7ߖm9DTC6 5 {y=\7tELmĖ ㅏ(L!W}^WB`V d[n%z 0[W +x%VG#Vzzw@`F} (l ϒ|gش䫌0~e?)'{֮̉ [{Xiy;^9f+F9oR/dO,fC;oY$)z_=i5ppe[]b+FΜa{P7^ZxS:0LҲFoXzG {Z=_= ݽ7*Zɶ**دPص5κvɏ}x?-mS97Vk\) €ICu#+ |Z&m/,^c{[;>&3iXzL2SzF"H$fq\'D\;;\D|]N8X 7lftڒ"~^>M +1D-,/PN+Lp݄dܿSYv1F6wXa]@\4Pe m(>o#Ɓgr&oa,l+JnYJI 2CCZ~l:$Y-TFZ~UgyWmoH_QEk:L(Tߴi:Zl@gri+(CS*Mˆ2*.:-t]JCHY֍Cw2429Wtc ^e[zeSx ׷Fspl$8mX7 a/ͩOİ Mk mtc5G>#kFA:y3sd"ýS ˞9OcƸFCPNh7sŦ-t8cżg]xgp: +X6sbؖ5Mם|˙@-BNJkg0,ǽcLJNHTb&`:<8ے +>X׶umNG&:ĺ*-X*XI~}2>l^2pb4zPIu^7Q>4]K>I"zcWlz dA?{=VQӑTQ{pK\ɗ"DQ}s%@k#/_Bu~쳌<EMYv5 VX觧ز@DvqL`ȣ5wԯ%vO KE dC`!uہt9uZMC"YJ&q f ><\~|ߦY!0>gaTzDKe"HJ_f֔ TU\~ƓPc9Õ'4_\/YeotK\iJM)6XY~զO}"i.6YȤHN|pdbqIk%>л󓳚e,_b61\;;;J"~2*lqG:Hr+6|,>,qgDE<,iࢣv, ܐ֥A/w=]fV.SLiV^/布cl@|Y GLmy?QGXJ|]&ɡoMurE \lǨpBH/Qj{:.M/*Ƶ9, *A\pND!)q3AdBa$QEL+QNsΠj@lA=v5K rc"hā7ڬBLrn1:LADuECT|g,C>(tJ]( ƉJыI$@烆\Boyߋ]:Z^ !_kN=r)izԖ^~V]O8}W\!F-(|,>,̒&Z +I7N`*ąhҸJRXPʈCs9|: G͟~O'';U)iP$OY55}MпL~o PS%kY=h5׆2'&W%%eFZR^RU*$/Ꙧ=EC3-?GN۲6%o}xǜ\")zBV +cp5J&~pٻdj6ӷp&׈äxJk40֊2q8YVUW5.5-^sކȭT?{ǥ䱜߽bsvX`<ۦ%0},ryn[FYw&X8&b|5,/hIPmJ0s!l >.Z +Ŋ Ld;t!C9sz wSIW! .S^8#41|9pʁu4n-igi.2r]FÏ@t %؀c63ZS;:,-*y^+,yr}_hvC7ǧ!<~dwT6l|ۋ0fz8j<:[_hu9^G9Y%~mj0 y +vH`K=6= &VRS[wliÏ>IݱK xT$N5G1D +"0 z-E( p҂(%UlVar'A%^ \zAմpN|UUUYq)! .iI +;iL{6OwxØl]샛O3η]5gm垕l%fd ]Mk09P `wz(vfxXNjqy<}6g9};ɭRKK"kCs C^50`mfw.֊+yVUUU}Ɠ6*GP>K؜]V`})=Fҷ:?CVRF/f[PH<1!|5ώ,/hIp mJ0E l ~.Z +Ŋ Ld*;␇K̽aru=hJeI_ :S}G'w: +a@Ld4E(aiX~2)km*ݳ:-O3.OT%z؁ff :Ήx%8,+Ӻ.adv?=hMzkrmRmZ߉QaA +:'t&6.򇦨Y uA +@ Es\@@BQpgCh8PSo{?벆T$OBXiYP|Q~3jfK3,n>HAԀXH) q8')) { + fwrite( + STDERR, + 'This version of PHPUnit requires PHP 5.3.3; using the latest version of PHP is highly recommended.' . PHP_EOL + ); + + die(1); + } + + if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); + exit; + } + + PHPUnit_TextUI_Command::main(); +} + +__HALT_COMPILER(); diff --git a/code/vendor/phpunit/phpunit/build/library-phar-autoload.php.in b/code/vendor/phpunit/phpunit/build/library-phar-autoload.php.in new file mode 100644 index 000000000..f919b7622 --- /dev/null +++ b/code/vendor/phpunit/phpunit/build/library-phar-autoload.php.in @@ -0,0 +1,9 @@ + + + + tests + + + + + src + + + + + + + + diff --git a/code/vendor/sebastian/global-state/src/CodeExporter.php b/code/vendor/sebastian/global-state/src/CodeExporter.php new file mode 100644 index 000000000..d595c22ed --- /dev/null +++ b/code/vendor/sebastian/global-state/src/CodeExporter.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +/** + * Exports parts of a Snapshot as PHP code. + */ +class CodeExporter +{ + /** + * @param Snapshot $snapshot + * @return string + */ + public function constants(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->constants() as $name => $value) { + $result .= sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param Snapshot $snapshot + * @return string + */ + public function iniSettings(Snapshot $snapshot) + { + $result = ''; + + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= sprintf( + '@ini_set(%s, %s);' . "\n", + $this->exportVariable($key), + $this->exportVariable($value) + ); + } + + return $result; + } + + /** + * @param mixed $variable + * @return string + */ + private function exportVariable($variable) + { + if (is_scalar($variable) || is_null($variable) || + (is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { + return var_export($variable, true); + } + + return 'unserialize(' . var_export(serialize($variable), true) . ')'; + } + + /** + * @param array $array + * @return bool + */ + private function arrayOnlyContainsScalars(array $array) + { + $result = true; + + foreach ($array as $element) { + if (is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!is_scalar($element) && !is_null($element)) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} diff --git a/code/vendor/sebastian/global-state/tests/SnapshotTest.php b/code/vendor/sebastian/global-state/tests/SnapshotTest.php new file mode 100644 index 000000000..1e97488ba --- /dev/null +++ b/code/vendor/sebastian/global-state/tests/SnapshotTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState; + +use ArrayObject; +use PHPUnit_Framework_TestCase; +use SebastianBergmann\GlobalState\TestFixture\SnapshotClass; + +/** + */ +class SnapshotTest extends PHPUnit_Framework_TestCase +{ + public function testStaticAttributes() + { + $blacklist = $this->getBlacklist(); + $blacklist->method('isStaticAttributeBlacklisted')->willReturnCallback(function ($class) { + return $class !== 'SebastianBergmann\GlobalState\TestFixture\SnapshotClass'; + }); + + SnapshotClass::init(); + + $snapshot = new Snapshot($blacklist, false, true, false, false, false, false, false, false, false); + $expected = array('SebastianBergmann\GlobalState\TestFixture\SnapshotClass' => array( + 'string' => 'snapshot', + 'arrayObject' => new ArrayObject(array(1, 2, 3)), + 'stdClass' => new \stdClass(), + )); + + $this->assertEquals($expected, $snapshot->staticAttributes()); + } + + public function testConstants() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, true, false, false, false, false, false, false); + $this->assertArrayHasKey('GLOBALSTATE_TESTSUITE', $snapshot->constants()); + } + + public function testFunctions() + { + require_once __DIR__.'/_fixture/SnapshotFunctions.php'; + + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, true, false, false, false, false, false); + $functions = $snapshot->functions(); + + $this->assertThat( + $functions, + $this->logicalOr( + // Zend + $this->contains('sebastianbergmann\globalstate\testfixture\snapshotfunction'), + // HHVM + $this->contains('SebastianBergmann\GlobalState\TestFixture\snapshotFunction') + ) + ); + + $this->assertNotContains('assert', $functions); + } + + public function testClasses() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, true, false, false, false, false); + $classes = $snapshot->classes(); + + $this->assertContains('PHPUnit_Framework_TestCase', $classes); + $this->assertNotContains('Exception', $classes); + } + + public function testInterfaces() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, true, false, false, false); + $interfaces = $snapshot->interfaces(); + + $this->assertContains('PHPUnit_Framework_Test', $interfaces); + $this->assertNotContains('Countable', $interfaces); + } + + /** + * @requires PHP 5.4 + */ + public function testTraits() + { + spl_autoload_call('SebastianBergmann\GlobalState\TestFixture\SnapshotTrait'); + + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, true, false, false); + $this->assertContains('SebastianBergmann\GlobalState\TestFixture\SnapshotTrait', $snapshot->traits()); + } + + public function testIniSettings() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, false, true, false); + $iniSettings = $snapshot->iniSettings(); + + $this->assertArrayHasKey('date.timezone', $iniSettings); + $this->assertEquals('Etc/UTC', $iniSettings['date.timezone']); + } + + public function testIncludedFiles() + { + $snapshot = new Snapshot($this->getBlacklist(), false, false, false, false, false, false, false, false, true); + $this->assertContains(__FILE__, $snapshot->includedFiles()); + } + + /** + * @return \SebastianBergmann\GlobalState\Blacklist + */ + private function getBlacklist() + { + return $this->getMockBuilder('SebastianBergmann\GlobalState\Blacklist') + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/code/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php new file mode 100644 index 000000000..9735818fe --- /dev/null +++ b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotClass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +use DomDocument; +use ArrayObject; + +/** + */ +class SnapshotClass +{ + private static $string = 'snapshot'; + private static $dom; + private static $closure; + private static $arrayObject; + private static $snapshotDomDocument; + private static $resource; + private static $stdClass; + + public static function init() + { + self::$dom = new DomDocument(); + self::$closure = function () {}; + self::$arrayObject = new ArrayObject(array(1, 2, 3)); + self::$snapshotDomDocument = new SnapshotDomDocument(); + self::$resource = fopen('php://memory', 'r'); + self::$stdClass = new \stdClass(); + } +} diff --git a/code/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php new file mode 100644 index 000000000..bcf554dcf --- /dev/null +++ b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotDomDocument.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +use DomDocument; + +/** + */ +class SnapshotDomDocument extends DomDocument +{ +} diff --git a/code/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php new file mode 100644 index 000000000..51d1c745a --- /dev/null +++ b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotFunctions.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +function snapshotFunction() +{ +} diff --git a/code/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php new file mode 100644 index 000000000..14e81e978 --- /dev/null +++ b/code/vendor/sebastian/global-state/tests/_fixture/SnapshotTrait.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\GlobalState\TestFixture; + +/** + */ +trait SnapshotTrait +{ +} diff --git a/code/vendor/thomaswelton/laravel-gravatar/src/Facades/Gravatar.php b/code/vendor/thomaswelton/laravel-gravatar/src/Facades/Gravatar.php new file mode 100644 index 000000000..418bb5535 --- /dev/null +++ b/code/vendor/thomaswelton/laravel-gravatar/src/Facades/Gravatar.php @@ -0,0 +1,18 @@ +setDefaultImage($config->get('gravatar.default')); + $this->defaultSize = $config->get('gravatar.size'); + $this->setMaxRating($config->get('gravatar.maxRating', 'g')); + + $this->enableSecureImages(); + } + + /** + * Return the URL of a Gravatar. Note: it does not check for the existence of this Gravatar. + * + * @param string $email The email address. + * @param int $size Override the size of the Gravatar. + * @param null|string $rating Override the default rating if you want to. + * + * @return string The URL of the Gravatar. + */ + public function src($email, $size = null, $rating = null) + { + if (is_null($size)) { + $size = $this->defaultSize; + } + + $size = max(1, min(self::MAX_SIZE, $size)); + + $this->setAvatarSize($size); + + if (!is_null($rating)) { + $this->setMaxRating($rating); + } + + return $this->buildGravatarURL($email); + } + + /** + * Return the code of HTML image for a Gravatar. + * + * @param string $email The email address. + * @param string $alt The alt attribute for the image. + * @param array $attributes Override the 'height' and the 'width' of the image if you want. + * @param null|string $rating Override the default rating if you want to. + * + * @return string The code of the HTML image. + */ + public function image($email, $alt = null, $attributes = [], $rating = null) + { + $dimensions = []; + + if (array_key_exists('width', $attributes)) { + $dimensions[] = $attributes['width']; + } + if (array_key_exists('height', $attributes)) { + $dimensions[] = $attributes['height']; + } + + if (count($dimensions) > 0) { + $size = min(self::MAX_SIZE, max($dimensions)); + } else { + $size = $this->defaultSize; + } + + $src = $this->src($email, $size, $rating); + + if (!array_key_exists('width', $attributes) && !array_key_exists('height', $attributes)) { + $attributes['width'] = $this->size; + $attributes['height'] = $this->size; + } + + return $this->formatImage($src, $alt, $attributes); + } + + /** + * Check if a Gravatar image exists. + * + * @param string $email The email address. + * + * @return bool True if the Gravatar exists, false otherwise. + */ + public function exists($email) + { + $this->setDefaultImage('404'); + + $url = $this->buildGravatarURL($email); + $headers = get_headers($url, 1); + + return substr($headers[0], 9, 3) == '200'; + } + + /** + * Get the HTML image code. + * + * @param string $src The source attribute of the image. + * @param string $alt The alt attribute of the image. + * @param array $attributes Used to set the width and the height. + * + * @return string The HTML code. + */ + private function formatImage($src, $alt, $attributes) + { + return sprintf('%s', $src, $alt, $attributes['height'], $attributes['width']); + } +} diff --git a/code/vendor/thomaswelton/laravel-gravatar/src/LaravelGravatarServiceProvider.php b/code/vendor/thomaswelton/laravel-gravatar/src/LaravelGravatarServiceProvider.php new file mode 100644 index 000000000..4becd9dca --- /dev/null +++ b/code/vendor/thomaswelton/laravel-gravatar/src/LaravelGravatarServiceProvider.php @@ -0,0 +1,36 @@ +setupConfig(); + } + + /** + * Setup the config. + */ + protected function setupConfig() + { + $source = realpath(__DIR__.'/../config/gravatar.php'); + $this->publishes([$source => config_path('gravatar.php')]); + $this->mergeConfigFrom($source, 'gravatar'); + } + + /** + * Register the service provider. + */ + public function register() + { + $this->app['gravatar'] = $this->app->share(function ($app) { + return new Gravatar($this->app['config']); + }); + } +}