From b96476d1006af88e5c294b48941f016bd40a578d Mon Sep 17 00:00:00 2001
From: kataras
Date: Fri, 18 Aug 2017 17:09:18 +0300
Subject: [PATCH] Update to 8.3.0 | MVC Models and Bindings and fix of #723 ,
read HISTORY.md
Former-commit-id: d8f66d8d370c583a288333df2a14c6ee2dc56466
---
ACQUIRED_HISTORY.md | 89 -
HISTORY.md | 225 +-
README.md | 54 +-
VERSION | 2 +-
_examples/README.md | 65 +-
_examples/hello-world/main.go | 4 +-
.../controller-with-model-and-view/main.go | 99 +
.../views/profile/index.html | 13 +
.../views/profile/me.html | 13 +
.../views/profile/notfound.html | 13 +
.../views/profile/profile.html | 13 +
_examples/mvc/hello-world/main.go | 105 +
_examples/mvc/session-controller/main.go | 47 +
_examples/mvc/web_mvc_diagram.png | Bin 0 -> 25515 bytes
_examples/routing/mvc/controllers/index.go | 18 -
.../routing/mvc/controllers/index_go19.go | 18 -
_examples/routing/mvc/controllers/user.go | 62 -
.../routing/mvc/controllers/user_go19.go | 81 -
_examples/routing/mvc/main.go | 25 -
_examples/routing/mvc/main_go19.go | 28 -
_examples/routing/mvc/models/user.go | 15 -
_examples/routing/mvc/persistence/database.go | 10 -
_examples/routing/mvc/views/index.html | 11 -
_examples/routing/mvc/views/user/index.html | 18 -
_examples/tutorial/mvc-from-scratch/README.md | 171 +-
.../websocket/third-party-socketio/main.go | 44 +
.../third-party-socketio/public/index.html | 38 +
.../public/jquery-1.11.1.js | 10309 ++++++++++++++++
.../public/socket.io-1.3.7.js | 3 +
configuration.go | 14 +-
context.go | 11 +-
context/context.go | 4 +-
context/gzip_response_writer.go | 49 +-
core/router/api_builder.go | 96 +-
core/router/controller.go | 290 -
core/router/controller_test.go | 152 -
core/router/party.go | 45 +-
core/router/status.go | 1 +
doc.go | 180 +-
faq.md | 38 +
iris.go | 16 +-
mvc/activator/activator.go | 312 +
mvc/activator/binder.go | 118 +
mvc/activator/callable_control.go | 53 +
mvc/activator/method_control.go | 81 +
mvc/activator/model_control.go | 54 +
mvc/activator/persistence_data_control.go | 103 +
mvc/controller.go | 129 +
mvc/controller_test.go | 284 +
mvc/session_controller.go | 35 +
sessions/session.go | 7 +-
version.go | 17 +-
view/view.go | 6 +
53 files changed, 12642 insertions(+), 1046 deletions(-)
delete mode 100644 ACQUIRED_HISTORY.md
create mode 100644 _examples/mvc/controller-with-model-and-view/main.go
create mode 100644 _examples/mvc/controller-with-model-and-view/views/profile/index.html
create mode 100644 _examples/mvc/controller-with-model-and-view/views/profile/me.html
create mode 100644 _examples/mvc/controller-with-model-and-view/views/profile/notfound.html
create mode 100644 _examples/mvc/controller-with-model-and-view/views/profile/profile.html
create mode 100644 _examples/mvc/hello-world/main.go
create mode 100644 _examples/mvc/session-controller/main.go
create mode 100644 _examples/mvc/web_mvc_diagram.png
delete mode 100644 _examples/routing/mvc/controllers/index.go
delete mode 100644 _examples/routing/mvc/controllers/index_go19.go
delete mode 100644 _examples/routing/mvc/controllers/user.go
delete mode 100644 _examples/routing/mvc/controllers/user_go19.go
delete mode 100644 _examples/routing/mvc/main.go
delete mode 100644 _examples/routing/mvc/main_go19.go
delete mode 100644 _examples/routing/mvc/models/user.go
delete mode 100644 _examples/routing/mvc/persistence/database.go
delete mode 100644 _examples/routing/mvc/views/index.html
delete mode 100644 _examples/routing/mvc/views/user/index.html
create mode 100644 _examples/websocket/third-party-socketio/main.go
create mode 100644 _examples/websocket/third-party-socketio/public/index.html
create mode 100644 _examples/websocket/third-party-socketio/public/jquery-1.11.1.js
create mode 100644 _examples/websocket/third-party-socketio/public/socket.io-1.3.7.js
delete mode 100644 core/router/controller.go
delete mode 100644 core/router/controller_test.go
create mode 100644 faq.md
create mode 100644 mvc/activator/activator.go
create mode 100644 mvc/activator/binder.go
create mode 100644 mvc/activator/callable_control.go
create mode 100644 mvc/activator/method_control.go
create mode 100644 mvc/activator/model_control.go
create mode 100644 mvc/activator/persistence_data_control.go
create mode 100644 mvc/controller.go
create mode 100644 mvc/controller_test.go
create mode 100644 mvc/session_controller.go
diff --git a/ACQUIRED_HISTORY.md b/ACQUIRED_HISTORY.md
deleted file mode 100644
index 844f3975..00000000
--- a/ACQUIRED_HISTORY.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# 10 July 2017
-
-## 📈 One and a half years with Iris and You...
-
-- 7070 github stars
-- 749 github forks
-- 1m total views at its documentation
-- ~800$ at donations (there're a lot for a golang open-source project, thanks to you)
-- ~550 reported bugs fixed
-- ~30 community feature requests have been implemented
-
-## 🔥 Reborn
-
-As you may have heard I have huge responsibilities on my new position at Dubai nowadays, therefore I don't have the needed time to work on this project anymore.
-
-After almost a month of negotiations and searching I succeed to find a decent software engineer to continue my work on the open source community.
-
-The leadership of this, open-source, repository was transferred to [hiveminded](https://github.com/hiveminded).
-
-These types of projects need heart and sacrifices to continue offer the best developer experience like a paid software, please do support him as you did with me!
-
-# 02 July 2017
-
-### DEPRECATED
-
-Iris has been acquired so development is up to the community, there are two active iris-based communities so far.
-
-Use one of these projects instead:
-
-https://github.com/get-ion/ion
-
-**Ion is a fast, simple and efficient micro web framework for Go. It provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.**
-
-- a bit faster than Iris version 7, based on `ab`
-- stable api
-- more examples
-- sessions, websockets, typescript and cloud editor(fixed) on different packages
-- test cov, including examples
-- slack bot for support automation
-- has a FAQ page which is part of the gitbook.com beta program
-- central issue portal
-- HuHu supported
-
-https://github.com/go-siris/siris
-
-**A fast, cross-platform and efficient web framework with robust set of well-designed features, written entirely in Go.**
-
-- three maintainers
-- plan to stabilize api, no unneeded changes
-- plan to increase test-coverage
-- plan to add more middlewares and examples
-
-
-> If your team's project is missing from this list, please contact with me.
-
-# 17 June 2017
-
-### IRIS HAS BEEN ACQUIRED
-
-Iris project has been acquired by a Dubai-based startup.
-
-Both sides agree that every related public data should remain open for at least 30 days.
-
-After the period of 30 days, company has the proprietary rights to delete or transfer this repository and all its related public data forever without any warnings.
-
-The company may or may not reveal its true identity to the public.
-
-Transaction of the public domains still in-progress:
-
-- http://iris-go.com
-- https://kataras.rocket.chat/channel/iris
-
-View-accessed users can clone the current state of the project's public repositories and use without any warranties.
-
-From now on, Original Author owns a high position to the company's table.
-
-At any circumstances,
-
-Original Author keeps the creation rights.
-
-### About the future of Iris
-
-Clone the repository today because if I can't find a new lead maintainer for the [v7.2](https://github.com/kataras/iris-v7-29d) you, as community, will have to find a way to communicate about its future, the name "iris go" was taken by the company too, so it will be nice if the future main contributor change its name too, if you don't do it I will not beat you but I don't know the full company's law-plan for this, yet.
-
-All donators, without any exception, will have my support for at least 6 months (for all iris versions), we have a private room at the [chat](https://kataras.rocket.chat/channel/iris).
-
-Don't worry **I will not let you down**, we're trying to find a decent open-source contributor to continue the Iris' open-source codebase. I'm already in touch with some good gophers but **If you're willing to maintain this project** please [send](#contact) me details about your experience, general bio and your github username.
-
-**I am really thankful for all of your support to me and the community, all donations, all bug reports, all comments without any exception. I did proceeded with all my physical abilities so far but unfortunately there weren't enough for my survivor. I'm really sorry if the latest news made iris open-source community disappointed but you have to see things from my point view, I was one step before bankruptcy, I had no other choice but accept the offer.**
\ No newline at end of file
diff --git a/HISTORY.md b/HISTORY.md
index f3db04c7..ea14693c 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -18,6 +18,223 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
+# Fr, 18 August 2017 | v8.3.0
+
+Good news for devs that are used to write their web apps using the `MVC` architecture pattern.
+
+Implement a whole new `mvc` package with additional support for models and easy binding.
+
+@kataras started to develop that feature by version 8.2.5, back then it didn't seem
+to be a large feature and maybe a game-changer, so it lived inside the `kataras/iris/core/router/controller.go` file.
+However with this version, so many things are implemented for the MVC and we needed a new whole package,
+this new package is the `kataras/iris/mvc`, but if you used go 1.9 to build then you don't have to do any refactor, you could use the `iris.Controller` type alias.
+
+People who used the mvc from its baby steps(v8.2.5) the only syntactic change you'll have to do is to rename the `router.Controller` to `mvc.Controller`:
+
+Before:
+```go
+import "github.com/kataras/iris/core/router"
+type MyController struct {
+ router.Controller
+}
+```
+Now:
+```go
+import "github.com/kataras/iris/mvc"
+type MyController struct {
+ mvc.Controller
+ // if you build with go1.9 you can omit the import of mvc package
+ // and just use `iris.Controller` instead.
+}
+```
+
+### MVC (Model View Controller)
+
+
+
+From version 8.3 and after Iris has **first-class support for the MVC pattern**, you'll not find
+these stuff anywhere else in the Go world.
+
+
+Example Code
+
+
+```go
+package main
+
+import (
+ "sync"
+
+ "github.com/kataras/iris"
+ "github.com/kataras/iris/mvc"
+)
+
+func main() {
+ app := iris.New()
+ app.RegisterView(iris.HTML("./views", ".html"))
+
+ // when we have a path separated by spaces
+ // then the Controller is registered to all of them one by one.
+ //
+ // myDB is binded to the controller's `*DB` field: use only structs and pointers.
+ app.Controller("/profile /profile/browse /profile/{id:int} /profile/me",
+ new(ProfileController), myDB) // IMPORTANT
+
+ app.Run(iris.Addr(":8080"))
+}
+
+// UserModel our example model which will render on the template.
+type UserModel struct {
+ ID int64
+ Username string
+}
+
+// DB is our example database.
+type DB struct {
+ usersTable map[int64]UserModel
+ mu sync.RWMutex
+}
+
+// GetUserByID imaginary database lookup based on user id.
+func (db *DB) GetUserByID(id int64) (u UserModel, found bool) {
+ db.mu.RLock()
+ u, found = db.usersTable[id]
+ db.mu.RUnlock()
+ return
+}
+
+var myDB = &DB{
+ usersTable: map[int64]UserModel{
+ 1: {1, "kataras"},
+ 2: {2, "makis"},
+ 42: {42, "jdoe"},
+ },
+}
+
+// ProfileController our example user controller which controls
+// the paths of "/profile" "/profile/{id:int}" and "/profile/me".
+type ProfileController struct {
+ mvc.Controller // IMPORTANT
+
+ User UserModel `iris:"model"`
+ // we will bind it but you can also tag it with`iris:"persistence"`
+ // and init the controller with manual &PorifleController{DB: myDB}.
+ DB *DB
+}
+
+// Get method handles all "GET" HTTP Method requests of the controller's paths.
+func (pc *ProfileController) Get() { // IMPORTANT
+ path := pc.Path
+
+ // requested: /profile path
+ if path == "/profile" {
+ pc.Tmpl = "profile/index.html"
+ return
+ }
+ // requested: /profile/browse
+ // this exists only to proof the concept of changing the path:
+ // it will result to a redirection.
+ if path == "/profile/browse" {
+ pc.Path = "/profile"
+ return
+ }
+
+ // requested: /profile/me path
+ if path == "/profile/me" {
+ pc.Tmpl = "profile/me.html"
+ return
+ }
+
+ // requested: /profile/$ID
+ id, _ := pc.Params.GetInt64("id")
+
+ user, found := pc.DB.GetUserByID(id)
+ if !found {
+ pc.Status = iris.StatusNotFound
+ pc.Tmpl = "profile/notfound.html"
+ pc.Data["ID"] = id
+ return
+ }
+
+ pc.Tmpl = "profile/profile.html"
+ pc.User = user
+}
+
+
+/*
+func (pc *ProfileController) Post() {}
+func (pc *ProfileController) Put() {}
+func (pc *ProfileController) Delete() {}
+func (pc *ProfileController) Connect() {}
+func (pc *ProfileController) Head() {}
+func (pc *ProfileController) Patch() {}
+func (pc *ProfileController) Options() {}
+func (pc *ProfileController) Trace() {}
+*/
+
+/*
+func (pc *ProfileController) All() {}
+// OR
+func (pc *ProfileController) Any() {}
+*/
+```
+
+Iris web framework supports Request data, Models, Persistence Data and Binding
+with the fastest possible execution.
+
+**Characteristics**
+
+All HTTP Methods are supported, for example if want to serve `GET`
+then the controller should have a function named `Get()`,
+you can define more than one method function to serve in the same Controller struct.
+
+Persistence data inside your Controller struct (share data between requests)
+via `iris:"persistence"` tag right to the field or Bind using `app.Controller("/" , new(myController), theBindValue)`.
+
+Models inside your Controller struct (set-ed at the Method function and rendered by the View)
+via `iris:"model"` tag right to the field, i.e ```User UserModel `iris:"model" name:"user"` ``` view will recognise it as `{{.user}}`.
+If `name` tag is missing then it takes the field's name, in this case the `"User"`.
+
+Access to the request path and its parameters via the `Path and Params` fields.
+
+Access to the template file that should be rendered via the `Tmpl` field.
+
+Access to the template data that should be rendered inside
+the template file via `Data` field.
+
+Access to the template layout via the `Layout` field.
+
+Access to the low-level `context.Context` via the `Ctx` field.
+
+Flow as you used to, `Controllers` can be registered to any `Party`,
+including Subdomains, the Party's begin and done handlers work as expected.
+
+Optional `BeginRequest(ctx)` function to perform any initialization before the method execution,
+useful to call middlewares or when many methods use the same collection of data.
+
+Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
+
+Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
+and it adds its logic to its `BeginRequest`, [here](https://github.com/kataras/iris/blob/master/mvc/session_controller.go).
+
+**Using Iris MVC for code reuse**
+
+By creating components that are independent of one another, developers are able to reuse components quickly and easily in other applications. The same (or similar) view for one application can be refactored for another application with different data because the view is simply handling how the data is being displayed to the user.
+
+If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
+
+
+Follow the examples below,
+
+- [Hello world](_examples/mvc/hello-world/main.go)
+- [Session Controller](_examples/mvc/session-controller/main.go)
+- [A simple but featured Controller with model and views](_examples/mvc/controller-with-model-and-view).
+
+### Bugs
+
+Fix [#723](https://github.com/kataras/iris/issues/723) reported by @speedwheel.
+
+
# Mo, 14 August 2017 | v8.2.6
Able to call done/end handlers inside a `Controller`, via optional `EndRequest(ctx context.Context)` function inside the controller struct.
@@ -49,8 +266,8 @@ Our `Controller` supports many things among them are:
- all HTTP Methods are supported, for example if want to serve `GET` then the controller should have a function named `Get()`, you can define more than one method function to serve in the same Controller struct
- `persistence` data inside your Controller struct (share data between requests) via **`iris:"persistence"`** tag right to the field
-- optional `Init(ctx) or BeginRequest(ctx)` function to perform any initialization before the methods, useful to call middlewares or when many methods use the same collection of data
-- optional `Done(ctx) or EndRequest(ctx)` function to perform any finalization after the methods executed
+- optional `BeginRequest(ctx)` function to perform any initialization before the methods, useful to call middlewares or when many methods use the same collection of data
+- optional `EndRequest(ctx)` function to perform any finalization after the methods executed
- access to the request path parameters via the `Params` field
- access to the template file that should be rendered via the `Tmpl` field
- access to the template data that should be rendered inside the template file via `Data` field
@@ -96,7 +313,7 @@ import (
// Index is our index example controller.
type Index struct {
- router.Controller
+ mvc.Controller
// if you're using go1.9:
// you can omit the /core/router import statement
// and just use the `iris.Controller` instead.
@@ -116,7 +333,7 @@ func (c *Index) Post() {}
> Tip: declare a func(c *Index) All() {} or Any() to register all HTTP Methods.
-A full example can be found at the [_examples/routing/mvc](_examples/routing/mvc) folder.
+A full example can be found at the [_examples/mvc](_examples/mvc) folder.
# Sa, 12 August 2017 | v8.2.4
diff --git a/README.md b/README.md
index 9b118b59..687f9f6c 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,22 @@
+**Powered by [KeyCDN](https://www.keycdn.com/)**, A Simple, Fast and Reliable CDN.
+
Iris is a fast, simple and efficient micro web framework for Go. It provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
-[Star or watch](https://github.com/kataras/iris/stargazers) this repository, it is still in **active development mode**.
+We have no doubt you will able to find other web frameworks written in Golang
+and even put a real fight to learn and use them for quite some time but
+make no mistake, sooner or later you'll be using Iris, no because of the ergonomic high-performant solution that it provides but its well-documented unique features are these will transform you to a real rockstar geek.
+
+No matter what you're trying to build, Iris takes cover
+every type of applications, from micro services to large monolithic web applications.
+It's actually the best piece of software for back-end web developers
+you could ever find online.
+
+Iris may have reached version 8, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
+
+[Star or watch](https://github.com/kataras/iris/stargazers) this repository to stay updated at general technology progress. It's the only thing we ask from you, it's our sign.
[](https://travis-ci.org/kataras/iris)
@@ -18,7 +31,6 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
-
@@ -30,7 +42,8 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
* [Learn](#-learn)
* [HTTP Listening](_examples/#http-listening)
* [Configuration](_examples/#configuration)
- * [Routing, Grouping, Controllers, Dynamic Path Parameters, "Macros" and Custom Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context)
+ * [Routing, Grouping, Dynamic Path Parameters, "Macros" and Custom Context](_examples/#routing-grouping-dynamic-path-parameters-macros-and-custom-context)
+ * [MVC (Model View Controller)](_examples/#mvc)
* [Subdomains](_examples/#subdomains)
* [Wrap `http.Handler/HandlerFunc`](_examples/#convert-httphandlerhandlerfunc)
* [View](_examples/#view)
@@ -248,7 +261,7 @@ Compared to the rest open source projects, this one is very active and you get a
* Remove trailing slash from the URL with option to redirect
* Virtual hosts and subdomains made easy
* Group API's and static or even dynamic subdomains
- * MVC [**NEW**](_examples/routing/mvc)
+ * MVC [**NEW**](_examples/mvc)
* `net/http` and `negroni-like` handlers are compatible via `iris.FromStd`
* Register custom handlers for any HTTP error
* Transactions and rollback when you need it
@@ -313,17 +326,19 @@ The most useful community repository for _iris_ developers is the
$ go get -u github.com/iris-contrib/middleware/...
```
-#### 📈 One and a half years with You...
+#### 📈 One and a half years...
-- 7210 github stars
+[](http://iris-go.com/graph)
+
+Iris exceeded all expectations, started as one-man project.
+
+- 7288 github stars
- 766 github forks
- 1m total views at its documentation
-- ~800$ at donations (there're a lot for a golang open-source project, thanks to you)
+- ~800$ at donations, small amount for the work we put here but it's a good start
- ~554 reported bugs fixed
- ~30 community feature requests have been implemented
-Thank You for your trust!
-
### 📌 Version
Current: [VERSION](VERSION)
@@ -354,12 +369,23 @@ Employers that are looking for brilliant Software Engineers with good experience
### 🥇 People
-The original author of _iris_ is [Gerasimos Maropoulos](https://medium.com/@kataras)
+The original author of _Iris_ is [@kataras](https://github.com/kataras), you can reach him via
+- [Medium](https://medium.com/@kataras)
+- [Twitter](https://twitter.com/makismaropoulos)
+- [Dev.to](https://dev.to/@kataras)
+- [Facebook](https://facebook.com/kataras.gopher)
+- [Mail](mailto:kataras2006@hotmail.com?subject=Iris%20I%20need%20some%20help%20please)
-The current lead maintainer is [Bill Qeras, Jr.](https://github.com/hiveminded)
+[List of all Authors](AUTHORS)
-[List of all contributors](https://github.com/kataras/iris/graphs/contributors)
+[List of all Contributors](https://github.com/kataras/iris/graphs/contributors)
-Help this project to continue deliver awesome and unique features with the higher code quality as possible
+Help this project to continue deliver awesome and unique features with the higher code quality as possible by donating any amount
-[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
\ No newline at end of file
+[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
+
+## License
+
+This software is licensed under the open-source 3-Clause BSD.
+
+You can find the license file [here](LICENSE), for any questions regarding the license please [contact](mailto:kataras2006@hotmail.com?subject=Iris%20License) us.
\ No newline at end of file
diff --git a/VERSION b/VERSION
index 98f0b636..74455118 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.2.6:https://github.com/kataras/iris/blob/master/HISTORY.md#mo-14-august-2017--v826
\ No newline at end of file
+8.3.0:https://github.com/kataras/iris/blob/master/HISTORY.md#fr-18-august-2017--v830
\ No newline at end of file
diff --git a/_examples/README.md b/_examples/README.md
index 32ee5fb5..4a25e898 100644
--- a/_examples/README.md
+++ b/_examples/README.md
@@ -82,7 +82,7 @@ Navigate through examples for a better understanding.
- [Overview](routing/overview/main.go)
- [Basic](routing/basic/main.go)
-- [Controllers](routing/mvc)
+- [Controllers](mvc)
- [Custom HTTP Errors](routing/http-errors/main.go)
- [Dynamic Path](routing/dynamic-path/main.go)
* [root level wildcard path](routing/dynamic-path/root-wildcard/main.go)
@@ -93,6 +93,66 @@ Navigate through examples for a better understanding.
* [new implementation](routing/custom-context/new-implementation/main.go)
- [Route State](routing/route-state/main.go)
+### MVC
+
+
+
+Iris has **first-class support for the MVC (Model View Controller) pattern**, you'll not find
+these stuff anywhere else in the Go world.
+
+Iris web framework supports Request data, Models, Persistence Data and Binding
+with the fastest possible execution.
+
+**Characteristics**
+
+All HTTP Methods are supported, for example if want to serve `GET`
+then the controller should have a function named `Get()`,
+you can define more than one method function to serve in the same Controller struct.
+
+Persistence data inside your Controller struct (share data between requests)
+via `iris:"persistence"` tag right to the field or Bind using `app.Controller("/" , new(myController), theBindValue)`.
+
+Models inside your Controller struct (set-ed at the Method function and rendered by the View)
+via `iris:"model"` tag right to the field, i.e ```User UserModel `iris:"model" name:"user"` ``` view will recognise it as `{{.user}}`.
+If `name` tag is missing then it takes the field's name, in this case the `"User"`.
+
+Access to the request path and its parameters via the `Path and Params` fields.
+
+Access to the template file that should be rendered via the `Tmpl` field.
+
+Access to the template data that should be rendered inside
+the template file via `Data` field.
+
+Access to the template layout via the `Layout` field.
+
+Access to the low-level `context.Context` via the `Ctx` field.
+
+Flow as you used to, `Controllers` can be registered to any `Party`,
+including Subdomains, the Party's begin and done handlers work as expected.
+
+Optional `BeginRequest(ctx)` function to perform any initialization before the method execution,
+useful to call middlewares or when many methods use the same collection of data.
+
+Optional `EndRequest(ctx)` function to perform any finalization after any method executed.
+
+Inheritance, see for example our `mvc.SessionController`, it has the `mvc.Controller` as an embedded field
+and it adds its logic to its `BeginRequest`, [here](https://github.com/kataras/iris/blob/master/mvc/session_controller.go).
+
+**Using Iris MVC for code reuse**
+
+By creating components that are independent of one another, developers are able to reuse components quickly and easily in other applications. The same (or similar) view for one application can be refactored for another application with different data because the view is simply handling how the data is being displayed to the user.
+
+If you're new to back-end web development read about the MVC architectural pattern first, a good start is that [wikipedia article](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller).
+
+
+Follow the examples below,
+
+- [Hello world](mvc/hello-world/main.go)
+- [Session Controller](mvc/session-controller/main.go)
+- [A simple but featured Controller with model and views](mvc/controller-with-model-and-view).
+
+
+
### Subdomains
- [Single](subdomains/single/main.go)
@@ -213,11 +273,14 @@ iris session manager lives on its own [package](https://github.com/kataras/iris/
iris websocket library lives on its own [package](https://github.com/kataras/iris/tree/master/websocket).
+The package is designed to work with raw websockets although its API is similar to the famous [socket.io](https://socket.io). I have read an article recently and I felt very contented about my decision to design a **fast** websocket-**only** package for Iris and not a backwards socket.io-like package. You can read that article by following this link: https://medium.com/@ivanderbyl/why-you-don-t-need-socket-io-6848f1c871cd.
+
- [Chat](websocket/chat/main.go)
- [Native Messages](websocket/native-messages/main.go)
- [Connection List](websocket/connectionlist/main.go)
- [TLS Enabled](websocket/secure/main.go)
- [Custom Raw Go Client](websocket/custom-go-client/main.go)
+- [Third-Party socket.io](websocket/third-party-socketio/main.go)
> You're free to use your own favourite websockets package if you'd like so.
diff --git a/_examples/hello-world/main.go b/_examples/hello-world/main.go
index b5b38874..ea2713bc 100644
--- a/_examples/hello-world/main.go
+++ b/_examples/hello-world/main.go
@@ -5,13 +5,13 @@ package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/context"
+
"github.com/kataras/iris/middleware/logger"
"github.com/kataras/iris/middleware/recover"
)
func main() {
app := iris.New()
-
// Optionally, add two built'n handlers
// that can recover from any http-relative panics
// and log the requests to the terminal.
@@ -21,7 +21,7 @@ func main() {
// Method: GET
// Resource: http://localhost:8080/
app.Handle("GET", "/", func(ctx context.Context) {
- ctx.HTML("Hello world!")
+ ctx.HTML("Welcome!")
})
// same as app.Handle("GET", "/ping", [...])
diff --git a/_examples/mvc/controller-with-model-and-view/main.go b/_examples/mvc/controller-with-model-and-view/main.go
new file mode 100644
index 00000000..39e7c578
--- /dev/null
+++ b/_examples/mvc/controller-with-model-and-view/main.go
@@ -0,0 +1,99 @@
+package main
+
+import (
+ "sync"
+
+ "github.com/kataras/iris"
+ "github.com/kataras/iris/mvc"
+)
+
+func main() {
+ app := iris.New()
+ app.RegisterView(iris.HTML("./views", ".html"))
+
+ // when we have a path separated by spaces
+ // then the Controller is registered to all of them one by one.
+ //
+ // myDB is binded to the controller's `*DB` field: use only structs and pointers.
+ app.Controller("/profile /profile/browse /profile/{id:int} /profile/me",
+ new(ProfileController), myDB) // IMPORTANT
+
+ app.Run(iris.Addr(":8080"))
+}
+
+// UserModel our example model which will render on the template.
+type UserModel struct {
+ ID int64
+ Username string
+}
+
+// DB is our example database.
+type DB struct {
+ usersTable map[int64]UserModel
+ mu sync.RWMutex
+}
+
+// GetUserByID imaginary database lookup based on user id.
+func (db *DB) GetUserByID(id int64) (u UserModel, found bool) {
+ db.mu.RLock()
+ u, found = db.usersTable[id]
+ db.mu.RUnlock()
+ return
+}
+
+var myDB = &DB{
+ usersTable: map[int64]UserModel{
+ 1: {1, "kataras"},
+ 2: {2, "makis"},
+ 42: {42, "jdoe"},
+ },
+}
+
+// ProfileController our example user controller which controls
+// the paths of "/profile" "/profile/{id:int}" and "/profile/me".
+type ProfileController struct {
+ mvc.Controller // IMPORTANT
+
+ User UserModel `iris:"model"`
+ // we will bind it but you can also tag it with`iris:"persistence"`
+ // and init the controller with manual &PorifleController{DB: myDB}.
+ DB *DB
+}
+
+// Get method handles all "GET" HTTP Method requests of the controller's paths.
+func (pc *ProfileController) Get() { // IMPORTANT
+ path := pc.Path
+
+ // requested: /profile path
+ if path == "/profile" {
+ pc.Tmpl = "profile/index.html"
+ return
+ }
+ // requested: /profile/browse
+ // this exists only to proof the concept of changing the path:
+ // it will result to a redirection.
+ if path == "/profile/browse" {
+ pc.Path = "/profile"
+ return
+ }
+
+ // requested: /profile/me path
+ if path == "/profile/me" {
+ pc.Tmpl = "profile/me.html"
+ return
+ }
+
+ // requested: /profile/$ID
+ id, _ := pc.Params.GetInt64("id")
+
+ user, found := pc.DB.GetUserByID(id)
+ if !found {
+ pc.Status = iris.StatusNotFound
+ pc.Tmpl = "profile/notfound.html"
+ pc.Data["ID"] = id
+ return
+ }
+
+ pc.Tmpl = "profile/profile.html"
+ pc.User = user
+}
diff --git a/_examples/mvc/controller-with-model-and-view/views/profile/index.html b/_examples/mvc/controller-with-model-and-view/views/profile/index.html
new file mode 100644
index 00000000..7a97e7df
--- /dev/null
+++ b/_examples/mvc/controller-with-model-and-view/views/profile/index.html
@@ -0,0 +1,13 @@
+
+
+
+ Profile Browser
+
+
+
+
+ This is the main page of the /profile path, we'd use that to browser profiles.
+
+
+
+
\ No newline at end of file
diff --git a/_examples/mvc/controller-with-model-and-view/views/profile/me.html b/_examples/mvc/controller-with-model-and-view/views/profile/me.html
new file mode 100644
index 00000000..f60f998c
--- /dev/null
+++ b/_examples/mvc/controller-with-model-and-view/views/profile/me.html
@@ -0,0 +1,13 @@
+
+
+
+ My Profile
+
+
+
+
+ This is the current's user imaginary profile space.
+
+
+
+
\ No newline at end of file
diff --git a/_examples/mvc/controller-with-model-and-view/views/profile/notfound.html b/_examples/mvc/controller-with-model-and-view/views/profile/notfound.html
new file mode 100644
index 00000000..ca4b1986
--- /dev/null
+++ b/_examples/mvc/controller-with-model-and-view/views/profile/notfound.html
@@ -0,0 +1,13 @@
+
+
+
+ Not Found
+
+
+
+
+ User with {{.ID}} doesn't exist!
+
+
+
+
\ No newline at end of file
diff --git a/_examples/mvc/controller-with-model-and-view/views/profile/profile.html b/_examples/mvc/controller-with-model-and-view/views/profile/profile.html
new file mode 100644
index 00000000..3d16a91e
--- /dev/null
+++ b/_examples/mvc/controller-with-model-and-view/views/profile/profile.html
@@ -0,0 +1,13 @@
+
+
+
+ Profile of {{.User.Username}}
+
+
+
+
+ This is the profile of a user with ID: {{.User.ID}} and Username: {{.User.Username}}
+
+
+
+
\ No newline at end of file
diff --git a/_examples/mvc/hello-world/main.go b/_examples/mvc/hello-world/main.go
new file mode 100644
index 00000000..8797db87
--- /dev/null
+++ b/_examples/mvc/hello-world/main.go
@@ -0,0 +1,105 @@
+package main
+
+import (
+ "github.com/kataras/iris"
+ "github.com/kataras/iris/mvc"
+
+ "github.com/kataras/iris/middleware/logger"
+ "github.com/kataras/iris/middleware/recover"
+)
+
+// This example is equivalent to the
+// https://github.com/kataras/iris/blob/master/_examples/hello-world/main.go
+//
+// It seems that additional code you
+// have to write doesn't worth it
+// but remember that, this example
+// does not make use of iris mvc features like
+// the Model, Persistence or the View engine neither the Session,
+// it's very simple for learning purposes,
+// probably you'll never use such
+// as simple controller anywhere in your app.
+//
+// The cost we have on this example for using MVC
+// on the "/hello" path which serves JSON
+// is ~2MB per 20MB throughput on my personal laptop,
+// it's tolerated for the majority of the applications
+// but you can choose
+// what suits you best with Iris, low-level handlers: performance
+// or high-level controllers: easier to maintain and smaller codebase on large applications.
+
+func main() {
+ app := iris.New()
+ // Optionally, add two built'n handlers
+ // that can recover from any http-relative panics
+ // and log the requests to the terminal.
+ app.Use(recover.New())
+ app.Use(logger.New())
+
+ app.Controller("/", new(IndexController))
+ app.Controller("/ping", new(PingController))
+ app.Controller("/hello", new(HelloController))
+
+ // http://localhost:8080
+ // http://localhost:8080/ping
+ // http://localhost:8080/hello
+ app.Run(iris.Addr(":8080"))
+}
+
+// IndexController serves the "/".
+type IndexController struct {
+ // if you build with go1.9 you can omit the import of mvc package
+ // and just use `iris.Controller` instead.
+ mvc.Controller
+}
+
+// Get serves
+// Method: GET
+// Resource: http://localhost:8080/
+func (c *IndexController) Get() {
+ c.Ctx.HTML("Welcome!")
+}
+
+// PingController serves the "/ping".
+type PingController struct {
+ mvc.Controller
+}
+
+// Get serves
+// Method: GET
+// Resource: http://context:8080/ping
+func (c *PingController) Get() {
+ c.Ctx.WriteString("pong")
+}
+
+// HelloController serves the "/hello".
+type HelloController struct {
+ mvc.Controller
+}
+
+// Get serves
+// Method: GET
+// Resource: http://localhost:8080/hello
+func (c *HelloController) Get() {
+ c.Ctx.JSON(iris.Map{"message": "Hello iris web framework."})
+}
+
+/* Can use more than one, the factory will make sure
+that the correct http methods are being registered for each route
+for this controller, uncomment these if you want:
+
+func (c *HelloController) Post() {}
+func (c *HelloController) Put() {}
+func (c *HelloController) Delete() {}
+func (c *HelloController) Connect() {}
+func (c *HelloController) Head() {}
+func (c *HelloController) Patch() {}
+func (c *HelloController) Options() {}
+func (c *HelloController) Trace() {}
+*/
+
+/*
+func (c *HelloController) All() {}
+// OR
+func (c *HelloController) Any() {}
+*/
diff --git a/_examples/mvc/session-controller/main.go b/_examples/mvc/session-controller/main.go
new file mode 100644
index 00000000..0e3e16d4
--- /dev/null
+++ b/_examples/mvc/session-controller/main.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "time"
+
+ "github.com/kataras/iris"
+ "github.com/kataras/iris/mvc"
+
+ "github.com/kataras/iris/sessions"
+)
+
+type VisitController struct {
+ // if you build with go1.9 you can omit the import of mvc package
+ // and just use `iris.Controller` instead.
+ mvc.SessionController
+
+ StartTime time.Time
+}
+
+func (u *VisitController) Get() {
+ // get the visits, before calcuate this new one.
+ visits, _ := u.Session.GetIntDefault("visits", 0)
+
+ // increment the visits counter and set them to the session.
+ visits++
+ u.Session.Set("visits", visits)
+
+ // write the current, updated visits
+ u.Ctx.Writef("%d visits in %0.1f seconds", visits, time.Now().Sub(u.StartTime).Seconds())
+}
+
+func main() {
+ mySessionManager := sessions.New(sessions.Config{Cookie: "mysession_cookie_name"})
+
+ app := iris.New()
+
+ // bind our session manager, which is required, to the `VisitController.SessionManager.Manager`
+ // and the time.Now() to the `VisitController.StartTime`.
+ app.Controller("/", new(VisitController), mySessionManager, time.Now())
+
+ // 1. open the browser (no in private mode)
+ // 2. navigate to http://localhost:8080
+ // 3. refresh the page some times
+ // 4. close the browser
+ // 5. re-open the browser and re-play 2.
+ app.Run(iris.Addr(":8080"), iris.WithoutVersionChecker)
+}
diff --git a/_examples/mvc/web_mvc_diagram.png b/_examples/mvc/web_mvc_diagram.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d7956835703a0ba694a5ae74674287962d113da
GIT binary patch
literal 25515
zcmZ_01yojB*fqLoq@_a?=>{dFrKCYZx*G%}1VKRQP#P2jq>&WqZjcg5NofHorBe~^
z!}-4Zk8#KN?;hiLJo@tPz1MnTK6B1>Ua70e+B+
zxeWN%H5Uc_rwD>IeDx1XeZ(64AfZ!
zD1Gz#Vo=ZL?xhuO$p9Qi7la7A((3lpe+($d=}R#&4*E8%76irX8!k4|rkdKudn4?U
za8o+j2*p1brOiD~NEzFo6p{3fmd-;YKTO}1Q=p-xnk++a^GYf
zRHWb_6*NaRPm$#Ac%DpkH-MjT@!%_hWX6%f+aCP$8YK8MI4BrhLtx6a*@}5dZ(*@~
zu{u=jB8ebN?k`6!IC(0m{Y5eToi4JOw$bfOa5FtlB5W!MC99Fljqkb#E>~;wASqm6j3bgy;Zm7axMsf{tqP@u7!1K^1`8#g{Dm28$
z@Tz6%3+_<|%VpyZTe;s!i6PHS9d@gt7LOFnS{p95Bm5pCA=AU#woZw~_t}w`ySr1m
zT!`~pf@Oa>{YSjwZt60fk7$jrjx2}DJ<*8+9<*H(ME8`YGruXNpr@jzHlj?I`R{>`T|(8?Mj2y{VQotUKDjA-|rqj&|sYhaXB7)Dv#YN|j29MQKarNm=?%
zC6DS`1Xa%?-g|ep-mN7QCOd2EvDdKslEr<&d8_o6`7I4Q&!fVkyrTIcTks`qS3M~6fFoVvMq3abU*i{{=Zk4`kgAB4`p&SGRsYu
zo4V<6EvYKKIBz@P7l(QT2z+KPADH%i#@_huA8liVcu`@NuneCPpKhh*!5f?VSj+ER
zPv5(%U0Y+#Pda${dY*KL?FSo89=}Tc9cv?pqQs;`y<&x8jbiz&X2DOt^V9O0A2jO!
z4Iq%}QDQM-k++Q*#7JAyn%0`j$H^yG6&E@B#9id@fhMa_*0c3V{`R}{78Cz;73ci<
z=gn(>ZoJ_6_voL~pZp_vJUz@xJVwkJyg^E_E&*3(FPN(K#^s{q2qW#gjg}dgow}X6bC@xiZ!;^aZmMplXsXudvE_-Xh^q7qd>zOhm`D~#
z6~8licP(`-bux9K*37`mfU~Bcrp{WiV#Od&f4ipXNr-{4{s%p61LI1$lC%=0iN=z~
z{OkGkY6)t^=@Yh{WqUP!HHEcQWvf$|GoNalYF?PzT2R>5c75vHPH0Ye(eNVbpEwqN
z*fc>gtpV-Jr&VsGB03`TDTD8~@4x6eOx)nH)f*xjn&MloA?auPY@eo;`c2-J!nXct
z&_Q^_E9Jm`?j#BBRGRL&-sTl0_aj&1CGXLrwA9Mfj^70{rQ3qr_S<2z8DIG`*wXd+
zo(^~R$}DQEj@-x1c+2as@bTqCnTG+k)oTnqmOOE~C6i^fA3Tdr%y%dxjwReOmL-W)vi5E;ZqS|}ME&LZ#4wimPxFY*mv}58
zVIjJq_oH3H^{7YgBnfzq%*s4nko_Z@C_A5Od*`8WDVLG}yF?N9W3lh|8u%OTcXQk5
ze;wCL(^GU*z9G!b{z>qOW6Q$%?}M-(X+Ln6eJI}X^cuT7&l>DF49O=i=Sh
z>w{SeW1Cd)RpwGf(#cbv(K+1}7iqo|6dxDsDqVD2HKE3K)Apy$v;L3PAr%*0PaXrJk{zqU)>_x5b>P((;l$!}j)C7_E5FRd-h023arv9L
zgLo;v=0m8mE*Gud&*qPhg)78dpU>bdlVNa#>R^5pt9xC;?lk_e__uD-rg)fM&9reEZ2ZM|3^OcR0YYQhg@kPEri}d-pg!S##Gg@0(p2yND`zdm%S}A`G
zEesnTew7%R*qrx1m!G*s_?}-(|GCn^qdxoZ8-d>vZ1;yE)83}Nc*%V>Yc`Wzxj9=CaB9*oS&pJ0Xr4PC??#SqG1#3aY`hvJ8Br3*<2
z3;($9_HW}nUuMW^h&i3&BKxBM69qZX%hU0#$-$9#BUwrNNeQh^z90T=wrx?B&UANl
z-)!mg;=5yVsPp%CyXUMY?M}`D;lmYQIzQG+yR-5mt>OH?2~-IQm*{Bk4$dw4xMl!v
z(pf5Ksv?Lt6N2~$A;{S!{CfjIJa`dg(+ojG-XREubG&Jn9D?XgD9TDb^7{TO&C`ga
z?{5#w?L%hOqBrWK7`Wv@tE8kFV=^d5kA6*wbG%=uOv?`}@Z6jpKKMmHrCuIjEUToR
zk}0{~988RQ`}W_zmMshaT>TY1J^6O-`*tx+r`bL`uH+l~Dn^Lk$?GpkM|2q)U4#Tk
z(%IA9d6+KtDm+~B$X{~Q|CHikhQy1@3-KZ>*`YQ`ZAo(Ghv^@)vjaC#M)-zfX@~2|PkV`TxC%xw*N9hQ|MXA~v*8D?i%dUW!p-&V2<)Sedk|skUXU9orG5z_7D6z+v;}7~w5=Yj4aI2ReLiZf-kcwCo;uCUn#QOj4_#$G>MvO`4_u_bp3J
zTcaZ)WRCpTsbWh5_`jwI;KD({Tz$;m@QL#e5$;gvskjgOQ4&RQ2k4Or}s
zqH%R~brck3XD_O$IZnN=p{YqN@c2zsR8)0!b!uvARTcl$?MVarS8s}YAND1%#Kpx$
z%`^oDO1HlJ_viR`aX~>rW@ct~Ha#hh!{f(A#l`*o{gsuKt-fcYdJ?(0xv)-_&d%^*
z@5i<*a-)*esAy=_RaGxfm#`j@ImvU9|NZ-Sq0V)AyjZuaP+3t?^&PMI>F(0A|1KpZ
zr3EoX6B83h$6pXY9i3NKm&F~#UoBr$M5Ljvj#IxPST0ASY?~KdQ4j7?ZrP(QUUmIu
z%t_BJzx9oc_GjE29HM>~=UbDdxFH>b?{(;ij9PrByShT!Pj}4T_gSTj`@o;6e?*Pr
zj}5JLUSMWq%=A8GrjJ{nt$*6k(6Bo){_UISi$Cr!Uc8tz`S9t}_UYboN=nKudtP3i
zl$4Z3Ii(^Rs+6qIjloQ*pv7eoi_hT@kM3K`--m~Zh=|OrtuadN92`(^6m)SGFL?73?uwW`LkA={&u&gaFGZ4Rmr11{-
z*qXR)+Bw<#_3N*N*87Kdy?uNp$H$L+ucA;)Ts%wJZKXG^tE&rkSiBw9ikO5%L!5u@
z;@{t9?;})1PfyPmBK$8ovxvy|wl?IC(0t2_h+LF_Q;E~Rf4^)txV`
z^d-Qp=e8V#UY-jTVcmH6@F9W(wbyy=(ce+_+Fg`d_oiEpl7RrAr>BpPk5?C$^nLMS
zN%Rb!P?mcw1ed~&pE^pe(S3tB9uqy==NcYfaD&ToH~x>Lm$
z-|VV_gV8G{+n_3RN7EMP<{qy0Q_y?*sY5BjyCq?C*8>IF&A2?5XCy8$s*gt%;I;Ys
z4%T4LtEt)NB-;cn8yEH}yQQV2xw$!)D*gR?M{8>)28M*xROP6;osEqRC?XXV74Q)7
zdAVjx+5GV6=tLFM&McX*&lY>5AC%b=2s_5c+#%zkctBl}hY#7ldi4rwcX?S^ENd|b
z2M7Gk19}Z_?}oUz>(;+shlf*&dsl^q%1KF~+@Y|`G(oec6mc)e-9SYazgIh;2AGZJ
zDn1&;K-vm4axn3!AL-~E?=H#kXG*%i#--p64h>aG8K>YeK}R@^>SDd~q;j~88-g)O
z!_~^4{r(Q21@+ayO=YmZpO2SUT}OwTi|cJ%T-G~}kdP2>Z|}Qz@2;+{LX|9l%1;+T
z!Sl?;C6+b8+|~zfJN69&@sExk?2TiYan<8Ytgo+^O@@|ciTLN_Flghy
z7khs933EKAES4}7ns50JnZ8(^ilp(|6KmrK=Rx}6D57OAmrehIYT8$RkAWeme|2+x
zosybbbG-YuOsImgG7Ad}32!)BJmzSb`-bM!(J6%rB>
zMDp1)!4aHp$(S4ZP+No4S>N37S^6ezE{}#z9S-^Jn#g1N32M0TF*zwIf*c+l6*@8V
z6H!ONN)iw#C9CAEOc`ovp&{y;nw!xHfB*a;{_KkOzjsCV^YeSPFVJ=-_TWL+wvcc{
z7rnwq?>bp(RTFxVL5Kt48`1FNrly3*dOXu8R83wf=3hJo#l=tSjhvmGy@LB!MTCUl
zzZ>i8UF(gmstYa-4k{8H5;9`PulIXyY$W#W+b#@f?%51p-rkyCeJ)SUUKc@e9?bXx
z-HfoCmYn?O<|Ymk@?&wa&7yqt+qZfnZj}%A&d!9b3o8rT*yR}bA4^IejKVetVd4jp
z7!ylV99QXn)N5aInQ|0N>JJ(9zkD@dQ#t*Mj6#g*7I%|5$vIfBuJAr9!Ix*9?SX}b
zY*BIthljzJA){?Jm9P{^NffCA1h}}%ZuN8J{3O&g#u+F%)M{aIz
z4J(c=DkHi52%>(q?r)NkF8xC75Qsi#J*B0k@L*%tVc~szd`?$CVNlzdnfb#;O6hzu
zV$)~|{bY>00_$OVp_lT#=my$
z8b7rvF((%fPpi+#Hqwi-ErW^%BRc=YsP3_U&6X$Hn}I
z8G%7TJM+ynWMmjI>+9>J7UFuJwNz4Fgt7HrLL^pKRx{%DjNX-!T$a}f-o_C@b~wJ;J!RR7<$u`FALBu
zH-Mj!nfWf|N{cDnUS1wx4FfMPkuJK7j0^=u(k;L9k-@=ewadvj(_@8NA98a|T732O
z^$UxNl*(yR5)%_+V>3T~baZflzC7YO@yS?u-V494uMfIb37H&PD;x=u;L*9w|2uhLNlA&ve>gedWe{tH`-Hqw&{O8Y~
zR6(cLk&&bkT~5NCfVfbQ9Ql|$y@Bv9i@L8HT@e(YId5OB#jmOI*jOS>(&|s2xOsV9
zHpdE2|EjC1I(5A!<1uLh{BE~XXl4rOqP6j49>6p*3_B#KWHl6|BNHDAGl9o=Xy6lC
zp^t{OY7ZX{4GeHGG6t*VU!E?<)IOPG=j0T7`tvKms8lbKmRBD?a^6rnJv-Cbcyd$B
ziwG%$AnM?rmMp&A$8V&oqrPcMx{wnq|{4UNG+tC6^K_=;e)K4J#BOP6$*MJmmp3vxT1{J<4Eg}Y;>N~vKoC17k%xzes;PoY-wI%-
zW@?<2m6f-)x3<4m<058GPF%#VdVNUFd|$#41$=MaTLo%XX-NJjPqsaOVTOfA#bOJ|7a@QWJax+9j(w3o~HE?@CTiS0D`wj}jr1
zQc`+6U11H~THG{#Z7AcL9Y1DJ+?zMD-6_6*kGZ1c65hU5RZyUNX@3QK8RVm3CTe9V
z)zi?}I~cmZyflAwbab+_upp#kFjQ$XK#lm<*9#~0Gfn>P@9P_xn7~8;hGvjr$c@4b
z$JMs#=6AMA;X7{*VF}?{Tw2P~p-20v#QoW`Cp(*kI||0ebI?aokOY>8%n_yNifHx<
zii&;x{af3`zUQYS0|Tl`N;rsQK6KBFi{syh(&I96a+pRO}S8LS*u0#UAvj%LQ69Yo$nst6B34rQ)|{SF3~ASN!`A4Cr+D8FjkE3z5fTtVW~&2eou`tIIu=mRjR57>ibCcrqISl?i*;yiBqM=ERjKrfSWs{JY
z|N0dV$t)~Pe)A@Ht!BE)p5#@c8@)Xp{?$nAxnp8G7@M9h@$%fODc|LE*Ic^UUQ|U|
zIuJH9636vye|1MM9wtMm0v)q8oaDINcnyFPN0!_d5U7U9vAGq7e}gK!>+V0Hbwc6Esi=rA+I+tET
zW<_*(XBgeyeuIQX4akny{AQFohK3wmTyra>^3gZ$@bYGk*inYQlBEaW!o|e}g~!v=
z^HfOO_td4$9|filIXO8PE<#%OkB)xNHP!|Oc3cHVW^+x=Gss8w|1JoR9>Z(MB6I=x
zIQ&o$7U2cJ+K4sBy-^_v2?-cIoC^8N^?Z+iO`9}(w;%uc_VB)|(2ZBrz7J$&BPs8o
z65g(b=EVJeHUP$!PlmMwgfBN17k8^wwOl&wB0d^azsBw#isLt9Vq&^H8}dszq(+eK
z?QH;&yREJNj#GnzWYpB4T#aWxh|no92r++`m-llZ_1@OzCV+exNpSoGJT}L&uP%BP
zmVBp~8gt5;=La_Md-x-0E3lEy(-j}~nB1N|eJ(Z+Eh!YAX5xpiVH5x0{%Zfs4~M#^
zYXpaVCnPL1=?!K$+!mf1%40&S;C!l?mDQX0c+~q89%fG0(;6@P_fVhV
zT_)ymEPJB6Ds`=#EDlVEDFt!
zvP7Y8)p;Ln#xp5rnr7Z{7kSYedmHk$rnc4-IGktC(uG`ql$mz|C6pTGGch`90E0&q
z&Asn+7ZpWCo0pdS-|V86MPa!i-)3iLfpuWSXN(P%c=5;AX;Yb#JS;dEHiCacAU7|s
zgCH0;5E`DHq$7TH@n?%?YWBm25BHwTDkRz;>?l}S
ztw1}5P!fPztExIMVOv*KT@CzK3-ClRJjuz+*Pa+aRXi<(&WMJ*6Lg|xdwfk%=3DFb
zOzlu#9~&2!Dl2JsPfufGqa~%F?cn>|T*d&&U1-9ps;Z|Pwzg|POuz*w&S|cJtg{T>
z?gs(3ynGLI*lNx6pI6xv^#Xs`@D;!|8cJI9hW3WpZ_L89uYpc%@;Tw=*TJ8Tnk-No73Eod~4
z_|RE|gz7wZ=1(830`T1XbL8-Yf`+MP%dTt0yK3{)_W}lL;E1{`$^!!0fi-)1z9;Wa
zp&+lXPh~W!qoENHHqXe+O8caT1oe2T8luF?$%*#XEyBOq@ID);iX+3r>yxFoGKOLz
zA{e|h;`oVD02sp>=;`ab3cZK177Ek$bYx(Jv;H0r;64i*n`ghK
z?m*)LtZ{q^ftMaA7m#-y3f3oW0MFsrEdMLTUO0hMLM$J29g09!W?rPt5tyxw7w8|Icr
zmZV4?#+R>OWk>z(P|Edwt*zBs_7G&L+c`PCSp7mKcGETDaN`@6HnjjR6rDLq;6wPC
z?%!Vk3V3G5kd^e4Nps48P3C!g&M$3Bnsfy!7i2jEAaQzAG;6@xSP0Sc;PBk6
znZnZ21*mWkFwmu-gu&fXY(*TF{ZY@TS<&!b4lVCt8%o3DHa~jwNF`lNn4MioPVOhH
z6x5&X-NNJTxtV7@=z*l8>c+-Vxl+W}aB*P-HBnRql1kCcY;ibCM)axR`h=W1TEV`r
z#4UVQMqIqk8s`NG`_WvOW##4N1ykn~jS=Mh&sHgL#d03X_+}Lvh_qT7}G@P>o0e1Dd
z&8;mI1WJpYq?CT0(KaxfIXOA-378|VX0#^_!|*;JN8D}eQk&buks6i6hN_X4md3}&
zhmVhM+T_)4b+>WHz)RB+9Rr3O)Jwxt;NKp>{M<1yA;`z~^l*IyuDK#y1aWrWF|2)(
z^*BGCpGcUKH$IfW4LE7YLmh4Hf%n1(Fo1fseA~`}I^4VU`1)_uJkr~4n@AN~wYBZcWbklsfDR;eFXWdQC1$0ufq?-`wRXmxy7j(i
z6=7jmq2{gU`~47Ctv=7c^!Ed}*7&Ym(YfRT=m8RVNep9CAT|E6affs#1CO^9S&!fG+KSC}iP2A^MeYpEf#Fw(e6?wKpWs|--~*3yMECzF;QDVK8FiJNXg*XOSXjimD>!YK&AcC+Y4m@
zBp%`-MrzS6H8mcgFxbB
z=SPe|$AkuSwOghXjNXy~=2O6dJI~g&oAE+10T`{kiwY{C1dvMNW-F_(e~3S~ie0ET
z{#-|eb^l<-eI6d3O8`tiwoq@VkByIKVMR90HhK~wyr5rczDfMjf{EAJLSDT$Y3erz
zH3!%xJv~bEf6=N?ynuj`yG^5jGb@q>C0b8UVI^04?3Vi27QJ!MT7!mX}PZDC;n(el~CHDMV#ieA0jhc|D=R{N9sMyrE9
z8aI-kQTO%(Z2NB(v0~%3{;&j%&)(kt_U+qIgeHy8oFJC@segBb;4(HSp88AE5oOv&
zMt<(;VPRr|kEy;FisQ82&T3ILF`0)J_Vnpf&>Fve`v%hEsjtKhvHxLTS=reE=S>2E
z2}})?#{+xmg{J3dh>-VTL1iVMnA_#W3qnFdn6E%H11&D$)AKYT*T7!k$?GM%L-#M+
zxY6|GNI-h`paH<@_wQ&DdI%)Rj3GO7!p6^^&p-?2PMQf&zQ@lGMFSU+%o!mjCg#QS
z0Y<`zvkD|KynuBnP}v?LSnnVQE0LKu1Rh5VVK|UkG&+k_y5ank%jk
z4j^;stk|J7U2ScsX4ba0`9PNE<-v^jT9*F27Fe3Y-Q9Pho=;{b&p&r{EoI%+`KY(F
zx@v7{sim)957Pk@$k@YyO8A}Ya(CR58?utzbW}vaLH(eG~)YQ*T-GCx~^5lt3C|*DuHDmeYF1$TJgk@VO(`kIRgP_$-
zIX-I~WB{6!fgy%g^ykV-@ZXgU_lDwP*oeo5gD&T%u1%D11DB*!*dRSl=AC}q_CSmz
zZewYlrRetMy=^GqZmUWT4qF6b%npb5Ch&@6!doZIOrd42vp;9Eq#|N%?y1Pe4Mmqk}g~wKXQc_=k2CxtYtLHSH
zs;W_vbdrS68w*NH2?+?&1srsiHA>k>b5qj|TH4gly_9z>KEpT$+w@)2zphT`H02|t4W~T_Vcki@AnBZ*o&xl6
zmzRQ<2JCM8E<8Xk2O^w@kMF?2bYgtmN#idW85yKT9njyv6YP139j>TTawh=>G!YA`
z2mLTDZEmOa@&ZOsg7;hk0^j#6*Bwh<7{giYuWZj0w3Kxn~trJC{!2ppyUlMxqhhPr+%8PG@s%&fmZ
za`?6OgHJVxxW0ltU%qSt2BM|Ju2OJva*~wvPe>q(`PFX@i?duU;FJHdg^{_TK--E_>lUvf<{C3ND
zN|#0Zb|-vA*5v!#>hA>htUNro1Rj$fastJgAtZQ1#2uU9Tue-?qO#IegHq6O3hE=<
z@;I;*!utYMLJh%tV?cyJBZVRDguSoYVUnMpe{qJDkx^D(KUps4#Z$qk!%=m0bu)8w
zlmW1OFr-kyd>9x(3!R4&FSN1I$IDBoD;;IU!EO)+aOeXl2=vYC*RL-$ivjLK!y*Wt
z$AXK3f?|KdDK+;>AqVzq7n*rLLmm@MYA(V>u=RzLJibv44I)b4$IW4}2=a-0BB{P<
zlMtoJvywtTC&R?KeN=SrtSsZl$$PYi!+-;I;vRDSN~Yev=!{4+>l-trqC-be1$w*a8XI-AY2`s-%H)hm={e
zKp{Sh|Bn?p+D_Kio8!jECFewtNLtav+!%6hr>jX`;AZ552y(@`QghHz5QL8pFpJyh
zoT#1~aY==}KDC*R1Pp(u+~#(ogg3?fJ72>H3&tT)U@wAwHhb%|73qjz%8hgA$i}cI
zdL`?^CDL;EuBpvf5AK!OR(YEghv}Ki=T*0*m&uSB31h*ZKSRjqQ%8YxFQ;R_dvX#s
zLItx^?|klEK0W|K>+T};9s9Ra%Kl^cOG>y3yIe0X{<*v$Vyo#BcpLiXk2fVI4=-;l
zbqT;kT;$41RY6nXveW@q3dKxr@I&xX;<2$6f^rwzynuCbK`ZPQdicO
zR2gogI>`@{gAE>A2ogcgMPE$@t&+Izk0`ji5Rl4d%d61P(SK^rRy$B6W0FFE^L}RN
zN}K==eb|ow`SY_cUzGTtuu$;UUDPuMjremBPN(>zVx*=318bQa*NDb(qz`22oAcU<1yZ=&I1~5J_
zb(#F?rEyX@na6Q&E}tDRz5*Q{h&JJ;KNkz}3fdu+Gd*OvS1xT!y^nt-H#h$SqY!ZN
z3e9re%X^TXT^6ny8rSi5K3JR=d
zpWtl)m<}ir|FF6oO7KVl@9Au3hmDWVRCtZ}0}TBDgl(*>KrCE-rN$Zr+=o#l*53`;
zq)_Op2m;Nl@cUS%{jkd2ec+Xi8=tAV<)VCg>iP6(P`w%}=9^G}RwTyMAX&j~!D7nA
z+*BRU!wib((5H+)re-W!mi+zX7w9?TV`DIcLo8k6!iRnd3JMhH?xz>(gg}D{3)h1-
zu(|p8ZsO&aw;Zr0Z;6aqxw)fbV&F~kHXC2Jo3)+VgIdT`#a}k
zE4tgG$bvQ}c|?bx^cI*Xu0B?YJFE8qJ{I+DDi&KLja#?{^R!LfkQ$TVMz0w!3?CS%
zDA&Wp6sUVUpcV&wj!jI&M9QkFa?7G%s(_kS2m(uTGEg8)cBNS#KjI<7jh@br?+Mw_
z@h7^6%hH$Yab9ciH`fDn<*j>Nni{YuA_>$;b1NYZ4!s5s1vPdQ6Dlr(sO*w610uw|JZ
zCp0GRq*UOaDJj=?c6PS59KgjjhKOZorx3|c7Y3tSH=k5LHH5tcZW&5g|
z@C|~betbNh=?HmfToYTeT1I=#6yrF+{M+^4|#c*svsP+=Nv9R
z-)m}WdZ%JzW=40O0$wX{azu1>@J50Y5s>c7vfA37p+1Z9#X_jKC`eXT)^0X!Z#4xm
zF)H#-#N+WvXpjBz%01zIReQVrh40@DK&%4d{l{}w1o{6}s7{KC*zr1H@yWRia)FR4
zDR}{oyrye4UT?{Vb^*MgpkaawiH3>_Ci$8N$y*4(1n6>b!4H9N;OEbufEsQHyR8IY
zBgs=sgFkHmMiTKaDRDAHmF)Wbc~SnI3bP|SW$4-IDQ~ih9fPB;*Hfo+luNu+l7zm)
z{r$k6ERn_EC3~g2g4YOtP;?O1TrW_ERs4*qTUv7z9hPjW%FP6H
zf3o0rOUNZNGE&9TR0c{ISORWb+Xl&cz^13W8`yZzo;wxlfk|QoMyIzoJR-se=J+jL
z`#n6!(^(Lw0MmeJbfML6K~G7KsqyjZ&IxI>
ztnxW=X6GNG>${4PQBgVB*`JK+-Ih8+x@&F;yUBu7_44BHPAe$KPt44qSN>XFe){*=
z326g7c>%n~e6x>(gTpOw-yJzrdtpMe1l|TXP8pfbPf?;3ATE;A8bWUYZh9Q%mI(f~
z2~>>iv(&=pT8=rAi%!*V$&yvSTZL6ubZ%d!Cxoi0WqbMf$m~S7w%-3L5sH@KTktWe
z)y&!X7AYzE3`~X`7gPjOnddV2Cgm0(@S$HiRdem%=*4R)iqFilI$4(Xyr32Qo-EA(
zzxgZKQ&So&30P_xUNa=J_ft-e7EpF!VOIe2mwB~UC=iL_Kx%+I1ZBmz?nNCKbFYA5
zD82)C2*ARsii*IwPUv3~L*PFivbqb2WQU
z5X2%UdGQ1LHXhh8vy56xZ4@U)JIC8o7r>q($m!Wy#*8Q(_)@LP^>%N9MQmxAf0#Al
z!SmW+*!!Ajg~RdO{mP8%IIJUi^7u
z)PmUygDv%6YU;~Ecx~VrRa?N;m64KybKmc5ALAmp-E4703H%~^2UT8};d-0|*RBDN
zdS=i!1OM?7yyY`
z&wlhhkN{?uPdz+B2zbtMqgt==pwqA)TKgZ{ItK)xh9?hgwm$1XSl}76TpWLZ;{vo}
zA#au(eolibu(TC){a=N;8tZ=Y|B>bZt-iZ=4T^B1mL^Iks7$-
z;85{Gm=&mi;5p|e{rDuXe-)}|Gzj;)=-`sH?oY(P#1vF42Y0uMiVDCam-4o0K(PpN
zD?>AWch}`VFmxxPr)_5eOd-IzffhA7dtVlRF%z^1MMkbm5Q1T;u4&Z}UDck&|1RJp
zykvEBCP3zIZ(njqRt4R;#&H@%EKF17sDO(+g2odCl#ykYC3;twn2PIDLaFQu22{3_)}oRF-EKX
zqF|E0u0}&r>6N+HD-YCwTE4A~jpmLZ_~)UZgH(q%BK+CnPU^t%-kz(Ai(QkTl(F%P
z@gnWXQqvuFPcq-MXD9BXCoqMXJa`Zymji!v
ziRPn6Rb^#B*xv%DA+KdO9w2n$$(;w~~bG4TvM
zEevvz{!asp!*g;h;XDE0_?DWufU)4PFyPzt4Gn21DdDyO|D18Y0T*ULm8`6!M2UwP
z)D9LaJIk4YfdSo5MrRNret!;Moic+=-T5jgU5b$yVq_a;KtL~!#P_63P5ofJ18i_A
zk+cmm-tgpPzMh*6wh>O%i@(o05{dXXIH+l8&=6SLLU)>H+q18R-h;9PrrN<|zWlx|
zT~Nlan*^@|{J4I+$6Cb$Jj;H@CaOxhS2ybyF9qsq+fxF+D-J8bIUD_9AoqJ
zJo(wkzOuRsWK=T%T=?dbsm|epPtzd90q^?^Jm@g=sCG1f!yKfbC({+HVD$z#weTSr
z-Wvk}IWEjXfUq(aj85-+L(VVV>ta(R2yL1rlHvaOzPm^Dc=lyS5H^hha<^{7Ue=591)#MZxR!2Y_BfhR#=#sU!LuM
z*qh)C2OI(g3J{bKqyx-GApE2Lx#Fq7u3lx{8G8TnJb}6a(Hh-aUk9{8G<|(OkRQfT
z*bg32(Z66J0#L$uI1t=1Xq7V;&Er4T%!VbeYEGb-2vY?2qGdaDtmxcq?ve{q<1DXpi82)*9O5x$A
zr>4Mn4>TK`mw+vUZH2Z9zk(M7psbGDF|76x@a-VU0`i4g1=rWFk*A{>@7@WyEJ^~l
z`1)1FmqetHO4tqFZUuykE4v_lQY8j#hf%Y)$Hn;>WRaSR3ggZ{@W=$JB>;6%XPoZ<
z&!(uN!pYA51+?uiU)W(X0@|{_A0p_&L}Lj!XLvUle?YuQPNq6{G&dh<_4C6Fk{bQH
z{_`ie*{l9X%SkY%0LuCAa?E`IQEoyvfXCcdL<=R)xMp9S(4=)#*Zy3pgawVB`m8mbgbP%<(>3XB~LIvt{@
z%kscqw(Hr$3ITpDYlMOEBaG4jQ{Y#4S`b)^ig09V06}Q*bdrU2Zoq+_Sz-~hHe{b#
z)6~@2#wIZ#;pzTLpLVg%_2xR$gNHUYHXXhFjC22LfTu+yA(SBg3YR9HJ$>2;6Qvb*
z`)q|Z%>7*!9yWVNo@*fQU|b#fiM9A3=diVT35G(%A4Fm!qZ{?xC@M4@OiW!im23xI
zU|4u>v8DQ5&BH?g?@=|7(D5lmBqV4-b|GP53E6E6Q*M=DyS+LGp$2Q9zb&PyN$h@p
zWr&qgzk*r7vp~ww^N{?Z2Em+r;d|3_KK(CvE%lDL+}t;X-56tf7+S=^GJ>fJ+C}D6
zB`l^?a=V5t<|}}{Z0zjx8s%`HE7Iq&we^E+l#$wdn?KjqI$LHTqr<79G4l%wu(=Px
z+eCjPc+gN^uOYfQau19EVG&wadRTT8*)Z-r1hxgH_f{Ra^&k!~pH2x03HkEHubt5I
z@sS))U-{A)Qz!-W$}PGe${2n1GyqG=jKFb&IBD7|k|}2dn6R5@$(o~}&H=hG!*&7X
z67>%)up4F(clh|WwhPL_cltqoSRy_Gj)0`80M0=ML<~T?QETviBEOOWhdYESz}5vk
z2xBxpIQU?<5&ymA9w0k3kK%v@UOPBbpazi$4i!eqwR-JYbVSbsaf{=50->22`F6G?
z(+so+m!t@Q>u{4+pAE3*WMtejT3O!QRO*ZRC**QQHHy&-MLJ6)lIC8t|Le0Wxyq*S
z!-uQAyNmhQ*|Vt)7+c@QsgL@VEjgt^a7sk3&YG7pjDR-#yz|PK4B{6fj?m6RYuLNR)Ie?NVUItvd?~8RbHR+{E`+#S9=K%`l@nD)LW$2PVsj7+!HOo=n$E$q-Ht_kr
zm0it;57seEYdF~0!xIysMNOaqDXGa-DS)qMUx2@7ZjP3_IcvmDIf=WZpy1TRaz-m(
zt({g2U_X>){~i#h_f)SzT7g(JmW0_0=FVLrMG(fCJhpW1;TNC!UI5#i#fXN4;!zP_
zzkXX|2vMPqtjfH@D;5Kq5!h)V^HC6>p|8Nl(i2BGij4AOZoNI1)_)2qLe}N!V
z-@n5kSDQ)=rrr)vO_vUHBO|{8;b;(9`mdTb0Ag^MO6rAu+s*(OmqBze8+{ZG<1uh|
zwN5kOY$^xkgqelK*4i3OAK2)Dfcd~@2^3s!Z!dTWz+J`z=G?`$fT$coXnv00t6r&d
zu(jXv(d!1wRoTN5QfJ3BhxN5#B#a&V9eHR5KCuD(9l!jE8`
zN-oKD3rc}MJq-**MV)Zz*GO+550E4RmY9s}EnFbJrl$|Wm-@a0y%h$7j?49#+ESP$
zz>W`|Ek{SkUYfFxse0glhl4G7D#?LGsE7;TFAzYZsRcG++$@o2_lDa6mkg&x^tH7^
z`h!1wSOsYjd@YvX*tD?7{_lV+qU8w33~$CHfg2<(4L`>j0N=8dC)hsWNI@O&=~o!~
zQ~hIFDyrn}kv{PK2m|p;If;A0LHr7Kyty&|_eP!2C~uZb}x>#hW1ce-n%0m>e`eD4<|a0tn74AmEhVn*W<35cdGaxht=n
ztt|@|7r0H7cKyp2!tr0xWtJ|1skq=~*`;;0L7mI(_I@@Z>_siigtyU9y=2>UHrB~x
z$${?GNJ8-&MG)|9GI$BoP=i?+Y8<%7un?6D30j2c#*Ii!Qf%4Dgm>@216T6l!&^{{
zTuh;FAAw~Fq_b;14A6(*5QwTOh`Vo2`vR^GKEg4feJ`;`gkbSQMZn%k*8^HO9eVkARjI31OWPkE;XRpjiV#;JtobkxIf>2%!Uc1Wt|{
zcS5yeYQi*AVL{!NeM|xNenj&8OnD5>q66OmRDlBZuDSzb7pNp^YTKa9qMzKj4Mzt6
zjPkMl>}-qy743@Q<0>pMY+WDzU>$AbNpmhA#GXV4&{_ZltrU
z3;1|2M|amU{FExrTwI=5TB0$OYDiK8s=7Kh$4w_HN)w~S7yyfhay}F$|IDn1MO>T~
z&i3I>fwh&5eCioV*J0R4pqlH!<03gYOE0j~hB!kzTDT9ypf*Ny|=E#!H;y${$y;pG3fZ#kjh4uTCtL15|a
zBm>qt;XqWNC^lm(^e#kldDhMfD{qROl@#UD6>N=us+v#56R5U
z4K8Dn-GXpg>I`F=I0oAuf}|&;p|-6*G~%W>f^{D*Ow=<)`{mWSCmfH-h#&4(4s04=
zB|7^|OqZJ}y$)00F2-a|U@LD49UTR51)el(&Om!NAQ$2PJ}{zBKr6ynUeg)vnXvwq
zG&U4S-Z41Bln7pRfS!F-wA5-$_WtJ1For-Ri1dTD1{uJXT+>M@I2Pe2zf#=`;T!~b
z_^nCmZ`9~Mwo1}?QaV#O2jjJyX+=E`#u!u6Av0d=^tvpxe;D*V|4$cZ9@bO-_VLpq
zp_CYm)iQ>Y}`DDjIHVH71s
zl6qdp%=28o>p6e8T;%JV@A-W0&wanQW0rr&R57*9Z&};%diKN-lEUc|2);rsL$<5q
zEHb+ZeTENV&tIXc1y=`|E(+z9z=G?*T{)UN*s6xNnG>W?_4L)NNeN-1nLNZr=VXlI
z`ts?~wVA1ydeO*U^5{{e`G4WJZO0DtgM~wf1rJlkYxcCgorUgGnzaJ*q)30<lEjB3*i!0Dtvb>Z4!J{vuUWmsXn@H
za(#Whh~8UyAD+F>As%U%sQ;cUz6z7*=E*rjR5TM=ciZGojD_<|Pq&-myEpY=MAGHd
z6@h)d|LXCq*WkD48?K?Dmyk^*lN6xGzB&GOJrY*du^e7rp6f)(m`4HZ2;Hty#;ND=40
zWg~oUwVAjkC=#}R8zIAl*E2X#hg
z*%|(%GUP<4UEAZUu{Aqh-ks`sM+Jg=*sxhun$h%FQsRkmbi@!d^q66&&=se}tyDX(wHZi_c?u7X&D~e0y5&BfBWsm&6|}6?~$8R
zoBlyr7O>EUPo~DxlOPD!(bEgtdTqKbpUmy;+jkoak*)Ln7tvZnhzzhgCYsUjicl5%d%I-1|#
zrxq}(WXcg?V%(63@Q4TlLqqG)11*xzpF3CH+}WW;owdNRrWUNBq0t8#MfWk++8U9H
zVP7n56wm>!1pC!tV#}8#lJeCabdyLS9sq0682LuLsY7$doW;+HDxBb6#lCOhy-=j2!7MgU1fb@AB
zsjME58c?9H=U^rcS!T)bj0gdl<+s}^H9mgnq)D=LCiP7U@JTq0
zIxGsAw^;lnBrN^Pj-ZnKpdH9&y2sPFQw>yuX|JNbI8<<+YH|(_VX4v8Df#r#`sF`Z
z`{D1u*S-BQdR`0c(T*U{Z-$fkix$1fyNDa1_g(d(^=a0#W;NoxrLuG^0yhytkT1k!
z6pFir9@l(Jhh>b9rje0iDe0wiL{K5=N3doXn4j7X6;NF{!F|o1qR*O
z=6m^efP!hc&5rhc>{`P%c}6Ky`SfXal%^^V!%&tN+@Hr}$^)@-Z5?w^;;T~TYMc7TM2w#{Z7aZapv9Bw
zYS9gPqh`R1ks?fsA`@yT7#N)X<4fJ)2xF0-*3e|W#V5m3q%T8gsJSJ}MrPMAMMZri
z$1G@u)C1$P(T!KF^zk`+{P?cFBm_32D6Ryg8yV@3w$*9(#_0HX)RM*q2Kba%MB{=&
zqt8psvOq4Hi@FtYpmE~4^XC_(#Q<{dSD$u~Cx8bPeO`15dH&?8b~4^Y(kn%34Ypl}I$Z$WK%ZzgzfhBanUB?c12j
zEz`Qsx;gI!PfJKl=C4u=N%o2In!O#5ydsvH*9=JjORqtn;tX*m
z#1%DK&z-9<+H3@kZF?(%&X|`zTY9?;JG3_=d9oeqn70G3%9+{Y7Z(a1
zKTfLmw31@76NjWI8i}hrkNOXK_z<@ps@VU_O3C^a(;%eJf+P2tg@h1#vB2Uf=u~&;
zy8BNG_ExSaz_r273h1IQ81&9)@ps=z2(VG_N0;GtpWbmQKtdk%FU#G_1b^LJCjAr3
z%Aa>h2=uH+w?0q&BB-hIGIVyXW?06(-kng?Sn#I1r-zU%E|NQ%XTJOU@34WK)g8M4
zwEoe@P?T4AeFOpD>dYSG~77gfE
zxFDUl_#}Sck%Bq~y$TAK3{CpBhX2y5T-fiYDmF+Y=0HjR@f%ckDkK@1*vc_zfbEVh;%U%?27_RCtPY=Tk
z&i@$0vyIhkx9T17AgEVNhAoxG9p0)phkAXzR(N@4ilx+`@$#o>$e}*QpDV*M^WXub
z!6u#zlrHzB(xfHn9q-@&c)_KjOuXOj6ga4N7KRpeF80@?RVMuazsG9%577^^B+tF>
z{a!OLv*YqTjA%dT)s~mb2ZdEVZEh}lwsEw&I-#x9n8gDRD;JlSN0mLZT4OCf@wkjO
ztNnTx6_u1Y{Iu3C5lfmt6RF{ycvZ}iC?J_`4^BT*Se#@6H7YfKs_}yDe?z1tiu>1WP+geHr-GV2+^Lo3X>m`
zJ{uVV9(&_b(45l!MR+#IctZP=_vY3ne8a7rP)aX+LJHynr0?`S*qY)rYqrXmawC?}jKwSUNeyFq2c#5lg2UJCOL4z&RVna7ZLp
zPY=&>{_NT58(U-#w!Q7@k`i=WuOIfh#>^#K)j^BKpa7o*LyX76sUWSlH;}efu86fm
zW?jUR88tp$+OaE!^}xdFzM?X=}>&*9VYs1+N71DCxm1#
z{k;8+d32-+tU3*8_UBK0HR}2+Ljc{PqbpIb8psWgux9sQQlO#2L(ki(_=^uM7+(k8
z#7L7IdDDGD>?mdB59MR3yt0n{9*SfbVg%DD_jl1GySsY}JQmPiP0eEWLyGTt6~Lm8
zF0b8;Mgc+j?-?25m(5%zA@IwZOAk59>HPWs)_*V%G?AYg8;>l?WCS-4?o-zw79fG;
zZTDMkkHYq!u7=WyMMe(!wfNJsc(;-u6LLX-h%>jn_ofW)968K?r0NG%!q%W`&!wbf
zLWt3#x4yZ>w2KhocL2)Mry+u_GbS=&U?VQd#0RPxbtpc5?<$v#8_E1yR@~6A;=R)A
z>QndV_SD`q8{<2*xv8I?nF-UZ=Bj*XR8eMrUk{-u@<9Rq{$iqqT5#QJP-_Ijs!>F^
zX}!(PqTP`cFe2a*3IAlOY2&3M55BMaB@_WrzdqYnZN`h&uU(;M@-O{Y*npL3;uZAf
zERNMG0dHGuk(R=?7u=(1+B>tdriK&5bH$1k499TO267CPPt?`b`9wxlByU;X_s_H{
zd*6jEVEn0Hw{Y>|I2;q#A?)Q{d`OQeQ5rB-o=N%!F5&M$?cu3pCB*$N&
z3?;?q-rk`aGfZjIZjC-&i$g>nTM%
zU6bm7y!FUTfsw^NujqLfJS7LusuV|B2i6awXO_Uw{6Chr&`&}kuggWObH{f6d?i_F
z#&ypB8~Qj*cYN_(uhq9?Zr$GX3W69ioC_yhr3-Fc5VWx&>}N^>?pzxx0E
z^{ZUaqp|H9FIr(~Byp*=7J!=0fGf(a#P{PWg)aPVW=9tuuS6eEQdIz*CkO{(V&zV*9T)d7^Ut7&s~uR07uPhJ85
zuGz=G*Dd5U^Yol>&EAyI3^l>h)wK^%;El=cn0f&31p&fCtGGGjdzaN~3)W0zPJ0@j
zQaZfsc~`v7vNVX_^z`c4XrP`{>pUmgSK;B+oQ!I7nMm(L_rKoT)6Mq{DmUWMvOTM@tC2`7pbm=hs|U
zJV8W}o=c`66s**L{rKwatR~RSKL>P+6iaR3U@_2%97Wt9!!EeUb$!4=i}1eDdEOIc
z?(4WdmCDV{?QPU~$D(mV@WYqXRt$y$j1fi94DH!d6E5fOX3S8T=1MvcV53L+qO83;
z4X@*ajQ7gW?Uo+Zf0@}h0Kv^uc0cp`scL=8<$I10_*g&k0Mmp#iN
zf^&z`T9obUfFC6@0i=H$!WF|$8jNg&pv
z+jrwKh$9);dQMt6eE2Y`@r$)2R!xB=n(R6!OAIuD4gn8OwKSbReRHv={RI~)f#{{3
zIGAF<{_!kum3P@<(1ZNWq_*+Ns{3
z-
-
-
- {{.title}}
-
-
-
- {{.message}}
-
-
-