From 6a050e6e94917fc1982c4d9ce3195d258f6d3b00 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Fri, 26 Feb 2016 20:59:24 -0500 Subject: [PATCH 01/10] Added web outline --- web/README.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ web/parts.md | 33 +++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 web/parts.md diff --git a/web/README.md b/web/README.md index 76912a2..c82f482 100644 --- a/web/README.md +++ b/web/README.md @@ -1 +1,89 @@ # Web + +## What you'll make +A dynamic \_\_\_\_\_\_\_\_ website that +shows a list of \_\_\_\_\_\_\_s ("posts") which users can interact with through \_\_\_\_\_\_\_s + +Examples: (to edit, try to use social justice theme, should be sufficiently extendable) + +- simplified Kickstarter, display great projects and allow users to add their own + +- a simple blog + +- [Rate with Science](http://ratewith.science/)/an idea generator listing cool combinations and letting users try their own combination + +- a personal website to showcase your projects and provide a contact form + + +## What you'll need + +- A text editor +- An [idea](link to ideas) +- Github, Git + +- (provided [HTML](), [CSS](), and [JS]() cheatsheets) + +## Table of Contents + +## Setup +A folder for your files to go in, and your favourite text editor (eg. Atom) + +### I'm just starting +// Instructions for setting up the environment for a complete beginner (e.g. download the Arduino IDE) + +Download the Atom text editor from https://atom.io/ + +### I've got some experience +// Instructions for intermediate learners (e.g. downloading certain libraries) + +## Vocabulary +// Glossary of technical terms used in the lesson plan + +- Servers, clients, front-end/back-end +- Server-side +- Client-side +- HTML, CSS, Javascript, PHP +- DOM +- dynamic vs static + +### Quick Overview + +#### How websites work +[just point form notes right now] +- HTML - structure +- CSS - styling +- JS - interactivity +- PHP (for example) - server-side logic +- server client model + +## Instructions +// Step-by-step instructions for building the core MVP + +1. Install Git +1. Make a basic website with HTML +2. Add styles with CSS +1. Meet the Chrome DevTools Inspector +3. Lay out a list of elements with CSS +4. Adding interactivity with JS - make a button +5. Implementing server-side logic with PHP - make a form +5. (Security) + +## Next Steps +// Less specific descriptions of additional features the learners should implement on their own + +- Create a landing page +- Add animations +- Make a scrolling menu bar +- Add post categories +- Create a "like" button +- List posts from one category only + +## Extend it further +// Bullet point ideas to inspire learners to extend & personalize their projects + +- Add a carousel to display top posts +- Make it public with Heroku +- Save user data +- Make posts searchable +- Create user accounts +- Use a web framework diff --git a/web/parts.md b/web/parts.md new file mode 100644 index 0000000..cfc51a1 --- /dev/null +++ b/web/parts.md @@ -0,0 +1,33 @@ +// temporary file + +### HyperText Markup Language (HTML) +Make your website outline (title, headings, a few words) + +The Document Object Model + +#### Tags +- `h1`, `a`, `div`, `span`, `p`, etc. + +### Cascading Style Sheets (CSS) + +#### The Basics +2016-ify all the things +- font, color, margin, padding +- [to be edited] + +### Cascading Style Sheets - the CSSequel +because we never go out of style ♫ + +#### Lorem Ipsum +// Add in your own sample projects/articles/etc, or use our [xtreme hi-tech generator](lorem ipsum) +// also high-Q pictures and icons (glyphicons) + +#### Layouts +- Grid +- List + +### Interacting With User Input + +#### Javascript +#### Forms +#### PHP From 51e6ed9d793971abd1756e5b80c91c204bac8c93 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 17:30:04 -0400 Subject: [PATCH 02/10] Created new web outline file --- web-outline.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 web-outline.md diff --git a/web-outline.md b/web-outline.md new file mode 100644 index 0000000..e69de29 From 256354c6c22776333419f0a5b6ac5e56d8667751 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 17:34:46 -0400 Subject: [PATCH 03/10] Initial web outline --- web-outline.md | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/web-outline.md b/web-outline.md index e69de29..09ffba8 100644 --- a/web-outline.md +++ b/web-outline.md @@ -0,0 +1,109 @@ +## Your First Website + +Create a folder on your computer for all your files to go inside. +In Atom, create a new untitled file (File > New File) and save it as "index.html" in the folder you just created. + +Inside `index.html`, type: + +```html + + + + + My First Tab Name + + +

My First Website Heading

+

My first paragraph

+ + +``` + +Now go to Documents (Windows) / Finder (Mac) > "Open `index.html` in Chrome". + +You should see your very first website! + +## Welcome to HTML +HTML, or Hypertext Markup Language, defines the basic structure and content of every website. + +### <h2>Elements, Tags, and Attributes</h2> + +To define the structure of a website, HTML uses tags: +```html +stuff that goes inside +``` +This is an HTML element. + +HTML follows grammar rules, which in programming we call `syntax`. +Tags pretty much always occur in pairs: an opening tag followed by a closing tag. +We'll see shortly that tags can be "nested" inside one another to contain each other. + +Aside from the tags shown here, we also have `div`. A `div` element is used as an all-purpose container. +Let's list a couple projects! +Add the following below the line for your first paragraph: +```html +
+

Project 1

+

My First Website

+
+
+

Project 2

+

My Second Website

+
+
+

Project 3

+

My Third Website

+
+``` +Notice the new `class="project"`. + +HTML elements can have `attributes` to describe them. Each attribute consists of a `name` and a `value`. +The `class` attribute lets us group related elements together so we can do the same things on all of them. + +## CSS Beauty Makeover + +Create a new file "File > New File" and name it "styles.css". Save it in the same folder as "index.html". + +Inside the new file, put: +```css +body { + font-family: "Helvetica"; +} + +h1 { + color: blue; + font-size: 40pt; +} + +.project { + background-color: orange; +} +``` + +### Language #2: CSS +Cascading Style Sheets + +Now let's get into the real makeover. + +### Selectors +Replace the previous code with this: + +```css + +``` + +`class, id, tag` + +Change up the text in your HTML elements to fit the website, too. + +Let's add a background image! +Set the background image for `body` using +``` + background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg"); +``` +Now make all the font white. (Hint: use the "color" attribute.) + +## Adding interactivity with Javascript (not Java!) + +Javascript is a programming language that's recently taken the developer world by storm. Here, we're going to use it to make a button for adding new projects. +Create a third file called "scripts.js". From bf8eedacfaa25bec35a5673c3a8c5c34a09006ab Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 14:54:00 -0400 Subject: [PATCH 04/10] Draft 1 of lesson plans up to stage 1 of 2 --- web-outline.md | 81 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/web-outline.md b/web-outline.md index 09ffba8..368068c 100644 --- a/web-outline.md +++ b/web-outline.md @@ -1,3 +1,15 @@ +## Hello, World (Wide Web) + +Long, long ago, when the great Tim-Berners Lee brought us the WWW, + +Clients and servers + +Model +View +Controller + +And that brings us to... + ## Your First Website Create a folder on your computer for all your files to go inside. @@ -105,5 +117,72 @@ Now make all the font white. (Hint: use the "color" attribute.) ## Adding interactivity with Javascript (not Java!) -Javascript is a programming language that's recently taken the developer world by storm. Here, we're going to use it to make a button for adding new projects. +Javascript is a programming language that's recently taken the developer world by storm (You may have seen Angular JS, Node JS, React JS, Meteor JS, Ember, Backbone, ... the list goes on). + + + +Here, we're going to use Javascript to make a clickable button for adding new projects. Create a third file called "scripts.js". + + +### Manipulating the DOM + +Of course, the world isn't powered by many code monkeys modifying the HTML every time someone adds a new comment or post. + + + +Computer scientists and programmers are fanciful creatures. The Document Object Model is one example of a `tree` of `nodes`. A node can be a `root`. Otherwise, it has ancestors, and specifically, `parents`. A node can also have `children`, unless it's a `leaf`. + +You can modify the DOM tree `dynamically` with the web's favourite language - Javascript - using methods named after the terms we just described, like `.appendChild`. [edit] + +`.createElement` +`.appendChild` +`.getElementById` +`.getElementsByClass` +etc. + +Remember this keyword, `dynamic`, especially when searching StackOverflow. + +## Fleshing out the website + +Let's start filling in the content. + +__Task 2:__ Turn each of the project divs into a clickable link to an HTML template titled `project_.html` (eg. "project_1.html"). + +__Task 3:__ Your turn now: Fill in `project_1.html`! + +## The Comment Thread + +__Task 4:__ Manipulate the DOM so that Internet users can add comments to your site dynamically. + +### Animations and Effects with CSS and Javascript + + + +## Sorry + +When you reload your page, all the comments disappear. All the posts you added disappear. Not even Ctrl+S can _save_ you. + +Unfortunately, this is the end for My First Website 2.0. + +What we're missing is a `database`. This is a persistent storage that lives elsewhere. Every time someone creates a new comment or post, this information is stored in the database and loaded up again when a user loads the website. + +Web apps really have three layers: + +- the presentation layer +- the + +[edit below section] +You could use PHP, which is a `server-side` `scripting` language. But goal #2 of this learnathon is to get you started with web development in 2016, and introduce you to web frameworks. + +And anyway, + +This is why developers use `web frameworks`, like Django, Ruby on Rails, Flask, + +## Recap + +HTML +CSS +Javascript + +## Onto Flask! From f9d4282924a80ce149020191d94bfda30948e8d1 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 17:43:04 -0400 Subject: [PATCH 05/10] Renamed file --- web-outline.md => web_lesson.md | 0 web_lesson_0.md | 243 ++++++++++++++++++++++++++++++++ web_lesson_1.md | 228 ++++++++++++++++++++++++++++++ 3 files changed, 471 insertions(+) rename web-outline.md => web_lesson.md (100%) create mode 100644 web_lesson_0.md create mode 100644 web_lesson_1.md diff --git a/web-outline.md b/web_lesson.md similarity index 100% rename from web-outline.md rename to web_lesson.md diff --git a/web_lesson_0.md b/web_lesson_0.md new file mode 100644 index 0000000..8f59464 --- /dev/null +++ b/web_lesson_0.md @@ -0,0 +1,243 @@ +## Hello, World (Wide Web) + +Long, long ago, when the great Tim-Berners Lee brought us the WWW, + +Clients and servers + +Model +View +Controller + +And that brings us to... + +## Your First Website + +Create a folder on your computer for all your files to go inside. +In Atom, create a new untitled file (File > New File) and save it as "index.html" in the folder you just created. + +Inside `index.html`, type: + +```html + + + + + My First Tab Name + + +

My First Website Heading

+

My first paragraph

+ + +``` + +Now go to Documents (Windows) / Finder (Mac) > "Open `index.html` in Chrome". + +You should see your very first website! + +## Welcome to HTML +HTML, or Hypertext Markup Language, defines the basic structure and content of every website. + +### <h2>Elements, Tags, and Attributes</h2> + +To define the structure of a website, HTML uses tags: +```html +stuff that goes inside +``` +This is an HTML element. + +HTML follows grammar rules, which in programming we call `syntax`. +Tags pretty much always occur in pairs: an opening tag followed by a closing tag. +We'll see shortly that tags can be "nested" inside one another to contain each other. + +Aside from the tags shown here, we also have `div`. A `div` element is used as an all-purpose container. +Let's list a couple projects! +Add the following below the line for your first paragraph: +```html +
+

Project 1

+

My First Website

+
+
+

Project 2

+

My Second Website

+
+
+

Project 3

+

My Third Website

+
+``` +Notice the new `class="project"`. + +HTML elements can have `attributes` to describe them. Each attribute consists of a `name` and a `value`. +The `class` attribute lets us group related elements together so we can do the same things on all of them. + +## CSS Beauty Makeover + +Create a new file "File > New File" and name it "styles.css". Save it in the same folder as "index.html". + +Inside the new file, put: +```css +body { + font-family: "Helvetica"; +} + +h1 { + color: blue; + font-size: 40pt; +} + +.project { + background-color: orange; +} +``` + +### Language #2: CSS +Cascading Style Sheets + +Now let's get into the real makeover. + +### Selectors +Replace the previous code with this: + +```css + +``` + +`class, id, tag` + +Change up the text in your HTML elements to fit the website, too. + +Let's add a background image! +Set the background image for `body` using +``` + background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg"); +``` +Now make all the font white. (Hint: use the "color" attribute.) + +## Adding interactivity with Javascript (not Java!) + +Javascript is a programming language that's recently taken the developer world by storm (You may have seen Angular JS, Node JS, React JS, Meteor JS, Ember, Backbone, ... the list goes on). + + + +Here, we're going to use Javascript to make a clickable button for adding new projects. +Create a third file called "scripts.js". + + +### Manipulating the DOM + +Of course, the world isn't powered by many code monkeys modifying the HTML every time someone adds a new comment or post. + + + +Computer scientists and programmers are fanciful people. The Document Object Model is one example of a `tree` of `nodes`. A node can be a `root`. Otherwise, it has ancestors, and specifically, `parents`. A node can also have `children`, unless it's a `leaf`. + +You can modify the DOM tree `dynamically` with the web's favourite language - Javascript - using methods named after the terms we just described, like `.appendChild`. [edit] + +`.createElement` +`.appendChild` +`.getElementById` +`.getElementsByClass` +etc. + +Remember this keyword, `dynamic`, especially when searching StackOverflow. + +### JQuery + +Adding new elements was messy. There's a more convenient way to do all this. + +## Fleshing out the website + +Let's start filling in the content. + +__Task 2:__ Turn each of the project divs into a clickable link to an HTML template titled `project_.html` (eg. "project_1.html"). + +__Task 2.5:__ the Navbar and About page + +__Task 3:__ Your turn now: Fill in `project_1.html`! + +## The Comment Thread + +Every good post has a place for its audience to discuss the post. [edit: rephrase] + +Whether it's a contact form so loving employers can drop you a line or a submission form for content, HTML has an element just for forms:
. [edit: rephrase] + +Example usage: +```html +
+ +
+ +
+ +
+ + + +
+``` +(edit: maybe don't give the exact code) + +__Task 4:__ Now edit the `script.js` file. Manipulate the DOM so that Internet users can add comments to your site dynamically. +To do this, you'll want to write the functions `addComment`, and `bind` the `submit` action for the `#comment-form` to code that calls `addComment`. +You can get the values stored in the HTML form's `fields` with `.val()`. +Remember that JQuery provides convenient selectors, so if your HTML input element has the id `#foo`, you can get its value with `$("#foo").val()`. + +### Good form (no pun intended) + +Here's an example final implementation: +```javascript +$("#comment-form").on("submit", function(e) { + e.preventDefault(); + var name = $("#name").val(); + $("#name").val(""); + var comment = $("#textarea").val(); + $("#textarea").val("comment here"); + if(validate(name) && validate(comment)) + addComment(name, comment); +}); +``` + +Notice the additional function `validate`. This is important! +Source: relevant xkcd comic. (little bobby tables) + +Try this: put the below input into the comment field (you can just leave the name field blank) +```javascript +TODO! +fill_me_in() +``` + +Real-life production code should never allow this, or else users could ~~take advantage of this security flaw to~~ run malicious scripts. + +### Animations and Effects with HTML and CSS and Javascript and JQuery + + + +## Sorry + +When you reload your page, all the comments disappear. All the posts you added disappear. Not even Ctrl+S can _save_ you. + +Unfortunately, this is the end for My First Website 2.0. + +What we're missing is a `database`. This is a persistent storage that lives elsewhere. Every time someone creates a new comment or post, this information is stored in the database and loaded up again when a user loads the website. + +Web apps really have three layers: + +- the presentation layer +- the + +[edit below section] +You could use PHP, which is a `server-side` `scripting` language. But goal #2 of this learnathon is to get you started with web development in 2016, and introduce you to web frameworks. + +And anyway, + +This is why developers use `web frameworks`, like Django, Ruby on Rails, Flask, + +## Recap + +HTML +CSS +Javascript + +## Onto Flask! diff --git a/web_lesson_1.md b/web_lesson_1.md new file mode 100644 index 0000000..afdb8a5 --- /dev/null +++ b/web_lesson_1.md @@ -0,0 +1,228 @@ +## Your First Flask Website + +Flask is a web framework that lets you write web apps in Python. We still use HTML, CSS, and Javascript, but a lot of the rest of the work is done in Python and there are now Python tools to simplify web development, like the Python templating engine Jinja2. + +#### Installs +To avoid interfering with your __local__ Python setup, it would be a good idea to install `virtualenv` (short for "virtual environment"). This is so that everything we mess around with relating to Python is contained inside of today's project. + +Once you've downloaded `virtualenv`, run the following command to create a new virtual environment in your current directory: + +###### Windows + + +###### Macs + +###### Linux + +Now to install Flask. You can install it [here](). + +### Initial commit + + +Flask requires some basic setup files. + +_app.py_: +```python +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello(): + return "Hello World!" + +if __name__ == '__main__': + app.run() +``` + +_requirements.txt_: +```text +Flask==0.10.1 +gunicorn==19.4.5 +``` + +_runtime.txt_: +``` +python-3.4.2 +``` + +__Protip__: Notice that the following file doesn't have any extension. +You can create files through your command prompt / terminal with the command `touch .` + +eg. +`touch Procfile` + +You can also edit files in the `command line` with one of the pre-installed editors like `emacs`, `vim`. + +eg. + +``` +vim Procfile +I +web: gunicorn app:app +Esc +X +``` + +You can see the contents of the file now with `cat Procfile`. + + +_Procfile_: +``` +web: gunicorn app:app +``` + +To see this website in your browser, run +`python app.py` in the terminal. + +This tells python to run the file `app.py`. +Now the last two lines in `app.py` come into play, because `app.py` is the main file running right now, so `app.run()` is called and the app is run. + +##### Checkpoint! + +Save your progress so far with +``` +git init +git add . +git commit -m "Initial commit" +``` + +## The Move to Flask + +It's time to translate our previous website to a Flask app. + +Copy over all the files we had before. +If you rerun the website now (TODO: can refresh?), you won't see any changes because Flask doesn't know about the files you just added. They're just sitting in the same directory. +To tell Flask to `serve` the `index.html` file and other files, you'll need to work with Flask's routes. + +### Routing + +All of this happens in `app.py`. +The [TODO] function can return text, like `return "Hello world!"` earlier. It can also return an HTML template, using the package `render_template`. + +Include `render_template` by changing the first line of `app.py` to: +```python +from flask import Flask, render_template +``` + +Here's a route: + +```python +@app.route('/') +def hello(): + return render_template(".html") +``` + +__Task 1__: Route the url '/' to index.html, and route the url '/about' to about.html. [TODO: methods] + +(Ruby on Rails uses routing, too.) + +### Templating Engines + +Now instead of `hardcoding` the url links in your HTML templates, you use the `url_for()` method to get the right url. +This way, certain [TODO: specify] changes you make in the future won't break anything, because `url_for()` will just return the new url. + +`url_for` works by going off of a standard structure for your app. All of the HTML templates go in a folder called templates. + +Here's what a basic project structure looks like: + +``` +/yourapplication + /yourapplication.py + /static + /style.css + /templates + layout.html + index.html + login.html + ... +``` +source: + +Images, JS (Javascript) files, and CSS stylesheets are `static`: the files themselves don't change when the website is run. +HTML goes under templates, since it lays out the basic structure. It's then served to the `client` (a user's local computer), then modified to bring it to what it should look like, and then possibly further modified as users interact with the website. + +### Rerun of the client server model + +### Back to routing + +Jinja2, the templating engine that comes with Flask, lets you write some useful Python code. +Any Python code inside an HTML template is specified with double curly brackets `{{}}`. +Now you can call `url_for()`. + +Here's an example of what the link to `scripts.js` should now look like: +```html + +``` + +Try rerunning your website with `python app.py`. The `console` will give you hints if things go wrong: a 404 file not found (TODO: get details on what console output looks like) means your url might not have been updated properly. The console will also tell you where the error occurred so you can find it. + +###### Testing - Houston, we have a problem + +In general, software development is accompanied with testing. Eg. `unit tests` will check that one part is working properly, while `regression tests` make sure that any new changes you made didn't break the old stuff. +For an example of what implementing +There's even `test driven development (TDD)`, where you write the test cases first and then write the minimal code to pass the tests. +Testing is great and makes you look less like a mad coder that hacks maniacally through the night on 50 cups of caffeine whose project is secretly ridden with bugs under the hood. [edit phrasing] + +##### Images, Glyphicons, and SVGs + + + +[TODO: use mouseenter] +```javascript +$(document).on("mouseenter", "img", function(event){ + $( this ).attr("src", "/static/heart-green-filled.svg"); +}).on("mouseleave", "img", function(event){ + $( this ).attr("src", "/static/heart-green.svg"); +}); +``` + +## Fancy Routing and Templating + +You can organize all your HTML templates by putting the project templates into their own subdirectory, so the project structure now looks like this: + +``` +/yourapplication + /static + heart.svg + heart-filled.svg + script.js + styles.css + /templates + /project + 1.html + 2.html + index.html + app.py + Procfile + requirements.txt + runtime.txt +``` + +Now for the slick part: dynamic routing. + +```python +@app.route('/project/', methods=['GET', 'POST']) +def asdf(id): + # return "/project/_"+id+".html" + comments = Comment.query.all() + return render_template("/project/"+id+".html", comments=comments) +``` + + +#### AJAX, the HTTP REST Protocol, GET, POST, and more + +The comment form no longer works. +You might have seen this message in the console when trying to submit a comment: + +```html +get output message +``` + +When a user submits a form, this POSTs the information to your website in the form of a POST request. +The default method allowed when routing is [TODO]. + +To fix this, specify the methods in a route like so: + +```python +@app.route('/my_url', methods=['GET', 'POST']) +``` From 38c8bf9eb386ea9a267f8ffb2d9fb7fd4232e0ff Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 17:14:18 -0400 Subject: [PATCH 06/10] Start on lesson plans part 2 --- web_lesson.md => web_lesson_2.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename web_lesson.md => web_lesson_2.md (100%) diff --git a/web_lesson.md b/web_lesson_2.md similarity index 100% rename from web_lesson.md rename to web_lesson_2.md From 42cc78ac128186f484d7b2056d843467b99d94f0 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 10 Apr 2016 18:10:44 -0400 Subject: [PATCH 07/10] Move lesson plans to web folder --- web_lesson_0.md => web/web_lesson_0.md | 0 web_lesson_1.md => web/web_lesson_1.md | 0 web_lesson_2.md => web/web_lesson_2.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename web_lesson_0.md => web/web_lesson_0.md (100%) rename web_lesson_1.md => web/web_lesson_1.md (100%) rename web_lesson_2.md => web/web_lesson_2.md (100%) diff --git a/web_lesson_0.md b/web/web_lesson_0.md similarity index 100% rename from web_lesson_0.md rename to web/web_lesson_0.md diff --git a/web_lesson_1.md b/web/web_lesson_1.md similarity index 100% rename from web_lesson_1.md rename to web/web_lesson_1.md diff --git a/web_lesson_2.md b/web/web_lesson_2.md similarity index 100% rename from web_lesson_2.md rename to web/web_lesson_2.md From 7c2f1ef52b310e37c863af6de9e42e111cd926d1 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Thu, 28 Apr 2016 00:01:38 -0400 Subject: [PATCH 08/10] Continue lesson plans --- web/web_lesson_1.md | 171 ++++++++++++++++++++++++++++++++++++++++ web/web_lesson_2.md | 188 -------------------------------------------- 2 files changed, 171 insertions(+), 188 deletions(-) diff --git a/web/web_lesson_1.md b/web/web_lesson_1.md index afdb8a5..abf989a 100644 --- a/web/web_lesson_1.md +++ b/web/web_lesson_1.md @@ -16,6 +16,9 @@ Once you've downloaded `virtualenv`, run the following command to create a new v Now to install Flask. You can install it [here](). +This is also a good time to install PostgreSQL. +Install Flask-Migrate with pip install Flask-Migrate + ### Initial commit @@ -77,6 +80,8 @@ To see this website in your browser, run This tells python to run the file `app.py`. Now the last two lines in `app.py` come into play, because `app.py` is the main file running right now, so `app.run()` is called and the app is run. + + ##### Checkpoint! Save your progress so far with @@ -226,3 +231,169 @@ To fix this, specify the methods in a route like so: ```python @app.route('/my_url', methods=['GET', 'POST']) ``` + +## Heroku + +So far, your website can only be viewed on your computer. So much for "web"site. :( Now that it's a Flask app, we can put our app on __Heroku__. + +Heroku is a Platform as a Service (PaaS) - aka it provides hosting for web apps. + +Normally, you need to purchase a `domain` and [elaborate]. + +We also had another option before, which is Github Pages. This is free but it's only for `static` sites and not web applications. It's a good option to keep in mind though, eg. for a personal website! + +Anyways (and most importantly), Heroku provides free hosting (for up to five web apps, and the websites can't be running all the time, but hey, free hosting). + +Here's the link to create a Heroku account (). + + + +Once you've created a new app in the Heroku dashboard, you should select the app, navigate over to the __Deploy__ tab, and choose __Github__ as the __Deployment method__. + +From there, you can type in your Github repository name into the input field and connect your new Heroku app to the repository. + +If you now go to Settings, you can find the url for your app. + +## Databases + +A database stores information, although how it stores data varies. + +Just like there are many programming languages, there are a couple `database management systems`. Some examples are MySQL, SQLite, PostgreSQL, MongoDB, Oracle. + +There are three (two?) main camps: `relational` databases, `object-oriented` databases, and `NoSQL`. [TODO: fact check!] + +Relational databases are really the main type of database. Virtually all relational database management systems (`RDBMSs`) use `SQL` as their language, which is why so many of the examples given had 'SQL' in their name. +(As you can guess from the lack of 'SQL' in the name, MongoDB isn't an RDBMS.) + +We'll be using PostgreSQL, which is actually in between a `object-oriented` DBMS and a `relational` DBMS. (It's an `object-relational` DBMS, or, `ORDBMS`.) + +A relational database stores the data in `tables` of `rows` and `columns`: +- every column is a field +- every row is another data entry + +An object-oriented database stores data as `objects`. + + + +`SQL` stands for `Structured Query Language`, because you do two things with a database: store information in a database, and `query` the database to get its information. + +A SQL query looks something like the following: + +```sql +SELECT * AS older_customers FROM customer_table WHERE age > 20 +``` +> this means select all "customers" (technically these are data entries in the database and not actual people) over the age of 20, and call this selection of customers "older_customers". + +Note: the web app won't use SQL because there's a Python package called SQLAlchemy that can interface with the database and lets us execute SQL queries in Python. But the basic idea of selecting data from database tables with conditions is relevant. + +### Databases with Heroku + +Heroku lets you "provision" your web app with a database, but it only supports PostgreSQL for Flask. + +To provision your app, follow the instructions at (Heroku guide) + +###### If you haven't got PostgreSQL installed properly yet +No worries: the Heroku database is on the Heroku servers, and the only reason for installing PostgreSQL locally (on your computer) is so that you can play with a local database. + +All Python installations come with SQLite, another database management system. You could play with that instead, but in the web application it's better to use the same database management system that Heroku's using or you'd run into messes when pushing to the Heroku servers. + +[commands to create a local SQLite database for demonstration purposes] + +###### If you do have PostgreSQL installed and working + +[commands to create a local PostgreSQL database for demonstration purposes] +```sql +psql +\d +\c +\l +\? +\q +select * from table_name; <-- need the semicolon! +``` + +## Connect to the database from a Flask web application + +Underneath `app = Flask(__name__)` in `app.py`, add the following: + +```python +app = Flask(__name__) +app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', + 'postgresql://localhost/techretreattemp') +db = SQLAlchemy(app) +``` + +This `configures` the `SQLALCHEMY_DATABASE_URI` variable by setting it to the `path` of your database. Aka it tells Flask where the database it should use is. +If `DATABASE_URL` isn't set, it'll fall back to the default. In the code above, this default is 'postgresql://localhost/techretreattemp' - the database stored locally on your computer. + +When you run the web app locally with `python app.py`, Flask uses the default local database. +When Heroku runs the web app, `DATABASE_URL` is set to the database that Heroku provides so Flask uses that one. + +## Storing comments + +Since we're using an `object-relational DBMS`, we work with objects. +First, define what a Comments object is by creating a `class`. + +We'll say a Comment has: +- + + + +In `app.py`: +import SQLAlchemy with + +```python +from flask.ext.sqlalchemy import SQLAlchemy +``` + +```python +class Comment(db.Model): + __tablename__ = 'comments' + + id = db.Column(db.Integer, primary_key=True) + poster = db.Column(db.String()) + comment = db.Column(db.String()) + + def __init__(self, poster, comment): + self.poster = poster + self.comment = comment + + def __repr__(self): + return '' .format(self.id) +``` + +### Database Migrations +Database migrations are changes to your database that are packaged and recorded down so that you can undo and redo database changes. [check this description...] + +We'll add a Python script that allows us to manage the migrations. [do they even know what a Python script is?] +```python +import os +from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_script import Manager +from flask_migrate import Migrate, MigrateCommand +from app import app, db + +app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', + 'postgresql://localhost/techretreattemp') + +migrate = Migrate(app, db) + +manager = Manager(app) +manager.add_command('db', MigrateCommand) + +class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(128)) + +if __name__ == '__main__': + manager.run() +``` + + +What does all this code mean? +[description] +But in short, I got it from the official documentation example: https://flask-migrate.readthedocs.org/en/latest/ +Moral of the story: documentation is your best friend. Stack-Overflow is your second. + +##### .gitignore diff --git a/web/web_lesson_2.md b/web/web_lesson_2.md index 368068c..e69de29 100644 --- a/web/web_lesson_2.md +++ b/web/web_lesson_2.md @@ -1,188 +0,0 @@ -## Hello, World (Wide Web) - -Long, long ago, when the great Tim-Berners Lee brought us the WWW, - -Clients and servers - -Model -View -Controller - -And that brings us to... - -## Your First Website - -Create a folder on your computer for all your files to go inside. -In Atom, create a new untitled file (File > New File) and save it as "index.html" in the folder you just created. - -Inside `index.html`, type: - -```html - - - - - My First Tab Name - - -

My First Website Heading

-

My first paragraph

- - -``` - -Now go to Documents (Windows) / Finder (Mac) > "Open `index.html` in Chrome". - -You should see your very first website! - -## Welcome to HTML -HTML, or Hypertext Markup Language, defines the basic structure and content of every website. - -### <h2>Elements, Tags, and Attributes</h2> - -To define the structure of a website, HTML uses tags: -```html -stuff that goes inside -``` -This is an HTML element. - -HTML follows grammar rules, which in programming we call `syntax`. -Tags pretty much always occur in pairs: an opening tag followed by a closing tag. -We'll see shortly that tags can be "nested" inside one another to contain each other. - -Aside from the tags shown here, we also have `div`. A `div` element is used as an all-purpose container. -Let's list a couple projects! -Add the following below the line for your first paragraph: -```html -
-

Project 1

-

My First Website

-
-
-

Project 2

-

My Second Website

-
-
-

Project 3

-

My Third Website

-
-``` -Notice the new `class="project"`. - -HTML elements can have `attributes` to describe them. Each attribute consists of a `name` and a `value`. -The `class` attribute lets us group related elements together so we can do the same things on all of them. - -## CSS Beauty Makeover - -Create a new file "File > New File" and name it "styles.css". Save it in the same folder as "index.html". - -Inside the new file, put: -```css -body { - font-family: "Helvetica"; -} - -h1 { - color: blue; - font-size: 40pt; -} - -.project { - background-color: orange; -} -``` - -### Language #2: CSS -Cascading Style Sheets - -Now let's get into the real makeover. - -### Selectors -Replace the previous code with this: - -```css - -``` - -`class, id, tag` - -Change up the text in your HTML elements to fit the website, too. - -Let's add a background image! -Set the background image for `body` using -``` - background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg"); -``` -Now make all the font white. (Hint: use the "color" attribute.) - -## Adding interactivity with Javascript (not Java!) - -Javascript is a programming language that's recently taken the developer world by storm (You may have seen Angular JS, Node JS, React JS, Meteor JS, Ember, Backbone, ... the list goes on). - - - -Here, we're going to use Javascript to make a clickable button for adding new projects. -Create a third file called "scripts.js". - - -### Manipulating the DOM - -Of course, the world isn't powered by many code monkeys modifying the HTML every time someone adds a new comment or post. - - - -Computer scientists and programmers are fanciful creatures. The Document Object Model is one example of a `tree` of `nodes`. A node can be a `root`. Otherwise, it has ancestors, and specifically, `parents`. A node can also have `children`, unless it's a `leaf`. - -You can modify the DOM tree `dynamically` with the web's favourite language - Javascript - using methods named after the terms we just described, like `.appendChild`. [edit] - -`.createElement` -`.appendChild` -`.getElementById` -`.getElementsByClass` -etc. - -Remember this keyword, `dynamic`, especially when searching StackOverflow. - -## Fleshing out the website - -Let's start filling in the content. - -__Task 2:__ Turn each of the project divs into a clickable link to an HTML template titled `project_.html` (eg. "project_1.html"). - -__Task 3:__ Your turn now: Fill in `project_1.html`! - -## The Comment Thread - -__Task 4:__ Manipulate the DOM so that Internet users can add comments to your site dynamically. - -### Animations and Effects with CSS and Javascript - - - -## Sorry - -When you reload your page, all the comments disappear. All the posts you added disappear. Not even Ctrl+S can _save_ you. - -Unfortunately, this is the end for My First Website 2.0. - -What we're missing is a `database`. This is a persistent storage that lives elsewhere. Every time someone creates a new comment or post, this information is stored in the database and loaded up again when a user loads the website. - -Web apps really have three layers: - -- the presentation layer -- the - -[edit below section] -You could use PHP, which is a `server-side` `scripting` language. But goal #2 of this learnathon is to get you started with web development in 2016, and introduce you to web frameworks. - -And anyway, - -This is why developers use `web frameworks`, like Django, Ruby on Rails, Flask, - -## Recap - -HTML -CSS -Javascript - -## Onto Flask! From c20df17997a510732fac6bb69b9b04f7aac94e5c Mon Sep 17 00:00:00 2001 From: friedeggs Date: Sun, 19 Jun 2016 00:24:19 -0400 Subject: [PATCH 09/10] Comments model and more --- web/web_lesson_1.md | 119 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/web/web_lesson_1.md b/web/web_lesson_1.md index abf989a..d82e21b 100644 --- a/web/web_lesson_1.md +++ b/web/web_lesson_1.md @@ -390,10 +390,125 @@ if __name__ == '__main__': manager.run() ``` - +We'll also add the file `migrations/env.py` [insert file] What does all this code mean? [description] But in short, I got it from the official documentation example: https://flask-migrate.readthedocs.org/en/latest/ Moral of the story: documentation is your best friend. Stack-Overflow is your second. -##### .gitignore +##### .gitignore & More Command Line! + +Git tracks files, including a lot of automatically generated files that aren't supposed to be kept track of. To exclude those, your repository has an invisible `.gitignore` file. +You can edit it in the terminal/command line: + +> vim .gitignore + +- this will open up a primordial text editor inside your terminal/command line window +Here are the commands: (good luck) + + + +or you can just +> atom .gitignore + +to open it in Atom. (atom <3) +Make sure you navigated to the right directory in the terminal/command line [ / shell?? ] before you type these. To check where you are, you can use the command +> pwd + +We won't need all the files in the `venv` folder, since those are all the python requirements [libraries?] downloaded by running ` ` and Heroku will run that command itself to create its own `venv` folder. + +# Comments! + +This is the lifecycle of a comment: + +Alice types a comment + +This is the `form`: +________________ +name: + +comment: + + Submit +________________ + +She submits it. + +--> The request goes to our server +with all the information we specified: +- name +- comment + + Now we're going to save it permanently in our database. + +# Objects + +We're saving a comment. +In our words, a comment is: +a _string_ (the name) +and another _string_ (the comment) + + + +# __Model__, View, Controller + +Fields: +- id +- poster +- comment + +Mandatory methods: +__init__ + +__repr__ + +## HTTP Requests +We create a new `route` called `store_comment`. +The HTTP method for this is `POST`, because we're passing information. +To get this information, we can find it in +```py +request.form['poster'] # the poster +request.form['comment'] # the comment +``` +Now we create a `Comment`, passing the poster and comment which will call the special `__init__` method into action. +To add it to the database, we call +```py +db.session.add() +db.session.commit() # submits the comment to the database +``` +lmao I did this already + +### Security + +### Updating requirements.txt + +[Add gunicorn as well] + +### Remote migration + +## Showing comments + +To show the comments, we need to update the HTML in `templates/project/1` to show all the comments we have so far. +However, the HTML file doesn't have the comments. We can get all the comments using python in our route method in `app.py`, and then pass them to the HTML with the `render_template` method. + +```py +comments = Comment.query.all() +return render_template("/project/"+id+".html", comments=comments) +``` + +In the HTML file, we want to add all the comments. This needs a `for loop`. +HTML isn't a programming language - it just defines the structure and content of a website - but we can `embed` [programming ] in it using Flask's `templating engine`. + +```html +{% for comment in comments %} +
{{comment.poster}}
{{comment.comment}}
+{% endfor %} +``` + +## Make the like button work! +When a user __clicks__ the button # jQuery & javascript +make the number inside it go up by one # jQuery & javascript - DOM manipulation +When a user __hovers__ over the button # CSS +change the button image to the filled in button image # CSS + +## Add an about page to spruce up the website From 8842add8ea216bc6db74eedecd86650268819bd2 Mon Sep 17 00:00:00 2001 From: friedeggs Date: Tue, 21 Jun 2016 01:55:13 -0400 Subject: [PATCH 10/10] Flesh out first half of web learnathon --- web/README.md | 24 ++- web/css_snapshot_1.css | 10 ++ web/html_dissected.png | Bin 0 -> 78671 bytes web/html_snapshot_0.html | 23 +++ web/html_snapshot_1.html | 28 ++++ web/web_lesson_0.md | 341 +++++++++++++++++++++++++++++++++++++-- 6 files changed, 396 insertions(+), 30 deletions(-) create mode 100644 web/css_snapshot_1.css create mode 100644 web/html_dissected.png create mode 100644 web/html_snapshot_0.html create mode 100644 web/html_snapshot_1.html diff --git a/web/README.md b/web/README.md index c82f482..7fe00b2 100644 --- a/web/README.md +++ b/web/README.md @@ -1,27 +1,17 @@ # Web ## What you'll make -A dynamic \_\_\_\_\_\_\_\_ website that -shows a list of \_\_\_\_\_\_\_s ("posts") which users can interact with through \_\_\_\_\_\_\_s +A very simple project showcase complete with its own comment section! -Examples: (to edit, try to use social justice theme, should be sufficiently extendable) - -- simplified Kickstarter, display great projects and allow users to add their own - -- a simple blog - -- [Rate with Science](http://ratewith.science/)/an idea generator listing cool combinations and letting users try their own combination - -- a personal website to showcase your projects and provide a contact form +## What you'll learn +An introduction to: + HTML, CSS, Javascript, JQuery, your first web framework (Flask), Python (again Flask), a templating engine, working with databases, and deploying to Heroku! ## What you'll need - A text editor -- An [idea](link to ideas) -- Github, Git - -- (provided [HTML](), [CSS](), and [JS]() cheatsheets) +- Github, Git (Optional) ## Table of Contents @@ -87,3 +77,7 @@ Download the Atom text editor from https://atom.io/ - Make posts searchable - Create user accounts - Use a web framework + +## Additional Resources +- For a fun CSS primer, check out http://flukeout.github.io/ +- [Google Fonts](https://www.google.com/fonts) diff --git a/web/css_snapshot_1.css b/web/css_snapshot_1.css new file mode 100644 index 0000000..7931e88 --- /dev/null +++ b/web/css_snapshot_1.css @@ -0,0 +1,10 @@ +body { + font-family: "Montserrat"; + color: white; + background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg"); +} + +.post p { + font-family: "Open Sans"; + /*font-weight: 300;*/ +} diff --git a/web/html_dissected.png b/web/html_dissected.png new file mode 100644 index 0000000000000000000000000000000000000000..67eb087823342a7c12211ae9dfb1f66196f37612 GIT binary patch literal 78671 zcmeEuWl&t*wk;Y01PKWcoM0hnaBU#Lg1ZC_PUGHa6M`goaCi6M4hin=?k+()_}hHv zoO^GbTOYZv>iv3ky9x@r_ugx-x#ya5jxpwHe|cGP3^YPCI5;>AiT5IkaBwKk;ouN( z9zO!!S$DXsgM)j53lgZK4Q(De|tjQE*-B_>T=fyT?g%zm^VvLH-FzF#1!U{wBJ6 zdPeI{!Dt9qDi?!V5moK&^kFle5hhm(1{@9sPjJNGfUo-tIIRdqY&kn&t0JGk`>>l^q7w-)p|*Z4^>(_-xK zl;T_2(_BH--hR}8uzoft^5!>A4Cj*YS|7_?evlfKzRNJWEQNgjyu}^INsO}m1M4>; z4ffCYN5)-) zs|e!aF^@8?I^Asya%Y~x+Ciz%ToRL|_#bGHPn1VL8<(bt)6U#{(`|7VH;y}RsVq0HF$bUfz0=V^g9_b5@RY!BEsem>LJ8p?|K`{Q!o6F*bRuSk3WCICVVH( zj;-DHxKwBl8^7%ts!&VV3n~H$QC4gLigz27Qek$P2zg>vlxJNfT?%W@?cldXtthd= zplck*sO|D}`2l7b9a~gm$Y*a`hqxVhD%n0h9H;%o3TFxfWJHd^niMZTwGi{T+}XJAM;-wWsyvcBYA#vFI_u~?gDUUo zI_Ti(h@)_#Na%7@NJm-f;O9dJvYE3EGgGo)c{nOE%EHR!%DpPE0;dAZ{JdPATY#QT_PYlE!Q+_FpxBX`8eFXk|j+BBrhe}VwE?dL8+;}PCXzf_+8s{2u>3Akb zC>;zJY!O^Z$Vdp|u$8gze;r@WVa_pVhAFL_v?UM*Dz#Yo=?|%pXS6SRQRJe*S5a5t zSK(OUU4cB4P$6fIwe`lK!LiI?VwZSwdopX+b?4Y&ayNPC#yhX$RBz)=L99uvx2uNh zPgjb@eb;%{)=Qepqs#V7nM)W3?IWf~+mCiJc%MANaKxZ|vPs1GVu2-`&5osqea3VQ ze7Z2z#y#vDXB&8A|I9H0JAxwOQG{DIyAty#|0wUMxv>Zvez`?CqcLbBw>HBi+a>tI z2z{)j`?E=Dd-`Pf$8EFj>ca8{uxRCRDP4uxcURN?@0it0RaT`?o^Bh(p3KS!-d7%# zwiVr_(_s5a)ppLYs;R2}^U9=JlG);pgLqKCzz{Udqu_=f1s$bWxTuv;yw1R9m9-PI zlUa5^_Faxx4%9?=Q+cz0fJ<9hJ6&6~E)haqb6Lk^X9>C4+E`OvavIuahS0+-V2uZZ zuM(s;WEXfYO20Ir^cvKEnTpts+o|nW+RNH(Kh8gKU9(*3KU!G7>Fex!)oYS6D5XoJ zn|Ang5eBt_qF>8i8r;6UHM*ULr-MI&ABV?6I74JZAVNU#CidPyPI^Lv^aZ&DqlqA$ zqy;k%i{ObPrZ%=KO%Nd-kt)eK?Hs?>iv>I{F(0wQz#4q;^X^a!e*}e>3cKkC;t3*~ zqIaCW+|;g;{gF8=RV*h4yawB)YzDB7rofY6O%e##0=v%h#jw=aH;icl%O2^T`l9LF zV8xNonNr>H-3@CK&lM%#^%O^@<-sd)N$)3Ya2Gku1aH%fZb~=55w;f_2r^*9H7PSD zm88z3$v%*|=?l;wFZCOFH^P~NQFb#$G|kMtka(5|Ek>#Se8n(N*+_BY;wRow7j!FK z($~`mpG;N~52K^!HaDNR+RNRWpQhyT=aH#hn;V$CKlIj*sJwK|9JJzP;aIsMD$UhB;FY#rbdGas*%Kb_+p@C0DZtBlZ@7k zYPxn=&DxRNTS1OUx=1uBYbJZ$Do38C#Oek&-Ed}KxXYIDSjkCTpquoWT*xKcHS;FfR!I8n~ z!7KT{@+V~c5+*DzwURz~GGfl}i zw^y1(MTFH{Eb%>9pAgd#J+I?0cZ0q4Q1~!wiOu*e9Gp*{UWSz!XS!IOe3UW{tl_Aw zgqdy)hj7BGD4RdrRn=>kG;YMsl#X}4?vx4%O{C>zaZt3eTT<@G>m*_0J4lx0_1N!= zWSci>9Y_|$cRp~)k=yh?A>Ev1uF>Xp9bVV(rOuJ+*ACGZheEDz-d8I@PK-8ti$KL7 z%f<+|P~D?CyPf>Iar?=4#oBeGE{!m;>tIc;ocZUAD|4<;zRmLjK3|C6Aybohjhd70 zHgp}a2*vS^^-3*bO2U0F?XI<0yfpMcz*rD^M0MA_*%Y2rkrOrOkTNb}Xf-#0UQI|t2A_&f52+W+%Uwz{Kpt#gG_YO1VD&@lkYi6yCX!2u_2Q zpYMkovFKZ;yKp=aCzD8=->;(Qhcl6rwVd1#v729fvT+v6^BsT8iVKwrS+re%tE?s~ z$)FaTUyzLsM6+h`!J?$Ju7D@6g#RVV&C1$fWVqHVOMA`<_v*wDCLr;$h3%Hp9#uQJ zu=K^X{4IuVoH;c$VSosL6Hsm5f3Syx!=t|cftOIE`UM9EpA1%3cTks+<~6XkWYROV z);D5uwzL6`hJ)jG<^}$=G;+`*ceb>!vgdUMQT%lTFYx#M+sqW?e;wjr4x&((ktY|n zwlgBl@U0C(cZ<%LC=}d%AWEc7x~w9M2zeW?7%h-U~4P#`|IlITRS>{C@Ag+`j6K? z?$gK_{GTIP+5dA|zyz7^KVfEJdc*u5*9K1Izkiok9_(ynp)LZpG_tY>#t>lPW?|+3 z>xBRI(SHv4Urzn-pQm!Lz4@O>$v+}Jetz-t!Ak)HebR+T5N&4P z6M}5KA0~NkGK~K<+5ejCzd7CiQ!jgmDDf>Siu$s9U;uONdywy+ z)&j8Py}hZasM_4!_b|J%Q;7?Z^)pXT9X_`S39&xsl9=A^cQ)?Q|^jP{2+7fP)FR+wC&S>8}Hx#%|${$ z3W!>IX@~xYuof8&=cNzsW@Y+=p`(DfR*J(fVg>(jl<^wDA(v-h{Lmr#aLi4`d5le~ z`uR&2;~yfDyaq6nEb88xKTJd!0Un`U@)ve3@*f_Jq!cid3PS??KTJfDTqso?m6W3T zVbAy^1V+WCjgDqm{gVr!0*{XVr|=ryA09F-BQTSZa3ifhOoYx0cy!dxU*{11@a(dv zfthHO1qD956osgPbW@0g6{h)b&h+p9{;w1MolE?$6aMWg{hvGG<02&34@PqN10}FUE-Ylo(`l?5EBr9p80YxFV#&=!c&ncxNK8pdDbQJ@ef#!gP_TEo^-Djpx${V-#It7n68MqZn=5MeF}92%#jJp_T!oCT zwD4rutaWqL{%mz1p}2_1`!ARzYXS7d-!v-CE*ddmm>w0o6GahOQZYS0y;1sK5ZZ89 zFKF>P?oe6IRD`9blG}gvD7h$E1SL10_4P^HhjxaNK@OTduFi3Y_nP5UODu0L&ty}o zEN3Ov)nj?ufj_7i8T)q~TuCh}%_pNDVGwe6ZXBvdx(%}o!YocgJ(~_)t+7C--wL(s zCy)YCJkI0V+S(EizuQ&coo& zGRRMX%=5am;pTi+r`7%DY|?nP#Pi0yb~`(v;k=&9l>B1KAhdfRiElmuG*O^ZKr@;n z$E6o@r32!{T?zD)ENM6n;J@6;kUivDa2Bp}+}&^=Y)*f<+2;Q&sb48uIxJfzA>OQp zYtenw$96!LjDC4}`FKo8u6tZfcORDl?@VU^X5o=|+mOI6GLb#*u92r6?zA)Z`eG|1 zv`<~T&LOOzxGtZn6?siP{~2OS7x~}%=40i@@ORXPxGTp}Fba-}vINz~cw=)O9%p>6 zr@0NMBT{y|Mb(mac4Z|@#h1<7?kret##AoCiPq{h1Sz$?aI;XSkF#$SAn;r zh@H0mIpiDG!ll+ah^!roR|h?B%cpRC2RiKOHEh8sm37<2^#`na>GOk_t9yq{(c9bG z$9-&rU9DJxdydK1uUK zqQ?z;WxWmAf|Xa(-lkKql|bhBJVi$|#UiKK8rux%c=m2-Z!_3vo^mDY(7XfM2*~Zc ztCzWX&jJcqw69K8aYiQRW06K>{+tJFp4DW4m`yZWXER+i&~WL8mlKys9(uGr@j?B0 zET>uLe5Ai=cADg-;cwdPCMR6q9q}qqb}(-_Q*{ftdkCw3+vCj_+QYD!Z2?O{IFDaH zQAiJ=V@o9_gUK{OV=(#|udt>FdBKZ>M%`-2{)`GQCGi`nvWSJ)$=GtUq@oVWP&Da=Pdt~JEi{oDA{>Ir= zkIU^G<9g^MSn=v?*W~cH)*)r_AQdxI@V18kxQo&_LMJA@7O)FeYG z-|Y8AKi6VxfQrY224lWTA>q)5wT5V&UhAV_lgt#O-e?}}iWSGRopI99hJ1wtPRm3y z6%R4jh6T4)Ka6!CmUDD&@tj?3@x7QV(Ph?kKk`i!@X**9FEGho88;%2PrggJyUt-Z zpXfvCIS^vUUS)d-f@rCG1=7fmeG|^<9fP!ND<{sJ&U<7%rM-0X0U~Ho+^sUqJZdF` zgfCP03ugA!(Q2m=kS_+(H2gOU9CyaLrwf&0%)XkUfPt6`Y2hup9NlR`pJ(SE`7Vd3 zV5tG(HXR~==CtilLI(YvW-rlv(QhsNal?YPMe<~$Uj{jwWFh(#& z9$h25`~79oRw|l-rTM4rYKQ;T<@k1KC+W#f3DIe_`5kngq#|CWK)rH*p-~fh(W_er z;oEhU)s@Lf8~?7>IwMp8$QP_Ztxpdsl_jcWfc->$!8 z7a0SyzLJhzdfH){HRWpo%<<^)h10>@cA`!J&AyFvPG#YT^6c`#M#W~RM`({I1UoA& zbN=#tpI#+jxwKhlD9f+HX+3Qrf`sCB-`dl@*r4m%%aLMjzD+j;H3zNPf>eWEy8wBN z(Nn_xHww-5hRnKthtMMy}qk?YZ{Ysj$qsH?-T9OqVkM(p4aa<*( z5$5iI%s4jYC-^uyh8TA9hlS=TSN|S;*pxp0E%%b{`zKlWym2=+yvG+Acu#K*N zAF+P?0p~V~tXbzx!sjp?vFi}&H2P*Z8Et&QnAx!&>JT7@QGiLz{V~6%0%4To@wOZB zyk3UKwXgb)3z_p0e5X#qVuh>Ka*OZA>5k{!4J>Bsvgz*DEmU*(3N~J`Bbx&|ojfJD zPmacynw}m3_1dlM^8@IDyy1#evT7q7M|! zjBp^@cyux8k+ai}0@&?{6mvH-^a$O4M!ViAZZj7*=)icfzVWnju(5yg@@!Jq8Y;!> zr%E;R(s(c#Bm*hZ8G}>u9(UZG@b^o-C~2Ln*_@pyGm^Rh9MAmoI#AmvWJ|~CXA@V2 zCh$7uZ{?^IsM2ihjTgi$V$TcsQ9lr0BZGx}v7Ybu@MFAEEmU95GDX3=6nG19OjQ#n zBf6I1+sQCb;{>NVY-hy;sAl4hjf{+3A57TD($DQ>AL^fu*E#L)n4cieJIz{FcjN22 z>$T8bwT@r27A&u+)~$-ZPcmK(+f1s4X9f@V;k z?H`>bv#XDp5IlXVXN~!L1T*@qda~BuM2R{dH(jGPS7wsqdR(ZKy_k}hSEIOYhwfzf zjs4L|JFCmF;WO6Vi%PSjIFsaI6@?rks~WF-(TtyDe_udFl6j$#(I)fQizT}sON4%t zD9EUw+v}k(arhju^38k*XUpmGbQ=dF?`Pbj45!?ULy-R~xnzc67X;hyItj8KU=%Ul z?BaU**MpE`pzsZVURQ1=yUA9(8`d3#z(A)%!a7PmGdJU-#l#iW$aZ;N>laXQS5Fiy zQiiCHpyW{b*jb5od{AN9vOQ`fzz zj4w;I=@aN6xt^Nc%X;+jobS0~LURv)(e;Q>WnqG>5m+uSZ&{<>X*(7{ z;Y_pfeB=<5Ll}ppsTay4(;V5CJJ6W)61{OC-Ih0X990&5SL(gwt*WR3e zPZWj~X;$;*&@rNHA{G99TpbagSFIie{5X`qaOxgQgR{;YVtc>^fDl^iZ70OVq#_QBVC z02#@7-QCMTLRO9N5T3F7?05Y67${jbR3o;T^1wh{{O^lSuMynbUtZ~cQiB3lh5XmQ zS_iJCEm&b!(-rBKZC{^ezhmO&o;pxmDiaOFE3=p`L%4cwlq35(Pf2P<22oB|29aie zmtKAhGCzGNKbFeG#5C|E?PX9?itT+2y@D)ym}JLPwDRk2<~^R>+bywpsW18+YmCE; z`*t-{uqX1h5JIQVbVep8LBDZb=&Rqh_#y%<0S5#~}vRlnf@n|hFo5*3rCnqxj zAZ*3Ft_KkXle^OtH=WNEc$9JFav1E+W47QZ@ze(8WIT4p5;JqrJk=TKQ9J&|q4w>o z)aJWVgU+6%pWe*B7hA6(qzv}wL68p^oQZIc6hl;#66?_cZbz?e?(Yzj6ZdX6N5J3k; zK-Q4`l$*AelLFl#?v-rEwUP6$gWkBJp4NlV=`$wvbbAlx>u+?v+{&IEF5bWv4{>P# z3?`xDvW^=k=-D&}jOyd>+nz)A_tDabR(=~nv^q-`h1dPJO&rLrs^RYTDt$D$F2T!{4m1`~BNF4#=(izn8qr47+jS*umk3hKA1_ViQ4b6Uv4p zAlEUZ*9YG+QxQ4X?;#(7=Wx2HPTG8p?WzcUNNN&D+nz@9Tk+V%|nMkZP-N(dW5l&*nTr%~E~e+ME-DHL~V(kGr)9 zIm7*#%JjpdlSzl2>BXA^h%uptr$>*STQDIm$(`9N>X!F@QA71%k}kY#HbczUZ<^5}L-*c8;s)=79DC-$Oj_w(5)| z1t{e+okkbefkng&6sKY0q%Lg>eK-%W#&k78S;@zz?t?TG6zM;h2!Bn>w1%%pkLIcv z0^oz~FfxJ7BxLc;uz*}39;3+O{uVPa;HNRuCAa~%@e47kY-Y~eB|w(%^${Taj#qV0 zB~{Fn=k_PlDCIOUNSDu7dP+-6>tP;kWu%!Y%v?ZGL<#6Ubv?rUXARfdN_xZRUg8aFV zyyXhnO|&LvY;26;G}~MODLg!Utt*UDvC>S1$K^P4tUyifwDFrLI4&-(405Pr-{AL_ zaANz#rH(*LO8Wbe2PyX47YI&As;R}%LM-y(LEaB|32>W2JlKyNQk@m#Tr@C~U&Z_# z|N76IC{&9Fh!B&ZUjl{yJ|*2h$W#kJI_;fEDW`n+it@$x$DOS%uRMT+?uVk)1i1UM zv(3E+OnX06q&aY$-Hme%_X9Ego_Ft21=I?Q8y>#DkDq@HwXOjirzJ?rg!zDj0ypnf z1}M)Anw|q8e=zv}PbMUFC&NGv*Ka|)7oWzy7BBavSpE-uOtd-lRo7wi$^Ou`lIL9YyYNAp54QT>68pIg zNbK|w+BNIvU$*iEvB$i?#bZ=b^PnZdDi&O%E+>1~i{MiLu1w&AWD^S&w{@Lzk$Z!D zk)(6bR;SglYVP|#ExH;6e5P%>1Kj9oTOMj&dv%*wdw1veHRS_ne=rcP#8P^pytJ%r zbgliSBtS_zY>%|>x_}+^zNx6xSWMzvSiux|;^G-TOi$xHdrFzXJ5RO0e? z8WmkyKeQq9)c3&sgJ7JiVsi3XEH1He+-ui|z01!yxCp=i=!tOk^-= z)ZK-*6W&bdugDCn8jOkC6)4Rn>c25L3PHBhoa9StdgdxfwfzCCy7*Cv<@es+tF_;8 z1{+h0uoPOEd@i=DeZ)QJIRsV)4mLi%GEZj54b;#x<*H{r2I@5?dRWn$1PICOvOSvW zRcs4WMn=YZsONQtbP}(MYGD_NX6U$?>`m^pH5Oxf1_)idz$q{ZP> zs+3M@Y)$#6Ftlc=&clhKgbP!Z&5Mp}en6l_YXPvjMs}ef+D2 z#N>CqRtmu)x@8tcM%LmVt2nY$?~R?Z;}$A@gow@&7>fE9nRj0C&~ddFs$@myJsIn1 zAF&`T%+Hc_%vWkNv*Gi+b&msTL!Kg4M%j3F3E4#Mmw@i(3D!X~c(^i&47&KDohez_b1TVKJPhJl=4+ZULtZ8CazCP}AliRJ@%TycqgLg{ z{Wv#1K0aLlX-Wx%0NRNw-2Ef8#tI#`7lc{@v90g;bNu=}u`UIA0P?RJz|KZMC4T{t z0-#f{c8Y)p6;tsNMB0_4?;(X1sJhTz%L{}T!;K&#BTHPJ@2~sf>wFU@^H7dv)TD`^ zk-@3&VWOv(T5vlAom>Di+iQc4Ak?MlK@6kDl_6+nx-3B*GdL7TIM05cJhyGTkNF@9 z4h|Ko+I2h$Ny+0KHjAk@wab1)nSd&m-o*Z3QRx*Zo;rdYa3G$e;?Vj93m>Bmkez7& zg8Pk1fKv+4dF95DDupp`rjp0lZDQYAd+5X*Wfkm(bV}??!obSVvt6fu2w|<5cf=w3 z&_8WlVmh>A1(2vwfVv@dxX?&VL(|b1_>dkC*dy?Lsv=6jx&7EDW&r^m8reizEP>yt z3?Itclo^c;4X3mbnk*CwqW0d;8F$RPuDx<`ns_#n?_`dOjvCXqCtceBmIG_^2`p{) z3od>7{j0&ph!MIuv)xRwn3D;u!b#uONYn_WCfU@9g2n z7YW?t3eR;Wo5b-%_@s2|3w#yrWU@>BtS7Uaq*|}Yimt!Z?#g1+b^X?9rfW?h%Mp9f zIJAcd(k`wCsJ@TBNZQCViT?w<#lTF|3U|>lQ0+scdZisFKK#M=e#F3er8DD}Gj<)4 zIz7Pf->)1%pv0!tVUUZz5_#~>0H+5Y8+6$2`alom4-W)CP-v^&F5}?p-#}JlZ{ZiD zFYmvd3A7v4M|VN$V|u#B47+;!gh}6-h}gTS&W8~ zX@aG>9w2ZUZ4b1ECr4S`BP~kd8H||kOAQd(x=;xzsfgyAeQQ>mB_Z%+agyVm^u^A( z9?3(it41C9vt6NTc!Hda3}g2){+@bcKaeZCfu<=VfMMNe0D2u_<^UnV_{MNY!o_vM zK-DIeTBXGRh@Ko57b2ercZskC)y15M_w998wlj|J@R}*QO(NV6CWCJ@u)Yl^Ss%@$ zggZ$lUk;Lq2)BRMNXmCyKZ2K&a(?fl#??5ehIgxQPQ8CNlS8zUp3gUlr<+s6?=WsH zBj^#rHSZ7swBATKJJ;|7QrAd{uAqNl;0X^V(Zg-NJvo12F6P1!IhXj@SkrfHKnGhd zz)vT>QNS%P%5S}9q@op(&B@``OIM{A^K(gOp5j%*Oupv1OEaFm>vNew-K+2>CBt7Q z=mDQCRgB*q^9szOAyyI&&!5h@x_>(hoexRmW$;YAN(2ar>4ZMTP^i&z3V%!6^(GA8 z0q8l95EHZd76)h?B~6$4PsjwclA)L2X`uH$227bO;$O zI&{B^es&Co<4#}2U?soR02s)~h~^}!`jCD0PWO5@|5IzW_YQQ5^+v`fl1{Q!>;=PG z#b7r1naN$Mr)jk+UkN@Cuo}#>vtbmR3p&v#aNaiP@NzYb3Cy`K##D4(6ytuy(z-B5 zt<07hH*tY)k#ZDD0MlPBI z1v;hzR;n-JpW}km$=@`&-br3`20$FgE8L1-Fl!TO8+&p{VUXx1-wE20yEI=cR4PDf zHv_Jc1nP7Puy$38HZ6LakMRo-Yyr}1Go-nX7uqmtG%_-|p3|Dp4~rkJT#I{oci*JO zg@LP^c-JshK3A8+UwO5qSil>@q)iv`N-i4c;ej!gL)W9!dSY1&?ps^J7l#Yjh#pQ` zOu0g-Is8Sxa03WKigX%h3d#ncbU^D@I3RLKhLVD)fVy!(Yl)SX_Wh$ul9lnz5wZD1 zq2ZmC;R&ijrzWo4i6MIFu_TR3XPI5E&eTbhKw@spn^WJYFl? zr%J}eH^TJvuvh!`O)x8$DDl`_zSBBF6?2BGh?7emlp0k)e5KA13l0yX6z?h4t~Wg2 zn`X6~VbAF-GwKx)bjsSSoYbKoFHnP=mw$e+Ncji8aNg)6a5?UN1)|#qVC8F#;M^z& z3?fd3hJPKp_fNhkVTMk-45nW>yYai_ zUFlQg^J>5QTqvIR(=epGdsDJt#?r8RKvV#RTcJ?!v>tM3m5+nZq_xG8Wg#MR;6{qh zo9yv$^@6LE+JX{5Xbc_#gb@A{B!`{;P=U)X_W-teuq|1J>dADGXus-6g&13RSI>8@ z3oSoiAO9HDh{}ckcr*2AfHP^rL41Yt+l%7T|Q6}1NoUoP) z-(1^UQFkc7cr;5-t+sj- zsbK33bM9D@uGMuP>hq+5^Pbur5scS2je!a|VvN#K($bMYdokf4u;@yJFJXtwfGA(C zW!FuewX*H_j`QIfA=3XZ4V&Zt2K~-SVcY;2s`rSkvv0#g!Yz=PHajyZ++vr^QbBLn z4z|A9&>}Jq`DOT-sX^nr>igt`W98O<%GY1AjH5JkZk%Wie$5zL55W|QH7X^uWs~TL zI4!<&@$~|Lo~RyZ-h8j6_46>+0aO1E$>AM3Qj>B?5Kjef2|$D$x1ck3P3i)&9nc;K zv^;aGMFE?P07*M}&aRh{hTGmL(k4sC!o$Qz$XY;w;Nx$_J@HUV_L5|}*Ro+JBXo851m)gzxeR$Wpk!;hy4GEp`%j?g zvd*C)aXfy`lVB^haC2a}E91AhLEg`G+>{BLxNVk{jNepF|G*QMy$Lq2Key!~+1)p? z{*6IbDOslEEtMp!dZ2m0(P6SH7&W?`ya}xQ)baV9i6VMA!J7)8y+xeeFeo$>=De6u z&%FEa`ifFZ9g%j?7@{1>7sY7%FA!XWMkbz$fIzf>f?8qpVFm%6CSe(tw(sTbZ?tRT}%^ zGF`CuN0smz5Yg9!js-6CTczng@f%%UUaiJ+A{usw_=ZPCP-_JL?r9P1J|hvA3XCr@ z{8?37HgFcwacv+1G~NRp3a9(ClhP!!>7wWeZP)MJ9}wK(k(l0j1yaa62u}V>Sq6i_ z2ilw3gM9-<7Y%wi=-ac9;g@?9EVA#)hYWATN^*;AKGxJ za>22CtWtArAcMHo#Z-|U5!KD(>?>onPC)wT820@Yv%i?9g!~0MFW)LNclz7 z#EUcjORf3*=DC?OS(6>*(4>i&0~w|mU7ZW_O~T_8-7A=-WAL2)g>#BQ8$j+8@y_ol zlV0))-0@sIEcDsfq9c7!GH{Ka#vP{Q;Hdm6uEjqOBVMGw&#g+aeqVf~RLnI-fp8fJ z^!dn-?uaBMvoo^wpMs2J9A}zzP=@I`S~y7z#+%6cPIGvp%p7+UdCy*BJY4wMp^J#& z2`JG8A$jTRMQ;5`{6&8HFY)(nGEdCI9OEaUxcmKB1fW+B<29kwYjohaJCiothlLGt zd1C-R8Ho-0KeY0J`O@A2$F1KH*!n)~3Xdb>0C*fXY_Iv{kI58J5$f_HN(KM+-HA$`UAbA@9DJjJw zlvT`3HI#L+`apoG^&m-aA|s<^?aFhJsg%X#XUQP^^xry=&0C;Tf|(Z5&O5zxLvM6j zRlAXn(t#BwMdVnZ&L!VhFi!Ux1Diu$nlIrsPkj1gFMSrCe!ZOV!JZhh;_$sa3-EYT z@+t9HHdQO`<&?qZJ7!R-tjyqddJ2X%g3Z%7hg}{%T`5aZ@0vr^I}4~x^Mxpvt(r96 zd8TX9VAE02v&aoWCW;1!+sdYXwA&8;V=~)>nax}MTTGXoN~7a?+n8>|YRD0qv0P$b zf$W&UXDxV-t=dm&)h0$pd@322Nq&E~G~@-4BsZJ+V?y0l=_F~t)96+1NzIsx@jy4! zQN;^tzMwp%iT=GzB4G2*$%@yoqxPFp*UYHdj=$0zYtIV-7xg!Sp~p8qbOYnQK5LCB zzw|LpGbm1OWpTN<`PNB1>=;8sqmqrI#=cMoyC= z9g$O%XV9!uGkSV48(GO}CT|WNA)ny!!~MG`<`uyB4W@fRf9_#(mPL+Dh_eBFe~{f# z(zcL3DY>M_f^AEm9&ZYsI%Y@8NDYSj2)~x~{Chr~u1QFzR=T}82F(!# zXvMtWW< zHDypK5WN6-*yLvEoh_E}zE!<}#3XV<#aP+OGR?FipVZYxu$w829+tieNeV04g+_b3 zMXbGVk&qDNS~niw2^+~)^4K0Jh!YXRCmu64HtfAzcPoToZM;`k&viH$AJb~Bwy%g(w6Nm+-S{5RevR>{9*-k0>`*%QO!LxVLUr?Z7CqMc%KWDr(=$YlbYrARyL_Fy zPK|z1yO?p?(ya`GBol|-R=ZGMx<$=H!?8Tsebo}DiVCn!>#fF$QT$oGOx3}#)e&Pu zXGqZFs9xUjh&zTEqq=Q-Eah3CyV4zigZFzc!BgM$QvHnnmLK<`04Q5L_<~GrpmCI* zUac7D$;<8n8Yug0fc=UrUMoSBqw8NhdOea??wbWJZL8PHyN~ayVte6RLto$IP}{TH zDAhX{HT^5*@1>zPK)=fc6vledj=P#=PC1x|)4W(NJ#p7Caj;W5=Wd#@)j7}Zyf^nt zF;s|!dOwo?EJg6EUqI6g3;oBj@1?Tib;rLyqG|l z<~F>N{kr9;%4yZqEWPDvw(aBoSGx^$=1bMa z(^ePr1(Z}%@!=y@=*Cvq7-SxmKJ%SMXCvBn64icoqcprWATFq(U3>8 z>}O|KSNFrxm7sN+Ez8eqcVXpy*&D(6-3_5uoE{GOLou)J$mu6Y93Wh~8tt$p`;*41 z`p{~K)oc=#0qMoe?M`W8rE?5mQK*YJ{>6<4Xf8prqIh)N4{39ue4*Yi9>2`U(6H-O zio3qRzdFrbPq$u%g@xfCvvTAAO=(Y~*w~W3AxkIn~{lA7q zvLAvG4Jh(8X652+D?Z!KhNWa`H)0Od+|8HD3uy1UBzJv(kBW{NqXd2>Bbd~3Le~3A z>ir=0v&SRqB7uDzkfOPx=4QK8;;$M_xZRZNmUD1#?@nNO|5c8WxP@D6Ydq(xhh`S zX_Il04p;Ke-s{+bDf3j>W~Q_0U7?UsgM#s|=e)4K=8|t)`O70KIEQqCAf1wzQ)u?= z@?-gex_apzq>Y@AE3oBfwbiE1$JhIq;CB-(hf6Wy-aWQkd^Ha!;l?7Koqv3lxPnkqr&lNz|5F2FXCD>Mx; zQBi-t)1NdOxllHg12vB)19WV*@qy9=IWsZ6J}v;cmoS*o%TGHDLTYr ziw!pUiqQUM2QuqJ`%r6Zdegk|&t4Y1Iw=Rih8=U3*@`Dp1v9X;Qf-i|?HJnBrf2=+ zwCa5Pqw)E*c=K|~w6Gd#lE&}=wvPZ<|t&Xd+IpVtyOMoqx3{XQA z!Wm1U>yHFr8MXZJfd6PNLJA;cAJDq8~pouPq`tjKB-*aZZ9TMQ_n7SBHSnrv}Us z(Gz3wyeeQt{Q!oWLA>dv+i&(?btWOjJt7zk;-Sr2>G^BOIvV5Lhl{%&vJv@+jJNs` z;;~aj*60LwyII*d3Y~XO19B6Uo2o?zvtgzxpATZr=ZO-`zZR&7Cc9rfqF3+de7kp! z@nvnbIy%l{zm6lE=&-qEkBw;HCM~UCK7b2~cZ?)YoRrfp}rnPxXBh0p1eVm$z~|99$g- z!`z9?K8S8cP|RVTZ?Z|;PCwKh1Ivmp+{InoZZ*!!)NGOp4?gS4HeR*fHAE04fmOmh z*^{faCsj|Az6pL9uYXoZCtFlE#%bI}J(3T0EVsHF-VWzI-fL|v1nUV?wW7_=r9^m@&I4!bN%Je3lRlRT_xl2#n?n16yH4mCpMrQvfjZr%LV=)^4|O( z>i+HjFEuHW3JIZ&?6McxDwKWS2hrGvvhPAsgc8~J?2LUdwj`8&8-p>Fox#LlFqZGr z_4!`c^|^g-o#*=x`2Kd@@;WhQUa#kIJdVe4e>7S9D&Y@8)PbdJ$Z<%vY4q;UCfc1w z)9mVA)^|nyq0(HtAEb$2vOnp}W$h>5a^;UckRifAhVOb2~4f8a47dog&oJ#-W)mQhS*sa zk9r#gz1Iw{P48EgB(8~w3ka^bUT`VdWb$~oi4vb21Ap&{W|4u@F({#RO{clMp%&vh zpT|;69!dsuJg6GHk2J4b9r0t)?5fRtk{rJO@b-NNwF0KqLZ`04_pF@u4AahLY|E#! z$4fm93`N`vo3aO_aRmpximR^G3|^?#$2k4cckZmwW;sP>YP|R2`=;g{F=UJ;+KE{h zdjCsF!RZuPtH!b7ld~L97Vn8l*30&`@y%o84QIh6QdQMNze(xKCsXqQVMC-FQH?Dk zXScC+r=>(s59i(}Hv5Egr1NcV#9;<;cRHzJi4vs4eKN&5^ifaW6caZ|M#vz_D6rY= zfUHdXDZ3rwX~qMZDbHx~u#6MIo(eon<+{Y#7ZcA zUccL}Ufj%PUG@Z1F_)=lH3H8{lrn5dyldK}T?Pq@Y^9BQLUxqlc|+29LvG-o_tHv~ zQtV}DM$b$pm}D+FG!PprHKLnWmOqqbmFg)LYJ$piVM_lbq^Ow zwpsbuD+UE+`LFyksRz%rZ4c;t`o?gh#;JZqet@T7s10~w9-||lKNMgl{mt{wNS%$2 z(Po?COvzA2+Wk0AA+++LQ~i8Ta#t980N2=98rm0Pwpo|Dg~tf0)kDwE|0(7X z=#C6~edg(wVc)`Nt$eX4GPeRPH(-t*L_}evI1je?dp&5VIwDzY4m;jQvPg8=r2BIM zGm}ebk~lnRF%NhVT`*A3pK%6e+`oT&>(!|%{c}eYep_?Lt>{UsnQ1C%6?;dp;ZJaf z8h#Uy4ZOx+@lOGdpra|c$@X3jL3k7A(giRO1u&l0DwhGLJZ+8pgeSvrYNY?IJsAbC zFvwADVxN2T;_I4D%G`Fg32aTH@_DJ=J1_BDq-&Y_aoK4m@g9}D?5l(zL(UbMwe#66 z=JN8%Lj!%`p;iMzbvnqsrJIxw0^VBt0Q2CA6)Lc6l^9lT-mzk^QZ4A%X ziL*{gzNt;NS6JFjI^(+?o3(&1BI6j|9T%wh<|$rjgdIoEG(N#j-JMx45Kj7hs~5q? z8@oqzmLd5@dTf2^y@oHAQet#g2=SzOW6MD|ij;a9io-+s{hPGhl}8vv2p{bA7fayD zTb|3~^+)g#_jF_!VVTq2L7|J0==ozXj`EAyDvxk)p~bMBS-RIRs%T|BSGf~M{U6F4 zFo^kV0@f6)SaqjKn*{6&kpg4;m0zZEZbeE=esIE}Ab+~g{}T$$G=;(&7}ULwPS!b? zKl`9QkcSrf_%TgSCg|QXB&SZ3aZ}IQuYLp4;OJ*7C>9@K{u$b_k!xAqXJsDUIJ_!! zNpBT*(j+u=UhP~#3-qb6hqa6@dkDm%%}VsD(UeU}@5bNRGa|@$AJmKaykIffXgzscT&t!x2Uf^~ zFO;mHt213y?GTGcWdnH{>_5A^-6#~uehLL=Yoee1qni7RUoK-2u06{SDxWyO+j(kf znRQ>k$Of!j-mf&9SW=JhyX5oA`u;*ii3*Fyr^1hVZO%Kk?mZQS5EcVx>kYVXzyRBSIF9>uRQ7Ymz{c7`LuUiu4LFQmNfapUW__G)-t1aiA`zh_r0wW z7~3;TC_c#;S<0(5;Ej%oXdZvgv**@1RO~9B?VGZXZwl_(_n(;BN@@0cR@Uw1S$Azb zePt3h63za!e7ocWE6atSZ|en#K7>^NS7)N3K4O&=$-#N-J)NB~Z-0C};N{@{ndE_{ zdbf$oeghwu6&FY92Je3}d*r>GM|kJAVIZwgq#7}}Y0mL+ge+OT`t%;RZ%H)L7LUsg z+=sK@Q59t=kwq*IJlQ;W_vx^%-O*=?ilKCIFhjS9erRZ@4O5l3Ee2NY4eT-72a}5R zwEsBP4ElEN^jh;FJ5~!aTL)%)1DeM+Y-{FWFc`W= zK2;qPXQgK4BcM7Q{*y&=>Lxn7h|+%NVWjJSO*Vhf&~LsHpsDX{l)htlKhFj|9?v4{ zO40U^#`L?oEa7MJ#^z||*XHbXzxYbaLl2u}jnp&l`#Ps`)LRXsTdS5GmR9)HWzF>v zY?Smwl^&Fk@Zr))7oOeXYXcUxSgo;Im*(3h1gR=4kSONDgW!)(J*i*X8Y_9(mEnp#P5=|Q$7jxz|xFU^w5PmA3lG9k9{P|Dlc9wNp63tT1D`@Y=&vhq2pTi zy>h^y9Y1TlIojw;>TK{P&I0fKA7^dz#~%NeD=xNW2Z6rK>~wh25Qvs z=v({CY|KuR2KhP~YbxV({c=Klw~I4|lKsI7$89XId+HiiP5AHLE4`~lVttU95uxpH z>7zF$$#3QTIdQ7>@Nci6ePcsQy@st$gk=WK<=$92n-WjH`NmU2_cLPk3)>am1ym_- zBx$ie&U=pLQTAWRJR>W~S}3JiY$VuT7h@Hs0U0j4JJi2C6+ev}Oqw4=jPkCz*F_JJ zo`vduSb7$n{=`+_;JNaoGj`0&sf{(9a!@%8V~+;s&Chy5lfwuxy&gDWT#grM!MZuv zh9wXJ4+H*rJjay=>VWkLst3w>4;q&Xi@NXJxFJ8^ooE&On4AIp$XyYFO}0%<#9pbv z%tw^I2IcWBEiJ%T-+MY-@<%Xy>d8WA{1x8^(sx^vE~A~KqMYK3jJGSB_e|{!Z)PEMLG>P%+nUo!&udZ-tNAf{m@DcfT+$$xjdMaX4+iL(wsC*Yl{i zAsq2SR_7Ih98lU3n-95QOeK|UuC0XHwiDoG^lrv$YZ%l#OK}|ms)Pcsv;$i3kSu`H zaa#5=KFMS%m!V&3Cha}@QK>jUA-PZl@XG8*ikD1%uj^Oz>N&7g6iT{N{r=$BjE79E z@^0Go=FT6cO1g?ay1;gyA6+=F7G$z0FYuy6yobn>`bi_#NQc9QoD%9Wv~ATJmyd}9-W>Omj;vD6oJPetxk>pAT&!0E*3!S+t8fed3@HgRq<&$hYEPRTD~2azCJq-7^~mjFS2= zu1fd%7|p3$JGkZ3OW0ck?=@L3<;fijy-b8!(Q~PG%;z^#3uitMn{c<_{0keC_PP;n z0zymA3snzXA||BAiGjiQeD>e(6U>`E_uTDG>c4&+T{3(dx1YJeU|!*Yp@Emx2Xw1hnZZh&trwbk$CMj(<^SPSB=c(B8 z_j@yND7n&aC%$da`8nmgib-kGOrWQcUXWDE0r;fo=U5ID{z(a;HXQinG_b%j_hn0< zeso!Du+B!r&ic&*Nmj?{%uf}6DkuCUU!Q?)m3Yq7B{N^B&UH9ga8T)BMsM3@Dg}$y0dq+BaWzbjfre1jXtFz%*+KzAeFIlx+R^fZMjvh4;Egdk3 zZ2umvrAr^o>U*;Mrov+%O2|?pzoYj;;Af`!$wp7IKz$cs$7n59#0F$Ck<4U?6ALLE zc*gowG<0cLnkmBg4=%>J*uV9{NR_@wjf~j+9(Ac5J{FNxF^SUm-=h~A2!|8F!uv`= zt?~e0#vYxP=E}!LG$8c73>*minD~+IVz^lv#;=xI13gr?2W{AvY)VzzcWi>2*5kx7 zUty_fqdbQOj%?3Af^2T^C^RZ)Z=bHE*WKSZ7+Vt@32ysD@rD%4 zr}zxw{a;R>y>E4S-ihO9*V*&&%mrdArFjo*JF3}#K?oKBuHIhqzx>(xG!H zVvk(+OT=e?iIjMtCr;i0i7PIS^@nC7GmuHc@bb!PAEe~B!+ugU0uI0CzhB49*B(51 zN^*40^ZcJ+SoI8Y5UFA{WZn>S^-h^ip1e9^+Bo86*SMKAZn7q3;^;-+IRr1-aFxgE zPiOrZ2)PF@xpa9iUTCuX`*;{~X}l;-uaqJ1eUrUd;pkHQW(8fSM+Ew{$w;<23%RxN zju$Sso%SO4ivs_-x%_q?T3hE}xYKMy{I$j+LG%2B2uE?belRL>)hwe#jopKF- zx&^k!x*nA@d8+C8u#SuN@paJ)-r8zq46urx3~0a~itc$0HSqQBEAIq6-4bT$$`U13 zI5o4nrX4Izv%U1h6hQrb@k=5u?qSTHr}Y2P0;hX5=(OM?^#VNw0V&Tlz73?KA&R0i zDY?-lw`8m{ss@e(@t1-Dm58ra;rfkQ|#Y^=tYyUeHtzyTR&V5(^<#Tc8Q72hJ(}mR{M2SgV8?$H$lN*tVV5s7= zyIr#6#P=N_!%DP>#%ZmJ4x`1n60EZQ%=oLZOFQ^Mn$P7@L&JJS?K-;gIRjQY?c+#c zKAkhu6AE}J=F88?-J0R*^O1svOugmT7p{l3$vRiK5oGdLAvqZ5@g+WH#YipV56Jv~ zG|y;CQrOvXuu$I2FqFi+)Bg|dmC-vC9IToXSdAH(`>Y<}|1<^vZ%)FT(W_UM-c6F% zSpH$G@!uDc4PU*QmT5Y%!uFrcDSUEroe4B#RjP+!#K>oW+m`VTAa7VO}{i4MkBxPKJ; z|IepZGYxW<>+?cUYxzG_bpP*>Q5Fp@T$`c(y7nL5N3K4K)AU_L^zYGsw_{%%?En{& z)*%+8e>|1`&lh5CPEv%=?JmD?{0|qdSb=h)A<)D83`J25q5{fPUu+z=tY2!}s691P z94F$qu*PbA?%XAEVT^7YN>qCEa>Ef9gUXEmsh!3;u_X*Y4cn zZ5ioVQX3kmP=f7H| z)zj{Q&G1zg%fA=clVTBg-=>i?CBB6nPmiwmCQ#)5{Uej{`1D4!XzULs+b?Al4mBm^ zjGQtyMq`O)?Fy7q#QsWZ>|;$?vHN0Y{}kYYKNrYqRA?;t&lb|Ic*U@eWGMxStEs;a zG~wWkoq8`0p*yfhRM(Y{2VPzrv#9OGmy=i|ypk7D`we2d&p+F|U3w9F^r*#>|iiY!B z`ynG7yV48na>|Q_`>=+;8wussA=63$;Zx%L^p7kC#v#b(2RAVfja|@|RnI@Cl}o57 zL%%Up?U(r#@?*<}^~{_7p+Vw0SEkdW8V7_cg`o$tU!IdYYQr2`qqZR90p|KtlwH8` z>u~I=23*jVGZ(I1eQ5~ZXqOfy2r~u&#gA|o6*t7~SI}ff8!$QU#{KcW@+BZ(HEwPX zKn({JquEwH#!AhbD<<4CNqWXeg`Ww0n=N7Dnu?aSK)%FoGV<=Q1svJ2z0DQDdCUMKUklt{muLLgxxBkMq=A!^O<1(< z+?53*_=;4IaXW917N*p9O!(Mg$jZ^d)KMZu#PVh2O{fX0vk^n2koRgeCW+#~@2vAJ zU>}|DtfT$4PD*yayBPNYX~U0>tqWJ30a!K^Y2Cxj0yqlNf4-jDO-nz|{f2G_fT%+H z5TH=+1q&ZcH78*)yjBl}x`O+-6-aA?n-`_Zhb)F@_33}9@ci;r(rR9aq0=dT*~8gV zhvp6`4+=>Y3i{jO^)-M89D*rkJ1=VB5dV8D1lCb64?~W)|EAdNgdBk990j`hu%dSduwwWsos=D(8uvlP4Tuo~2gt`KwM&6>%=`lJ57*%`c_&*?>DmAHVgr4A``(i?D zE!M-01XTZW?=3Xi?rMjbNquB8S{^}QPBu2Cw2dZm$(HO4e1@AYkK}RWjm^TQDB-<3 zTS}$);Z&s5aCNHwwu5KG(lZykxY8^B&!+eeJH;K%39v@n{kGE%#zl##<6E1i3y8IA z=>S=YQLtO+O^w)kWoi{|cm5%a2XGjEik4=b$VO%BzIqN8VOkRWoaWL3!63&5Fh|^6 z!&p5Mx@M_MX*B>7VhC1V;M)O?hvbS{Tzo2UC4Zx37qV1{6_Ixb> zS#NWLZr2Zx(-3gjx?YJf+6VOi9oJk{yT#r2QEYT~)T`X_E~qKAIAFO}UtpC?KzM?~ z@1{C@tOAAvJi8iItFuiz(-AE`?tz75g^nu%;J|Ukh&L2}$!o<}f)@d?H)G0VuGGe@ zA`HvF0dT_dgShqlwMWtSrq4Qn6SWEcQ`HDrakPgo#WmLWlRdy065nM}VpM6}1gFml z4(tt>FGbBAgIK8#*wFsj0y?mAifP%Ye?kp!D-e}C4{9v{-2>qD#0(6eOFSZX zuQhDfaIZLQdOi-o27<>i(jLuYphha6O#fZMD}s|rC%uypa7L2P7!v$*_nPI3JV4?5uBs6U8FO& zTUmIxyi~C>-y(vSoa6oG+J~vGyymnnNqM$M%?RP7-3m_1xuj`Y505;s+JA%`U|HZr z*^aH~iLBOcpIRw?6n3U)>w`J&X%v%4yL7)n{3oAU^VqN^DsArCEFqH2&aYF94|Fr+ zVn<4xt!A=t`5GEP88$sRl znEjX&x(W!PG^mhBGeTccB!Fb%sx!&ZlPSqgsGsJn4FO=gP26Sn35LVk>wz@n1EOiq zo|qa;({K)+orPd!_(J8JcVhOKH94YmL?i?rN%?%Nw=oMm*t&e~%$$A0Rcd;-2ZRP< zurCja8%N}!^1&O=#rfZNU)Ci9+%CQX(z)I!61HOE&l&9a#Z0eG?Scb&`012_`9s@;gPEs3} zi}@!vN)TB~>6TYA(qun=tj4zY`eD;)%&^1GEFu~C$jLps_tff?aU@kgNrF{9A(j#s zZIy+InUwRz70oj<#z^Ave}{t`&qv0047}ZBEI+#72}1by(7@@=OHGxSE*&jix6o@CQk7l zO1eKPo}u+jE%Z406DC`mkz=k=dU&uPsAD08T9l!w*#awMr_=8QK>e>QUfWky;m1^; zJ+hVSDCj%kzs6WaKPz>!6dC&xPta)DdLf6NLU^FZCv9wJ$zA4*1hGlaccC?^n}-p4 zE){H6f%C_2cCkOBTVi`;1Mwi)6+$;h$5!t;fP!rI+mMt_KWimr2Jae}zI@YV?LQMK zadRyqn2mbtuN8Ja7e)CkZHBqUuJxCNEb_1?H0&}Dy!u&WOr>Azk7$9t+llG zbw#~P)m)IO=Lpz6ohA3EY}=#9X5M9 zf98@L5JK)MhJfuJ=-zDr;bi!reNnL`WN9eR1Q2sV*3{9`c?jp?ETJ>ig3?~w?Dxo4 zclWj!o}9ChxWFX*MOVZ$_G6w!+FI9Q*7D?Yt7()g2ITZwntXGbE;&TVn3U&hw}S@Q|H8nxZ3%qU15EvbH~XLu$&VE*%DBo4W_f z61zoOEMdewl2TcJazCy9p-<-&&c-X4@w{#>$)6p!B;$Z)-989muloM_F=?0J%;>x_ z0oKjNrqumM{ROym%%%)V)%<@>b^C5L`B}!s?59Nx_ysXGiqFBYWmf*0%_b3S;>8%4 z0G-y9yz~CO@y!s1xVhwjb(AM7`}1u)+eo#JVkx`nF#cBbgXzOfD>~T8BHrmmL{`w9j=N(xYLR0-V2L~_1^vK&x*S*214EH2Z%r4aqHg_ zYvUhj9r$+oOjU{bV5sGT1**$!_X}CZi_blll$Z z;}Y}E=Yxu;&pmtDrp1m$jZFI~Bm^sxX_12{N9wRbB*gTsYi%86WwBcDHzFzDwv=5Y z_2Z)h$W%Y!i#uEBHh!9&sEM zVR^=mKDGh;m7%0hI?2Q&&Rd=YJG_!9RYhwcWMk}efqtUycv!} z9-vw7VEn)P{(RkeLSvI6YQ198s+w_a8D$(dF*aU~T#w)F&`crRZ!Y=P#Peb2pu#z% zC+gm>C-uMnM1*)ByyeK_T|F(BpD>iO(J{Fbqf~vr9RhPtb&q8Dg-OWW%M1=43OIN4 zN`a|)`HEOGd)W?ld2Rrd_-oJO2#sm{EnAx(z7Q1;6Vw)Qr#b(F_E@ z{dOA8&#gMV#7BG2ywyWLdQ2315Z_v41>W`fy3vo}*INN9pI!MUkU`}ltj?-Iki6`@Qm2~?vwTkm5^}v834k8FfO@HC zB1s9M&yJw@N%g#ti$yD)j%R}y(j9POpk$O7U2#J8XrE{?a4cyM*+!x-(%WuCpq}V) zmk`df#-pOPB7!T|QC!>j?>;?CxuPRUoi&b!%XRbVrNY8t9X_RHCobMbNPZYajMkW`dmerR$H%Pb0nq z#)7GV=kMIuvE1$(()kxWdl(U|ZeLziaL2<9FY}p}+tnQ`MHU6`{&{^QXV^8^=w!KN zAsws9q^oyVvE{O~t${&mC-WX?_@Shv!Y^S>8V}ZA9~XSkQj1n;I#x-yvnbo)n~>+R zEwSeJ7c)Xktv{hG0bkqWac&GM;F`3LTmSd<4t93c_>tGBmTTgUhIV4+zdRVO>KJ^1 z@OR2>Mo=^}dukgsbqeT=Xej)+yBXbFTZ)R`Y=yu&ds3w^VtL)_?p*~IN#+|=oqLI1 z0nj78AW2B)mYL=|v>3v?23(!nc|l(y)#r%2wW}|xyA`MOBD5 z!I>QHe6Uv{94uxDU1mslFYru6_QzF3_NxpO8N4mi714U%I?CW!af6(yrdt7ryEd4f z5|6CwC9-P{#WR*hQD0N^Dk-Sk3H~$Kz|AH}mnq)DSBZtIs1=y`LU#AaZYabKNyVRQm_ zuS!b$GR85fSErF7n3%{GtY z_>%FiT~lY^a34X-$Y*Flb)mpEr5&i5^-Bl8(@)VpxZB59X?qGXA9p|R<)QBLJOuwi z)~iw!yY6+4(iO4h00lp^@52@e_i`ra{;#;B!Yyx@NPB%>b!xg;z8!>*yt$P9Q=9u( z?>PqEuSw?^Ly)>$3EXpj z?LnW`%!@ZQ5r|?+0+pg|3NNfqfe~5?LbcjVPh~?GIilBg?LzO#Q=+m(CV&aL1+jEEoLYktit^f?VB_8S?EVAcYOu zs2b#;^PwT{I^XMfHu0Mv&#(DLc9GJL_B&ExWCHF)_XeZ^iK&gw`xM03e{i~@z2)@{Ef?o^tC-j)G9C?(7JcB zKD6^wTJ+gXSJ^vH9UWm5vj9!R7V1WNs#vn@ ztFss>5Os5ICsaP+Z%f@X717?@T^ceeRJPK^m7V@Pv>P_kL2K}@^IAR za6-fze&%eoAOYMds@JyH6d|9{@SO09cNj(ow} zMeH6v0O@GKFV*9o-UmTp5@#%5e4Tt9>x{12wX(0>%+561eRs?1ErXzpA|+i>ZTkiSzNh$!S?Nt0fAzL3lu6 zke2}ud^iTJFhD-Bll`S9_lPNy%A>#!w3q^}oqZMBnpsuREwY5pKZRjLDxW1nf>qQ@ zLM0`e*Y&g8JQ?Qw(>BQrRfkQ5d9Sk4?ee~oKn6`BZ$iG+kf9HRJAzTrBdBJPXmC9c z#L39h1&|tAg0&Mgzd{$X`J^JB?d{A&ciERLf8?D{+WG=)3IN^f0=ePyNUfgOfn5v6)haNcW=@iJL2=ZZKc+V z>kLCSr>J_COy3qO`kO7BnW&bX9@=$R#D7H~fiXlRH(uF7N767bpV0@J}gu4}5p(Rzm)@y!V%Q$F~T zk#Z}g;CDh0qrdH)Yib>zSsQT<;8_jzS<2Z4u@qPUubxFUoF@zt6a^bphp39Vtu$am zy=q85s}|u86K665hZU8lCs)43ehGGu%;&t2UoUaWW-{si8>+Q87g`EmZ}0ZLmoQa) z;^ptP_gy`NpZmrg6YGw()$MMHqbQTWu~3f5y}+>%Y1(hu{97LmqNLVdB;KYHUW=e? z0IU)D{@Z?OC_%Re3?*C_rx_u&%2H%Sqp;2tV#wfO(*fq&dol(9hXO^&Ac!MafG_0D zoSP*WgsuJ9oH-SkqJN}0m-RI?;EQH|)=O!$o>lEWxKC+NROm+UetF6_2Jz!n?LoQ{ z)dLF*lbLJf0IPF4q6q|XT8yXn`)`2tjJ-;Z!$K>~_C#}0!~$Rm6Z+0`BZw4v^d*G~@qHw%Du;XAAjNBVsld1vxO~ zDj?L^XRgyQ+}?k=OZ$?x(P8M3DQ5!@m(+Tlb2?Siqbv#SjIErfeb@>-GCZLz;GiT zs#vQK$#PEm{s&F1axC{@uT2gj7_Tcyv)rlS?WM9l&}@XKJVp&FZX7wxoZHP2=`aEae^N8= zMT!A}5E4+f>Y=ZE#AT7?z$!pRVe9yH!we*pt*&7J^J&QZUW6L%&>zIBI_*DA4GY!< z5sTadas?H=Y~Hh%;)C3K?=OskY({%AhY?YTo@i(a+cdeY?>ccI+dLV-!}*TaL#Q{? z!8j~eXxD9JIZE5g^mATT1{^3d(D~GgOi}$wZ~!b&1T1?Hk3+E@Z=1${)xv<=Zv3?;ZW9riK zLnCNuq3wj`Y95z@?1UyMU1j_E;<27Cj-&owou9q!6434PO+L>MXYcMRcC?jsZp~K# z7f~p+V%NOVa~*Ai8drYuW4AG;iC0wr8DT_e&OQ&!A_r*P9fyC~8u+5Lvi1~&avD~~ z)iqP=acX6zj{V+An0B|Bn8?FyeS0-QLkTX+F&5bT^jCP_0EK=PdSPN_Am{pky)jP@MfP01Bm=^o1-@``1H9$f2k+jZDCDf zQ!G-M*f1M@vjPHd*lSpxLfYeYnFJV3U&wdAvUJHS0D5FIs@JXY{4i``;a zeRIqYi0;7$c9xZ->?llt*L|n$98|%f<7CGrY}I-|%4OaR!(l?*+xA8#XmVy)s!d<0 z@His^hgI{`6ATfOlIki-AU;Z77}92^=+8}+ZDBY!Vqdi{hW+v8 z^1Z9SURgCA8OZ3s!moo#rn=q6D=7$>0OPwfzf-)Z3&+`z(uX@U9^Wr1&Dsyjwwv-Q zzm&g66qs}py|5tC_rQwktRf?8rEm`Y6H;mU5i`^vNF1UIkZhBSdQOKGT>_P0KcN0A zle-YhJ7dFAvqp=j#WNUXX#C&rOGr^gTa2R2edk`9d z#O%~`Uhd%)Q8W5^M3g&gpvmcXEl& zjKHL4$7G*KAPekg2hVzwh$R{=3>)s?RoG37K#$z$L5-D!tWAD;#$@y;NJSPuXzr@L zpo1fsd`ikRuiW7!H7t{L#XC*E{Pm_F zdF3ekT=Yh8F~1rvTmN-d%_!LWy87JW*fTIv<2)7qVsUQ%Dw<;n#9W)!#DK7-vN&D? z6haOiY4=jNs~_Up0e{_d4O`>viGzloQis^K>uKd?kB^sIg;6h7+YV%z z!mQ`r*^(ukJvtEG<`9gV>@YY?O4XEMsfZ5XI8vSQTJC^QDf46?*s1B~l4drwC>)RoJBcWzBqXlLgLBUiYF-$_Jty9bC+;gu zK03_julL7w%_fMlWX9Ec*~$(0*MS?F&rXjN8(9G#g6oTh<(Q8amS0;=Q=FyOluJwi zYEyg>E?12u_#MP8`yUufvF}eXg>%e2;XQkrV)=|=(4)X&k-#^o$3XI_!e_+;n1 zaifLRd8R#b_3f>wGN9@kyq#IN{%_vBpfd1XLdE{t0sminM;XD?{d0DZ=2zEb{`yA! z?_ZkX=_?_6-a+)V;lKUZV{n-j=m=rwd)NLtociCNo&N;%;s3w;hCdmWEV{_$0!Gb0 z21N!-Jd9Z#KriMkJ8_eJ>d+-k;f&>@f3kfaw}u&)&jHWq_qOidUL?r)NHQxjD0MoX zKO=jQj=szIJB|)}S5%%nDXZ7mPezwcUNYWFA;U#TM$B~@&AJMg zEY7!46ms*v1Tk;&t04IG#?6GQ#iJr06UqYn*8~@s{QVRE)g-&k7<6!JS&zcNb!jk1 z+-c$-CZ>W8BwmnV)68U2eL5CANXKcLRm=5E0;+)~f?ex|n)RG^&}_`yvh}WI9hE1gHB!%$=cJoRO{1p>z%dDK+dgZ-psPV+G6u4f)(qh*285P0C=OE%03HrI&F;>S- zWcR=Ohhxoy4mdONUts_(1f>6J$zyUDp>A416yeSwA>{?6PnvY>&r!e2?)pvT7mJWx zEq56sbTZ}8oD4cRkAs&IZ8TI(V*c;_B?;M!YH*c3YZfUYKTGp3o%-@4xPG&7$XiLO z2a`twP~;%>?RFj(PscKd!@?LI6ss&z2L-9A|Jzf(oXN_e?Vg$2cCn<81j|GZRUiC_S|?Y}@k4W{F21WzE=pYVv+hFa|2XB9u@)`<=dM23~5={QUVU zl2W#LN_=&6QIN`Pp*U#{@+dv}ET{bu_tX2>Jew6!h>aEOp3`VqOZBJ3M7v|(9Sw*{ z52azT25UZLfPQg?Z1JC7*;(Dr^KI|npHCLD3T-^x!(Tf2_^5*GuGj8dJoeSeQ~L@p z&1LuhJi=D1Ktq%M%Q6Q7hHBlj4cr{vK0rqnXa%m;0uw2@W9LW&Ley4xRy+BsbDT=m2Wo*m)6C}}_mg?mapMcQ^Qn>e&H25q` z7Z$Mex%S?_)2tS}MRmr^wL3i|!vyaGUzuQItX+}S8=r+STKmY~mvz-_Y4EgJTQ%0h zPDw7Vy;c1>SrT1=LNAZW-gz*+E#iw-jfdr*$O;ek!T69J&FbGIM;|UMB0GG@I9vAK zUM5iK$F0GZ;1AhL?;6XOdGfGg;RY^4~0MHT)1?F?8;V~B9jLw{de2F2pws{u`K0C( ztR|*_OrRPCAjEDTmI{j7fLVJ;PZrERSGq;|ZaWNg%=}d*{qLKd%Z7qMCZiTu5$jix zSV#Ow$;lGox!A~25ZMD%J@2xHyL8M%XMj*@;RJieXYD_&BYx`C?@OBZ$}RoxyRwt{g~ez3^f{MiLA*p)r9EEMNgeV z16kXvtiz@i0p(N0)rDA|A8F^W_|T~}bU%gZ?LlioF>OiUC@E)ASwFUF0h9jNG2P1< zgbrUc)vc+T3Os6T1m5UeKHy^hx3dkPKlA^bRCt;D<;&xIHuqsIaS$V31R}G(Z+z=M zE|`%XaP@Wz(9=LAQX!L*DjU?42bS=>468=_Evpi$&vO_>O(9XCy|9ns&wkhT$D}-M z=0DO?(fOo(EBFhyS=oS~*@jiOwsdu9{0$@Q2ppTN##CuJ{4muS7=EFQdEjvp0w$Ld z`C6mh0~U`s|Gm;vhFUnEE*SRIlazX*rdFLM?ThM`Se2Lme(9ECZF81~XHs0;9BOeh zdS_5Ykbifj?%qkMRUM{RN>yv0in?@==Y@RCxOPlo zZI8#w9nKvE#P$ zP$ve3A`%Ag@f4w3u_T$g{q6m-3 z`g@VyGT=VMfz${XNF=BL0EP*3IHCEdi9B@AKi<^k-LZjoekZ(B-4C?p!K(tO#jKb< z_4CG4QUm#lw;1JgEnbijaHK0qy(mfB-TBRaZi8Gpd#vgoW7N*usRb&|!guKsd=Xe} zm%&Mdm4uTyd*x&(D~qGa-sCz@Yn-sKu&F_=bMpbR@c)gt7Ve zoh^$g>eSN%Z;wGD2Z@na(=C@Aa34ek#1 z3{nRT`@dLMUUwDdmEtwP2TAQ?~g9r|IX&Kc!LZ^ zermW5;COv78g=g93Uq))<_FOB@Mmk1?#rWK!+sU;AIbo|JsBlU6A#vU-q@(gmou7q z-7wtFlRfx3oMUf9@9ygJq_Q;_8kxHLxd$pOl|ZY(b$4g7yJof0o1HQO1Z_Bo4Y>rr zm81k}Qd^d0E|&kY zIUeuE93Bt?O+dmN9|z3&%op=$ga7y%#0M(;9W#pCqFANIfl1W6KFxFIZgR7A8hrGlNa*z7mdZ-GAX}&2eeN4Szjc(png{A=dJ!A-|8+P2<#*;m z%H^zWh&HQy0I6 zr0E$Y8C!5 z!aS2fn2AF=M~r7(0M;|5Vv83WWoz_Eyx^Tl%ZC96JE4_I)T#CQn)Z;2ZO1QI6}yzv zYYr_rbEeNrlzyDBHg!Bi`@HNlmThCfI#?L@RT*G&!VCuE-gS=tus<&RXDKCLjRL5s zGm`C=ROAsHU7*caEiCJDQv-?YrNGd^IP^5lmboS4Kc2oP$mevxWXW%mSYeMH1!)~% zLI#sSk^Kb5OXOv3&$WN~>CYly(B6p}`NyP5FhVoxWn`a>2xrs_Ir)z>GPrkwwV448 za{24(e;j1~=6(wPYsmj;Dih82pJwR~x7+{!3;*Ww{dLX$zqp36CSd*fH0I72%JJyK zmxQ!5M;iNEDDV&`uF^irs}MKVA0w=-6h6{slqm@uyI>b@{ZH=@yv^4_K-3)5@oJ5A zHJzTrm8=E9Ke!sC5itCYzJs${M!xom{Ppyo(S+6Jt_sUh4(5Y)6vMyV5f{#7bo9%< zrsUw^`Q>trnaLnh+3JN!0*;(sZzxjOqUy=dMkLiJg z`WWWk^=}yjqv}&D6s%QjV3Vwz+7M~Odj7rHAwRt7ZGB?UoV?Ks-+ye`IhoNFIHAn7i*9`335Ts2Y!RzAF ziaxurf`t#B2S=OYDRurItc(+?Efh|zBL+P2+1;C2!80=@E)%9cXqFWjNQYJHRe)3pkPYKpU=! z{{?MGD4AEI271*!={T+Lv$;I|Djj6K-2Hx4 zeoB6Y{N0i0@or+2JVwFPbgUR4RYyl7W{wNEv?FUAe0GaQxFIRw9aHIex?$y$Wah8_ z{hb1_!=HK;hiW{jg5Y0Qsv_$vOU0(T@>d`-jQ(m{M+cx+bNG>T69|sjpdA0uAySUh2OurkIt(b=L zjU@Bp*5;_|?kqucTyCNds}CtiTi5fAUkQUO2Ee#81mWXY%=L3h?u*Qoq`VUrQ3D^G z4gh_9tkmy*kDhm?W2h%klm}EjI7lkF!TXE_nPppaVEtXc@u$)Uxceml$|P6%;hn1d z0dlmr(M&f+>gWJFqXcA8e=VnEdGasdOdrUk`wN_5KII3N?pMiQA3=3bPtGNpIe~!S z4Q5Dj@<=i=rBAImvOjB#!(NF_rn^X|WCKWrJsR&q=8k06pqSYg^`|Ey1Wsyq9A;i` zr1FJX)Ee!2M%Ec`Rn&^^t1$QU?*ZQZ2u^Ovxp5D%)4J1Zibvd5lC80G4Z{!R z;i~nn!D{eN2+K&}QM2@u2cv#|qI9`OcSZuo*4R|l)xz@-)ZZ9W0$n@GJ2%%T&`TiH zY=nrPa=v;$x;&ho_w*QFicXLyX8wbHp;>*PUVWhYv}oSfe}3u}J~QU&lC=Olq1s6% z-Dm!iP6Ev1Hn_hX0TI#ZO~co(zrQC_4#S=L#Lw^`(v(s-t(kXfhVQBQPKsl!JM0d8 znzikrRTU{_)lR`APR8Sv^le{LhEw$7QWih`liq200SU6zH?`!O6i4Z}3E!2Qp%#GU?1_=;YY>BpEmTdxmR#;S_bfHZNa z!d}0lFXSlpfiTp3I&Y`ugBtYVJ3B}CM2Wa#ss zh)%&!oZ7^Z%-m$qlti1iC1)Mu+m^+uBK@^0rRLBOoPm5g{-Lr$uOU&Ce>gja`PUA{ zn+9MLq|KpNmsGPlfLe^ppS=y(Xrus-YB2;_M_3l@PfuU+X*$bmFoE{NyrdEMvj*e8 zfZGg2z+b@?!1JDCj#ZFk5A+qFIu*CND)1f&z>b%74{Sm419Vk)$J}Y-uzT#aKkAp9 z(oiRe@6InH%6-=6P4%o@q$@ClpN_eiXd|7v}p+u~zCKb*tPIw8^iKsOQT!#Mc%Gg=zT zTuM1k;uG$Hm^E}~T*Yrdw;u+|{?!OU;Af-8Z2C&x6%o4m0_R;}aWdH%VbH%T>I1yXWs`*A=;zjGjoeRG$ z67+7OSS|J5l--cE%w=`H*|mI!S+GEJR){cXERvD8d1-JSu)z;oJb^P%qlYOyK>~r9Db{%o)d7a${UURX^Vrs+p`MugS0FE+>Qw3DBuEb3l95S`yYs9jvO}I7E_g* z9w;pp3ZY^ePV<^vnjeHL$6i({bYB1|ut{EQ;=D>XpYd2^K8d;~5!7i?Rp=Z4;rz;B z@)y%-C(X9^ha<1C*%x7-?&)q=BcS45h$IKM6fy=;Z8KM0Hyx*829oXY|Las z$d~P_Lq{cW!Pm&TI%MCN4?Po+Ir0HFAOFft9(q$d#ZrMh7mHVYUu1( z0CMFJ_|ey{G?}#TLRI7M1e3hm`E!RiW}4RkAHoIp{es%gpts6D4Lt(&3ZJR?UWp`= zn}t%)RRY~!1LW(#nr-#!XocV%)|*7!8_9|4(3zK4d1$;QUfUTy(vFbSdy;Mr2b`}D zoLmFdq1_teW4j0eew~e$^>nIAee?AVz5A!e^fii}JO);;^HZsEt)eJTitW+BulVN2 zj?CE}j=ry<#VELcn6htbjtBV zB6EIc*VqB5wTjTv(o&AX zhjy?_TZ_g!YYRMyRk^rcv+PIKDx-dAk90jFzBgkgk8YO04PSKDsk&ox;8(k*<2J~8 zG@-NCETZEk6jL$%Mk&x9AWIPy?Ls71hzWdeTI0U%Lxcq#wa-_?wYgOHaT>#aSsXoq z^!4rWw8VdXsJkqzv3+tYGYY}J*- zJps5I`*>jrw$rwV+_5-7W8df9V7^h?+Hr%gW9?0*6sBj!_1aQOAekW7(zzV$I}Qqb z{_*)a2h!mIjpYGIoS(dQ#7~27wLF|j{O(iHl1e{!^+Tj1PpLsVv6_1?QVG=XY!P^I(JZ z9_PnqN-66T~j*u9l6|*<*+`uK8XU z&JH1;_yM{FVwm@9@BK3lQ*{%bxN+Nu4VR3uZwae9Ye4=Uef_W-L-YjdC*c)$Xlcw7AXu(+SvySiyl`Q)$9m$r_!o1! ze5$f-08M`2usiq+yitaLt{ogdgD1Cvj>u3?FTps+`~lGTgzU~)S+qnj3dhE!rXCw# z1HNf!aOMLj^!xS(i7?;?T!>B5FgSDv@PtaTArj%Aor|}f2lC#62;~|DhVA*zGQcm? zuL3Qbu#0g9&30Zk*9PDgkXEik`8vFQ%lWyFvM26%8fHLAwp>$0oENT|AvF+8!c=Vj z0zI_thMzZN-(vlQOo{`Egdu>(x_R>WaH{_fT$!xJ$O0MH{-pE4SZ!?8^kfAUv+yk+ zgmMr^4d8L_HE;1dP58))pyY}U&`UoLb@ZkxT5jK~{9`%GkRso|uNu)WJ;wBi>oz}k zUcIGPS7?v9;$>bvXP6ZU{wdSzdOf_A5bR|L^iWq4;=lfQah5*BJbBc91ofG;mi@x< zN(9gl{<5q{q-?4M`Uv3zK?4I?9$?UodAx768lY?X5y4S}9?(x;x$IImgc- z4d9@MhlxM^c8+A{me2n49k=Q#iNL&uU+is$Krhg5P8w_Y!Y2|9S$nY+Tr$y=vI&m;S>PWj1BNr&Mci1@86>iR9crUji?OPvP(dK8E`$cRXP$b6k| zfnNUTcUBaK>?pfceIW7C?z5Czwin9NlarUwyVOiylQm`Gj}e*nSYR0>B_3HKjNkeGz-8c7<6@4-9}q`&$>Hd@3O#fp`Q|LQ6LLScFpR^9F!rBmf{dR1PS`Pl=bQhcmO!b zT!q99z+Ds{Nu?BV?dIzoK}etGSWIyA{IcJC3$q!ugdUUA!=Sdf#P!kXYsr7a^E0SV zfg(uemW;bw`eij$RsuQlHQEt71rI=QWr;F$plUW-Sv^}X*(Ax+&irhN481PO9!7p| z=1nH*PG!@o>V%)&c;!zf_)Wvnk$@K2R`0Q};`c$avx7o7H;4%FL}o}o<+)$jv;{Cw zt4**W%E82(a`CszE@2S0q|fD@!A+1#76}N>oTU03^z^`*Zpwc<%2v&c zME~!jy|dBKcRSW?2WBkI`=H?eIurlrpUU8Tc(cP-c01}*u0ySk`fYCON+WFx)Jo1y z(URqAu31ny?9x)g+j!f@_TpvkekRWVl->?61ZRIjeA-;=gTu#F?xe+5C9u+o+TX0; z`j%v$B})}Iu?w_QqYc3)i$FP3p}oat?h4?7F+f6c0+RQ#r8`6?jX|2UN1*bXprbGQ3&jnZAjEGC=oiB;J(0)qG=>KngOWa=yO>{^pqjXD7n~TY1r>pKr{fm^%Jr>!$(u>I9|=R4@K1 zSN`2EzrlY012SKf8cuV7Utg~jI`dEq2dRfS7I3;htM^S`GV#z@NxF4p0S=rA?-Jk6 zg1nRkk}m=XkoXnS-~LW^IWKEGhKJ-$hGrXFsFE{_kXFx~6!R@!Y9nXCh=bG%|2OC< zFcUAC_X0Uz`g)ztJH&7$%y@#%Iib+Zfdu>K;6F+MCYtBolLaFb9>xZX*oGDO-NPP% z1!2TR>`hEZv7^6I~^a)MQ>#I-{)=0@G(6I`59`HfN21C@-hdVjKQox(5>XhM|7}>p~ z-NoLk`m#bjC#~7DIF;Yo`Up~fTe@f&FMl{g=Lu*ZtwxNcYGm0YTQ1pDY;C5a6M8A< zwe$y2x-=XBvqHk+>+9DFbHKdN9BBQ5kc1h?|E_JX*L;K3gZt=?MY24}muoDY+BCi~ zkIPN!{Y?YIZ)YJiU<#b8>?3AB&%<2?`635nQ)&y@nMms5FY_#tMHyLU4A*EVZj`5y zX96~9su{O8AkrXMS+_`ZHo#ZC)g+u(ZgUq-`oYtdL4=ixL+}Ty%Khlu4mnMno57Bh z*^B|XHv4$-3>@t#0O)(=Kv{Ns{^lZ^hb)6hw6JIp;B;NryP=Jb!7(2PT$t1&Ymp}| zY0RiN5r9i!3GfEJg57%O_G8fHk}zM_?Pp^^E!;2(*t|*i?>uQbbW9)N_HaMRUE`TK0H%I7vZp-;L)K=m3mNpA*Bp|pcLy;51D=^-IAqE_t;Uh+gh zwD2Np+K>+NJv!XSafH_ulc>Xqw^t|_h}{s#K9vqbAxR;<20jHmWvTY2_r`S6dXx)h z+1hwYMu2ja=~|@MsEE#LegD0AmV(`s7BfJp=wIDa#2Z8nd;=yORu$J-QxMVvHo#Slu67@{5&nsyisx+ImBrvLq~-!TwE@NV|voHja|aBl{)cq zV%z-rf}IV6F+>fi6Vl8p-58)rLu~;i^=yKQ!>>-h_v=8SPRk@1szcXp8UOt~FNNX- zxP2}~*Jo%1%e4-{6dr=2e~o2g;&FyyW41w~a?sdf*dC>Y3I#o5+$s>Q)CJ4SzL`o? zX{4>^>(^0fyb1Y5%rvh^=U$>XU#@N#7O1s1K{6sauNIe&OLjS-9y}a$ILqk@yeIW6 zgAjSFDit%#uEEZ}+@sBi(s;6Y764;Qg>fF4&9 z3mt>xeEovz`0ye1MRFP*@+(qA^GuX>^E&I`piOq@Q0^Tz_{opE>&q3P>_LwolW1}@|2T$NmZ^fXLD{zoS>o>MkMZ{wk% z(~%ND7@c@`b1BB#PxoJZzA5T_XsPL zhYhwT>E^_7c!ouWya8P>4O6~gjmtfwlFS!8I~n?oHH+pUAM|5aNVWkTH^L{=kfl?d z1zis4(QD-sqm<(ZeE-&`ou(FkH|Z zMtGfSjCw`xtbT(MK5Qz!AOVYjs)``AlP1=Tl zRiHSl*3&{FA73DNkTEBo3oOdtVWx+rt z5CMteexydtV(%~$6&`+5;)06eO^f@bpE5JASg6=qKorqCD9rj{x3935wT-8Zmv!RA zjMpKxd&ifB$%9%iYvQ=gNB7Y$Dug@yS))N_OD2y5z?A1P1&fhmS^6NOu7%AYM8|lU zy(;K16Jdm7(ZE^vg+8+ktX}UVW+Owj5E?3hJm{~lKMLAw)_9p5?Vb>bL(|4|YG1Ex zw9pecAONh?_1O8`(d8FMd&{5K;S*+r#=F0d1acng{Sgt+ybM%H{XY)Dh8B(cIY6{@ z=sl?=%QtY+2;^2eq-`9&Cf+wH)~lVkVkJa7g=aG^@&8yfQavhUTvvC)SfJ*yVeak? z!->e{&%mo@Tt}gY;_i-%AzkyXw#$9Rvk=ptkSzwyfqnjs&lxeOdknf-mqeew9^+wa zIKx>M62GNY6*8I|lCpe7+=L-R7waj|zWiuF$j&=OH#b)!{bR{l*{?%VzMGGv#su%M z7dyFj_@7V+&Li1SFDsU{M(aw(Q?k6`L?4{UOZYAeY!B)@0tSdrGA}r(;=@D&eWXvm zu5&y_az%=u>2xhH-v_qat zRFWf>heRg>in?E-5m1S@BFhoG!12W<){s@?3@*lO%*1CdUJGD)Iwy8W791j-yaF@R zZ{*~Uq^}`3O>xEXtqc6=ga7dYDBm7i3LJp%ZqE>AmONx$jR~)m(+%)q0;IbX^&}^~ zEn|I{G0sG(U!)F*WaeF@&1F1$h2Uq~m3mvzD~B-2B4(|&kvy=g#Z=HLxuaM*yw|*) zKTb8{=}|*?`&vZScgJPomY@=hn{A`72F^$U?Qv_%C8&`ue;@6 zLv?h{U?sV7sme&)QE+`o*e3b4z!Z|{i~wto$kh4u$-?Y_+_BPZVtgCHBQ3OE;{y9E zFiOYa=!>v)oK5nn8;Z_F{ZkRc>p~EYc({?4isECqJiD>)-kT!~y=ieq9bK$oGjznG zR7J5xc}j&qEohMc`~#}6Ii1lli&JW`O{)Dau|*{C?FL5JGo5fb=D0VaOLdIL*tcSt z{uDh?g2rC5{Pf#I!M9V|e2WGgDpRbKfrtWqE8&%6#wL{@Gq}d$v7m0-SlG&qs6TEjq>worjb;x$i}tD+2%T7N#64n!pX@AyT*o|dc8=B=CLl~CY4OhcEd(y zxw>F=u6a0GS{7hK4gk6N%N++NUU5}+T=!K3N^hVgRpjV0n&Wzymp5Td7#V=m*!KjN4W(pP6bOt9 zoNGPS(KA6^y7L_;lD6yNAbpRsP)|W?q6_%B=}g=|0*!ka5LXPOy~oirTJaJTqUa>- zzTkk1ECDObSG+&`6Q4C*u+DL-G8)%7jPrut59YEuzf_iSfi;%es}XS`xb;{!2PHZh z_trSMIJAp*BHB(Y{-=CQDfVIX4AQ6h;K9P}XKEU7t%4IV-En#eCFoyJe52gr?79+r zSRFlM;hqZYberIeL%sJ({};8Xwi(Me*h(BUVA7w-{&(0}3U&|8k~=j;)F9Dtb;spx zu;@5(!C-|!`3|6xPk6nL=Iy0>+_d&py#NNESnYr?o5Xr>X^(l>p)pgpaG22`+pcX- z>+G=STLNx9vY!&su6ovWfv>LyF+$YhEApIN1+QGKujzX4`6t*OQ3(LoTPF41bc*r8 zpOC3w|pSk0j$H?;KWa@_EPcZ@p?WzJJ|I5HQ99OX=ESy?vyj9!~h-<0WccAr;P zXHtv7^OxIL2af!~MQ_}v7;KNt|3)e@8l$xGo^OHV_Gjolfn{1POEQ6s-;ETU)lNPE4N8sArdpn^ zmL52?4A0H@Ko|myXXZYrd!*Z^p)&ooHUv`lxoxbj?3d~`!&vNCA=)HC8D#^nwC9wk zmAA&=P1wRYk#nr;7^%slc3)b;;cpyY^O~@+-Y~H|2rErI!R3GF`=OAgHIlPHY&_6M zjhCfOqNaHGx?Vbw6-f`4hkXEGp7xWjF}2)%6-oZ=K4*YO>?DI!!pyjKv%9!Wef>Ub8X~Fvp#Ys9W>K*O;}*{BdfT0|dsKG)oLaj3V5pUn!8iT;8~9sVW*OF&sjy&QTsw~}T}Np*%E2!cszDhcDg)dIkWMr!GU>B8DFgB( zB0bVs{Q`Yo7yF5u@^_h$bt|i+pMd7U!A`5NUj0zqa=mOWZ# z>vzRE>SJiV`q6KA+72;i-IMcKKheAYvE&zlQrJhr+71%l0vBRI8Z+k35@maQ_@3vj zvoY@Hr)byiB(vUj%VzD?nlk>(4~z1FEMf%fs@vq1)_;MAms+ct;mw*YqO6Gn&^7?v<72hk&)@~d7WY}MZYBu*8 z??aa0G#>%LlkouupV(&XjdCvOzsA$Imh@G)-;{gMy=M>Hm@Y0R=zIDwIP`5HlyjI? z;1y3(+d147Y2B8I=H;#`fjH%0ue4mc-(lTK!GS~}&8iN<`391Usf|8-XJqhVi0_h^ zKdDQ2^ZOn8X?ztO-?Dep?D{J_EIBv+OChKK3k(ImtstYIPBN|~dGX^W+~<$f?Ti|* zl*-XE2)=9ON8*tQuUt87&?So{MOouMH*^BMq9aQv;`*#$&Sq@ZhrVIw<-2Z8FU~qS zp!4G2Ub4EsSUt#Y?88~bA;$^M1jU?IkPSMnWCKamjdRqSY@7CsyL`+SsV4$w7nFgN z3j_C9=Ggvbe=9X^R?WgZYBaD3hY;!nzk?L^FVmlJF~Tr(b$2oyU!EI)V?rF87P6dl z7#CkB)?RebYPEaG0M%#?BQaG!BZ5xgdaG zVCJIXR6?^iJL(U3NOv{HMCIFif9FnQILotaau~p}Ueh!Yz|;N=>eY25BW%hl<7?=B zM>EP8(XHelSt}hlRqI?c3ysyp*O@NU8XGRmn17H{J=)GLeHbHylS+heVmK>^K>3TQ zr~05*`K;!qdIb{2NIZ8peIMK)l1`rWA&B;NeaTt5g9=pI$+kc38%ws&>W=NJe>vE@AXz?SQWpZ^I2H7>#fKv+MUE?^OksJg?gZ&6Uaxx@+F(HS>eS%|?agHK0TXXBj7o(Ov;s z5{Tut1ebZ2RdoA}Vq8&x4={#xoNJE{l<>w*l|K#5Y)8v103yUN(z5l9tKp)e(%Z9E zhcOp#HU*MkH++%VF#s38aMnLF#Xbd0&A>rgAB{cdqQD=Y>a(gQJdJg4Xf0$M>-QXs zjl;1pCpNv1aG77)D4jplnc!1ko}zEawlC#KS`JV;yZ|b0GdC{Uiny@O;DU;;30LWPuU2i%>yk+5f+Skiw$?a zx|C8Wz@{6k({KvhbR`Ok$npI|X2{&gwoN=K4ni)h2 zKD9Dwq2kK7$aJX4yJum|;NPRFvi^Niqn(zt8yr(tay)h0|kzkBkm8H45_V2Tk7{jgG>j`zERG84GPbcXycCs$go@ZDJH zZVt+6N~eG;A~-v~uIx!x7H!nvrcTmIH4x7%fOjLfb(PKPZfgQq;EA6xp=Fc0QC)1g z0v+&21=(A6TOAYeRrSZj^_=bAW5Gq47k_j&P_SYEOnyqVz)^5M#|Hr=txGyU#pU&O zS{V^m=Cm!1*^OrnbjoSixeP6GIYD%+JUV(;{<2q<8--+ ziX!0BB;9@l{QC{6oF+`XqCgB3Ot&hZM8P7t-+U|m4QbaVZEQ_gN7K>9G%?8tS8dDn zPGGGux>hxXL-C5gG68!T7`_Z_IY`a}JQq)@ADr>pUdHEp!NexS&iymWI4E$5JP@fjw=T#16P|lQH!>qrKs;{?q&Ce9KBz@{mgG-_Q;e)+%690 zIbua}Z}U2q2jKhOVpa^3-#yCKeb*<@O@N4d%-#@i*D|bUd4DtcO|QoURSSnhNW5DQ zF$kbr7?v$fYYdt&L@a!ck*9z#Ruq=2OoF?+N_xcvyMR1Nc~|*w$QTR zoAywB#ZlSkb)Kkvwk`zn+pLY*AD3)#dKqr_tsePFK(p&OxM*1=eyHD`i{m;GyZUM2 zen?2&*QJ}?mGjP}6)AzJnvf>0%;J?PtfT1)@5I3>cRKocW(e1^T~THPoqt@pJj81t zppcvg*y!8Vp_rqpax;`NGpV_2n^YPLy~$co)+Y%R*qNK(n+~@7SyW52!$!gESwMAX zKO-F7aR?CW59_|0D(U=@%KNKRp}AHK(mNnSDVPg3tE@7%!e$XECy3f>EF9jI z&IL(b-CB{14kTV)q<3ClIPb@cjE7s;0m%$uzA!pYoXvzEnk|c^XgXQ9TXAZHNASP3l^R;8^#%RBx1fK!T&f zi{`s-_WCg7v)Ke!LiawJF1roc{6Zj(IDT_pSQP883pkJx%C5}IuGg|68W%jz=M)2z z;#2!S6j!oS0(#E@t7=Hb&e>OiC8hJ1;8Xob&XtJ@Co!`YPy1nsq&@oEgKlx&Ngl4d z?~!TMnRKT4_kK4zX9^u&e9ApIsPr4W-099|(xyE^d9gX+8}Ns7pS)3FSHqMWrJS?h z)IN~-Y}L$fubupgo3X;HSA}^*{%Dg9tdNKiy7)GOrz(_G{< zK${|-%E!#CfAUt`((O`A*MQhEPDkjgOU<0ZIwHIGqVPUYR^X)ZnpH|~ucWnW1?(L= zwj!%)_fn94m$eS0?*-8l=A_SgjHSkdP2!ep%4mt?gRWO|QwZx1YJ7DQUcB>AAaH29Dq?&3~^>4=E$gU5d{xAOeSmvM%( zoQQ0h>D-0_G^H7kA=sP2$l5xRJO%eD{`kAoOt>ZhDT-)^`}Hj~aPFMu=^n6jy~Oqg zoq8?lqRR~uzf-sRIqG~{6zhVh0OJoT8*2ggZLj(Rn2Q?V0jt~ZWspYzujM*>vys#p zTuEr!-)S4>t^g*SiGgi-A^GxM{a_{d)r-O`{i<}i{C`zW8Fd9%Do7EU&2d$Mf)%=z z-&#fDN|#rA9Didle@~vPNx$L1XGE**pIzXdE3}Y^Cf@FQ25+9XPW1^^K3n|?*4kf% zt^X3sXOKf~nSN^J+MNSuq-br^XTVJ$i7Vb}aol^t^2ciZ@WplDQxB=Pdwp%MrA8k~$gq174Sr8H09pbd&)M$Ow!Vs^hv97!7<9=lezr6a_>JU*9o3aT>umxm=oOn# zd=#tXC?F1S{*ZJW9Uvj2KlF#rLQWp<76*;|`u;wW?I*j}_>%y_4nOKnO-4pW1IUnc z@Q^z&88ZxgLCnp-tM{kSQ=?_WT*n%aixTl34_X9$*qNU8S`E zyR?`C_G-(R*xEl9UT|y4+yjBZ(XPe1e$Wz<8d2C_p6)NP2FXUX`}8_#VK)H^Y2%@( z--K>7Xq;2n3i5;Wlb>fI*Fip+Q%8c3 zd*c*vp-QGMy`rB8nd6@p3#o|NUBMd#m`4bwwN~Q0hKR99ryS5B|zdsRU&8`tKZxcDp;KZs+pUdY6Grj z;2;5X%iQS_5GZ$1jajnPZLZzC0xef0UJT}E$zxuiT8(y*+R6j}&!EmUA>h?TpH@EF9j z36xKo*^Ct9CbL@7eCW`(4raq9TQyPHi;E{e;hGXE=u1#SAjK+1QeKl5tAlM;a7?Jl}qE} zmkNzbGf3cMcW{p4|6@Z1 z9?EU-P~tM|(ID+DMbs(+aQMCfi!ofZh3ceriu5`oP-&M{*pD|F4cOai_-`cz)Gx~j zmNX$%{3@3nm3%)w9Wflyx-aKAddnE~G@OP_swe6_a|Mb)tX=vL%$&BM-WCEd&D|4t zEmyBfz#U1^FVzXOvOAiIwR!Gm{_+(7fA;xS9f!W`aQI@+T3e0H;rkBj85OW3CVSfyBngZLG$m=L7v{@FoCf<3J1MrycwSv}!{z z=C%71Q4)ZTDYyTNo}@^faH|*t*5TM%3$WInt;{dfgiQe(vL+99z}K@Tb>cFwa1$8g z6bZ-7%KkbMmqlNHt)O5CxZv3Lk>+M#3$0huCG_S{r*C&__t7>kMk&}PEvt+4<|JV! zr=Un27Wk-S58D@S3czt@LBxuD<45wR%|NO~A%M|`ay2-;Vwk)fX8bZ;XIpceoaP7s zD8v-%u*XENgZ{ppH2+S#pV=F*!U0*_W2{aiFMB95baW)>m^e`}=X&FaF*5T z%yy$ipFVOQ0Pe9>Q_N=my>%egi6j<3pd}ensjWN`AVh23n>Kc?ox|C~Sm7N7+xwuL z!G2Y%+KEgOyr+scm-nq$EC&)va!Lpzu_j35wLew;{v2zi)3#9oEUS{e2gl{N_5OHd zoO5A#s79z#NWNS87Veo6UaoT)Yd0SknN#D=AlVK>F`Q{=o>YSVo@`u`V)I1O4aY;Imc>hX9`NobVGk=K-D^3|BCk zFc9h)e|%7XTvkt9bIt)&op<@ssA+~B7GQ{4kd^VRnNEH+0Ry>td}pUe$rH>-)t}o> zqk$t`R*1#TK~?dBh6ONGf83Fnu|GGNSM6D&S{+!k-^O^AE^6C4o6*pl$jCbq4r|^VMVTjc8z7<`56E&9^KtFtFfY>&e#%Lrk(3YeSBxOjAXrit ze>)0m4T$ZIpsB)bCobQ90f;FgK5f=w2bNMGP3>`AY2)Aqz724u7DB2i3)`3`t`a7n zf#}s$#DxV_Lg3?p=OJr^x`En!qJ63XB+;^?aNTA!Xj15~s}H?Ah`1l}TBS{U5Wn|@ z!t|M}(H`Z8GwJo{x1@X|2LNS6`E^sU{yF*=w0GHD=hl<`C_4=|`Ni|ge^drP7yZPr zbLocpbE;~7*8J*Biavos6s1TE3T8JqKq+BZ<&y@JpR?1smnAZz&{W1wRK_hCZt%n(IIkNaTAi`&$O*lg~_HjgZuGmtIt1 zD|(W=+7ncDpDuI&w8uD(qTP-t&wQ zyZ|`OmXygJ7WLo$Eq|h2VKC`qc*HRqVK@I{DDJT{pL5XT_@dQrr2}7}iH33YuS){? z=2BVlIz>DvAu5i=D@B}=*ADizSw9m42PI}QXE*U9K)@QZUgDCrQBEEXULeW1HfQaf zu}EGfXYjZrQTV{uYR0?3=)4?f!0}2LGa+y&=1kn~qrdA;1uzO8X3_D)X9Yc*OJsHA z5K7aP!9-KT+sz%e?5yp#k#cGLMq|uLk zVScdX)80NHYw&tYA;W&PANHxz9g$W?iH;XjWEm#wIs65yJ%H2$ef<%<1Mx#xzAgzB z$)F2v{u`uavK8H(jpxQiZ|;1I%PgS z9?T8vdDGC$=RdXXv(JSzjH)*>SS9VBC`gE}NCk@yWWm^J8kXwWB!J40d=0fa`M2?; zwNnBNG%>;}J!UenTvD=cS|Xk3emlY)G?x?$d0dI85e5UjdrJV^bY^Y<1k!+#Z&h!* zyf_QBUZtVt{q?Jh@1p~10dO#Z9Qd4%@*13YR=xO9@A(c1Q0`EOOBO+g0hTKd_GLi= zNme4Psbk_|L?#q;hoHvC+Uup5|HgRaE7KQHp8|!I^n-ttRD~#!AD^E8#C_ZGvJL&y-(p31!v}bSGVxN?tE8zue zT5dbjj#AUr-Vday+6UDxc^#*lP8M)KLD!#4YnOJ(qne1ues_9r2(~51X|CzpJzl>p zIpr+s2(V5a-3Tt*ASgQ)Ad8D;NpShyeky{{F@fHNZ z@LWyTUttAcvXvwTDFOy50fo9%U`ORIic(XdIFU;Mt4n6a2)8K^4kK=MC5!Q1tk+KV zCjnZqZ*wcV0XrAVZZDbG8mwDdWph|J&+fWd&HF8lTBdKd$KP?fRIp~!dQHQxDVT<}~ zQ}8V*22h$R9M9dUe+w-I(?s_3Xyuh7gXYUP%ze4pll?|!Ka5uaT-7<1i2 zq+n4-i(|8E3OddCH!ZL^#TnoV&e9VP%`TnXIcH}1+LjvWqQw)3z5~_IzItUglAB(w z@GY!)&z?E0=(9x3;$g>wu)LSBOyo%QXu_fH#l`J|Z88C?8h+J>jnNySwz8}G2_Hqz z4q9cRQ*@u-FxgQsYh;0_-YWVb;+X4j4f3M=x|YJD#$wQFV&-=;250`bh*$@1|b7uc7EAm(NbO!f8CrUSRllQ~2`o4iv0_ zjbf#BCrf>M?KHOYt&8 z(w1Dsv>4Cn>ZR55&ZfN=Jw3>dApgY@qsh_!4eAw7=_X=ad0lGGdyGOb=wnZS{%ChL z`3&$veMPka#`NZj>{(MFbXj8a@8L~l&wt(r1|p`_K+yHjP&WZ_ z$f4(5>ti8@w?Tj64FL!z7|4Y)l1`6#D^kN9v7kG8aY@-~!qG_`Iapvg1^=8YaVx<> zR7ElGYP3`nY2_;}VdgqUaMB21hm^eoId-*G9p>88nsjRw8j$A<=2~7Qb!eD}e9J&$2EC_&gu=5|Y-VMQg^_$Lj6V z$OC37>8;KI>r@5NJHi&;uuNMks|1EPD(95;+b_oWos1Sfb$>xdwXRWn&9F{(QJ6J% zSmZXtn?BZ=BBqJ~Zc+hG`Zmc0bf$2Wjc3K`w@Q`fm+dAF&|>I0&GBluWl79W9){^7 zz|)&SkL{AFn8_#mjbi86|AgUz|Kk$@V3TbuwCc@~BKm`lxsv7Q{5%<%D6(9fQva5@ zl<1Gi%EP9clVDF-XH?n)W~yI%jO(0MuKe37umjswcQk9+^;q! zsHv-cmZIPhLIwb`dFGkqUnpO67%Q4FLb^#Ui6r~jT)weElRP@%JDXQ^@^FK_H*D2I zB{g)9Uq4_f&!5EWM#oFg4>+=IYxwxr3(P`cXAAfm3iWLtnFb!>Oa@!gLN6^v?R;k< zAik;wPjjy8Sw-8w85_>*4iSCwKS1?=vC02@)ey}DZq`?V8DVwHqdKr{{)X*uS7S~U zEnhA*rBqIJZM}p%=zZF%`2(5oyYHzDzs$Y;n}$ObK2_#sfp=lbAV72hXJG0*1F4tt z)^FGbS`W`gb=eJ#xQE$4xFBWi+5<2|E4hvjSgFDA`Od(-yC<&o%r;rF`DJ>QQ*pJq zh05^Gyz6zh-RFl%#22nC4c2GZe~?^ZWXV>p+>iYTK`&fj*KvH0?^1e0yye(Z4}G*xX!2<>Ds`|HpngTKgR8Mk_(lfZTO-hTlALdFr&35lwGSwB0G0*upPQ6w<0(@`> zsl_K~uG%|Qk4yF&VnS6n!E8MnIG>4{svix?@?-O+c!WeJ&-B~2+J`%V>5Zd-=j6qg zj~0hE*1O9VJ$@iY(4T7Vqe|SQPj2MAp`xwYpDvP_C&Z$;$N%fTuihq9QAuFw(q~o5 z!($)(yBQN8Jg@PX{mil>*Pw)u9}whLHJSfZ&3AB&AORxp^|jSx8>{f4m-u%bh$l4P zh`P67mBj98SqLY5S%cR_)|;mHSwO$`9qtEO1)wU zMF0K6>KBp)SjHftC?Dj*cn^$dSPJ;8GRAhn$M*$lGO={{;x^Q~o?@?_h;Vp8T?uP{LM9*wK;}M>Ci(@~Gx5KP`>oA;E_ZJ5kiA_b&LS^a()E&7 zSGn?av)cqY0gjj>plg`sUVuruHCAhn7w&mRi! z-vHyFux$x_jVD~boO^A|YwW?2Q!Fn19Y^l;+BJJLCJO$;J> zoPnV|cwyRefZkV%R~Pu{TYvWN;_yjdJd@&*_{Fn;*0PRZ4^wg0TUPpNob^Z9oeWguht{%{=g~wvs>cQH_6w<3qVQ>#0jmS0PZ!@a=!7Ut zBrn`TPcVw;MpH!%^6|uTATD8??FE zXaB>$rID#3lR{lw(Vi`pC<1UwyqJSrG6(bgY@K6s^$#ni@|oF(mVh)PUw?b3UQ?m`t>U0d7)d3+{4(2e zZY?gooVvv^wrCXOsHqIqbGxpSc{X0x_g6magzx#IxL zPs*LCU+x%6%P4ky_vQB2a5r`=U&ehg6&b)amv`lN4p!c`&rRd0%VXp?(!Ml2d5v_c zA!C2HroaeT#`)2lIlz1|MTJtLs`jYW3TXEOXtb)kN;cM4Uo)PKJ8#mK@RH)NGVNod z825GO_H)W5v-s&7O<@S>{k50gQ}FRpvjyH>5QMW{GS$W#8IBN!)vd)?^_NzatkYnk z=hjtr?n*k3W~_4PJ(RizE_L*mXYJ!^=#Z6&+qzl+>vjDsJJ^xK(%7 zX8O#{<6G1Oa(^39?8;JJExvv}t}$5nVTJ8T*173lF$}2_VTbqfq^88&h|eYcfGlg= zQyR`1qgTrhMwe%6N82Tzi-~mZvwFS!{Kj$I7qWuGP#^Yor;s^}7U*YJa;gsGBVuLl zb^&D!l{QkVs6JLJ6>dCnWc;bLXC4C8V*s{aM_V#$&cZATV$vO*&)!S)&?=3W*Q7cc zGN1BkuHMPMs_}+xR)c_=Na8S<+*_PAP^r`%!eB~Uy<2`e`Vb)$tG!pPmAFLz&UGx@ z_<4)g!uSy@so@ctTgfA=9Wh?MeH#p#w}0vW?ORv|c&yM5x&}{PTkjjh0+P5~FIX|H zwKjgM@`kI%!w+j(T|Aalho-wt^<9rkO+C?Ey4Tg2wK7UhdUIm4zOe4FXGWcO*E{d* zQ#PTI!qQKVg}ghhCdVmYS)&y>Pa;@abdL?pymiC&#S0A)Mb1^^@j9b;l64df<+EHf zG!!!_dDY6Rr(zcRZTES{(%=I)q$Hi{^p=B)?z4XYp@`u*&8&-oRT{~PY#`zO7<7aw z|KhI=Gl&=2!09Pn6|_6|3nlbA5cE0+_Rz4NI#e3sStH-!%&%A8A92y-F3f}Znuil^ zYu1*5e}M>lV$fA>V+{3;+B+o8GUW8sk3_adJq$w^=pknfLLOJk$ncudfJ(|RP(#`FHjMJ)2rc2v9Yy< znIWn~%gaj+D4a=VGfKF-%SxN6{#PwD8(aBz-iL{?5 z`t8=iJHa+t`-A5-t8Zj2uO+$J8-nq$|O3&s58NT&eRP?C-+;jluvPZdKT0FvMGAeqE3Nbe6)z1*cP?1sXa5`Y$;zQLooQeK(PshCF0YMFLAQngmVAGDde4#kPVfPI{BRN zSg}3m-ezu3J;T6{aQuF%E@M|1OhngUGQ@XZ1#+Tq=;V3%U2LMDO!IhyjOGT)(-x<@ zTkYw@jayhF#cYkN7eSktV$!61;{jDirg{8%fp!9y&SvILf|@XEK7}trxzCQN`5cx@ zOMQ+3cMa(v0%XcNbZHwiXt#JZF}@e|J)B=!T6*R&!aHB2!6J_hAi1~Qkl209U^K#_ z(Z4J1DLDb1FamB)Aq0%Uc&Cv^ zN@@rZa@Dfh2H4MNl|VvyOuxa?%eSoyz&G?&KGFF!OQ*?QHfG1X8Pi^kWSQtXN1ejx z@y}vE<4_~e&m>je@sXQ}FO9B_H7L5%?fe&O=4Y>zd?`)A$)oG_vFhWQ+q-iaftglQ z#&mt}-%9JBz3np%q!S-aIKK&Q_!rwn5b+~3`aiG_D}cGa{JCw-xt?hw#GvU1_l5sn z{Qu+#6V%~@b+-36YXUp6FzxN#!#Fb$Vo-ye!8V_37{(z!qqYRL#0XT=+pG{C2)R{Z+<0u&Jahybd-~4D$Ome&^+FvBa+IhIJ_a z^)5mwLR^ST`Wh4=k4o=3N`-b4Z0OJL=9vcRh39?El4sssXLmlCB6j!P-gLMZ1vspZ zS8z%V-VNQ1-ty;#RZ66EXgGfuwiyc~gw1}kts2;T>Yu0(3Wq|>rQS|ePNZI}wt9g{ znm?=;x#3r~Kt>haX?#EojWq^YX`aE7cne;5PUFW?aAAJA%TQ8_dv9pA-fR(yFv(@6 zT@k|8v93SeYdS}d>n*epI?frWa|`BK^;0{D9)<(CFmX(MI&ac>p4C!6&YR z$s{`M$B*ZPQ|iJa@Qj$vGKU!e67VO7#&F2g3c)vPR<3*pTXrz#r==Y!UeA3ShNJiFPG{ta zHI}l_In?CCHMag;`k;JMLqne4tW5!`%vAjj(b?H~>7zH@@5x0FJ0=X&AaHkGz~{&dhe@M5Em%=1zqFLo7J!CRWplt(f1 zn6<}QBs8|a^_VR2O`6}adUNxasB9n4&<_f-C#laLxgmP1K9iAHuzJv8FIL5C$_3 zoYsJoX{4c_m#@98qc7#5jgMZQeMxfBNlS#Pth|{F^X_XfSTD=fchOhem+ZP6fl78~ z?zX&(W&cXhcL}U_k~Umjd1YRuIi^TGWpJ{S%HaEXAJLU72|9wSe-6T(U{N zRn7|OQM;evRm(nneYw_GXQlCvK|Yl2Y!VIyV5i&*hDU~wPn-|rK;24l1$l^9Qj{QF z#1@L$PpRcGzP1b7j2iI!qUTP&64I)qc6tD6%G)_pFpEAH-w(cvNMbO;4=DutC!@Ar zzZa4K^-v@R$-NBaMKNk#HXT^Epxf`|>?ZxXFE9h9Mn(F|4{`SfpW4pU$%e-J)yVy@ zB;zb~Hdymkhp&G5z^^8dqvx|O?7Y-&`Jt`wrUhzU7#CaWb!pYIbBRPZ3;0-bRFsTK|-|Me(?VIJ$JuLstbbu>NL*PC3 zhpc`Foo@>18+2XSeh=nY>Nyv+gCdJ83doV&C1mC~>-MfdOC91|XQ@kvgBuLog4_gW zUHn&gPxJ<44u}WVe}8bsUuB`4v7Ft!y9{$HJvf@BsW$Q+RAZ3)hj>24G<;RV=9j0S z`Vdt1;_UIw+PXiUZE;_z)s{WtWo_@@k2j z(bPD=(=kYF>1<6=6Z5`=xrG)0)(+45beC%c5gfe0?5R&_fZANZ? zG=)7eDR-qS^W1*?DZ==V9}l~wle4AZ8Qy-xZ}JQa&iqVnctod}bj^)^b|)T7Zzs>2 z7RBp(59b5sbeqKr$1=bc@6e8Xat zgrjB0=_z)h-kH`E+VCxJu7hVBJ-MYG-vzQI39%so4E*Gg^w#u8wNny6ARz%ShLJY7PvR5C{ z`l4f)@y^w}>)YI($o2TDW5KM|69&{8GN(HjNJ#d}kMHxsE=x0CxJAmkbzQ=}{F#Y) zM$NKar?y#ND#)c#Z}^ROJ?>?=(~tRDBJkCW5m&;-C#nwd{E#3}I+-Y61rHx$km#qq z{@th2@(w|!pj$lU_jeW%TX0LDj zSpL09{*j`F->OJ!Rzv<5HT9vgo=9@<(>fBMrt#U6w93%?5V&`It6yMH?a-CWVCW78 zmOVfwI@&E+B)PD4#V(;6_g$@w&HZ7bBn4*t>?-Loi)GGuj7tk-&D_}c^aB=)HAWyw zB#rm9e3(*#E7b{C88q<8EI?$*ufi2hW#ulEDJw)ZF1b4VLf=_&X(^XOsOT=tdQr&_ z<{qv&DDhvK?|M7okd9$+sMg`aWeKHp3Z$gz>zgab*}zVFm#H?|sy)Zz*=Pz+oy~fD^$I8#MqTn^mWA^cYBJU zoD+8XqVEDhBjz(bxk;9{sxUxUAJ)e9egj@DWg{Z@jliq%-dw(0ZyuSyxR9ct7ya#d zW`E9GURB3eJ|sy_tynhd{a`qJBFud*a&oB?sy=6hl(MIct_OLh2a>Gb7pC=1f4D!V zKd>4+d!qtPwin3u<&s*C%^Z5(@ATX>Td#IqnP_%;skn;au-c+@?-L}R0E(DiBx`dI ztF_1IXLGu^l?SDpG5G(ukZP8mdD4yC-^3Wi#4)_c4upsXAf7rH^t{cpr3;D(jah8~ zA&(8Z;;j3xV1$;YH49L1qx*#`hYnW^{vgoyjGUPsjQ1%k`9d2x-z-td7-%X|bU;Cz zY9T^2Z(84FgCHyNI`~bLE);!{#B$B_!KF&a3+7X$SZ|)I(d|&oNL#pTjh`^@^GXfX zd^TRhae-_EGsHs~fM707l`@e=q&Z|d`Av*AiuUxX#>(eeEEA)F-K$Sxc6>rG0Y+H{ z7?D9pA#@s;12r`kQIED+sMqw9_nvu-neI#<&tnWJDTX8!{SXQ#q=X;q6f(}#DfC*- zlRuO4y)9~d;9O~$mnYLA;T>l65q3@o7tThscyf7d@+405!;+3zLam&1jjXv=dNA#z z^YgX2*Vk!qPovsZV=v7nHGaL-92F5ftKib(Gi253H$yZU((kXejucOBG#}!ux^6y)=j)*Ny#Gv#+B&6@}8Duk&BfZPY4pD6knsj*B|0c3V ztaLpEmFZl@m>XHLyb~Q%;O}M=-kGY*HM@cj&ZXy{lxjFIu=b>Qdeu?9sbz4k^dGAl zoGO)4Av30@YF`j=6GvyR6W8KLj#XaTOgHdnmm1FhjG?}XDES3_w8aqw*_fWRuNLuq zN+3B=cGUk1Q6KeId;AK26rFEVIGt9o%*Si~)Cm2;Qq$nHTg8}!8M-2nS7)INpg{3S zQsEq3dGs(|DwCa>RXAgy&fYa*vBf=0bZLO#y}x>ZxhQ{#!%f{JGEVZ}#+CokCwp9> zqUp%czL4p1BJRlWZMM@~gV^iWOX>E-55C#npBF=!8J{O+4^zs3>p?U4%xjQ6x%K_1$za5 z;c&WoR{O|3hk`eu7AQZ~U$N+KC&RR^J)+v`Hh9x=>4TA$Isun_eCso9L=B9#?I9_BE+!DiNB`LUadGR`>|l!Q&Kji*S&OAdTVf#|R~Ud3 zq*cv##Ah4V|0~h}hW&r{hHBY$(U_I;a@~^m!d66cvYBws7+FWHzMVIRV)gr~b|{B4 z9k%qxCA0FIwr23ObUMEY72vOX>nI-xHV-ougRIO5ppFOGs>wl!gr-Nwv-nTk%nZay z4gUR93~T~hU<=nKX{ zfvjR}7RoqFvy;831rGm4W**dZwN)pNphi5uknQnG`BSH4=u#&%7lO9DDV_~e zq$YmAhz3br#&Ai3jgniJOu&&lg{BO#LScH(I6C+nuPL613quDMTWRm_-E1uJM}x#^ z?-?zqQuu&u>H@9^nGl`b1!2JS3|JoRyDzwIIgk&_vI^*5nI~QoTrnN*$mPej;+V>v zT0++gPCoyL<&axUH}aWrD`p#X)(<9OGgm(hF0WXmvs1Hcv}RcXQ~#>cbz;TEZ&buk z9IsXpuX`lfv=B!ETm_|Zu(!%=LANL{IG05Nq&!w`%XnA9lo_tI4E4>%KtM4t!`*gx z8fcjQAOmO(nsbh~x5Qt6Z^V6w1}_PNchGf9NPP6zEW6X8lb0#vq0lt1-f!a2^^)ggnD;}&< zdq&QDWOdRszN1kyQ)jfO9+a=3^LqdI0Gnx*4;>DnfPhQ=jRAI+Es$tfy>OuKeo19> zH^)eN!Dt;UnOu$Ff$T@axw4z#$R+R$nR#*|=!-vn`K@rl>LT;rPhE6~Qc&0;BFA#Z zkB=~WXPnjgRcAvg2AK)CyRU7Sqt-PvNq`95{+29xGgp1B%b}Lqkr-OqtZ_o3dvJA( z&1GV3qZn@r0?_Lv_QS3zx~1{+OdFT(1z7YIeaLbjp3;v+W=4&4Yu*YQQwot^Dn)Ur zLFd?{D;uFmYl?X=b#|Yz9UsJ0Ky^%Vcr~MV6mT?QHbMgJ*1HPL!MYTWPcXTfK_H3& z%N0)j9woFj68DCL90K6*`EwJZCXTz7j{5>%O-v_@xS|Gyy94q# z9L;*Vs16GaI+H_>0i3cRV>wnXM>aKnsM7Y#~mqQAjKtWP$ zAU6DuL+0SGWXPK|^0s~VRyV)W0Sa}=xZ*oiy9rZi>EAG8odyZXO5_a4&GM76v>7F zsb=Xc~4GLUwiT`+-T!I0u2ecJJU%QCTnPT+5+$0 z3Du9cIBaLGMzD$RJj-i!J6Y}q5nEd$K7@Fa@hKvKG?4(`A;kUa?E-`|6E0j23btr&GrHCd6IP6FwzBY$AKBa|YE9;${%XU7(hglzko@?M2M~bb*WhD*C}xs zQ#KS)R8&+gmssDCeIT=)P6EPN(cJ~PBj^?J?LY)Y&j3gR3}?A`PalZ`1c0O$N$_Y( zkW&t~MHQJ-Yl=`+vpaqC)bF)&Uqz{6( z+rP5UN~+OvkMJvhaC+q9ApTZ2V$~}>UcQ#V*)s7GSj5I>x&^FZO-Y3iJKf#N}U)*~dE>xAh zZv+kZ&r_QI>Vo!iK06CyA(^btFcGzY*?#DY7Y4K}!Uf<}Xxu;^z4XVu`{#D{UroP* zpr@Vj8G5DudP`14rvLiqzyC64J{8~$FxP7;*Atc4vdY*=se-f=^c*Z(V||>@dJ(?I z>dJkGtuOiB1jI7ql%()BTnacw@L5?RkIwx4$bZ7Wkd*`e$c=~Q0sp+5|2m<6dv(PK zTzi^krW<>=p%pxP4g}Jd-+Q);v2Q}87`@%t{LPqAe;zj{;=6KoKKJ}KD<>SW&&|)~ zH}=`)i`;+{t@_EpV4LeEh(ZWGdU`Dfwp+vba$ zMHnHzPXo6BW9rP|yo9^Div8~b{Pzp~zYFl^3+(^m0+bbt)5_FoP)M+&*lwTMLGg%U z^cTWSG3K!7=w|ZP^Gc8(-r!VX&m|O49&S9*rcGTcM!C&xodz5;hY-QIGnL>fbmVF^ zw=KU-+}5}5(bI8S(RUq2mGfwhpF-1)kB)}xf;Z8m>2rr|_SP$$o*u0?c%C+0t7t6P zec1K&HpLpJ#}%W!36~z)$^3 zZ@>Qg8zy+fg + + + + My First Tab Name + + +

My First Website Heading

+

My first paragraph

+
+

Project 1

+

My First Website

+
+
+

Project 2

+

My Second Website

+
+
+

Project 3

+

My Third Website

+
+ + diff --git a/web/html_snapshot_1.html b/web/html_snapshot_1.html new file mode 100644 index 0000000..0f5c146 --- /dev/null +++ b/web/html_snapshot_1.html @@ -0,0 +1,28 @@ + + + + + + + My First Tab Name + + + + + +

My First Website Heading

+

My first paragraph

+
+

Project 1

+

My First Website

+
+
+

Project 2

+

My Second Website

+
+
+

Project 3

+

My Third Website

+
+ + diff --git a/web/web_lesson_0.md b/web/web_lesson_0.md index 8f59464..e07ed1e 100644 --- a/web/web_lesson_0.md +++ b/web/web_lesson_0.md @@ -1,12 +1,13 @@ ## Hello, World (Wide Web) -Long, long ago, when the great Tim-Berners Lee brought us the WWW, +Long, long ago, when the great Tim-Berners Lee brought us the WWW, websites were documents that lived on some machine somewhere. Those documents had names called URLs by which anyone from anywhere across the world could request the document. The machine that hosted those documents would then 'serve' the document over the Internet to the person that had requested it. -Clients and servers +This anyone from anywhere across the world was called the `client`, +and the some machine somewhere that serves web resources like documents is known as the `server`. -Model + And that brings us to... @@ -38,6 +39,10 @@ You should see your very first website! ## Welcome to HTML HTML, or Hypertext Markup Language, defines the basic structure and content of every website. +### Basic structure of an HTML Document + +![alt text](html_dissected.png "Boring alt text for an image of a labelled HTML document") + ### <h2>Elements, Tags, and Attributes</h2> To define the structure of a website, HTML uses tags: @@ -46,8 +51,8 @@ To define the structure of a website, HTML uses tags: ``` This is an HTML element. -HTML follows grammar rules, which in programming we call `syntax`. -Tags pretty much always occur in pairs: an opening tag followed by a closing tag. +HTML follows grammar rules, which in programming we call `syntax` rules. +Tags pretty much always occur in pairs: an opening tag followed by a closing tag. (Except `
`, which is a line break.) We'll see shortly that tags can be "nested" inside one another to contain each other. Aside from the tags shown here, we also have `div`. A `div` element is used as an all-purpose container. @@ -70,8 +75,13 @@ Add the following below the line for your first paragraph: Notice the new `class="project"`. HTML elements can have `attributes` to describe them. Each attribute consists of a `name` and a `value`. +For example, "class" is an attribute with name "class" and its value in the HTML elements we just defined is "project". The `class` attribute lets us group related elements together so we can do the same things on all of them. +The `id` attribute is used to name a unique element. + +Here's what your HTML file should look like now: [html file](html_snapshot_0.html) + ## CSS Beauty Makeover Create a new file "File > New File" and name it "styles.css". Save it in the same folder as "index.html". @@ -92,19 +102,313 @@ h1 { } ``` +Now tell the HTML file to use your new CSS file by updating the head to this: +```html + + + My First Tab Name + + +``` + +Refresh the page! You should see a slightly more colorful webpage now. + ### Language #2: CSS -Cascading Style Sheets +CSS stands for Cascading Style Sheets. CSS files are what set the appearance of a website, from the font color of the text to the positions of each element. Now let's get into the real makeover. -### Selectors -Replace the previous code with this: +### We Never Go Out Of Style + +Take a look at this final CSS file: + +```css + +body { + font-family: "Montserrat"; +} + +.post p { + font-family: "Open Sans"; + /*font-weight: 300;*/ +} + +/*img { + height: 700px; + width: 500px; +}*/ + +/*h1 { + color: #006AFF; + color: white; +}*/ + +.navbar { + /*background-color: rgba(255, 255, 255, 0.1);*/ + padding: 5px; + /*#C2EAFF;*/ + height: 20px; + width: 70%; + margin: 0 auto 40px auto; +} + +.navbar ul { + margin: 0; +} + +.navbar ul li { + padding: 2px 3px; + margin: 0 30px 0 0; + /*change to nav not .navbar*/ + display: list-item; + list-style-type: none; + float: left; +} + +#current { + /*border: solid 1px #006AFF; + border-radius: 4px;*/ + border-bottom: solid 2px white; +} + +.navbar ul li a { + /*color: #006AFF;*/ + color: white; + float: left; +} + +.navbar ul { + padding: 0px; + /*display: list-item;*/ + /*list-style-type: none;*/ +} + +.navbar li { + float: left; + margin: 0 10px 0 0; +} + +.navbar ul li a { + text-decoration: none; +} + +.project { + opacity: 1; + transition: all 0.1s; + border: solid 1px white; + border-radius: 10px; + padding: 0 10px; + margin: 3px 3px; + width: 30%; + min-width: 150px; + float: left; + min-height: 125px; + position:relative; +} + +.project:hover { + background-color: rgba(255, 255, 255, 0.2); + transition: background-color 0.1s; +} + +/*Better solution to clickable div http://jsfiddle.net/hf75B/1/*/ +.link-spanner{ + position:absolute; + width:100%; + height:100%; + top:0; + left: 0; + z-index: 1; +} + +.fading { + opacity: 0; + border: solid 1px white; + border-radius: 10px; + padding: 0 10px; + margin: 3px 3px; + width: 30%; + min-width: 150px; + float: left; +} + +#container { + margin: auto; + width: 70%; + position: relative; +} + +.add { + position: absolute; + right: 0px; + font-size: 15pt; + z-index: 2; + /*background: none; + border: none;*/ +} + +#title { + padding: 100px; + text-align: center; + /*text-transform: uppercase;*/ + font-family: "Montserrat"; + font-size: 40pt; +} +/* +#footer { + position: fixed; + bottom: 0; + width: 100%; + height: 700px; +}*/ + +/*.add:active { + color: blue; +}*/ + +.list-hover-slide>li { + position: relative; + overflow: hidden; +} +.list-hover-slide>li>a { + z-index: 1; + transition: .35s ease color; +} +.list-hover-slide>li>a:before, .list-hover-slide>li.dropdown.open>a:before { + content:''; + display: block; + z-index: -1; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: translateX(-100%); + border-right: solid 5px tomato; + background: gray; + transition: .35s ease transform; +} +.list-hover-slide>li>a:hover:before, .list-hover-slide>li.dropdown.open>a:before { + transform: translateX(0); +} +.list-hover-slide>li.dropdown.open { + overflow: initial; +} + +body { + width: 100%; + color: white; + background-color: #303030; + background-image: url("image.jpg"); + /*background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg");*/ + /*background-size: cover;*/ + background-size: 100% 800px; + background-repeat: no-repeat; +} + +.post { + background-color: white; + color: #5E5E5E; + border-radius: 20px; + padding: 20px; + margin-bottom: 40px; +} + + .post h1 { + text-align: center; + /*color: gray;*/ + } + + a { + color: inherit; /* blue colors for links too */ + text-decoration: inherit; /* no underline */ +} + +input, textarea { + border: solid 1px #CCCCCC; + margin-bottom: 15px; + font-family: "Montserrat"; + /*background-color: gray;*/ +} +textarea { + width: 100%; + box-sizing: border-box; +} + +#thread .poster:after { + content: " said:"; + font-family: "Open Sans"; + font-weight: 300; +} + +#thread .comment { + margin-bottom: 30px; +} + +#thread .poster { + margin-bottom: 10px; + color: gray; +} + +#thread .comment-text { + border-left: solid 3px tomato; + color: #5E5E5E; + padding-left: 15px; +} + +.like img { + /*background-image: url("heart-green.svg");*/ + width: 20px; + height: 20px; + display: inline; +} + +.like { + /*width: 40px;*/ + /*position: relative;*/ + display: inline; + float: right; +} + +.like p { + display: inline; + vertical-align: top; + padding-left: 5px; + line-height: 20px; + color: gray; +} + +`` +[TODO: update this css file] + +### Selectors +In the previous file: +- . selects all elements with class "something" +- \# selects the element with id "something" +- selects all elements with tag "something" +- selects all elements matching "another selector" that are __descendants__ of an element matching "some selector" +- __>__ selects all elements matching "another selector" that are __immediate children__ of an element matching "some selector" [(see relevant Stack Overflow answer)](http://stackoverflow.com/a/746557/5391146) +(You can check out http://flukeout.github.io/ for a fun CSS primer!) + +For now, replace the previous CSS code with this: ```css +body { + font-family: "Montserrat"; +} +.post p { + font-family: "Open Sans"; +} ``` -`class, id, tag` +You'll need to add two more lines to your HTML head in order to use the fonts "Montserrat" and "Open Sans", so that the head looks like this: +[TODO: should it be in head?] +```html + + +``` +These were found using [Google Fonts](https://www.google.com/fonts). Change up the text in your HTML elements to fit the website, too. @@ -113,11 +417,14 @@ Set the background image for `body` using ``` background-image: url("http://kriswhitewrites.com/wp-content/uploads/2013/06/landscape-mountains-snow-sky.jpg"); ``` -Now make all the font white. (Hint: use the "color" attribute.) +Now make all the font white. (Hint: use the "color" attribute and "body" selector.) + +Using the above file as a reference, modify your current CSS file. +[Here's an example updated CSS file.](css_snapshot_1.css) ## Adding interactivity with Javascript (not Java!) -Javascript is a programming language that's recently taken the developer world by storm (You may have seen Angular JS, Node JS, React JS, Meteor JS, Ember, Backbone, ... the list goes on). +Javascript is a programming language that's recently taken the developer world by storm (There's Angular JS, Node JS, React JS, Meteor JS, Ember, Backbone, ... the list goes on). @@ -141,11 +448,15 @@ You can modify the DOM tree `dynamically` with the web's favourite language - Ja `.getElementsByClass` etc. -Remember this keyword, `dynamic`, especially when searching StackOverflow. +Remember this keyword, `dynamic`, especially when searching StackOverflow - eg. "jQuery on click event not working for dynamically created items". + + ## Fleshing out the website