Week 17 of 2026

Development log of Tad Lispy website

22 items
  1. Present devlogs in reverse chronological order
  2. Update Nix dependencies
  3. Introduce craft taxonomy for projects
  4. Create two featured project pages
  5. List crafts for all projects in front matter
  6. Implement the unescape macro for Devlog entries
  7. Excavat devlog of the Elm Tree workshop
  8. Excavate more of the TBB devlog
  9. Excavate latest entries from the devlog excavator
  10. Describe (stub) more projects and excavate devlogs
  11. Disable global RSS and Atom feeds
  12. Don't break RSS and Atom subscribers
  13. Correctly escape closing Tera delimiter \%}
  14. Standardize the crafts taxonomy terms
  15. Describe the TBB project
  16. Move crafts list to the header of a project
  17. Write a short description of TBB for HTML meta
  18. Describe the REST in Python course
  19. Extract description from Agile Plan Exporter page
  20. Extract descriptions from several projects
  21. Describe the Better Tech Club website project
  22. Devlog: Prevent vertical overflow of inline SVGs

Present devlogs in reverse chronological order

On by Tad Lispy

So latest entries first. I think it makes more sense that way, so readers can easily see what happened recently. Otherwise they would have to scroll through a lot of historical entries.

index d80af22..f75a753 100644
--- a/templates/project.html
+++ b/templates/project.html
@@ -28,7 +28,7 @@
=    {# NOTE: If there is no devlog, then the heading will be hidden using CSS #}
=    <h2 id="devlog-heading">Devlog</h2>
=
-    {% for entry in devlog.pages %}
+    {% for entry in devlog.pages | sort(attribute="date") | reverse %}
=    {% if entry.extra.projects is containing(page.title) %}
=    <section class="devlog-entry">
=        <h3><time datetime="{{ entry.date }}">{{ entry.date | date(format="%A, %F")}}</time></h3>

Update Nix dependencies

On by Tad Lispy

index 03cb250..4cee253 100644
--- a/flake.lock
+++ b/flake.lock
@@ -20,11 +20,11 @@
=    },
=    "nixpkgs": {
=      "locked": {
-        "lastModified": 1775036866,
-        "narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
+        "lastModified": 1776169885,
+        "narHash": "sha256-l/iNYDZ4bGOAFQY2q8y5OAfBBtrDAaPuRQqWaFHVRXM=",
=        "owner": "NixOS",
=        "repo": "nixpkgs",
-        "rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
+        "rev": "4bd9165a9165d7b5e33ae57f3eecbcb28fb231c9",
=        "type": "github"
=      },
=      "original": {

Introduce craft taxonomy for projects

On by Tad Lispy

I want to highlight the crafts (like programming languages, frameworks, methodologies, tools and techniques) applied to each project. In Zola this is best expressed using taxonomies.

Each craft is listed in the project card and dedicated page. There is also a crafts list page under /crafts/ and each craft has a page listing projects it's applied to.

index e276ece..56a70c8 100644
--- a/config.toml
+++ b/config.toml
@@ -14,6 +14,10 @@ build_search_index = true
=generate_feeds = true
=feed_filenames = ["atom.xml", "rss.xml"]
=
+taxonomies = [
+    { name = "craft", render = true }
+]
+
=[markdown]
=bottom_footnotes = false
=
index f55c819..fe4f05e 100644
--- a/content/works/tad-better-behavior/index.md
+++ b/content/works/tad-better-behavior/index.md
@@ -1,6 +1,12 @@
=---
=title: Tad Better Behavior
=weight: 0
+
+taxonomies:
+    craft: 
+    - Rust
+    - Behavior Driven Development
+    - Command Line Tools
=---
=
=# TBB: Tad Better Behavior
new file mode 100644
index 0000000..59464a7
--- /dev/null
+++ b/templates/craft/list.html
@@ -0,0 +1,43 @@
+{% extends "base.html" %}
+
+{% block variables %}
+{{ super() }}
+{% set page_class  = "taxonomy crafts" %}
+{% set title = "Tad Crafts" %}
+{% set description = "Technologies, methods, techniques and skills I cultivate in my work." %}
+{% endblock variables %}
+
+{% block content %}
+<header>
+    <nav aria-label="breadcrumb">
+        <ul>
+            <li><a href="{{ get_url(path='@/_index.md') }}">Tad Lispy</a></li>
+            <li><strong>Crafts</strong></li>
+        </ul>
+    </nav>
+</header>
+
+<main>
+
+    <p>In my work I proudly cultivate the following crafts.</p>
+
+    {% for craft in terms %}
+    <article class="craft-card">
+        <p><a href="{{ craft.path }}">{{ craft.name }}</a></p>
+        <p>
+            Practiced in {{ craft.pages | length }} featured
+            {{ craft.pages | length | pluralize(singular="project", plural="project")}}:
+
+            {% for project in craft.pages %}
+            <a href="{{ project.path }}">{{ project.title }}</a>
+            {% endfor %}
+        </p>
+
+    </article>
+    {% endfor %}
+
+</main>
+
+{% include "includes/footer.html" %}
+{% endblock content %}
+
new file mode 100644
index 0000000..3977306
--- /dev/null
+++ b/templates/craft/single.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+
+{% block variables %}
+{{ super() }}
+{% set page_class  = "taxonomy crafts" %}
+{% set title = term.name %}
+{% set description = "It's a craft." %}
+{% endblock variables %}
+
+{% block content %}
+<header>
+    <nav aria-label="breadcrumb">
+        <ul>
+            <li><a href="{{ get_url(path='@/_index.md') }}">Tad Lispy</a></li>
+            <li><a href="{{ get_url(path='/craft/') }}">Crafts</a></li>
+            <li><strong>{{ term.name }}</strong></li>
+        </ul>
+    </nav>
+</header>
+
+<main>
+
+    <p>
+        Practiced in {{ term.pages | length }} featured
+        {{ term.pages | length | pluralize(singular="project", plural="project")}}.
+    </p>
+    
+    {% for project in term.pages %}
+        <article>
+            <h2>{{ project.title }}</h2>
+            <p><a href="{{ project.path }}">Read more about {{ project.title }}</a> </p>
+        </article>
+    {% endfor %}
+
+</main>
+
+{% include "includes/footer.html" %}
+{% endblock content %}
+
index f75a753..eccccad 100644
--- a/templates/project.html
+++ b/templates/project.html
@@ -25,6 +25,13 @@
=
=    {{ page.content | safe }}
=
+    <ul class="craft-list">
+        {% for craft in page.taxonomies | get(key="craft", default=[]) %}
+
+        <li><a href="{{ get_taxonomy_url(kind="craft", name=craft) }}">{{ craft }}</a></li>
+        {% endfor %}
+    </ul>
+
=    {# NOTE: If there is no devlog, then the heading will be hidden using CSS #}
=    <h2 id="devlog-heading">Devlog</h2>
=
index ec1694f..49b18af 100644
--- a/templates/works.html
+++ b/templates/works.html
@@ -29,6 +29,12 @@
=        {% for project in section.pages %}
=        <article>
=            <h2>{{ project.title }}</h2>
+            <ul class="craft-list">
+                {% for craft in project.taxonomies | get(key="craft", default=[]) %}
+
+                <li><a href="{{ get_taxonomy_url(kind="craft", name=craft) }}">{{ craft }}</a></li>
+                {% endfor %}
+            </ul>
=            <p><a href="{{ project.path }}">Read more about {{ project.title }}</a> </p>
=        </article>
=        {% endfor %}

On by Tad Lispy

At this moment I mainly want to see how the new taxonomy will work with multiple projects. There are already some devlog entries for them, and they share some crafts, so these are good candidates to add. Later I'll list many more projects.

And of course as soon as I added them, I found a bug in pluralization :P

new file mode 100644
index 0000000..35bfae2
--- /dev/null
+++ b/content/works/better-tech-club-website/index.md
@@ -0,0 +1,15 @@
+---
+title: Better Tech Club website
+weight: 1
+taxonomies:
+    craft:
+    - Web Development
+    - No JS
+    - Modern CSS
+    - Handcrafted HTML
+    - Graphic Design
+---
+
+# Better Tech Club website
+
+<https://bettertechclub.eu>
new file mode 100644
index 0000000..fe5fdff
--- /dev/null
+++ b/content/works/devlog-excavator/index.md
@@ -0,0 +1,16 @@
+---
+title: Devlog Excavator
+weight: 1
+
+taxonomies:
+    craft: 
+    - Nushell
+    - Behavior Driven Development
+    - Command Line Tools
+    - Markdown processing
+---
+
+# Devlog Excavator
+
+I use it to excavate the content below, and in other projects listed on this site.
+
index fe4f05e..aa74ada 100644
--- a/content/works/tad-better-behavior/index.md
+++ b/content/works/tad-better-behavior/index.md
@@ -7,6 +7,7 @@ taxonomies:
=    - Rust
=    - Behavior Driven Development
=    - Command Line Tools
+    - Markdown processing
=---
=
=# TBB: Tad Better Behavior
index 59464a7..c506e7a 100644
--- a/templates/craft/list.html
+++ b/templates/craft/list.html
@@ -26,7 +26,7 @@
=        <p><a href="{{ craft.path }}">{{ craft.name }}</a></p>
=        <p>
=            Practiced in {{ craft.pages | length }} featured
-            {{ craft.pages | length | pluralize(singular="project", plural="project")}}:
+            {{ craft.pages | length | pluralize(singular="project", plural="projects")}}:
=
=            {% for project in craft.pages %}
=            <a href="{{ project.path }}">{{ project.title }}</a>
index 3977306..67ab0de 100644
--- a/templates/craft/single.html
+++ b/templates/craft/single.html
@@ -22,7 +22,7 @@
=
=    <p>
=        Practiced in {{ term.pages | length }} featured
-        {{ term.pages | length | pluralize(singular="project", plural="project")}}.
+        {{ term.pages | length | pluralize(singular="project", plural="projects")}}.
=    </p>
=    
=    {% for project in term.pages %}

List crafts for all projects in front matter

On by Tad Lispy

Some of them had crafts listed in content. Others didn't have them at all. Some of the items aren't really crafts, but more like features or categories of software. Later I'll sort it out with different taxonomies or something. But for now it's an improvement.

index cb5136c..c406ab7 100644
--- a/content/works/agile-planner/index.md
+++ b/content/works/agile-planner/index.md
@@ -1,17 +1,19 @@
=---
=title: Agile Planner
=weight: 3
+taxonomies:
+  craft:
+  - Agile software development
+  - Rust programming
+  - Collaboration software
+  - Free / Open source software
+  - Markdown processing
+  - iCalendar processing
=---
=
=
=# Agile Plan Exporter
=
-  - agile software development
-  - Rust programming
-  - collaboration
-  - free / open source software
-  - markdown processing
-  - iCalendar 
=
=At Software Garden we attempted to create a work environment where we can be efficient and happy at the same time. Part of it was agile planning - a simple and collaborative process where everyone had a chance to catch up with all the latest developments and choose the priorities they will work on. To facilitate it, we created several tools, including this one. It exports our schedule from a Markdown document,  to iCalendar format. Markdown is a flexible and lightweight plain text format, ideal for taking notes while planning, while iCalendar is the industry standard for calendaring data. This enabled  every team member to synchronize their personal calendars with our shared plan! 
=
deleted file mode 100644
index 154cb72..0000000
--- a/content/works/ehtical-software-garden/elm-tree-worksho/index.html
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: Elm Tree Workshop
-weight: 2
----
-
-# Elm Tree Workshop
-
-  - teaching
-  - Elm programming
-  - web development
-
-A workshop that will give you a glimpse into the way software is created. It is intended for people with no prior experience in programming and doesn’t require any technical knowledge. Everybody is welcome! During this 5 days workshop (3 hours each day) you will learn to solve problems using a functional programming language. Together, we will build a program that simulates growth of a tree. In 2019 the workshop was presented to students of Utrecht University.
-
-[Visit the website](https://elm-tree.software.garden/)
index bc59274..d683a4c 100644
--- a/content/works/elm-springs/index.md
+++ b/content/works/elm-springs/index.md
@@ -1,15 +1,16 @@
=---
=title: Elm Springs
=weight: 2
+taxonomies:
+  craft:
+  - Elm programming
+  - Physics simulation
+  - Procedural animations
+  - Free / Open source software
=---
=
=# Elm Springs
=
-  - Elm programming
-  - physics simulation
-  - animations
-  - free / open source software
-
=An Elm package implementing a rough model of a physical mass attached to a spring, as described in physics by Hooke's law. Good for making smooth and organic looking animations or modeling oscillating values (for example emotions). High physical accuracy is not a priority - performance and API simplicity is more important.
=
=[See the package](https://package.elm-lang.org/packages/tad-lispy/springs/latest/).
similarity index 80%
rename from content/works/elm-tree-worksho/index.md
rename to content/works/elm-tree-workshop/index.md
index 154cb72..967c096 100644
--- a/content/works/elm-tree-worksho/index.md
+++ b/content/works/elm-tree-workshop/index.md
@@ -1,6 +1,13 @@
=---
=title: Elm Tree Workshop
-weight: 2
+weight: 1
+taxonomies:
+  craft:
+  - Web development
+  - Education technology
+  - Elm programming
+  - Procedural animation
+  - Free / Open source software
=---
=
=# Elm Tree Workshop
similarity index 84%
rename from content/works/ehtical-software-garden/index.md
rename to content/works/ethtical-software-garden/index.md
index 8812b5b..202921a 100644
--- a/content/works/ehtical-software-garden/index.md
+++ b/content/works/ethtical-software-garden/index.md
@@ -1,18 +1,18 @@
=---
=title: Ethical Software Garden
=weight: 3
+taxonomies:
+  craft:
+  - Collaboration
+  - Digital ethics research
+  - Content Management Systems
+  - GitLab API
+  - Free / Open source software
+  - GraphQL
=---
=
=# Ethical Software Garden
=
-  - collaboration
-  - digital ethics
-  - CMS
-  - GitLab API
-  - free / open source software
-  - GraphQL
-  
-
=After taking part in the Offsite Sustainability meetup hosted by [Railslove](https://railslove.com/) we realized that resources about digital ethics are very scattered. This website uses our GitLab GraphQL API and Elm Pages static site generator to produce a catalogue of articles, videos, websites etc. about digital ethics. The idea is to use GitLab issues, comments, reactions (up and down votes) and moderation to generate web content. The webpage is re-generated daily in a CI/CD pipeline for fast loading, but the content is also loaded live.
=
=[See the catalogue](https://ethical.software.garden/)
index b7a52b8..bc34395 100644
--- a/content/works/lead-studio/index.md
+++ b/content/works/lead-studio/index.md
@@ -1,19 +1,21 @@
=---
=title: Lead Studio
=weight: 1
----
-
-# Lead Studio
-
-  - data engineering
-  - business process automation
-  - web extensions
-  - user experience
-  - internal tooling
+taxonomies:
+  craft:
+  - Data engineering
+  - Business process automation
+  - Web extensions
+  - User experience
+  - Internal tooling
=  - LinkedIn Sales Navigator
=  - Google Cloud
=  - Elm programming
=  - JavaScript programming
+---
+
+# Lead Studio
+
=
=In 2021, we promised our client, Saleslift Studio, to deliver a working software solution to improve prospecting performance within a fixed time and at a fixed price. In just 6 weeks (a single development cycle), we provided them with a useful solution - Lead Studio. Further incremental development enabled them to improve a number of key performance indicators. Thanks to their partnership with Software Garden, prospecting specialists at Saleslift Studio are now able to enrich leads 20⨉ faster than before.
=
index aa74ada..f4bbb9a 100644
--- a/content/works/tad-better-behavior/index.md
+++ b/content/works/tad-better-behavior/index.md
@@ -6,7 +6,7 @@ taxonomies:
=    craft: 
=    - Rust
=    - Behavior Driven Development
-    - Command Line Tools
+    - Command Line Tools development
=    - Markdown processing
=---
=
index 431d6d1..c3d1fbc 100644
--- a/content/works/word-snake/index.md
+++ b/content/works/word-snake/index.md
@@ -1,14 +1,16 @@
=---
=title: Word Snake
=weight: 2
+taxonomies:
+  craft:
+  - Game development
+  - web development
+  - Education technology
+  - Elm programming
=---
=
=# Word Snake
=
-  - game development
-  - web development
-  - education technology
-  - Elm programming
=
=Think fast - guess a password, collect letters and save the snake from a fire trap! In this simple word puzzle game you control a snake while guessing a password. Challenges are designed to let players learn various facts about the world or improve their arithmetic and logical thinking while playing. It's extra fun to play in a group on a phone or a tablet - one player controls the snake while others help to guess the password. It can be a nice family exercise that brings people together.
=

Implement the unescape macro for Devlog entries

On by Tad Lispy

Sometimes I have Zola shortcodes in content, either in a code block, or in prose, but it's not for Zola to process, but a fragment of "foreign" code that I want to display verbatim. I can't find a reliable way to do that. I wish there was a parameter to the markdown filter to disable shortcodes. Something like:

content | markdown(shortcodes=false)

But there's no such thing. There is the {{/* shortcode() */}} trick, but it's flaky. For example, {{/* not_a_shortcode */}} would print the slashes and stars. Also the trick doesn't work for shortcodes with bodies (they use the {% and %} delimiters and I don't know how to escape it). For Devlog Excavator I need a straight forward system to escape those sequences (in diffs and prose).

So as a workaround, in excavated markdown content I escape part of the delimiters (inner curly or percent) with a backslash. This makes the code meaningless to Zola. Then using the new unescape macro I remove the slashes, but only after rendering to HTML. This can potentially break syntax highlighting, but I can always be used selectively.

If there is ever a literal sequence of slash followed by curly or percent, I just need to add another slash in front of it and it should be fine.

index 8184f40..c7d5e6e 100644
--- a/templates/components.html
+++ b/templates/components.html
@@ -14,3 +14,24 @@
=  {% endfor %}
=  {{ demoted | safe }}
={% endmacro table_of_contents %}
+
+{% macro unescape(body) %}
+{# This is a hack.
+
+Remove slashes from escaped Zola delimiters. Useful to display Zola templates on
+this website without triggering Zola to interpret them.
+
+If there is ever a literal sequence of slash followed by curly or percent, just
+add another slash in front of it and it should be fine.
+
+#}
+
+{{ 
+    body
+    | markdown()
+    | replace(from="{{", to="{{")
+    | replace(from="\}}", to="}}")
+    | replace(from="{%", to="{%")
+    | replace(from="\%}", to="%}")
+}}
+{% endmacro unescape %}
index eccccad..7e9b2d6 100644
--- a/templates/project.html
+++ b/templates/project.html
@@ -40,9 +40,8 @@
=    <section class="devlog-entry">
=        <h3><time datetime="{{ entry.date }}">{{ entry.date | date(format="%A, %F")}}</time></h3>
=
-        {{- components::demote_headings (content=entry.content, levels=3)
-        | safe
-        -}}
+        {% set unescaped = components::unescape(body=entry.content) %}
+        {{- components::demote_headings (content=unescaped, levels=3) | safe -}}
=    </section>
=    {% endif %}
=    {% endfor %}

Excavat devlog of the Elm Tree workshop

On by Tad Lispy

new file mode 100644
index 0000000..537e0e4
--- /dev/null
+++ b/content/devlog/2018-10-22-elm-tree-workshop.md
@@ -0,0 +1,782 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Extract workshop slides and samples from fpart-wiki
+
+
+
+``` diff
+new file mode 100644
+index 0000000..9114974
+--- /dev/null
++++ b/.gitignore
+@@ -0,0 +1,2 @@
++/elm-stuff
++/**/.DS_Store
+```
+
+``` diff
+new file mode 100644
+index 0000000..a115152
+Binary files /dev/null and b/assets/Elm MVU architecture.jpg differ
+```
+
+``` diff
+new file mode 100644
+index 0000000..a975a29
+Binary files /dev/null and b/assets/mac-launchpad-terminal.png differ
+```
+
+``` diff
+new file mode 100644
+index 0000000..5bc5df1
+Binary files /dev/null and b/assets/mac-terminal-window.png differ
+```
+
+``` diff
+new file mode 100644
+index 0000000..988c3a5
+--- /dev/null
++++ b/elm.json
+@@ -0,0 +1,31 @@
++{
++    "type": "application",
++    "source-directories": [
++        "src"
++    ],
++    "elm-version": "0.19.0",
++    "dependencies": {
++        "direct": {
++            "elm/browser": "1.0.0",
++            "elm/core": "1.0.0",
++            "elm/html": "1.0.0",
++            "elm/svg": "1.0.1",
++            "ianmackenzie/elm-geometry": "1.2.1",
++            "ianmackenzie/elm-geometry-svg": "1.0.2",
++            "mdgriffith/elm-ui": "1.1.0"
++        },
++        "indirect": {
++            "elm/json": "1.0.0",
++            "elm/time": "1.0.0",
++            "elm/url": "1.0.0",
++            "elm/virtual-dom": "1.0.2",
++            "ianmackenzie/elm-float-extra": "1.0.1",
++            "ianmackenzie/elm-interval": "1.0.1",
++            "ianmackenzie/elm-triangular-mesh": "1.0.2"
++        }
++    },
++    "test-dependencies": {
++        "direct": {},
++        "indirect": {}
++    }
++}
+\ No newline at end of file
+```
+
+``` diff
+new file mode 100644
+index 0000000..a2ea019
+--- /dev/null
++++ b/index.md
+@@ -0,0 +1,314 @@
++---
++presentation:
++  enableSpeakerNotes: true
++  theme: solarized.css
++---
++
++<!-- slide -->
++
++# FP-Art!
++## A functional programming workshop
++### for non-programmers
++
++<!-- slide -->
++
++## Setup
++
++<!-- slide -->
++
++> This setup instructions are based on an assumption that you are using a Mac.
++>
++> If you are using Linux or BSD, then you probably know how to install stuff.
++>
++> If you are using anything else, then... well, good luck.
++>
++> The rest of the instructions should work with any platform.
++
++<!-- slide -->
++
++We will need:
++
++- a text editor (I use [Atom][])
++- and [Elm programming language][]
++
++[Atom]: https://atom.io/
++[Elm programming language]: https://elm-lang.org/
++
++<!-- slide -->
++
++We will use the terminal a little bit.
++
++# :fa-terminal:
++
++Don't be scared. It's easy :)
++
++<!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
++
++> <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
++
++<!-- slide -->
++
++You should see a window like this
++
++<img class="plain" alt="Mac Terminal app window" src="images/mac-terminal-window.png" />
++
++<!-- slide -->
++
++Now we are going to install few things.
++
++- Homebrew (to install other things)
++- Elm programming language
++- Atom editor
++
++<!-- slide -->
++
++### Install Homebrew
++
++Follow instructions on the [Homebrew website](https://brew.sh/) by typing the following in the terminal (you probably want to copy and paste it):
++
++```sh
++/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
++```
++
++<small>
++Make sure that the command is exactly like the one above, including the `/` character at the beginning, the quotes and parentheses.
++
++It will ask you to confirm several actions and ask for your password. It may take few minutes to finish, so get your coffee
++</small>
++
++:fa-coffee:
++
++Once it's done you should see a command prompt like that:
++
++```
++~ your-name$
++```
++
++
++<!-- slide -->
++
++### Install the Elm programming language
++
++<small>Once we have Homebrew installed, we can use it to install the language.</small>
++
++Type the following in the terminal.
++
++```sh
++brew install node elm
++```
++
++and check if it works by typing:
++
++```
++elm repl
++```
++
++<!-- slide -->
++
++Note that the command line changes. You should see something like that
++
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++>
++```
++
++It's the Elm REPL
++
++<small>Read - Evaluate - Print Loop</small>
++
++*[REPL]: Read - Evaluate - Print Loop.
++
++<!-- slide -->
++
++Inside the REPL type
++
++```
++2 + 2
++```
++
++And expect to see
++
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++> 2 + 2
++4 : number
++>
++```
++
++Easy, huh?
++
++<!-- slide -->
++
++We will learn more about REPL later. For now type `:exit` to close it.
++
++The command line should look like before again.
++
++<!-- slide -->
++
++### Install Atom text editor
++
++Computer programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use [Atom] here.
++
++<!-- slide -->
++
++Type following in the terminal:
++
++```
++brew cask install atom
++```
++
++And start it with:
++
++```sh
++atom
++```
++
++<!-- slide -->
++
++One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
++
++```
++apm install language-elm
++```
++
++<small>APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.</small>
++
++<!-- slide -->
++
++**We are all set!**
++
++:smile:
++
++<!-- slide -->
++
++
++# First program!
++
++<!-- slide -->
++
++As mentioned before, programs are represented as text (called *the source code*).
++
++The source code is stored in files
++
++:fa-file:
++
++and files are organized in directories
++
++:fa-folder:
++
++<!-- slide -->
++
++So the first step is to create a directory for our new program. Let's call it `fpart`.
++
++In the terminal type
++
++```sh
++mkdir fpart/
++```
++
++and then
++
++```
++cd fpart/
++```
++
++<small>This creates a new directory and makes it the current one. Again, don't worry about the details.</small>
++
++<!-- slide -->
++
++To easily create a new program, we can type
++
++```
++elm init
++```
++
++<!-- slide -->
++
++Then to create a file with source code, type
++
++```
++atom src/Main.elm
++```
++
++<small>This command should open a new Atom window with empty text file.</small>
++
++<!-- slide data-transition=zoom -->
++
++### `main.elm`
++
++```elm
++module Main exposing (main)
++
++import Html
++
++
++main =
++    Html.text "Hello, Tree!"
++```
++
++<small>Type the above in the editor and save the file</small>
++
++<!-- slide -->
++
++To see the program running type following in the terminal
++
++```sh
++elm reactor
++```
++
++<small>This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.</small>
++
++<!-- slide -->
++
++# Voila!
++
++<small>Open following address in the web browser</small>
++
++http://localhost:8000/src/Main.elm
++
++<small>Note that the same address was printed by Elm reactor</small>
++
++<!-- slide -->
++
++# Let's make a dot!
++# :fa-circle:
++
++<!-- slide -->
++
++We are going to use a technology called SVG
++
++<small>Scalable Vector Graphics</small>
++
++
++*[SVG]: Scalable Vector Graphics
++
++<!-- slide -->
++
++Let's install an Elm package to help us work with SVG.
++
++Stop the Reactor running in terminal by pressing
++
++`CTRL-C`
++
++and type the following
++
++```
++elm install elm/svg
++```
++
++Then start the reactor again
++
++```
++elm reactor
++```
++
++<small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
++
++<!-- slide -->
++
++<iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
++</iframe>
+```
+
+``` diff
+new file mode 100644
+index 0000000..609bebd
+--- /dev/null
++++ b/src/CartesianCoordinates.elm
+@@ -0,0 +1,136 @@
++module CartesianCoordinates exposing (main)
++
++import Browser
++import CartesianPlane exposing (graph)
++import Element
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    { x : Float
++    , y : Float
++    }
++
++
++type Msg
++    = SetX Float
++    | SetY Float
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( { x = 0, y = 0 }
++    , Cmd.none
++    )
++
++
++view : Model -> Html.Html Msg
++view model =
++    Element.layout
++        [ Element.height Element.fill -- (Element.px 600)
++        , Element.width Element.fill
++        ]
++    <|
++        Element.column
++            [ Element.height Element.fill
++            , Element.width <| Element.px 600
++            , Element.centerX
++            , Element.spacing 30
++            , Element.padding 30
++            ]
++            [ Element.el [ Element.height <| Element.px 400 ] <|
++                Element.html <|
++                    graph
++                        [ circle
++                            [ cx <| String.fromFloat model.x
++                            , cy <| String.fromFloat model.y
++                            , r "0.01"
++                            , fill "magenta"
++                            ]
++                            []
++                        , text_
++                            [ x <| String.fromFloat (model.x + 0.03)
++                            , y <| String.fromFloat model.y
++                            , fontSize "0.05"
++                            , dominantBaseline "central"
++                            ]
++                            [ text <| Debug.toString ( model.x, model.y ) ]
++                        ]
++            , Input.slider
++                [ Element.behindContent
++                    (Element.el
++                        [ Element.width Element.fill
++                        , Element.height (Element.px 2)
++                        , Element.centerY
++                        , Background.color <| Element.rgb 0.7 0.7 0.7
++                        , Border.rounded 2
++                        ]
++                        Element.none
++                    )
++                ]
++                { onChange = SetX
++                , label =
++                    Input.labelBelow [ Element.centerX ] <|
++                        Element.text ("x value: " ++ String.fromFloat model.x)
++                , min = -1
++                , max = 1
++                , value = model.x
++                , thumb = Input.defaultThumb
++                , step = Just 0.01
++                }
++            , Input.slider
++                [ Element.behindContent
++                    (Element.el
++                        [ Element.width Element.fill
++                        , Element.height (Element.px 2)
++                        , Element.centerY
++                        , Background.color <| Element.rgb 0.7 0.7 0.7
++                        , Border.rounded 2
++                        ]
++                        Element.none
++                    )
++                ]
++                { onChange = SetY
++                , label =
++                    Input.labelBelow [ Element.centerX ] <|
++                        Element.text ("y value: " ++ String.fromFloat model.y)
++                , min = -1
++                , max = 1
++                , value = model.y
++                , thumb = Input.defaultThumb
++                , step = Just 0.01
++                }
++            ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update msg model =
++    case msg of
++        SetX x ->
++            ( { model | x = x }, Cmd.none )
++
++        SetY y ->
++            ( { model | y = y }, Cmd.none )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
+```
+
+``` diff
+new file mode 100644
+index 0000000..394005d
+--- /dev/null
++++ b/src/CartesianPlane.elm
+@@ -0,0 +1,38 @@
++module CartesianPlane exposing (graph)
++
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++graph : List (Svg msg) -> Html msg
++graph shapes =
++    let
++        background =
++            g []
++                [ line
++                    [ x1 "-1"
++                    , y1 "0"
++                    , x2 "1"
++                    , y2 "0"
++                    , stroke "black"
++                    , strokeWidth "0.001"
++                    ]
++                    []
++                , line
++                    [ x1 "0"
++                    , x2 "0"
++                    , y1 "-1"
++                    , y2 "1"
++                    , stroke "black"
++                    , strokeWidth "0.001"
++                    ]
++                    []
++                ]
++    in
++    svg
++        [ viewBox "-1 -1 2 2"
++        , preserveAspectRatio "xMidYMid meet"
++        , Svg.Attributes.style "width: 100%, height: 100%"
++        ]
++        (background :: shapes)
+```
+
+``` diff
+new file mode 100644
+index 0000000..fead82e
+--- /dev/null
++++ b/src/PolarCoordinates.elm
+@@ -0,0 +1,180 @@
++module PolarCoordinates exposing (main)
++
++import Browser
++import Element
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    { angle : Float
++    , radius : Float
++    }
++
++
++type Msg
++    = SetAngle Float
++    | SetRadius Float
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( { angle = 0, radius = 0.5 }
++    , Cmd.none
++    )
++
++
++view : Model -> Html.Html Msg
++view model =
++    let
++        point =
++            cartesian model
++    in
++    Element.layout
++        [ Element.height Element.fill -- (Element.px 600)
++        , Element.width Element.fill
++        , Background.color <| Element.rgb 0.6 0 0
++        ]
++    <|
++        Element.column
++            [ Element.height Element.fill
++            , Element.width <| Element.px 600
++            , Background.color <| Element.rgb 0 0.6 0
++            , Element.centerX
++            , Element.spacing 30
++            , Element.padding 30
++            ]
++            [ Element.el [] <|
++                Element.html <|
++                    svg
++                        [ viewBox "-1 -1 2 2"
++                        , preserveAspectRatio "xMidYMid meet"
++                        , Svg.Attributes.style "width: 100%, height: 100%"
++                        ]
++                        [ line
++                            [ x1 "-1"
++                            , y1 "0"
++                            , x2 "1"
++                            , y2 "0"
++                            , stroke "black"
++                            , strokeWidth "0.001"
++                            ]
++                            []
++                        , line
++                            [ x1 "0"
++                            , x2 "0"
++                            , y1 "-1"
++                            , y2 "1"
++                            , stroke "black"
++                            , strokeWidth "0.001"
++                            ]
++                            []
++                        , circle
++                            [ cx <| String.fromFloat point.x
++                            , cy <| String.fromFloat point.y
++                            , r "0.01"
++                            , fill "magenta"
++                            ]
++                            []
++                        , line
++                            [ x1 "0"
++                            , x2 <| String.fromFloat point.x
++                            , y1 "0"
++                            , y2 <| String.fromFloat point.y
++                            , stroke "magenta"
++                            , strokeWidth "0.001"
++                            ]
++                            []
++                        , text_
++                            [ x <| String.fromFloat (point.x + 0.02)
++                            , y <| String.fromFloat (point.y + 0.01)
++                            , Svg.Attributes.fontSize "0.03pt"
++                            ]
++                            [ text <|
++                                Debug.toString ( point.x, point.y )
++                            ]
++                        ]
++            , Input.slider
++                [ Element.behindContent
++                    (Element.el
++                        [ Element.width Element.fill
++                        , Element.height (Element.px 2)
++                        , Element.centerY
++                        , Background.color <| Element.rgb 0.7 0.7 0.7
++                        , Border.rounded 2
++                        ]
++                        Element.none
++                    )
++                ]
++                { onChange = SetAngle
++                , label =
++                    Input.labelBelow [ Element.centerX ] <|
++                        Element.text ("angle value: " ++ String.fromFloat model.angle)
++                , min = 0
++                , max = 360
++                , value = model.angle
++                , thumb = Input.defaultThumb
++                , step = Just 1
++                }
++            , Input.slider
++                [ Element.behindContent
++                    (Element.el
++                        [ Element.width Element.fill
++                        , Element.height (Element.px 2)
++                        , Element.centerY
++                        , Background.color <| Element.rgb 0.7 0.7 0.7
++                        , Border.rounded 2
++                        ]
++                        Element.none
++                    )
++                ]
++                { onChange = SetRadius
++                , label =
++                    Input.labelBelow [ Element.centerX ] <|
++                        Element.text ("radius value: " ++ String.fromFloat model.radius)
++                , min = 0
++                , max = 1
++                , value = model.radius
++                , thumb = Input.defaultThumb
++                , step = Just 0.01
++                }
++            ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update msg model =
++    case msg of
++        SetAngle angle ->
++            ( { model | angle = angle }, Cmd.none )
++
++        SetRadius radius ->
++            ( { model | radius = radius }, Cmd.none )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
++
++
++cartesian : { angle : Float, radius : Float } -> { x : Float, y : Float }
++cartesian model =
++    { x = model.radius * cos (degrees model.angle)
++    , y = model.radius * sin (degrees model.angle)
++    }
+```
\ No newline at end of file
new file mode 100644
index 0000000..f92f695
--- /dev/null
+++ b/content/devlog/2018-10-23-elm-tree-workshop.md
@@ -0,0 +1,761 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 4
+
+
+# Gitignore .swp files
+
+
+
+``` diff
+index 9114974..0c19f1d 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,2 +1,3 @@
+=/elm-stuff
+=/**/.DS_Store
++*.swp
+```
+
+# Begin reconstructing the 'fpart' presentation as an Elm program
+
+We can render markdown and html elements
+
+Next todo: scaling images to fit on slide
+
+``` diff
+index 988c3a5..67b87e4 100644
+--- a/elm.json
++++ b/elm.json
+@@ -10,6 +10,8 @@
+=            "elm/core": "1.0.0",
+=            "elm/html": "1.0.0",
+=            "elm/svg": "1.0.1",
++            "elm-community/list-extra": "8.1.0",
++            "elm-explorations/markdown": "1.0.0",
+=            "ianmackenzie/elm-geometry": "1.2.1",
+=            "ianmackenzie/elm-geometry-svg": "1.0.2",
+=            "mdgriffith/elm-ui": "1.1.0"
+```
+
+``` diff
+new file mode 100644
+index 0000000..88cb6e2
+--- /dev/null
++++ b/src/Main.elm
+@@ -0,0 +1,400 @@
++module Main exposing (main)
++
++import Dict
++import Element
++import Html exposing (Html)
++import Html.Attributes as Html
++import Presentation exposing (Slide, markdown)
++
++
++currentSlide =
++    5
++
++
++main =
++    let
++        slide =
++            slides
++                |> Dict.get currentSlide
++
++        notFound =
++            Element.text "404: Slide not found"
++    in
++    Element.layout [ Element.width Element.fill, Element.height Element.fill ] <|
++        Element.row
++            [ Element.centerX, Element.centerY ]
++            [ slide
++                |> Maybe.map (Element.column [])
++                |> Maybe.withDefault notFound
++            ]
++
++
++slides =
++    Dict.fromList <|
++        List.indexedMap Tuple.pair <|
++            [ markdown """
++        # FP-Art!
++        ## A functional programming workshop
++        ### for non-programmers
++    """
++            , markdown """
++        # Stuff
++    """
++            ]
++                :: [ markdown """
++               ## Setup
++           """
++                   , markdown """
++               # Stuff
++           """
++                   ]
++                :: [ markdown """
++            > This setup instructions are based on an assumption that you are using a Mac.
++            >
++            > If you are using Linux or BSD, then you probably know how to install stuff.
++            >
++            > If you are using anything else, then... well, good luck.
++            >
++            > The rest of the instructions should work with any platform.
++
++        """ ]
++                :: [ markdown """
++
++        We will need:
++
++        - a text editor (I use [Atom][])
++        - and [Elm programming language][]
++
++        [Atom]: https://atom.io/
++        [Elm programming language]: https://elm-lang.org/
++
++      """
++                   ]
++                :: [ markdown """
++
++        We will use the terminal a little bit.
++
++        # :fa-terminal:
++
++        Don't be scared. It's easy :)
++
++        <!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
++
++        > <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
++
++      """
++                   ]
++                :: [ markdown """
++                    You should see a window like this
++                """
++                   , Element.el [ Element.height Element.fill, Element.width Element.fill ] <| Element.html <| Html.img [ Html.class "plain", Html.alt "Mac Terminal app window", Html.src "../assets/mac-terminal-window.png" ] []
++                   , markdown """
++                    Here is some more markdown.
++                """
++                   ]
++                :: [ markdown """
++
++Now we are going to install few things.
++
++- Homebrew (to install other things)
++- Elm programming language
++- Atom editor
++
++"""
++                   ]
++                :: [ markdown """
++
++### Install Homebrew
++
++Follow instructions on the [Homebrew website](https://brew.sh/) by typing the following in the terminal (you probably want to copy and paste it):
++
++```sh
++/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
++```
++
++<small>
++Make sure that the command is exactly like the one above, including the `/` character at the beginning, the quotes and parentheses.
++
++It will ask you to confirm several actions and ask for your password. It may take few minutes to finish, so get your coffee
++</small>
++
++:fa-coffee:
++
++Once it's done you should see a command prompt like that:
++
++```
++~ your-name$
++```
++
++
++"""
++                   ]
++                :: [ markdown """
++
++### Install the Elm programming language
++
++<small>Once we have Homebrew installed, we can use it to install the language.</small>
++
++Type the following in the terminal.
++
++```sh
++brew install node elm
++```
++
++and check if it works by typing:
++
++```
++elm repl
++```
++
++"""
++                   ]
++                :: [ markdown """
++
++Note that the command line changes. You should see something like that
++
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++>
++```
++
++It's the Elm REPL
++
++<small>Read - Evaluate - Print Loop</small>
++
++*[REPL]: Read - Evaluate - Print Loop.
++
++"""
++                   ]
++                :: [ markdown """
++
++Inside the REPL type
++
++```
++2 + 2
++```
++
++And expect to see
++
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++> 2 + 2
++4 : number
++>
++```
++
++Easy, huh?
++
++"""
++                   ]
++                :: [ markdown """
++
++We will learn more about REPL later. For now type `:exit` to close it.
++
++The command line should look like before again.
++
++"""
++                   ]
++                :: [ markdown """
++
++### Install Atom text editor
++
++Computer programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use [Atom] here.
++
++"""
++                   ]
++                :: [ markdown """
++
++Type following in the terminal:
++
++```
++brew cask install atom
++```
++
++And start it with:
++
++```sh
++atom
++```
++
++"""
++                   ]
++                :: [ markdown """
++
++One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
++
++```
++apm install language-elm
++```
++
++<small>APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++**We are all set!**
++
++:smile:
++
++"""
++                   ]
++                :: [ markdown """
++
++
++# First program!
++
++"""
++                   ]
++                :: [ markdown """
++
++As mentioned before, programs are represented as text (called *the source code*).
++
++The source code is stored in files
++
++:fa-file:
++
++and files are organized in directories
++
++:fa-folder:
++
++"""
++                   ]
++                :: [ markdown """
++
++So the first step is to create a directory for our new program. Let's call it `fpart`.
++
++In the terminal type
++
++```sh
++mkdir fpart/
++```
++
++and then
++
++```
++cd fpart/
++```
++
++<small>This creates a new directory and makes it the current one. Again, don't worry about the details.</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++To easily create a new program, we can type
++
++```
++elm init
++```
++
++"""
++                   ]
++                :: [ markdown """
++
++Then to create a file with source code, type
++
++```
++atom src/Main.elm
++```
++
++<small>This command should open a new Atom window with empty text file.</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++### `main.elm`
++
++```elm
++module Main exposing (main)
++
++import Html
++
++
++main =
++    Html.text "Hello, Tree!"
++```
++
++<small>Type the above in the editor and save the file</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++To see the program running type following in the terminal
++
++```sh
++elm reactor
++```
++
++<small>This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++# Voila!
++
++<small>Open following address in the web browser</small>
++
++http://localhost:8000/src/Main.elm
++
++<small>Note that the same address was printed by Elm reactor</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++# Let's make a dot!
++# :fa-circle:
++
++"""
++                   ]
++                :: [ markdown """
++
++We are going to use a technology called SVG
++
++<small>Scalable Vector Graphics</small>
++
++
++*[SVG]: Scalable Vector Graphics
++
++"""
++                   ]
++                :: [ markdown """
++
++Let's install an Elm package to help us work with SVG.
++
++Stop the Reactor running in terminal by pressing
++
++`CTRL-C`
++
++and type the following
++
++```
++elm install elm/svg
++```
++
++Then start the reactor again
++
++```
++elm reactor
++```
++
++<small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
++
++"""
++                   ]
++                :: [ markdown """
++
++<iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
++</iframe>
++
++"""
++                   ]
++                :: []
+```
+
+``` diff
+new file mode 100644
+index 0000000..c017bb5
+--- /dev/null
++++ b/src/Presentation.elm
+@@ -0,0 +1,41 @@
++module Presentation exposing (Slide, markdown)
++
++import Element exposing (Element)
++import List.Extra as List
++import Markdown
++
++
++type alias Slide msg =
++    List (Element msg)
++
++
++markdown : String -> Element msg
++markdown string =
++    let
++        dedentedString =
++            string
++                |> String.lines
++                |> List.map dedent
++                |> String.join "\n"
++
++        dedent line =
++            line
++                |> String.toList
++                |> List.indexedMap Tuple.pair
++                |> List.dropWhile (\( index, character ) -> index < indentation && character == ' ')
++                |> List.map Tuple.second
++                |> String.fromList
++
++        indentation =
++            string
++                |> String.lines
++                |> List.filter (\line -> String.trim line /= "")
++                |> List.head
++                |> Maybe.withDefault ""
++                |> String.toList
++                |> List.findIndex ((/=) ' ')
++                |> Maybe.withDefault 0
++    in
++    Markdown.toHtml [] dedentedString
++        |> Element.html
++        |> Element.el [ Element.centerX ]
+```
+
+# Evolve program to Browser.sandobx, center text on slides
+
+
+
+``` diff
+index 88cb6e2..50a8c46 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,45 +1,71 @@
+=module Main exposing (main)
+=
++import Browser
+=import Dict
+=import Element
++import Element.Font as Font
+=import Html exposing (Html)
+=import Html.Attributes as Html
+=import Presentation exposing (Slide, markdown)
+=
+=
+-currentSlide =
+-    5
++main =
++    Browser.sandbox
++        { init = init
++        , view = view
++        , update = update
++        }
+=
+=
+-main =
+-    let
+-        slide =
+-            slides
+-                |> Dict.get currentSlide
+-
+-        notFound =
+-            Element.text "404: Slide not found"
+-    in
+-    Element.layout [ Element.width Element.fill, Element.height Element.fill ] <|
++type alias Model =
++    { currentSlide : Int
++    }
++
++
++type Msg
++    = Next
++    | Previous
++
++
++init : Model
++init =
++    { currentSlide = 0 }
++
++
++view : Model -> Html Msg
++view model =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++    <|
+=        Element.row
+-            [ Element.centerX, Element.centerY ]
+-            [ slide
++            [ Element.centerX, Element.centerY, Font.center ]
++            [ slides
++                |> Dict.get model.currentSlide
+=                |> Maybe.map (Element.column [])
+-                |> Maybe.withDefault notFound
++                |> Maybe.withDefault (Element.text "404: Slide not found")
+=            ]
+=
+=
++update : Msg -> Model -> Model
++update msg model =
++    case msg of
++        Next ->
++            { model | currentSlide = model.currentSlide + 1 }
++
++        Previous ->
++            { model | currentSlide = model.currentSlide - 1 }
++
++
+=slides =
+=    Dict.fromList <|
+=        List.indexedMap Tuple.pair <|
+=            [ markdown """
+-        # FP-Art!
+-        ## A functional programming workshop
+-        ### for non-programmers
+-    """
+-            , markdown """
+-        # Stuff
+-    """
++                # FP-Art!
++                ## A functional programming workshop
++                ### for non-programmers
++            """
+=            ]
+=                :: [ markdown """
+=               ## Setup
+```
+
+# Implement keyboard navigation
+
+
+
+``` diff
+index 67b87e4..d17943a 100644
+--- a/elm.json
++++ b/elm.json
+@@ -9,6 +9,7 @@
+=            "elm/browser": "1.0.0",
+=            "elm/core": "1.0.0",
+=            "elm/html": "1.0.0",
++            "elm/json": "1.0.0",
+=            "elm/svg": "1.0.1",
+=            "elm-community/list-extra": "8.1.0",
+=            "elm-explorations/markdown": "1.0.0",
+@@ -17,7 +18,6 @@
+=            "mdgriffith/elm-ui": "1.1.0"
+=        },
+=        "indirect": {
+-            "elm/json": "1.0.0",
+=            "elm/time": "1.0.0",
+=            "elm/url": "1.0.0",
+=            "elm/virtual-dom": "1.0.2",
+```
+
+``` diff
+index 50a8c46..0e51aaa 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,22 +1,29 @@
+=module Main exposing (main)
+=
+-import Browser
++import Browser exposing (Document)
++import Browser.Events
+=import Dict
+=import Element
+=import Element.Font as Font
+=import Html exposing (Html)
+=import Html.Attributes as Html
++import Json.Decode as Decode exposing (Decoder)
+=import Presentation exposing (Slide, markdown)
+=
+=
+=main =
+-    Browser.sandbox
++    Browser.document
+=        { init = init
+=        , view = view
+=        , update = update
++        , subscriptions = subscriptions
+=        }
+=
+=
++type alias Flags =
++    ()
++
++
+=type alias Model =
+=    { currentSlide : Int
+=    }
+@@ -27,35 +34,72 @@ type Msg
+=    | Previous
+=
+=
+-init : Model
+-init =
+-    { currentSlide = 0 }
++init : Flags -> ( Model, Cmd Msg )
++init flags =
++    ( { currentSlide = 0 }
++    , Cmd.none
++    )
+=
+=
+-view : Model -> Html Msg
++view : Model -> Document Msg
+=view model =
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-    <|
+-        Element.row
+-            [ Element.centerX, Element.centerY, Font.center ]
+-            [ slides
+-                |> Dict.get model.currentSlide
+-                |> Maybe.map (Element.column [])
+-                |> Maybe.withDefault (Element.text "404: Slide not found")
++    { title = "FP-Art!"
++    , body =
++        [ Element.layout
++            [ Element.width Element.fill
++            , Element.height Element.fill
+=            ]
++          <|
++            Element.column
++                [ Element.centerX, Element.centerY, Font.center ]
++                [ slides
++                    |> Dict.get model.currentSlide
++                    |> Maybe.map (Element.column [ Element.height Element.fill ])
++                    |> Maybe.withDefault (Element.text "404: Slide not found")
++                ]
++        ]
++    }
+=
+=
+-update : Msg -> Model -> Model
++update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+=    case msg of
+=        Next ->
+-            { model | currentSlide = model.currentSlide + 1 }
++            ( { model
++                | currentSlide =
++                    min (Dict.size slides - 1) (model.currentSlide + 1)
++              }
++            , Cmd.none
++            )
+=
+=        Previous ->
+-            { model | currentSlide = model.currentSlide - 1 }
++            ( { model
++                | currentSlide =
++                    max 0 (model.currentSlide - 1)
++              }
++            , Cmd.none
++            )
++
++
++subscriptions model =
++    let
++        handleKeyPress : Decoder Msg
++        handleKeyPress =
++            Decode.field "key" Decode.string
++                |> Decode.andThen
++                    (\key ->
++                        case Debug.log "Key" key of
++                            "ArrowLeft" ->
++                                Decode.succeed Previous
++
++                            "ArrowRight" ->
++                                Decode.succeed Next
++
++                            _ ->
++                                Decode.fail "Unsupported key"
++                    )
++    in
++    Browser.Events.onKeyPress handleKeyPress
+=
+=
+=slides =
+```
\ No newline at end of file
new file mode 100644
index 0000000..6ab1342
--- /dev/null
+++ b/content/devlog/2018-10-24-elm-tree-workshop.md
@@ -0,0 +1,1498 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 6
+
+
+# Embed the cartesian coordinates sample in the slide
+
+Making the SVG element fit the slide was difficult. In the end we are 
+relying on setting the width of it's parent to absolute value in pixels.
+
+Co-Authored-By: Sam Phillips <samuel.rodney.phillips@gmail.com>
+
+``` diff
+index 609bebd..8d5eb8f 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/CartesianCoordinates.elm
+@@ -1,4 +1,14 @@
+-module CartesianCoordinates exposing (main)
++module CartesianCoordinates exposing
++    ( Flags
++    , Model
++    , Msg
++    , init
++    , main
++    , subscriptions
++    , ui
++    , update
++    , view
++    )
+=
+=import Browser
+=import CartesianPlane exposing (graph)
+@@ -44,80 +54,17 @@ init () =
+=
+=view : Model -> Html.Html Msg
+=view model =
+-    Element.layout
+-        [ Element.height Element.fill -- (Element.px 600)
+-        , Element.width Element.fill
+-        ]
+-    <|
+-        Element.column
+-            [ Element.height Element.fill
+-            , Element.width <| Element.px 600
+-            , Element.centerX
+-            , Element.spacing 30
+-            , Element.padding 30
+-            ]
+-            [ Element.el [ Element.height <| Element.px 400 ] <|
+-                Element.html <|
+-                    graph
+-                        [ circle
+-                            [ cx <| String.fromFloat model.x
+-                            , cy <| String.fromFloat model.y
+-                            , r "0.01"
+-                            , fill "magenta"
+-                            ]
+-                            []
+-                        , text_
+-                            [ x <| String.fromFloat (model.x + 0.03)
+-                            , y <| String.fromFloat model.y
+-                            , fontSize "0.05"
+-                            , dominantBaseline "central"
+-                            ]
+-                            [ text <| Debug.toString ( model.x, model.y ) ]
+-                        ]
+-            , Input.slider
+-                [ Element.behindContent
+-                    (Element.el
+-                        [ Element.width Element.fill
+-                        , Element.height (Element.px 2)
+-                        , Element.centerY
+-                        , Background.color <| Element.rgb 0.7 0.7 0.7
+-                        , Border.rounded 2
+-                        ]
+-                        Element.none
+-                    )
+-                ]
+-                { onChange = SetX
+-                , label =
+-                    Input.labelBelow [ Element.centerX ] <|
+-                        Element.text ("x value: " ++ String.fromFloat model.x)
+-                , min = -1
+-                , max = 1
+-                , value = model.x
+-                , thumb = Input.defaultThumb
+-                , step = Just 0.01
+-                }
+-            , Input.slider
+-                [ Element.behindContent
+-                    (Element.el
+-                        [ Element.width Element.fill
+-                        , Element.height (Element.px 2)
+-                        , Element.centerY
+-                        , Background.color <| Element.rgb 0.7 0.7 0.7
+-                        , Border.rounded 2
+-                        ]
+-                        Element.none
+-                    )
+-                ]
+-                { onChange = SetY
+-                , label =
+-                    Input.labelBelow [ Element.centerX ] <|
+-                        Element.text ("y value: " ++ String.fromFloat model.y)
+-                , min = -1
+-                , max = 1
+-                , value = model.y
+-                , thumb = Input.defaultThumb
+-                , step = Just 0.01
+-                }
++    let
++        wrapper element =
++            Element.el
++                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
++                element
++    in
++    ui model
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill -- (Element.px 600)
++            , Element.width Element.fill
+=            ]
+=
+=
+@@ -134,3 +81,79 @@ update msg model =
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+=    Sub.none
++
++
++ui model =
++    Element.column
++        [ Element.width Element.fill
++        , Element.centerX
++        , Element.spacing 30
++        , Element.padding 30
++        ]
++        [ Element.el
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++          <|
++            Element.html <|
++                graph
++                    [ circle
++                        [ cx <| String.fromFloat model.x
++                        , cy <| String.fromFloat model.y
++                        , r "0.01"
++                        , fill "magenta"
++                        ]
++                        []
++                    , text_
++                        [ x <| String.fromFloat (model.x + 0.03)
++                        , y <| String.fromFloat model.y
++                        , fontSize "0.05"
++                        , dominantBaseline "central"
++                        ]
++                        [ text <| Debug.toString ( model.x, model.y ) ]
++                    ]
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = SetX
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("x value: " ++ String.fromFloat model.x)
++            , min = -1
++            , max = 1
++            , value = model.x
++            , thumb = Input.defaultThumb
++            , step = Just 0.01
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = SetY
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("y value: " ++ String.fromFloat model.y)
++            , min = -1
++            , max = 1
++            , value = model.y
++            , thumb = Input.defaultThumb
++            , step = Just 0.01
++            }
++        ]
+```
+
+``` diff
+index 394005d..0d82b4a 100644
+--- a/src/CartesianPlane.elm
++++ b/src/CartesianPlane.elm
+@@ -33,6 +33,8 @@ graph shapes =
+=    svg
+=        [ viewBox "-1 -1 2 2"
+=        , preserveAspectRatio "xMidYMid meet"
++        , Svg.Attributes.width "100%"
++        , Svg.Attributes.height "100%"
+=        , Svg.Attributes.style "width: 100%, height: 100%"
+=        ]
+=        (background :: shapes)
+```
+
+``` diff
+index 0e51aaa..da86dd4 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -2,11 +2,13 @@ module Main exposing (main)
+=
+=import Browser exposing (Document)
+=import Browser.Events
++import CartesianCoordinates
+=import Dict
+=import Element
+=import Element.Font as Font
+-import Html exposing (Html)
+-import Html.Attributes as Html
++import Element.Keyed
++import Html
++import Html.Attributes
+=import Json.Decode as Decode exposing (Decoder)
+=import Presentation exposing (Slide, markdown)
+=
+@@ -26,18 +28,29 @@ type alias Flags =
+=
+=type alias Model =
+=    { currentSlide : Int
++
++    -- Nested programs
++    , cartesianCoordinates : CartesianCoordinates.Model
+=    }
+=
+=
+=type Msg
+=    = Next
+=    | Previous
++      -- Nested programs
++    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init flags =
+-    ( { currentSlide = 0 }
+-    , Cmd.none
++    let
++        ( cartesianCoordinatesModel, cartesianCoordinatesCmd ) =
++            CartesianCoordinates.init ()
++    in
++    ( { currentSlide = 0
++      , cartesianCoordinates = cartesianCoordinatesModel
++      }
++    , Cmd.batch [ Cmd.map CartesianCoordinatesMsg cartesianCoordinatesCmd ]
+=    )
+=
+=
+@@ -51,10 +64,19 @@ view model =
+=            ]
+=          <|
+=            Element.column
+-                [ Element.centerX, Element.centerY, Font.center ]
+-                [ slides
++                [ Element.centerX
++                , Font.center
++                , Element.height Element.fill
++                , Element.width (Element.maximum 800 Element.fill)
++                ]
++                [ slides model
+=                    |> Dict.get model.currentSlide
+-                    |> Maybe.map (Element.column [ Element.height Element.fill ])
++                    |> Maybe.map
++                        (Element.column
++                            [ Element.width Element.fill
++                            , Element.centerY
++                            ]
++                        )
+=                    |> Maybe.withDefault (Element.text "404: Slide not found")
+=                ]
+=        ]
+@@ -67,7 +89,7 @@ update msg model =
+=        Next ->
+=            ( { model
+=                | currentSlide =
+-                    min (Dict.size slides - 1) (model.currentSlide + 1)
++                    min (Dict.size (slides model) - 1) (model.currentSlide + 1)
+=              }
+=            , Cmd.none
+=            )
+@@ -80,6 +102,16 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        -- Nested programs
++        CartesianCoordinatesMsg msg_ ->
++            let
++                ( model_, cmd_ ) =
++                    CartesianCoordinates.update msg_ model.cartesianCoordinates
++            in
++            ( { model | cartesianCoordinates = model_ }
++            , Cmd.map CartesianCoordinatesMsg cmd_
++            )
++
+=
+=subscriptions model =
+=    let
+@@ -95,6 +127,12 @@ subscriptions model =
+=                            "ArrowRight" ->
+=                                Decode.succeed Next
+=
++                            "a" ->
++                                Decode.succeed Previous
++
++                            "d" ->
++                                Decode.succeed Next
++
+=                            _ ->
+=                                Decode.fail "Unsupported key"
+=                    )
+@@ -102,7 +140,7 @@ subscriptions model =
+=    Browser.Events.onKeyPress handleKeyPress
+=
+=
+-slides =
++slides model =
+=    Dict.fromList <|
+=        List.indexedMap Tuple.pair <|
+=            [ markdown """
+@@ -111,6 +149,23 @@ slides =
+=                ### for non-programmers
+=            """
+=            ]
++                :: [ markdown """
++                    Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
++                """
++                   , model.cartesianCoordinates
++                        |> CartesianCoordinates.ui
++                        |> Element.map CartesianCoordinatesMsg
++                        |> Element.el
++                            [ Element.centerX
++                            , Element.width (Element.maximum 600 Element.fill)
++                            ]
++                   ]
++                -- :: [ markdown """
++                --        # FP-Art!
++                --        ## A functional programming workshop
++                --        ### for non-programmers
++                --    """
++                -- ]
+=                :: [ markdown """
+=               ## Setup
+=           """
+@@ -157,7 +212,16 @@ slides =
+=                :: [ markdown """
+=                    You should see a window like this
+=                """
+-                   , Element.el [ Element.height Element.fill, Element.width Element.fill ] <| Element.html <| Html.img [ Html.class "plain", Html.alt "Mac Terminal app window", Html.src "../assets/mac-terminal-window.png" ] []
++                   , Html.img
++                        [ Html.Attributes.alt "Mac Terminal app window"
++                        , Html.Attributes.src "../assets/mac-terminal-window.png"
++                        ]
++                        []
++                        |> Element.html
++                        |> Element.el
++                            [ Element.height Element.fill
++                            , Element.width Element.fill
++                            ]
+=                   , markdown """
+=                    Here is some more markdown.
+=                """
+```
+
+# Indent the slides content
+
+Co-Authored-By: Sam Phillips <samuel.rodney.phillips@gmail.com>
+
+``` diff
+index da86dd4..d00c059 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -147,7 +147,7 @@ slides model =
+=                # FP-Art!
+=                ## A functional programming workshop
+=                ### for non-programmers
+-            """
++              """
+=            ]
+=                :: [ markdown """
+=                    Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
+@@ -167,51 +167,50 @@ slides model =
+=                --    """
+=                -- ]
+=                :: [ markdown """
+-               ## Setup
+-           """
++                        ## Setup
++                     """
+=                   , markdown """
+-               # Stuff
+-           """
++                       # Stuff
++                     """
+=                   ]
+=                :: [ markdown """
+-            > This setup instructions are based on an assumption that you are using a Mac.
+-            >
+-            > If you are using Linux or BSD, then you probably know how to install stuff.
+-            >
+-            > If you are using anything else, then... well, good luck.
+-            >
+-            > The rest of the instructions should work with any platform.
+-
+-        """ ]
++                        > This setup instructions are based on an assumption that you are using a Mac.
++                        >
++                        > If you are using Linux or BSD, then you probably know how to install stuff.
++                        >
++                        > If you are using anything else, then... well, good luck.
++                        >
++                        > The rest of the instructions should work with any platform.
++
++                     """ ]
+=                :: [ markdown """
+=
+-        We will need:
++                        We will need:
+=
+-        - a text editor (I use [Atom][])
+-        - and [Elm programming language][]
++                        - a text editor (I use [Atom][])
++                        - and [Elm programming language][]
+=
+-        [Atom]: https://atom.io/
+-        [Elm programming language]: https://elm-lang.org/
++                        [Atom]: https://atom.io/
++                        [Elm programming language]: https://elm-lang.org/
+=
+-      """
++                     """
+=                   ]
+=                :: [ markdown """
++                        We will use the terminal a little bit.
+=
+-        We will use the terminal a little bit.
++                        # :fa-terminal:
+=
+-        # :fa-terminal:
++                        Don't be scared. It's easy :)
+=
+-        Don't be scared. It's easy :)
++                        <!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
+=
+-        <!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
++                        > <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
+=
+-        > <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
+-
+-      """
++                     """
+=                   ]
+=                :: [ markdown """
+-                    You should see a window like this
+-                """
++                        You should see a window like this
++                     """
+=                   , Html.img
+=                        [ Html.Attributes.alt "Mac Terminal app window"
+=                        , Html.Attributes.src "../assets/mac-terminal-window.png"
+@@ -223,312 +222,312 @@ slides model =
+=                            , Element.width Element.fill
+=                            ]
+=                   , markdown """
+-                    Here is some more markdown.
+-                """
++                        Here is some more markdown.
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-Now we are going to install few things.
++                        Now we are going to install few things.
+=
+-- Homebrew (to install other things)
+-- Elm programming language
+-- Atom editor
++                        - Homebrew (to install other things)
++                        - Elm programming language
++                        - Atom editor
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-### Install Homebrew
++                        ### Install Homebrew
+=
+-Follow instructions on the [Homebrew website](https://brew.sh/) by typing the following in the terminal (you probably want to copy and paste it):
++                        Follow instructions on the [Homebrew website](https://brew.sh/) by typing the following in the terminal (you probably want to copy and paste it):
+=
+-```sh
+-/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+-```
++                        ```sh
++                        /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
++                        ```
+=
+-<small>
+-Make sure that the command is exactly like the one above, including the `/` character at the beginning, the quotes and parentheses.
++                        <small>
++                        Make sure that the command is exactly like the one above, including the `/` character at the beginning, the quotes and parentheses.
+=
+-It will ask you to confirm several actions and ask for your password. It may take few minutes to finish, so get your coffee
+-</small>
++                        It will ask you to confirm several actions and ask for your password. It may take few minutes to finish, so get your coffee
++                        </small>
+=
+-:fa-coffee:
++                        :fa-coffee:
+=
+-Once it's done you should see a command prompt like that:
++                        Once it's done you should see a command prompt like that:
+=
+-```
+-~ your-name$
+-```
++                        ```
++                        ~ your-name$
++                        ```
+=
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-### Install the Elm programming language
++                        ### Install the Elm programming language
+=
+-<small>Once we have Homebrew installed, we can use it to install the language.</small>
++                        <small>Once we have Homebrew installed, we can use it to install the language.</small>
+=
+-Type the following in the terminal.
++                        Type the following in the terminal.
+=
+-```sh
+-brew install node elm
+-```
++                        ```sh
++                        brew install node elm
++                        ```
+=
+-and check if it works by typing:
++                        and check if it works by typing:
+=
+-```
+-elm repl
+-```
++                        ```
++                        elm repl
++                        ```
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-Note that the command line changes. You should see something like that
++                        Note that the command line changes. You should see something like that
+=
+-```
+----- Elm 0.19.0 ----------------------------------------------------------------
+-Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+---------------------------------------------------------------------------------
+->
+-```
++                        ```
++                        ---- Elm 0.19.0 ----------------------------------------------------------------
++                        Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++                        --------------------------------------------------------------------------------
++                        >
++                        ```
+=
+-It's the Elm REPL
++                        It's the Elm REPL
+=
+-<small>Read - Evaluate - Print Loop</small>
++                        <small>Read - Evaluate - Print Loop</small>
+=
+-*[REPL]: Read - Evaluate - Print Loop.
++                        *[REPL]: Read - Evaluate - Print Loop.
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-Inside the REPL type
++                        Inside the REPL type
+=
+-```
+-2 + 2
+-```
++                        ```
++                        2 + 2
++                        ```
+=
+-And expect to see
++                        And expect to see
+=
+-```
+----- Elm 0.19.0 ----------------------------------------------------------------
+-Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+---------------------------------------------------------------------------------
+-> 2 + 2
+-4 : number
+->
+-```
++                        ```
++                        ---- Elm 0.19.0 ----------------------------------------------------------------
++                        Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++                        --------------------------------------------------------------------------------
++                        > 2 + 2
++                        4 : number
++                        >
++                        ```
+=
+-Easy, huh?
++                        Easy, huh?
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-We will learn more about REPL later. For now type `:exit` to close it.
++                        We will learn more about REPL later. For now type `:exit` to close it.
+=
+-The command line should look like before again.
++                        The command line should look like before again.
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-### Install Atom text editor
++                        ### Install Atom text editor
+=
+-Computer programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use [Atom] here.
++                        Computer programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use [Atom] here.
+=
+-"""
++                    """
+=                   ]
+=                :: [ markdown """
+=
+-Type following in the terminal:
++                        Type following in the terminal:
+=
+-```
+-brew cask install atom
+-```
++                        ```
++                        brew cask install atom
++                        ```
+=
+-And start it with:
++                        And start it with:
+=
+-```sh
+-atom
+-```
++                        ```sh
++                        atom
++                        ```
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
++                        One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
+=
+-```
+-apm install language-elm
+-```
++                        ```
++                        apm install language-elm
++                        ```
+=
+-<small>APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.</small>
++                        <small>APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-**We are all set!**
++                        **We are all set!**
+=
+-:smile:
++                        :smile:
+=
+-"""
++                    """
+=                   ]
+=                :: [ markdown """
+=
+=
+-# First program!
++                        # First program!
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-As mentioned before, programs are represented as text (called *the source code*).
++                        As mentioned before, programs are represented as text (called *the source code*).
+=
+-The source code is stored in files
++                        The source code is stored in files
+=
+-:fa-file:
++                        :fa-file:
+=
+-and files are organized in directories
++                        and files are organized in directories
+=
+-:fa-folder:
++                        :fa-folder:
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-So the first step is to create a directory for our new program. Let's call it `fpart`.
++                        So the first step is to create a directory for our new program. Let's call it `fpart`.
+=
+-In the terminal type
++                        In the terminal type
+=
+-```sh
+-mkdir fpart/
+-```
++                        ```sh
++                        mkdir fpart/
++                        ```
+=
+-and then
++                        and then
+=
+-```
+-cd fpart/
+-```
++                        ```
++                        cd fpart/
++                        ```
+=
+-<small>This creates a new directory and makes it the current one. Again, don't worry about the details.</small>
++                        <small>This creates a new directory and makes it the current one. Again, don't worry about the details.</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-To easily create a new program, we can type
++                        To easily create a new program, we can type
+=
+-```
+-elm init
+-```
++                        ```
++                        elm init
++                        ```
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-Then to create a file with source code, type
++                        Then to create a file with source code, type
+=
+-```
+-atom src/Main.elm
+-```
++                        ```
++                        atom src/Main.elm
++                        ```
+=
+-<small>This command should open a new Atom window with empty text file.</small>
++                        <small>This command should open a new Atom window with empty text file.</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-### `main.elm`
++                        ### `main.elm`
+=
+-```elm
+-module Main exposing (main)
++                        ```elm
++                        module Main exposing (main)
+=
+-import Html
++                        import Html
+=
+=
+-main =
+-    Html.text "Hello, Tree!"
+-```
++                        main =
++                            Html.text "Hello, Tree!"
++                        ```
+=
+-<small>Type the above in the editor and save the file</small>
++                        <small>Type the above in the editor and save the file</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-To see the program running type following in the terminal
++                        To see the program running type following in the terminal
+=
+-```sh
+-elm reactor
+-```
++                        ```sh
++                        elm reactor
++                        ```
+=
+-<small>This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.</small>
++                        <small>This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-# Voila!
++                        # Voila!
+=
+-<small>Open following address in the web browser</small>
++                        <small>Open following address in the web browser</small>
+=
+-http://localhost:8000/src/Main.elm
++                        http://localhost:8000/src/Main.elm
+=
+-<small>Note that the same address was printed by Elm reactor</small>
++                        <small>Note that the same address was printed by Elm reactor</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-# Let's make a dot!
+-# :fa-circle:
++                        # Let's make a dot!
++                        # :fa-circle:
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-We are going to use a technology called SVG
++                        We are going to use a technology called SVG
+=
+-<small>Scalable Vector Graphics</small>
++                        <small>Scalable Vector Graphics</small>
+=
+=
+-*[SVG]: Scalable Vector Graphics
++                        *[SVG]: Scalable Vector Graphics
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-Let's install an Elm package to help us work with SVG.
++                        Let's install an Elm package to help us work with SVG.
+=
+-Stop the Reactor running in terminal by pressing
++                        Stop the Reactor running in terminal by pressing
+=
+-`CTRL-C`
++                        `CTRL-C`
+=
+-and type the following
++                        and type the following
+=
+-```
+-elm install elm/svg
+-```
++                        ```
++                        elm install elm/svg
++                        ```
+=
+-Then start the reactor again
++                        Then start the reactor again
+=
+-```
+-elm reactor
+-```
++                        ```
++                        elm reactor
++                        ```
+=
+-<small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
++                        <small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
+=
+-"""
++                     """
+=                   ]
+=                :: [ markdown """
+=
+-<iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
+-</iframe>
++                        <iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
++                        </iframe>
+=
+-"""
++                     """
+=                   ]
+=                :: []
+```
+
+# In PolarCoordinates, use CartesianPlane.graph to draw cartesian plane
+
+Remove background colors from PolarCoordinates.view
+
+Co-authored-by: Tadeusz Łazurski <tadeusz@lazurski.pl>
+
+``` diff
+index fead82e..65f6718 100644
+--- a/src/PolarCoordinates.elm
++++ b/src/PolarCoordinates.elm
+@@ -1,6 +1,7 @@
+=module PolarCoordinates exposing (main)
+=
+=import Browser
++import CartesianPlane
+=import Element
+=import Element.Background as Background
+=import Element.Border as Border
+@@ -50,43 +51,19 @@ view model =
+=    Element.layout
+=        [ Element.height Element.fill -- (Element.px 600)
+=        , Element.width Element.fill
+-        , Background.color <| Element.rgb 0.6 0 0
+=        ]
+=    <|
+=        Element.column
+=            [ Element.height Element.fill
+=            , Element.width <| Element.px 600
+-            , Background.color <| Element.rgb 0 0.6 0
+=            , Element.centerX
+=            , Element.spacing 30
+=            , Element.padding 30
+=            ]
+=            [ Element.el [] <|
+=                Element.html <|
+-                    svg
+-                        [ viewBox "-1 -1 2 2"
+-                        , preserveAspectRatio "xMidYMid meet"
+-                        , Svg.Attributes.style "width: 100%, height: 100%"
+-                        ]
+-                        [ line
+-                            [ x1 "-1"
+-                            , y1 "0"
+-                            , x2 "1"
+-                            , y2 "0"
+-                            , stroke "black"
+-                            , strokeWidth "0.001"
+-                            ]
+-                            []
+-                        , line
+-                            [ x1 "0"
+-                            , x2 "0"
+-                            , y1 "-1"
+-                            , y2 "1"
+-                            , stroke "black"
+-                            , strokeWidth "0.001"
+-                            ]
+-                            []
+-                        , circle
++                    CartesianPlane.graph
++                        [ circle
+=                            [ cx <| String.fromFloat point.x
+=                            , cy <| String.fromFloat point.y
+=                            , r "0.01"
+```
+
+# Merge remote-tracking branch 'origin/elm' into elm
+
+
+
+
+
+# Embed PolarCoordinates sample in slides
+
+Co-authored-by: Tadeusz Łazurski <tadeusz@lazurski.pl>
+
+``` diff
+index 8d5eb8f..6be4d7b 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/CartesianCoordinates.elm
+@@ -63,7 +63,7 @@ view model =
+=    ui model
+=        |> wrapper
+=        |> Element.layout
+-            [ Element.height Element.fill -- (Element.px 600)
++            [ Element.height Element.fill
+=            , Element.width Element.fill
+=            ]
+=
+```
+
+``` diff
+index d00c059..f40b7a2 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -10,6 +10,7 @@ import Element.Keyed
+=import Html
+=import Html.Attributes
+=import Json.Decode as Decode exposing (Decoder)
++import PolarCoordinates
+=import Presentation exposing (Slide, markdown)
+=
+=
+@@ -31,6 +32,7 @@ type alias Model =
+=
+=    -- Nested programs
+=    , cartesianCoordinates : CartesianCoordinates.Model
++    , polarCoordinates : PolarCoordinates.Model
+=    }
+=
+=
+@@ -39,6 +41,7 @@ type Msg
+=    | Previous
+=      -- Nested programs
+=    | CartesianCoordinatesMsg CartesianCoordinates.Msg
++    | PolarCoordinatesMsg PolarCoordinates.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+@@ -46,11 +49,18 @@ init flags =
+=    let
+=        ( cartesianCoordinatesModel, cartesianCoordinatesCmd ) =
+=            CartesianCoordinates.init ()
++
++        ( polarCoordinatesModel, polarCoordinatesCmd ) =
++            PolarCoordinates.init ()
+=    in
+=    ( { currentSlide = 0
+=      , cartesianCoordinates = cartesianCoordinatesModel
++      , polarCoordinates = polarCoordinatesModel
+=      }
+-    , Cmd.batch [ Cmd.map CartesianCoordinatesMsg cartesianCoordinatesCmd ]
++    , Cmd.batch
++        [ Cmd.map CartesianCoordinatesMsg cartesianCoordinatesCmd
++        , Cmd.map PolarCoordinatesMsg polarCoordinatesCmd
++        ]
+=    )
+=
+=
+@@ -112,6 +122,15 @@ update msg model =
+=            , Cmd.map CartesianCoordinatesMsg cmd_
+=            )
+=
++        PolarCoordinatesMsg msg_ ->
++            let
++                ( model_, cmd_ ) =
++                    PolarCoordinates.update msg_ model.polarCoordinates
++            in
++            ( { model | polarCoordinates = model_ }
++            , Cmd.map PolarCoordinatesMsg cmd_
++            )
++
+=
+=subscriptions model =
+=    let
+@@ -150,8 +169,8 @@ slides model =
+=              """
+=            ]
+=                :: [ markdown """
+-                    Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
+-                """
++                        Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
++                     """
+=                   , model.cartesianCoordinates
+=                        |> CartesianCoordinates.ui
+=                        |> Element.map CartesianCoordinatesMsg
+@@ -160,17 +179,25 @@ slides model =
+=                            , Element.width (Element.maximum 600 Element.fill)
+=                            ]
+=                   ]
+-                -- :: [ markdown """
+-                --        # FP-Art!
+-                --        ## A functional programming workshop
+-                --        ### for non-programmers
+-                --    """
+-                -- ]
+=                :: [ markdown """
+-                        ## Setup
++                        Move sliders to change the `angle` and `length` properties of the line connecting the dot and origin. The x and y coordinates will be calculated like this:
++
++                        ```
++                        x = cos(angle) * length
++
++                        y = sin(angle) * length
++                        ```
+=                     """
+-                   , markdown """
+-                       # Stuff
++                   , model.polarCoordinates
++                        |> PolarCoordinates.ui
++                        |> Element.map PolarCoordinatesMsg
++                        |> Element.el
++                            [ Element.centerX
++                            , Element.width (Element.maximum 600 Element.fill)
++                            ]
++                   ]
++                :: [ markdown """
++                        ## Setup
+=                     """
+=                   ]
+=                :: [ markdown """
+```
+
+``` diff
+index 65f6718..7a0d79c 100644
+--- a/src/PolarCoordinates.elm
++++ b/src/PolarCoordinates.elm
+@@ -1,8 +1,18 @@
+-module PolarCoordinates exposing (main)
++module PolarCoordinates exposing
++    ( Flags
++    , Model
++    , Msg
++    , init
++    , main
++    , subscriptions
++    , ui
++    , update
++    , view
++    )
+=
+=import Browser
+=import CartesianPlane
+-import Element
++import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+=import Element.Input as Input
+@@ -44,95 +54,109 @@ init () =
+=
+=view : Model -> Html.Html Msg
+=view model =
++    let
++        wrapper element =
++            Element.el
++                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
++                element
++    in
++    ui model
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++
++
++ui : Model -> Element Msg
++ui model =
+=    let
+=        point =
+=            cartesian model
+=    in
+-    Element.layout
+-        [ Element.height Element.fill -- (Element.px 600)
+-        , Element.width Element.fill
++    Element.column
++        [ Element.width Element.fill
++        , Element.centerX
++        , Element.spacing 30
++        , Element.padding 30
+=        ]
+-    <|
+-        Element.column
++        [ Element.el
+=            [ Element.height Element.fill
+-            , Element.width <| Element.px 600
+-            , Element.centerX
+-            , Element.spacing 30
+-            , Element.padding 30
++            , Element.width Element.fill
+=            ]
+-            [ Element.el [] <|
+-                Element.html <|
+-                    CartesianPlane.graph
+-                        [ circle
+-                            [ cx <| String.fromFloat point.x
+-                            , cy <| String.fromFloat point.y
+-                            , r "0.01"
+-                            , fill "magenta"
+-                            ]
+-                            []
+-                        , line
+-                            [ x1 "0"
+-                            , x2 <| String.fromFloat point.x
+-                            , y1 "0"
+-                            , y2 <| String.fromFloat point.y
+-                            , stroke "magenta"
+-                            , strokeWidth "0.001"
+-                            ]
+-                            []
+-                        , text_
+-                            [ x <| String.fromFloat (point.x + 0.02)
+-                            , y <| String.fromFloat (point.y + 0.01)
+-                            , Svg.Attributes.fontSize "0.03pt"
+-                            ]
+-                            [ text <|
+-                                Debug.toString ( point.x, point.y )
+-                            ]
++          <|
++            Element.html <|
++                CartesianPlane.graph
++                    [ circle
++                        [ cx <| String.fromFloat point.x
++                        , cy <| String.fromFloat point.y
++                        , r "0.01"
++                        , fill "magenta"
++                        ]
++                        []
++                    , line
++                        [ x1 "0"
++                        , x2 <| String.fromFloat point.x
++                        , y1 "0"
++                        , y2 <| String.fromFloat point.y
++                        , stroke "magenta"
++                        , strokeWidth "0.001"
+=                        ]
+-            , Input.slider
+-                [ Element.behindContent
+-                    (Element.el
+-                        [ Element.width Element.fill
+-                        , Element.height (Element.px 2)
+-                        , Element.centerY
+-                        , Background.color <| Element.rgb 0.7 0.7 0.7
+-                        , Border.rounded 2
++                        []
++                    , text_
++                        [ x <| String.fromFloat (point.x + 0.02)
++                        , y <| String.fromFloat (point.y + 0.01)
++                        , Svg.Attributes.fontSize "0.03pt"
+=                        ]
+-                        Element.none
+-                    )
+-                ]
+-                { onChange = SetAngle
+-                , label =
+-                    Input.labelBelow [ Element.centerX ] <|
+-                        Element.text ("angle value: " ++ String.fromFloat model.angle)
+-                , min = 0
+-                , max = 360
+-                , value = model.angle
+-                , thumb = Input.defaultThumb
+-                , step = Just 1
+-                }
+-            , Input.slider
+-                [ Element.behindContent
+-                    (Element.el
+-                        [ Element.width Element.fill
+-                        , Element.height (Element.px 2)
+-                        , Element.centerY
+-                        , Background.color <| Element.rgb 0.7 0.7 0.7
+-                        , Border.rounded 2
++                        [ text <|
++                            Debug.toString ( point.x, point.y )
+=                        ]
+-                        Element.none
+-                    )
+-                ]
+-                { onChange = SetRadius
+-                , label =
+-                    Input.labelBelow [ Element.centerX ] <|
+-                        Element.text ("radius value: " ++ String.fromFloat model.radius)
+-                , min = 0
+-                , max = 1
+-                , value = model.radius
+-                , thumb = Input.defaultThumb
+-                , step = Just 0.01
+-                }
++                    ]
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
+=            ]
++            { onChange = SetAngle
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("angle value: " ++ String.fromFloat model.angle)
++            , min = 0
++            , max = 360
++            , value = model.angle
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = SetRadius
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("radius value: " ++ String.fromFloat model.radius)
++            , min = 0
++            , max = 1
++            , value = model.radius
++            , thumb = Input.defaultThumb
++            , step = Just 0.01
++            }
++        ]
+=
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+```
+
+# Create content for slides.
+
+Co-authored-by: Tadeusz Łazurski <tadeusz@lazurski.pl>
+
+``` diff
+index f40b7a2..f18bc73 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -163,39 +163,11 @@ slides model =
+=    Dict.fromList <|
+=        List.indexedMap Tuple.pair <|
+=            [ markdown """
+-                # FP-Art!
++                # Software Garden
+=                ## A functional programming workshop
+=                ### for non-programmers
+=              """
+=            ]
+-                :: [ markdown """
+-                        Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
+-                     """
+-                   , model.cartesianCoordinates
+-                        |> CartesianCoordinates.ui
+-                        |> Element.map CartesianCoordinatesMsg
+-                        |> Element.el
+-                            [ Element.centerX
+-                            , Element.width (Element.maximum 600 Element.fill)
+-                            ]
+-                   ]
+-                :: [ markdown """
+-                        Move sliders to change the `angle` and `length` properties of the line connecting the dot and origin. The x and y coordinates will be calculated like this:
+-
+-                        ```
+-                        x = cos(angle) * length
+-
+-                        y = sin(angle) * length
+-                        ```
+-                     """
+-                   , model.polarCoordinates
+-                        |> PolarCoordinates.ui
+-                        |> Element.map PolarCoordinatesMsg
+-                        |> Element.el
+-                            [ Element.centerX
+-                            , Element.width (Element.maximum 600 Element.fill)
+-                            ]
+-                   ]
+=                :: [ markdown """
+=                        ## Setup
+=                     """
+@@ -215,7 +187,7 @@ slides model =
+=                        We will need:
+=
+=                        - a text editor (I use [Atom][])
+-                        - and [Elm programming language][]
++                        - and the [Elm programming language][]
+=
+=                        [Atom]: https://atom.io/
+=                        [Elm programming language]: https://elm-lang.org/
+@@ -223,7 +195,25 @@ slides model =
+=                     """
+=                   ]
+=                :: [ markdown """
+-                        We will use the terminal a little bit.
++                        ### Elm Installation
++                     """
++                   ]
++                :: [ markdown """
++                        To install the Elm programming language, go to the [website][Elm] and follow the installation instructions.
++
++                        [Elm]: http://elm-lang.org/
++                     """
++                   ]
++                :: [ markdown """
++                        Some of the tools we use with Elm require Node.js.
++
++                        Go to the [Node.js website][Node.js] and install the current version (the green button on the right).
++
++                        [Node.js]: https://nodejs.org/en/
++                     """
++                   ]
++                :: [ markdown """
++                        We will need to use the terminal a little bit.
+=
+=                        # :fa-terminal:
+=
+@@ -557,4 +547,32 @@ slides model =
+=
+=                     """
+=                   ]
++                :: [ markdown """
++                        Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
++                     """
++                   , model.cartesianCoordinates
++                        |> CartesianCoordinates.ui
++                        |> Element.map CartesianCoordinatesMsg
++                        |> Element.el
++                            [ Element.centerX
++                            , Element.width (Element.maximum 600 Element.fill)
++                            ]
++                   ]
++                :: [ markdown """
++                        Move sliders to change the `angle` and `length` properties of the line connecting the dot and origin. The x and y coordinates will be calculated like this:
++
++                        ```
++                        x = cos(angle) * length
++
++                        y = sin(angle) * length
++                        ```
++                     """
++                   , model.polarCoordinates
++                        |> PolarCoordinates.ui
++                        |> Element.map PolarCoordinatesMsg
++                        |> Element.el
++                            [ Element.centerX
++                            , Element.width (Element.maximum 600 Element.fill)
++                            ]
++                   ]
+=                :: []
+```
\ No newline at end of file
new file mode 100644
index 0000000..17f375f
--- /dev/null
+++ b/content/devlog/2018-10-25-elm-tree-workshop.md
@@ -0,0 +1,596 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Put slides back in MD file for easier editing
+
+Also make Git ignore Emacs temporary files.
+
+Co-Authored-By: Sam Phillips <samuel.rodney.phillips@gmail.com>
+
+``` diff
+index 0c19f1d..1cf77ef 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,3 +1,4 @@
+=/elm-stuff
+=/**/.DS_Store
+=*.swp
++.#*
+```
+
+``` diff
+index a2ea019..5704253 100644
+--- a/index.md
++++ b/index.md
+@@ -6,7 +6,45 @@ presentation:
+=
+=<!-- slide -->
+=
+-# FP-Art!
++## Conspect
++
++- Setup the development environment
++
++  - Elm
++
++  - Node.js
++
++    why?
++
++  - Test REPL (2 + 2)
++
++  - Atom
++
++
++
++- Setup the project
++
++  Do we want source control?
++
++  - Create directory
++
++  - `elm init`
++
++  - Open a file in the editor
++
++  - Type hello world program
++
++  - Elm Reactor
++
++  - Web browser
++
++
++
++
++
++<!-- slide -->
++
++# Software Garden
+=## A functional programming workshop
+=### for non-programmers
+=
+@@ -29,14 +67,34 @@ presentation:
+=We will need:
+=
+=- a text editor (I use [Atom][])
+-- and [Elm programming language][]
++- and the [Elm programming language][]
+=
+=[Atom]: https://atom.io/
+=[Elm programming language]: https://elm-lang.org/
+=
+=<!-- slide -->
+=
+-We will use the terminal a little bit.
++### Elm Installation
++
++<!-- slide -->
++
++To install the Elm programming language, go to it's website:
++
++http://elm-lang.org/
++
++and follow the installation instructions.
++
++<!-- slide -->
++
++Some of the tools we use with Elm require Node.js.
++
++Go to the [Node.js website][Node.js] and install the current version (the green button on the right).
++
++[Node.js]: https://nodejs.org/en/
++
++<!-- slide -->
++
++We will need to use the terminal a little bit.
+=
+=# :fa-terminal:
+=
+@@ -44,13 +102,17 @@ Don't be scared. It's easy :)
+=
+=<!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
+=
+-> <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
++<p style="color: white">
++  In Launchpad find a program called <code>terminal</code> and start it.
++</p>
+=
+=<!-- slide -->
+=
+=You should see a window like this
+=
+-<img class="plain" alt="Mac Terminal app window" src="images/mac-terminal-window.png" />
++<!-- slide -->
++
++<img alt="Mac Terminal app window" src="../assets/mac-terminal-window.png" class="plain"/>
+=
+=<!-- slide -->
+=
+@@ -185,7 +247,6 @@ apm install language-elm
+=
+=<!-- slide -->
+=
+-
+=# First program!
+=
+=<!-- slide -->
+@@ -236,7 +297,7 @@ atom src/Main.elm
+=
+=<small>This command should open a new Atom window with empty text file.</small>
+=
+-<!-- slide data-transition=zoom -->
++<!-- slide -->
+=
+=### `main.elm`
+=
+@@ -308,7 +369,21 @@ elm reactor
+=
+=<small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
+=
++
++<!-- slide -->
++
++Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
++
++`<CartesianCoordinates program>`
++
+=<!-- slide -->
+=
+-<iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
+-</iframe>
++Move sliders to change the `angle` and `length` properties of the line connecting the dot and origin. The x and y coordinates will be calculated like this:
++
++```
++x = cos(angle) * length
++y = sin(angle) * length
++```
++
++
++`<PolarCoordinates program>`
+```
+
+# Work on outline for slides
+
+
+
+``` diff
+index 5704253..334b686 100644
+--- a/index.md
++++ b/index.md
+@@ -39,6 +39,236 @@ presentation:
+=  - Web browser
+=
+=
++- Let's make a dot!
++
++  - Introduce a problem: We want to have a dot on the screen
++
++  - Show complete code
++
++  - Explain:
++
++    The dot has a position (x + y coordinates) and size (radius)
++
++    Coordinate planes: we can use CartesianCoordinates
++
++  - Introduce SVG
++
++
++- Give  a color to the dot
++
++  - Explain SVG attributes (svg elements take a list of attributes)
++
++  - Everyone can pick a color
++
++
++- Multiple dots
++
++  - Introduce a problem: We want to have two dots on the screen
++
++  - Explain a list, and show there is already a list there (with one item)
++
++    Do you see any other lists?
++
++  - Element takes a list of children
++
++  - Mention, how can we have a list with one or zero elements? Shopping list with one one item. Empty spoon drawer (list of spoons)
++
++  - Give your new dot a color and different coordinates
++
++  - Show the code for 2 dots
++
++  - Exercise: make 3 more dots (5 total)
++
++
++- Place the dots in a circle
++
++  - Introduce a problem: We want to place the dots in a circle: show an example in the slides
++
++  - How we will get there: we will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
++
++  - How can we figure out the correct cartesian coordinates?
++
++    - Story about the flowerpot and the table: two ways to measure relative position, one is distance from a x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
++
++    - What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a "360"). We see that circles and angles are closely related!
++
++    - Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
++
++    - We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
++
++    - Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
++
++    - So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
++
++      Using a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
++
++      Let's use a chart to figure out the sin and cos of our angles
++
++      *It's important to get a chart, otherwise we have to use calculators which work with radians*
++
++    - Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
++
++    - Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
++
++      We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
++
++      Sample viewbox program to demonstrate
++
++      Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
++
++
++- Let the computer do the math
++
++  - Introduce the problem: Can we avoid all these repetitive and manual steps?
++
++  - What is a program made of? (in the REPL):
++
++    - Values and names
++
++      ```
++      5
++      10.4
++      "Hello World"
++      [1, 2, 3, 4, 5]
++      ```
++
++      Numbers, strings, lists containing numbers and strings are all values. They are self-referential. The simply state what they are. There are other kinds of values that we will see later.
++
++
++      You can give (or assign) a name to a value, as shown.
++
++      ```
++      family = 5
++      price = 10.4
++      morningGreeting = "Hello World"
++      fingers = [1, 2, 3, 4, 5]
++      ```
++
++      Here, `family` is a name and `5` is the value assigned to `family`.
++
++      You can get the value back by calling its name.
++
++      ```
++      ---- Elm 0.19.0 ----------------------------------------------------------------
++      Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++      --------------------------------------------------------------------------------
++      > family = 5
++      5 : number
++      > family
++      5 : number
++      ```
++
++    - Operators (+, -, ++)
++
++      ```
++      > 2 + 5
++      7 : number
++      ```
++
++      ```
++      > 4 - 6
++      -2 : number
++      ```
++
++      ```
++      > "Hello" ++ " world!"
++      "Hello world!" : String
++      ```
++
++      ```
++      > 1 :: [2, 3, 4, 5]
++      [1,2,3,4,5] : List number
++      ```
++
++      Operators take two values and return a new value. We know that names refer to values, so we can use them in place of values:
++
++      ```
++      > family = 5
++      5 : number
++      > family + 2
++      7 : number
++      ```
++
++      You can also give a name to the value returned by an operator:
++
++      ```
++      > family = 5
++      5 : number
++      > familyAndPets = family + 2
++      7 : number
++      > familyAndPets
++      7 : number
++      ```
++
++      Note that different values have different types.
++
++      ```
++      5 : number
++      10.4 : Float
++      "Hello World" : String
++      [1,2,3,4,5] : List number
++      ```
++      Different operators work on different types. Adding a number and a string doesn't make sense. So if you try, Elm will complain (and give a helpful hint):
++
++      ```
++      > "Hello " + 5
++      -- TYPE MISMATCH ----------------------------------------------------------- elm
++
++      I cannot do addition with String values like this one:
++
++      5|   "Hello " + 5
++           ^^^^^^^^
++      The (+) operator only works with Int and Float values.
++
++      Hint: Switch to the (++) operator to append strings!
++      ```
++
++      (Int and Float are both types representing numbers)
++
++
++    - Functions
++
++      ```elm
++      fun something = something ++ " is fun."
++      ```
++
++      Mention that operators are really functions:
++
++      ```elm
++      (-) 5 10
++      5 - 10
++      ```
++
++  - Exercise: In our Main.elm, try to identify some values, names and functions:
++
++    Hint: `"darkred"` is a value, `main` is a name, `width` is a function.
++
++  - Where can we use some functions?
++
++  - Apply to our trigonometry calculations
++
++    - Compose and copy and paste the trigonometry functions to calculate cx and cy:
++
++      ```elm
++      (String.fromFloat (sin (degrees 72) * 100))
++      (String.fromFloat (cos (degrees 72) * 100))
++      ```
++
++    - Eliminate repetition:
++
++      - define and reuse radius (100)
++
++      - define and reuse `x : angle -> cx` and `y : angle -> cy`
++
++      - define and reuse `dot : angle -> color -> Svg`
++
++    - Make it more general
++
++      - Show List and list operations (map and indexedMap)
++
++      - Make a `palette : List color`
++
++      - Use `List.indexedMap dot palette` to generate the dots
+=
+=
+=
+```
+
+# Add more to the conspect
+
+Co-Authored-By: Sam Phillips <samuel.rodney.phillips@gmail.com>
+
+``` diff
+index 334b686..936c34e 100644
+--- a/index.md
++++ b/index.md
+@@ -132,8 +132,7 @@ presentation:
+=      [1, 2, 3, 4, 5]
+=      ```
+=
+-      Numbers, strings, lists containing numbers and strings are all values. They are self-referential. The simply state what they are. There are other kinds of values that we will see later.
+-
++      Numbers, strings, lists containing numbers and strings are all values. They are self-referential. They simply state what they are. There are other kinds of values that we will see later.
+=
+=      You can give (or assign) a name to a value, as shown.
+=
+@@ -232,36 +231,146 @@ presentation:
+=      fun something = something ++ " is fun."
+=      ```
+=
+-      Mention that operators are really functions:
++      Explain: a function is a thing that takes some values (called arguments) and return one value. So it's similar to operators. In fact operators are functions!
+=
+=      ```elm
+=      (-) 5 10
+=      5 - 10
+=      ```
+=
+-  - Exercise: In our Main.elm, try to identify some values, names and functions:
++      You can think of a function as a machine. You put something in the machine, and it produces something in return. For example think about a machine that produces rubber ducks. You put a bucket of white plastic pellets and a bucket of red paint, and you get a bunch of red rubber ducks!
++
++      What you get will depends on what you put. The color of the ducks depends on the paint you put. Quantity of ducks depends on how much plastic you put in.
++
++      ```elm
++      makeMeSomeDucks color plastic =
++          String.fromInt plastic ++ " " ++ color ++ " rubber ducks"
++      ```
++
++      Once you have a function, you can call it like this:
++
++      ```elm
++      > makeMeSomeDucks "blue" 12
++      "12 blue rubber ducks" : String
++      ```
++
++      Note: functions help organize code into nice reusable chunks.
++
++      How do you get functions? There are three ways.
++
++        1.  Some functions are always there for you, e.g. `(+)`, `(-)`
++
++        2.  Some functions you can import using code like this:
++
++            ```elm
++            import Svg
++            ```
++
++            and then
++
++            ```
++            Svg.circle [ cx "10", cy "10", r "20" ] [ ]
++            ```
++
++            To call a function that was imported, you have to prefix it with the name of the module (in this example `Svg`).
++
++        3.  Finally, you will write some functions, just like we saw in the `fun` and `makeMeSomeDucks` examples.
++
++
++      Finally there is one special thing: the first line of the program is a module declaration. For now it's enough for us to know, that it has to be there and it has to match the name of the file.
++
++  - Exercise: In our Main.elm, try to identify some values, names and function calls:
+=
+=    Hint: `"darkred"` is a value, `main` is a name, `width` is a function.
+=
++    Hint: There is nothing else there now, but we will soon introduce our own functions.
++
+=  - Where can we use some functions?
+=
++    As we said before, functions are good when we have some repetitive operation that can be parametrized (like rubber ducks production).
++
++    Obviously calculating `x` and `y` coordinates is repetitive and can be parametrized (parameters are `radius` and `angle`).
++
+=  - Apply to our trigonometry calculations
+=
+=    - Compose and copy and paste the trigonometry functions to calculate cx and cy:
+=
+=      ```elm
+-      (String.fromFloat (sin (degrees 72) * 100))
+-      (String.fromFloat (cos (degrees 72) * 100))
++      cx (String.fromFloat (cos (degrees 72) * 100))
++      cy (String.fromFloat (sin (degrees 72) * 100))
+=      ```
+=
+=    - Eliminate repetition:
+=
+=      - define and reuse radius (100)
+=
++        Assign a value of `100` to a name `radius`:
++
++        ```
++        radius = 100
++        ```
++
++        and plug it to our functions:
++
++        ```elm
++        cx (String.fromFloat (cos (degrees 72) * radius))
++        cy (String.fromFloat (sin (degrees 72) * radius))
++        ```
++
++        As you see the names are good for repetitive things too.
++
+=      - define and reuse `x : angle -> cx` and `y : angle -> cy`
+=
++
++        ```elm
++        x angle =
++          String.fromFloat (cos (degrees angle) * radius)
++        ```
++
++        ```elm
++        y angle =
++          String.fromFloat (sin (degrees angle) * radius)
++        ```
++
++        Then plug it into the code making circles:
++
++        ```elm
++        circle
++          [ cx (x 72)
++          , cy (y 72)
++          , r "10"
++          , fill "darkred"
++          ]
++        ```
++
+=      - define and reuse `dot : angle -> color -> Svg`
+=
++
++        ```elm
++        dot angle color =
++            circle
++                [ cx (x 72)
++                , cy (y 72)
++                , r "10"
++                , fill color
++                ]
++        ```
++
++        and plug it into our SVG picture:
++
++
++        ```
++        svg [ viewbox "-100 -100 200 200" ]
++          [ dot 0 "darkred"
++          , dot 288 "gold"
++          , dot 72 "fuchsia"
++          , dot 144 "saddlebrown"
++          , dot 216 "deepskyblue"
++          ]
++        ```
++
++        Want some more cool color inspiration. Check out https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords
++
+=    - Make it more general
+=
+=      - Show List and list operations (map and indexedMap)
+```
\ No newline at end of file
new file mode 100644
index 0000000..1278ccb
--- /dev/null
+++ b/content/devlog/2018-10-29-elm-tree-workshop.md
@@ -0,0 +1,250 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Outline lines and gradients
+
+
+
+``` diff
+index 936c34e..2daa781 100644
+--- a/index.md
++++ b/index.md
+@@ -20,7 +20,7 @@ presentation:
+=
+=  - Atom
+=
+-
++Day 1
+=
+=- Setup the project
+=
+@@ -80,6 +80,8 @@ presentation:
+=  - Exercise: make 3 more dots (5 total)
+=
+=
++Day 2
++
+=- Place the dots in a circle
+=
+=  - Introduce a problem: We want to place the dots in a circle: show an example in the slides
+@@ -116,6 +118,11 @@ presentation:
+=
+=      Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
+=
++      ```elm
++      svg [ viewbox "-100 -100 200 200" ] []
++      ```
++
++Day 3
+=
+=- Let the computer do the math
+=
+@@ -349,8 +356,8 @@ presentation:
+=        ```elm
+=        dot angle color =
+=            circle
+-                [ cx (x 72)
+-                , cy (y 72)
++                [ cx (x angle)
++                , cy (y angle)
+=                , r "10"
+=                , fill color
+=                ]
+@@ -362,10 +369,10 @@ presentation:
+=        ```
+=        svg [ viewbox "-100 -100 200 200" ]
+=          [ dot 0 "darkred"
+-          , dot 288 "gold"
+=          , dot 72 "fuchsia"
+=          , dot 144 "saddlebrown"
+=          , dot 216 "deepskyblue"
++          , dot 288 "gold"
+=          ]
+=        ```
+=
+@@ -375,11 +382,176 @@ presentation:
+=
+=      - Show List and list operations (map and indexedMap)
+=
++        There are a number of functions that operate on lists, for example `List.length` and `List.map`.
++
++        Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket. 
++
++        Demonstrate: 
++
++        ```elm
++        > things = ["Programming", "Swimming", "Dancing", "Saddle brown"]    
++        ["Programming","Swimming","Dancing","Saddle brown"]
++            : List String
++
++
++        > List.length things
++        4 : Int
++
++        > List.map fun things
++        ["Programming is fun!","Swimming is fun!","Dancing is fun!","Saddle brown is fun!"]
++            : List String
++
++        > things
++        ["Programming","Swimming","Dancing","Saddle brown"]
++            : List String
++        ```
++
++        Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it. 
++
+=      - Make a `palette : List color`
+=
++        ```elm
++        palette = 
++          [ "darkred"
++          , "fuchsia"
++          , "saddlebrown"
++          , "deepskyblue"
++          , "gold"
++          ]
++        ```
++
+=      - Use `List.indexedMap dot palette` to generate the dots
+=
++        Another function that operates on lists is `List.indexedMap`. Let's see it at work:
+=
++        ```elm
++        dot index color =
++            circle
++                [ cx (x ((360 / (List.length palette)) * index))
++                , cy (y ((360 / (List.length palette)) * index))
++                , r "10"
++                , fill color
++                ]
++
++        List.indexedMap dot palette
++        ```
++
++        We can introduce a `let` block to make our code more readable and avoid repetition. 
++
++        ```elm
++        dot index color =
++            let
++                angle = 
++                    (360 / count) * index
++    
++                count =
++                    List.length palette
++            in
++            circle
++                [ cx (x angle)
++                , cy (y angle)
++                , r "10"
++                , fill color
++                ]
++        ```
++
++        Take a look at the [documentation for `List.indexedMap`](https://package.elm-lang.org/packages/elm/core/latest/List#indexedMap).
++
++
++Day 4
++
++Introduce SVG groups
++
++    - We'll need one group for the dots and one group for the lines
++
++    ```elm
++    svg [ viewBox "-100 -100 200 200" ]
++        [ Svg.g [] (List.indexedMap dot pallete)
++        , Svg.g [] [ Svg.line [ x1 "123", y1 "112", x2 "41", y2 "11", stroke "black" ] [] ]
++        ]
++    ```
++
++    Exercise: add a few lines with different colors
++
++    Now we'll learn how to color a line with a gradient (going from one color to another)
++
++    ```
++    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++        [ Svg.g [] (List.indexedMap dot pallete)
++        , Svg.defs []
++            [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0" ]
++                [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++                , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
++                ]
++            ]
++        , Svg.g []
++            [ Svg.line [ x1 "0", y1 "0", x2 "100", y2 "100", stroke "url(#MyGradient)" ] []
++            , Svg.line [ x1 "0", y1 "0", x2 "-100", y2 "-100", stroke "url(#MyGradient)" ] []
++            ]
++        ]
++    ```
++
++    We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative. 
++    
++    ```
++    main =
++        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++            [ Svg.g [] (List.indexedMap dot pallete)
++            , Svg.defs []
++                [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
++                    [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++                    , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
++                    ]
++                ]
++            , Svg.g []
++                [ Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(45),scale(100,1)", stroke "url(#MyGradient)" ] []
++                , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(180),scale(100,1)", stroke "url(#MyGradient)" ] []
++                , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(270),scale(100,1)", stroke "url(#MyGradient)" ] []
++                ]
++            ]
++    ```
++
++    We need to fix the viewbox!
++
++    Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here. 
++
++    If you're interested in what `gradientUnits "userSpaceOnUse"` does, come see me after the lesson.
++
++    ```elm
++    gradient : Int -> String -> Svg msg
++    gradient index color =
++        Svg.linearGradient [ Svg.Attributes.id ("Gradient-" ++ String.fromInt index), x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
++            [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++            , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor color ] []
++            ]
++    
++    
++    line : Int -> String -> Svg msg
++    line index color =
++        let
++            angle =
++                (360 / toFloat (List.length pallete)) * toFloat index
++    
++            transformation =
++                "rotate(" ++ String.fromFloat angle ++ "),scale(" ++ String.fromInt radius ++ ",1)"
++    
++            url =
++                "url(#Gradient-" ++ String.fromInt index ++ ")"
++        in
++        Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform transformation, stroke url ] []
++    
++    
++    main =
++        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++            [ Svg.g [] (List.indexedMap dot pallete)
++            , Svg.defs [] (List.indexedMap gradient pallete)
++            , Svg.g [] (List.indexedMap line pallete)
++            ]
++    ```
++
++    This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside. 
++
++    
+=
+=<!-- slide -->
+=
+```
\ No newline at end of file
new file mode 100644
index 0000000..f5e6752
--- /dev/null
+++ b/content/devlog/2018-10-30-elm-tree-workshop.md
@@ -0,0 +1,150 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Outline changing the background color when a dot is clicked
+
+
+
+``` diff
+index 2daa781..b4c52b5 100644
+--- a/index.md
++++ b/index.md
+@@ -551,7 +551,128 @@ Introduce SVG groups
+=
+=    This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside. 
+=
+-    
++
++Day 5
++
++    Our project is looking pretty good at this point. We've created some interesting graphical elements, and learned some interesting ways to place them programatically. However, it doesn't take user input like many of the programs we use. We'll be doing some pretty interesting things with user input later in this workshop, but for now we'll start with a deceptively simple step: setting the background color. 
++
++I say this step is deceptively simple, because we'll need to make some big changes to our program and introduce a number of new concepts. 
++
++Show complete code. 
++
++There's a lot going on here, and it's not obvious how it all works. 
++
++Our `main` function has changed. 
++
++```
++main : Program () Color Msg
++main =
++    Browser.sandbox
++        { init = init
++        , view = view
++        , update = update
++        }
++```
++
++Previously, our `main` function contained an SVG element. Now it contains a call to `Browser.sandbox`. A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element. It also needs a `state`. The state of an application is basically the information used to determine how the application should be rendered. Think about the Atom program we are all running at the moment. We know we are all running the same program, however we are not all looking at exactly the same thing. The text in our neighbours editor might be slightly different than the text in our own. If they opened another file, a letter to a friend for example, the text would be very different. The text input is part of the state of the Atom application. Maybe your neighbour has scrolled to another point in the screen. The scroll point is also part of the state of the Atom application. The cursor might be on another line in their viewport. The cursor position is also part of the state of the Atom application. Atom uses all of this state information to render exactly what you are seeing on your computer screen. Anything that can change will be represented in a state variable. If you input or delete text, move the cursor, scroll up or down, or change any of the application settings, you are updating the state of the Atom application.
++
++```
++type alias State =
++    Color
++
++
++type alias Color =
++    String
++```
++
++Here we create something called a type alias. We've already discussed types. A type alias is basically a way to give an alternative name for a type. We give the alias `Color` to string and the alias `State` to `Color`. All of these types are strings, and Elm will treat them all as strings. But it will make our code more readable to use these aliases. We are writing an application that allows the user to change the background color. That should explain why our `State` is a `Color`. 
++
++Our `Browser.sandbox` takes something with the name `init`. This is our initial state. Let's take a look at the value of `init`. 
++
++```
++init : State
++init =
++    "white"
++```
++
++We see that the initial state is `"white"`. We'll see how our application uses this value in a moment. 
++
++Looking back at our `sandbox`, we see it also takes a `view`. Let's take a look at our view function: 
++
++```
++view : State -> Html Msg
++view color =
++    svg
++        [ width "600"
++        , height "600"
++        , viewBox "-300 -300 600 600"
++        , Svg.Attributes.style ("background: " ++ color)
++        ]
++        [ Svg.g [] (List.indexedMap dot pallete)
++        , Svg.defs [] (List.indexedMap gradient pallete)
++        , Svg.g [] (List.indexedMap line pallete)
++        ]
++```
++
++You'll notice that this `view` function looks very similar to the `main` variable from earlier. Before, our application was only a view. Now the view is only one piece of our interactive application.
++
++One important way our `view` function differs from the `main` variable from ealier is that `view` is a function. We see it takes a variable of type `State` (a string), which we have named `color`. In an Elm sandbox project the `view` always takes a variable with the same type as the `init` variable. This is the current state of the application. `view` can use the state to render the application properly, just as Atom uses its state to render text for its user. 
++
++We see that we've added a line to the svg attributes, `Svg.Attributes.style ("background: " ++ color)`. Here we use the state of the application to set the background color of the svg element. 
++
++
++All we need now is some way to update the state (the background color) of the application.
++
++Taking a look at our `main` function, we see that it take a third and final function, `update`. Let's look at our update function: 
++
++```
++type Msg
++    = SetBackground Color
++
++
++update : Msg -> State -> State
++update msg state =
++    case msg of
++        SetBackground color ->
++            color
++
++```
++
++We see our `update` function takes two variables, of type `Msg` and `State`. If we think of update as a machine which takes an old state and spits out a new state, this state variable corresponds to the old state. 
++
++The `msg` variable is something new. Looking above, we see that we have defined a `Msg` variable type. This is similar to our `State` and `Color` union types, in that we have defined a new type that Elm understands. However, unlike the `type alias` constructor, the `type` constructor does not simly give a new name for an existing type. Instead, here we define a completely unique type. We see values with type Msg must have the form `SetBackground Color`. `SetBackground "white"`, `SetBackground "blue"`, `SetBackground "saddlebrown"` are all valid values of the type `Msg`. This is a bit complex, but will become familiar once we've seen some examples in use. 
++
++What is important to understand is that our `msg` variable is used by `update` to determine how it should update the state. We see that update first checks that `msg` has the form `SetBackground color`, and then returns the value of `color`. This will become our new state. Every time update is called, the sandbox will rerender our view with the new state. 
++
++So now we see how our application changes the background color of the svg element. However, we haven't seen when it will update the background color. Well, we want to change the background color by clicking a dot. So let's take a look at our `dot` function to see if we can find a hint there. 
++
++```
++dot : Int -> String -> Svg Msg
++dot index color =
++    let
++        angle =
++            (360 / toFloat (List.length pallete)) * toFloat index
++    in
++    circle
++        [ cx (x angle)
++        , cy (y angle)
++        , r "40"
++        , fill color
++        , Svg.Events.onClick (SetBackground color)
++        ]
++        []
++```
++
++We see there is a new line here: 
++
++```
++Svg.Events.onClick (SetBackground color)
++```
++
++`onClick` is an event. It basically tells our svg element to listen for someone to click on it. It will then handle the event by emitting the Msg `SetBackground color`. We know `color` is the color of the dot. We see then that when someone clicks on the dot, the Msg will be emitted, `update` will be called with this Msg and will update our state. Our sandbox will rerender the view with this new state. The user will see the svg element with a new background color. 
++
++Notice that our `view` function, as well as all the functions that call functions responsible for rendering svg elements, including `gradient`, `line`, and `dot` have return values with type `Svg Msg` or `Html Msg`. This is because svg element can emit Msgs which will be handled by `update`. 
++
+=
+=<!-- slide -->
+=
+```
\ No newline at end of file
new file mode 100644
index 0000000..03de9f2
--- /dev/null
+++ b/content/devlog/2018-11-01-elm-tree-workshop.md
@@ -0,0 +1,473 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 2
+
+
+# Outline tree growing and a basic implementation of a tree program
+
+
+
+``` diff
+index b4c52b5..b6f46a0 100644
+--- a/index.md
++++ b/index.md
+@@ -384,9 +384,9 @@ Day 3
+=
+=        There are a number of functions that operate on lists, for example `List.length` and `List.map`.
+=
+-        Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket. 
++        Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket.
+=
+-        Demonstrate: 
++        Demonstrate:
+=
+=        ```elm
+=        > things = ["Programming", "Swimming", "Dancing", "Saddle brown"]    
+@@ -406,12 +406,12 @@ Day 3
+=            : List String
+=        ```
+=
+-        Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it. 
++        Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it.
+=
+=      - Make a `palette : List color`
+=
+=        ```elm
+-        palette = 
++        palette =
+=          [ "darkred"
+=          , "fuchsia"
+=          , "saddlebrown"
+@@ -436,14 +436,14 @@ Day 3
+=        List.indexedMap dot palette
+=        ```
+=
+-        We can introduce a `let` block to make our code more readable and avoid repetition. 
++        We can introduce a `let` block to make our code more readable and avoid repetition.
+=
+=        ```elm
+=        dot index color =
+=            let
+-                angle = 
++                angle =
+=                    (360 / count) * index
+-    
++
+=                count =
+=                    List.length palette
+=            in
+@@ -491,8 +491,8 @@ Introduce SVG groups
+=        ]
+=    ```
+=
+-    We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative. 
+-    
++    We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative.
++
+=    ```
+=    main =
+=        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
+@@ -513,7 +513,7 @@ Introduce SVG groups
+=
+=    We need to fix the viewbox!
+=
+-    Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here. 
++    Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here.
+=
+=    If you're interested in what `gradientUnits "userSpaceOnUse"` does, come see me after the lesson.
+=
+@@ -524,23 +524,23 @@ Introduce SVG groups
+=            [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
+=            , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor color ] []
+=            ]
+-    
+-    
++
++
+=    line : Int -> String -> Svg msg
+=    line index color =
+=        let
+=            angle =
+=                (360 / toFloat (List.length pallete)) * toFloat index
+-    
++
+=            transformation =
+=                "rotate(" ++ String.fromFloat angle ++ "),scale(" ++ String.fromInt radius ++ ",1)"
+-    
++
+=            url =
+=                "url(#Gradient-" ++ String.fromInt index ++ ")"
+=        in
+=        Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform transformation, stroke url ] []
+-    
+-    
++
++
+=    main =
+=        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
+=            [ Svg.g [] (List.indexedMap dot pallete)
+@@ -549,23 +549,23 @@ Introduce SVG groups
+=            ]
+=    ```
+=
+-    This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside. 
++    This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside.
+=
+=
+=Day 5
+=
+-    Our project is looking pretty good at this point. We've created some interesting graphical elements, and learned some interesting ways to place them programatically. However, it doesn't take user input like many of the programs we use. We'll be doing some pretty interesting things with user input later in this workshop, but for now we'll start with a deceptively simple step: setting the background color. 
++Our project is looking pretty good at this point. We've created some interesting graphical elements, and learned some interesting ways to place them programatically. However, it doesn't take user input like many of the programs we use. We'll be doing some pretty interesting things with user input later in this workshop, but for now we'll start with a deceptively simple step: setting the background color.
+=
+-I say this step is deceptively simple, because we'll need to make some big changes to our program and introduce a number of new concepts. 
++I say this step is deceptively simple, because we'll need to make some big changes to our program and introduce a number of new concepts.
+=
+-Show complete code. 
++Show complete code.
+=
+-There's a lot going on here, and it's not obvious how it all works. 
++There's a lot going on here, and it's not obvious how it all works.
+=
+-Our `main` function has changed. 
++Our `main` value has changed.
+=
+=```
+-main : Program () Color Msg
++main : Program () State Msg
+=main =
+=    Browser.sandbox
+=        { init = init
+@@ -574,7 +574,8 @@ main =
+=        }
+=```
+=
+-Previously, our `main` function contained an SVG element. Now it contains a call to `Browser.sandbox`. A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element. It also needs a `state`. The state of an application is basically the information used to determine how the application should be rendered. Think about the Atom program we are all running at the moment. We know we are all running the same program, however we are not all looking at exactly the same thing. The text in our neighbours editor might be slightly different than the text in our own. If they opened another file, a letter to a friend for example, the text would be very different. The text input is part of the state of the Atom application. Maybe your neighbour has scrolled to another point in the screen. The scroll point is also part of the state of the Atom application. The cursor might be on another line in their viewport. The cursor position is also part of the state of the Atom application. Atom uses all of this state information to render exactly what you are seeing on your computer screen. Anything that can change will be represented in a state variable. If you input or delete text, move the cursor, scroll up or down, or change any of the application settings, you are updating the state of the Atom application.
++Previously, the value of `main` was an SVG element. Now it contains a call to `Browser.sandbox`. A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element. It also needs a `state`. The state of an application is basically the information used to determine how the application should be rendered. Think about the Atom program we are all running at the moment. We know we are all running the same program, however we are not all looking at exactly the same thing. The text in our neighbors editor might be slightly different than the text in our own. If they opened another file, a letter to a friend for example, the text would be very different. The text input is part of the state of the Atom application. Maybe your neighbor has scrolled to another point in the screen. The scroll point is also part of the state of the Atom application. The cursor might be on another line in their viewport. The cursor position is also part of the state of the Atom application. Atom uses all of this state information to render exactly what you are seeing on your computer screen. Anything that can change will be represented in a state variable. If you input or delete text, move the cursor, scroll up or down, or change any of the application settings, you are updating
++the state of the Atom application.
+=
+=```
+=type alias State =
+@@ -585,9 +586,11 @@ type alias Color =
+=    String
+=```
+=
+-Here we create something called a type alias. We've already discussed types. A type alias is basically a way to give an alternative name for a type. We give the alias `Color` to string and the alias `State` to `Color`. All of these types are strings, and Elm will treat them all as strings. But it will make our code more readable to use these aliases. We are writing an application that allows the user to change the background color. That should explain why our `State` is a `Color`. 
++Here we create something called a type alias. We've already discussed types. A type alias is basically a way to give an alternative name for a type. We give the alias `Color` to `String` and the alias `State` to `Color`. So in the end all three names point to the same type!
++
++Why are we doing this? All of these types are strings, and Elm will treat them all as strings. But it will make our code more readable to use these aliases. We are writing an application that allows the user to change the background color. That should explain why our `State` is a `Color`.
+=
+-Our `Browser.sandbox` takes something with the name `init`. This is our initial state. Let's take a look at the value of `init`. 
++Our `Browser.sandbox` takes something with the name `init`. This is our initial state. Let's take a look at the value of `init`.
+=
+=```
+=init : State
+@@ -595,9 +598,9 @@ init =
+=    "white"
+=```
+=
+-We see that the initial state is `"white"`. We'll see how our application uses this value in a moment. 
++We see that the initial state is `"white"`. We'll see how our application uses this value in a moment.
+=
+-Looking back at our `sandbox`, we see it also takes a `view`. Let's take a look at our view function: 
++Looking back at our `sandbox`, we see it also takes a `view`. Let's take a look at our view function:
+=
+=```
+=view : State -> Html Msg
+@@ -616,14 +619,13 @@ view color =
+=
+=You'll notice that this `view` function looks very similar to the `main` variable from earlier. Before, our application was only a view. Now the view is only one piece of our interactive application.
+=
+-One important way our `view` function differs from the `main` variable from ealier is that `view` is a function. We see it takes a variable of type `State` (a string), which we have named `color`. In an Elm sandbox project the `view` always takes a variable with the same type as the `init` variable. This is the current state of the application. `view` can use the state to render the application properly, just as Atom uses its state to render text for its user. 
+-
+-We see that we've added a line to the svg attributes, `Svg.Attributes.style ("background: " ++ color)`. Here we use the state of the application to set the background color of the svg element. 
++One important way our `view` function differs from the `main` variable from ealier is that `view` is a function. We see it takes a variable of type `State` (a string), which we have named `color`. In an Elm sandbox project the `view` always takes a variable with the same type as the `init` variable. This is the current state of the application. `view` can use the state to render the application properly, just as Atom uses its state to render text for its user.
+=
++We see that we've added a line to the svg attributes, `Svg.Attributes.style ("background: " ++ color)`. Here we use the state of the application to set the background color of the svg element.
+=
+=All we need now is some way to update the state (the background color) of the application.
+=
+-Taking a look at our `main` function, we see that it take a third and final function, `update`. Let's look at our update function: 
++Taking a look at our `main` function, we see that it take a third and final function, `update`. Let's look at our update function:
+=
+=```
+=type Msg
+@@ -638,13 +640,58 @@ update msg state =
+=
+=```
+=
+-We see our `update` function takes two variables, of type `Msg` and `State`. If we think of update as a machine which takes an old state and spits out a new state, this state variable corresponds to the old state. 
++We see our `update` function takes two variables, of type `Msg` and `State`. If we think of update as a machine which takes an old state and spits out a new state, this state variable corresponds to the old state.
++
++The `msg` variable is something new. Looking above, we see that we have defined a `Msg` variable type. This is similar to our `State` and `Color` union types, in that we have defined a new type that Elm understands. However, unlike the `type alias` constructor, the `type` constructor does not simply give a new name for an existing type. Instead, here we define a completely unique type. We see values with type Msg must have the form `SetBackground Color`. `SetBackground "white"`, `SetBackground "blue"`, `SetBackground "saddlebrown"` are all valid values of the type `Msg`.
++
++What is important to understand is that our `msg` variable is used by `update` to determine how it should update the state. We see that update first checks that `msg` has the form `SetBackground color`, and then returns the value of `color`. This will become our new state. Every time update is called, the sandbox will re-render our view with the new state.
++
++So now we see how our application changes the background color of the svg element. However, we haven't seen when it will update the background color. Well, we want to change the background color by clicking a dot. So let's take a look at our `dot` function to see if we can find a hint there.
++
++- `Model`:
++
++  the structure of the state of the program
++
++  TODO: Consistently use `Model` and `model` instead of less standard `State` to make it easier to our participants.
++
++- `Msg`
+=
+-The `msg` variable is something new. Looking above, we see that we have defined a `Msg` variable type. This is similar to our `State` and `Color` union types, in that we have defined a new type that Elm understands. However, unlike the `type alias` constructor, the `type` constructor does not simly give a new name for an existing type. Instead, here we define a completely unique type. We see values with type Msg must have the form `SetBackground Color`. `SetBackground "white"`, `SetBackground "blue"`, `SetBackground "saddlebrown"` are all valid values of the type `Msg`. This is a bit complex, but will become familiar once we've seen some examples in use. 
++  What events can there be in the program. Currently have only one possible event - setting the background to a given color.
+=
+-What is important to understand is that our `msg` variable is used by `update` to determine how it should update the state. We see that update first checks that `msg` has the form `SetBackground color`, and then returns the value of `color`. This will become our new state. Every time update is called, the sandbox will rerender our view with the new state. 
++- `update`
++
++  the instructions of how to change the state when a particular message comes in.
++
++- `view`
++
++  the instruction of what to display on the screen. It also contains instructions of what messages to send when certain events (like clicks on elements) happen. Every time the state changes the instructions will be applied.
++
++- `init`
++
++  What is the initial state right after the program is started.
++
++- `main`
++
++  glues it all together.
++
++Next steps:
++
++- How do we model the tree
++
++  - introduces the model for later interactive program
++
++  - naturally introduces union types
++
++
++- Make a tree grow
++
++  - Make a function that given the age of the tree and rules returns a tree
++
++  - Use time subscription to make the tree grow
++
++<!-- slide -->
+=
+-So now we see how our application changes the background color of the svg element. However, we haven't seen when it will update the background color. Well, we want to change the background color by clicking a dot. So let's take a look at our `dot` function to see if we can find a hint there. 
++``
+=
+=```
+=dot : Int -> String -> Svg Msg
+@@ -663,15 +710,15 @@ dot index color =
+=        []
+=```
+=
+-We see there is a new line here: 
++We see there is a new line here:
+=
+=```
+=Svg.Events.onClick (SetBackground color)
+=```
+=
+-`onClick` is an event. It basically tells our svg element to listen for someone to click on it. It will then handle the event by emitting the Msg `SetBackground color`. We know `color` is the color of the dot. We see then that when someone clicks on the dot, the Msg will be emitted, `update` will be called with this Msg and will update our state. Our sandbox will rerender the view with this new state. The user will see the svg element with a new background color. 
++`onClick` is an event. It basically tells our svg element to listen for someone to click on it. It will then handle the event by emitting the Msg `SetBackground color`. We know `color` is the color of the dot. We see then that when someone clicks on the dot, the Msg will be emitted, `update` will be called with this Msg and will update our state. Our sandbox will rerender the view with this new state. The user will see the svg element with a new background color.
+=
+-Notice that our `view` function, as well as all the functions that call functions responsible for rendering svg elements, including `gradient`, `line`, and `dot` have return values with type `Svg Msg` or `Html Msg`. This is because svg element can emit Msgs which will be handled by `update`. 
++Notice that our `view` function, as well as all the functions that call functions responsible for rendering svg elements, including `gradient`, `line`, and `dot` have return values with type `Svg Msg` or `Html Msg`. This is because svg element can emit Msgs which will be handled by `update`.
+=
+=
+=<!-- slide -->
+```
+
+# Basic implementation of a tree growing program
+
+Accidentally not included in previous commit.
+
+``` diff
+new file mode 100644
+index 0000000..1939a19
+--- /dev/null
++++ b/src/Tree.elm
+@@ -0,0 +1,179 @@
++module Tree exposing (main)
++
++import Browser
++import Browser.Events
++import Dict exposing (Dict)
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    { time : Float
++    , rules : Rules
++    }
++
++
++type Msg
++    = Progress Float
++
++
++type Tree
++    = Tip Segment
++    | Node Segment (List Tree)
++
++
++type alias Color =
++    String
++
++
++type alias Segment =
++    { color : Color
++    , angle : Float
++    , length : Float
++    }
++
++
++type alias Rules =
++    Dict Color (List Segment)
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( { time = 1
++      , rules =
++            Dict.fromList
++                [ ( "red"
++                  , [ Segment "yellow" (degrees 15) 10
++                    , Segment "yellow" (degrees 175) 10
++                    ]
++                  )
++                , ( "yellow", [ Segment "blue" (degrees 15) 10 ] )
++                , ( "blue", [ Segment "red" (degrees 15) 10 ] )
++                ]
++
++      -- [ ( "red"
++      --   , [ Segment "blue" (degrees 90) 10
++      --     , Segment "yellow" (degrees -45) 4
++      --     ]
++      --   )
++      -- , ( "yellow", [ Segment "red" (degrees 90) 6 ] )
++      -- ]
++      }
++    , Cmd.none
++    )
++
++
++view : Model -> Html Msg
++view model =
++    Html.div []
++        [ Html.h1 [] [ Html.text <| String.fromFloat model.time ]
++        , Segment "red" 0 0
++            |> Tip
++            |> grow model.rules (model.time / 1000)
++            |> svgTree
++            |> List.singleton
++            |> svg [ viewBox "-1000 -1000 2000 2000", height "800px", width "800px" ]
++        ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update msg model =
++    case msg of
++        Progress delta ->
++            ( { model | time = model.time + delta }
++            , Cmd.none
++            )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Browser.Events.onAnimationFrameDelta Progress
++
++
++
++-- Sub.none
++
++
++htmlTree : Tree -> Html Msg
++htmlTree tree =
++    case tree of
++        Tip segment ->
++            Html.li [] [ Html.text <| "Tip: " ++ Debug.toString segment ]
++
++        Node segment trees ->
++            Html.li []
++                [ Html.text <| "Tip: " ++ Debug.toString segment
++                , Html.ul [] <| List.map htmlTree trees
++                ]
++
++
++svgTree : Tree -> Html Msg
++svgTree tree =
++    case tree of
++        Tip segment ->
++            circle [ cx "0", cy "0", fill segment.color, r "10" ] []
++
++        Node segment trees ->
++            let
++                x =
++                    segment.length * cos segment.angle
++
++                y =
++                    segment.length * sin segment.angle
++
++                transformation =
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++            in
++            g [ transform transformation ] <|
++                circle [ cx "0", cy "0", fill segment.color, r "10" ] []
++                    :: List.map svgTree trees
++
++
++grow : Rules -> Float -> Tree -> Tree
++grow rules age branch =
++    if age > 0 then
++        case branch of
++            Tip segment ->
++                let
++                    tips =
++                        rules
++                            |> Dict.get segment.color
++                            |> Maybe.withDefault []
++                            |> List.map
++                                (\rule ->
++                                    { rule
++                                        | angle = rule.angle + segment.angle
++                                        , length = rule.length * age
++                                    }
++                                )
++                            |> List.map Tip
++                in
++                tips
++                    |> Node segment
++                    |> grow rules (age - 1)
++
++            Node segment trees ->
++                trees
++                    |> List.map (grow rules age)
++                    |> Node segment
++
++    else
++        branch
+```
\ No newline at end of file
new file mode 100644
index 0000000..5ab7a8f
--- /dev/null
+++ b/content/devlog/2018-11-12-elm-tree-workshop.md
@@ -0,0 +1,439 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 2
+
+
+# Implement the viewbox example
+
+
+
+``` diff
+new file mode 100644
+index 0000000..5b77dc1
+--- /dev/null
++++ b/src/ViewBox.elm
+@@ -0,0 +1,262 @@
++module ViewBox exposing
++    ( Flags
++    , Model
++    , Msg
++    , init
++    , main
++    , subscriptions
++    , ui
++    , update
++    , view
++    )
++
++import Browser
++import CartesianPlane exposing (graph)
++import Element
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    { left : Float
++    , top : Float
++    , width : Float
++    , height : Float
++    }
++
++
++type Msg
++    = Move Float Float
++    | Resize Float Float
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( { left = 0
++      , top = 0
++      , width = 400
++      , height = 400
++      }
++    , Cmd.none
++    )
++
++
++view : Model -> Html.Html Msg
++view model =
++    let
++        wrapper element =
++            Element.el
++                [ Element.width (Element.maximum 1200 Element.fill)
++                , Element.height Element.fill
++                , Element.centerX
++                ]
++                element
++    in
++    ui model
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update msg model =
++    case msg of
++        Move left top ->
++            ( { model | left = left, top = top }, Cmd.none )
++
++        Resize width height ->
++            ( { model | width = width, height = height }, Cmd.none )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
++
++
++ui model =
++    Element.column
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        , Element.spacing 30
++        , Element.padding 30
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            , Element.centerX
++            , Element.spacing 30
++            , Element.padding 30
++            ]
++            [ Element.el
++                [ Element.height Element.fill
++                , Element.width Element.fill
++                ]
++                (Element.html <|
++                    svg [ viewBox "0 0 1000 1000" ]
++                        [ g [] world
++                        , rect
++                            [ x (String.fromFloat model.left)
++                            , y (String.fromFloat model.top)
++                            , width (String.fromFloat model.width)
++                            , height (String.fromFloat model.height)
++                            , opacity "0.8"
++                            , stroke "white"
++                            , Svg.Attributes.style "fill: hsl(120, 100%, 85.1%)"
++                            ]
++                            []
++                        ]
++                )
++            , Element.el
++                [ Element.height Element.fill
++                , Element.width Element.fill
++                , Background.color <| Element.rgb 0.7 1 0.7
++                ]
++                (Element.html <|
++                    svg
++                        [ [ model.left, model.top, model.width, model.height ]
++                            |> List.map String.fromFloat
++                            |> String.join " "
++                            |> viewBox
++                        , preserveAspectRatio "none"
++                        , Svg.Attributes.style "width: 100; height: 100%"
++                        ]
++                        [ g [] world
++                        ]
++                )
++            ]
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \left -> Move left model.top
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("left: " ++ String.fromFloat model.left)
++            , min = 0
++            , max = 1000 - model.width
++            , value = model.left
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \top -> Move model.left top
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("top: " ++ String.fromFloat model.top)
++            , min = 0
++            , max = 1000 - model.width
++            , value = model.top
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \width -> Resize width model.height
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("width: " ++ String.fromFloat model.width)
++            , min = 0
++            , max = 1000 - model.left
++            , value = model.width
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \height -> Resize model.width height
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("height: " ++ String.fromFloat model.height)
++            , min = 0
++            , max = 1000 - model.top
++            , value = model.height
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        ]
++
++
++world : List (Svg Msg)
++world =
++    [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
++    ]
++
++
++
++--   <|
++--     Element.html <|
++--         graph
++--             [ circle
++--                 [ cx <| String.fromFloat model.x
++--                 , cy <| String.fromFloat model.y
++--                 , r "0.01"
++--                 , fill "magenta"
++--                 ]
++--                 []
++--             , text_
++--                 [ x <| String.fromFloat (model.x + 0.03)
++--                 , y <| String.fromFloat model.y
++--                 , fontSize "0.05"
++--                 , dominantBaseline "central"
++--                 ]
++--                 [ text <| Debug.toString ( model.x, model.y ) ]
++--             ]
++-- ,         ]
+```
+
+# Implement three simple examples: Simplest (just SVG), FillTheScreen and CenterdDot
+
+Let the cartesian plane take the edge lenght argument.
+
+``` diff
+index 0d82b4a..83986b5 100644
+--- a/src/CartesianPlane.elm
++++ b/src/CartesianPlane.elm
+@@ -5,33 +5,54 @@ import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+=
+-graph : List (Svg msg) -> Html msg
+-graph shapes =
++graph : Float -> List (Svg msg) -> Html msg
++graph size shapes =
+=    let
++        top =
++            0 - size / 2
++
++        left =
++            0 - size / 2
++
++        bottom =
++            size / 2
++
++        right =
++            size / 2
++
++        hairline =
++            size / 1000
++
++        fontsize =
++            size / 200
++
+=        background =
+=            g []
+=                [ line
+-                    [ x1 "-1"
++                    [ x1 (String.fromFloat left)
+=                    , y1 "0"
+-                    , x2 "1"
++                    , x2 (String.fromFloat right)
+=                    , y2 "0"
+=                    , stroke "black"
+-                    , strokeWidth "0.001"
++                    , strokeWidth (String.fromFloat hairline)
+=                    ]
+=                    []
+=                , line
+=                    [ x1 "0"
+=                    , x2 "0"
+-                    , y1 "-1"
+-                    , y2 "1"
++                    , x2 (String.fromFloat top)
++                    , y2 (String.fromFloat bottom)
+=                    , stroke "black"
+-                    , strokeWidth "0.001"
++                    , strokeWidth (String.fromFloat hairline)
+=                    ]
+=                    []
+=                ]
+=    in
+=    svg
+-        [ viewBox "-1 -1 2 2"
++        [ [ left, top, size, size ]
++            |> List.map String.fromFloat
++            |> String.join " "
++            |> viewBox
+=        , preserveAspectRatio "xMidYMid meet"
+=        , Svg.Attributes.width "100%"
+=        , Svg.Attributes.height "100%"
+```
+
+``` diff
+new file mode 100644
+index 0000000..0382141
+--- /dev/null
++++ b/src/CenteredDot.elm
+@@ -0,0 +1,18 @@
++module Simplest exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-1000 -1000 2000 2000" ]
++                [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
++            )
++        )
+```
+
+``` diff
+new file mode 100644
+index 0000000..753928b
+--- /dev/null
++++ b/src/FillTheScreen.elm
+@@ -0,0 +1,24 @@
++module FillTheScreen exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
++                [ Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "30"
++                    , Svg.Attributes.cy "30"
++                    ]
++                    []
++                ]
++            )
++        )
+```
+
+``` diff
+new file mode 100644
+index 0000000..8710e3c
+--- /dev/null
++++ b/src/Simplest.elm
+@@ -0,0 +1,15 @@
++module Simplest exposing (main)
++
++import Svg
++import Svg.Attributes
++
++
++main =
++    Svg.svg [ Svg.Attributes.style "background: pink" ]
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.cx "30"
++            , Svg.Attributes.cy "30"
++            ]
++            []
++        ]
+```
\ No newline at end of file
new file mode 100644
index 0000000..e8bb256
--- /dev/null
+++ b/content/devlog/2018-11-13-elm-tree-workshop.md
@@ -0,0 +1,351 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Render svgTree in one pass directly from rules
+
+In tree sample remove Tree type and merge grow and svgTree functions. 
+Use SVG transformations for rotation, scaling and translation, 
+offloading the difficult geometry transformation to the browser.
+
+This way we don't have any union types except for Msg.
+
+The downside is that the branches do not grow linearly. The size of the 
+branch is:
+
+    treeAge! / (treeAge ^ (ceil treeAge))
+
+``` diff
+index 1939a19..5e5f50b 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -4,6 +4,7 @@ import Browser
+=import Browser.Events
+=import Dict exposing (Dict)
+=import Html exposing (Html)
++import Json.Decode exposing (Decoder)
+=import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+@@ -29,11 +30,7 @@ type alias Model =
+=
+=type Msg
+=    = Progress Float
+-
+-
+-type Tree
+-    = Tip Segment
+-    | Node Segment (List Tree)
++    | Regress Float
+=
+=
+=type alias Color =
+@@ -53,25 +50,21 @@ type alias Rules =
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( { time = 1
++    ( { time = 0
+=      , rules =
+=            Dict.fromList
+=                [ ( "red"
+-                  , [ Segment "yellow" (degrees 15) 10
+-                    , Segment "yellow" (degrees 175) 10
++                  , [ Segment "green" 15 0.2
++                    , Segment "green" 175 0.1
++                    ]
++                  )
++                , ( "green", [ Segment "blue" 15 0.3 ] )
++                , ( "blue"
++                  , [ Segment "red" 15 0.7
++                    , Segment "purple" 45 0.1
+=                    ]
+=                  )
+-                , ( "yellow", [ Segment "blue" (degrees 15) 10 ] )
+-                , ( "blue", [ Segment "red" (degrees 15) 10 ] )
+=                ]
+-
+-      -- [ ( "red"
+-      --   , [ Segment "blue" (degrees 90) 10
+-      --     , Segment "yellow" (degrees -45) 4
+-      --     ]
+-      --   )
+-      -- , ( "yellow", [ Segment "red" (degrees 90) 6 ] )
+-      -- ]
+=      }
+=    , Cmd.none
+=    )
+@@ -81,12 +74,15 @@ view : Model -> Html Msg
+=view model =
+=    Html.div []
+=        [ Html.h1 [] [ Html.text <| String.fromFloat model.time ]
+-        , Segment "red" 0 0
+-            |> Tip
+-            |> grow model.rules (model.time / 1000)
+-            |> svgTree
++        , "red"
++            |> svgTree model.rules (model.time / 5000) (model.time / 5000)
++            |> g [ transform ("scale(" ++ String.fromFloat (model.time / 1000) ++ ")") ]
+=            |> List.singleton
+-            |> svg [ viewBox "-1000 -1000 2000 2000", height "800px", width "800px" ]
++            |> svg
++                [ viewBox "-1000 -1000 2000 2000"
++                , height "800px"
++                , width "800px"
++                ]
+=        ]
+=
+=
+@@ -98,82 +94,68 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        Regress delta ->
++            ( { model | time = model.time - delta }
++            , Cmd.none
++            )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+-    Browser.Events.onAnimationFrameDelta Progress
+-
+-
+-
+--- Sub.none
+-
+-
+-htmlTree : Tree -> Html Msg
+-htmlTree tree =
+-    case tree of
+-        Tip segment ->
+-            Html.li [] [ Html.text <| "Tip: " ++ Debug.toString segment ]
+-
+-        Node segment trees ->
+-            Html.li []
+-                [ Html.text <| "Tip: " ++ Debug.toString segment
+-                , Html.ul [] <| List.map htmlTree trees
+-                ]
++    let
++        handleKeyPress : Decoder Msg
++        handleKeyPress =
++            Json.Decode.field "key" Json.Decode.string
++                |> Json.Decode.andThen
++                    (\key ->
++                        case Debug.log "Key" key of
++                            "," ->
++                                Json.Decode.succeed (Regress 10)
++
++                            "." ->
++                                Json.Decode.succeed (Progress 10)
++
++                            "<" ->
++                                Json.Decode.succeed (Regress 1000)
++
++                            ">" ->
++                                Json.Decode.succeed (Progress 1000)
++
++                            _ ->
++                                Json.Decode.fail "Unknown key press"
++                    )
++    in
++    Sub.batch
++        [ Browser.Events.onKeyPress handleKeyPress
++        , Browser.Events.onAnimationFrameDelta Progress
++        ]
+=
+=
+-svgTree : Tree -> Html Msg
+-svgTree tree =
+-    case tree of
+-        Tip segment ->
+-            circle [ cx "0", cy "0", fill segment.color, r "10" ] []
+-
+-        Node segment trees ->
+-            let
+-                x =
+-                    segment.length * cos segment.angle
+-
+-                y =
+-                    segment.length * sin segment.angle
+-
+-                transformation =
+-                    "translate("
+-                        ++ String.fromFloat x
+-                        ++ ", "
+-                        ++ String.fromFloat y
+-                        ++ ")"
+-            in
+-            g [ transform transformation ] <|
+-                circle [ cx "0", cy "0", fill segment.color, r "10" ] []
+-                    :: List.map svgTree trees
+-
+-
+-grow : Rules -> Float -> Tree -> Tree
+-grow rules age branch =
+-    if age > 0 then
+-        case branch of
+-            Tip segment ->
+-                let
+-                    tips =
+-                        rules
+-                            |> Dict.get segment.color
+-                            |> Maybe.withDefault []
+-                            |> List.map
+-                                (\rule ->
+-                                    { rule
+-                                        | angle = rule.angle + segment.angle
+-                                        , length = rule.length * age
+-                                    }
+-                                )
+-                            |> List.map Tip
+-                in
+-                tips
+-                    |> Node segment
+-                    |> grow rules (age - 1)
+-
+-            Node segment trees ->
+-                trees
+-                    |> List.map (grow rules age)
+-                    |> Node segment
++svgTree : Rules -> Float -> Float -> Color -> List (Svg Msg)
++svgTree rules treeAge branchAge color =
++    if branchAge > 0 then
++        rules
++            |> Dict.get color
++            |> Maybe.withDefault []
++            |> List.map
++                (\segment ->
++                    let
++                        transformation =
++                            "rotate("
++                                ++ String.fromFloat segment.angle
++                                ++ ")"
++                                ++ ", scale("
++                                ++ String.fromFloat ((branchAge - 1) / treeAge)
++                                ++ ")"
++                                ++ ", translate("
++                                ++ String.fromFloat (segment.length * branchAge)
++                                ++ ", 0)"
++                    in
++                    g
++                        [ transform transformation ]
++                        (svgTree rules treeAge (branchAge - 1) segment.color)
++                )
++            |> (::) (circle [ cx "0", cy "0", fill color, r "1" ] [])
+=
+=    else
+-        branch
++        []
+```
+
+# Add more elements to the ViewBox sample
+
+Render the ViewBox indicator () in both pictures.
+
+``` diff
+index 5b77dc1..551db09 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -118,9 +118,9 @@ ui model =
+=                            , y (String.fromFloat model.top)
+=                            , width (String.fromFloat model.width)
+=                            , height (String.fromFloat model.height)
+-                            , opacity "0.8"
++                            , opacity "0.3"
+=                            , stroke "white"
+-                            , Svg.Attributes.style "fill: hsl(120, 100%, 85.1%)"
++                            , fill "pink"
+=                            ]
+=                            []
+=                        ]
+@@ -128,7 +128,6 @@ ui model =
+=            , Element.el
+=                [ Element.height Element.fill
+=                , Element.width Element.fill
+-                , Background.color <| Element.rgb 0.7 1 0.7
+=                ]
+=                (Element.html <|
+=                    svg
+@@ -140,6 +139,16 @@ ui model =
+=                        , Svg.Attributes.style "width: 100; height: 100%"
+=                        ]
+=                        [ g [] world
++                        , rect
++                            [ x (String.fromFloat model.left)
++                            , y (String.fromFloat model.top)
++                            , width (String.fromFloat model.width)
++                            , height (String.fromFloat model.height)
++                            , opacity "0.3"
++                            , stroke "white"
++                            , fill "pink"
++                            ]
++                            []
+=                        ]
+=                )
+=            ]
+@@ -237,26 +246,6 @@ ui model =
+=world : List (Svg Msg)
+=world =
+=    [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
++    , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
++    , circle [ cx "400", cy "600", r "20", fill "red" ] []
+=    ]
+-
+-
+-
+---   <|
+---     Element.html <|
+---         graph
+---             [ circle
+---                 [ cx <| String.fromFloat model.x
+---                 , cy <| String.fromFloat model.y
+---                 , r "0.01"
+---                 , fill "magenta"
+---                 ]
+---                 []
+---             , text_
+---                 [ x <| String.fromFloat (model.x + 0.03)
+---                 , y <| String.fromFloat model.y
+---                 , fontSize "0.05"
+---                 , dominantBaseline "central"
+---                 ]
+---                 [ text <| Debug.toString ( model.x, model.y ) ]
+---             ]
+--- ,         ]
+```
+
+# Set min value of the width and height in the ViewBox examle to 1
+
+
+
+``` diff
+index 551db09..de064d5 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -212,7 +212,7 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("width: " ++ String.fromFloat model.width)
+-            , min = 0
++            , min = 1
+=            , max = 1000 - model.left
+=            , value = model.width
+=            , thumb = Input.defaultThumb
+@@ -234,7 +234,7 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("height: " ++ String.fromFloat model.height)
+-            , min = 0
++            , min = 1
+=            , max = 1000 - model.top
+=            , value = model.height
+=            , thumb = Input.defaultThumb
+```
\ No newline at end of file
new file mode 100644
index 0000000..80115c8
--- /dev/null
+++ b/content/devlog/2018-11-15-elm-tree-workshop.md
@@ -0,0 +1,1954 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 7
+
+
+# Fix cartesian plane's Y axis
+
+
+
+``` diff
+index 83986b5..6e4ad58 100644
+--- a/src/CartesianPlane.elm
++++ b/src/CartesianPlane.elm
+@@ -40,7 +40,7 @@ graph size shapes =
+=                , line
+=                    [ x1 "0"
+=                    , x2 "0"
+-                    , x2 (String.fromFloat top)
++                    , y1 (String.fromFloat top)
+=                    , y2 (String.fromFloat bottom)
+=                    , stroke "black"
+=                    , strokeWidth (String.fromFloat hairline)
+```
+
+# Create simple SVG transformations example
+
+Not interactive yet
+
+``` diff
+new file mode 100644
+index 0000000..80a3718
+--- /dev/null
++++ b/src/Transformations.elm
+@@ -0,0 +1,137 @@
++module Transformations exposing (main)
++
++import Browser
++import Browser.Events
++import CartesianPlane
++import Dict exposing (Dict)
++import Element
++import Html exposing (Html)
++import Json.Decode exposing (Decoder)
++import List.Extra as List
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    List Transformation
++
++
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++type Msg
++    = Progress Float
++    | Regress Float
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( [ Identity
++      , Scale 2 2
++      , Translate 20 0
++      ]
++    , Cmd.none
++    )
++
++
++view : Model -> Html Msg
++view model =
++    let
++        wrapper element =
++            Element.el
++                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
++                (Element.html element)
++
++        shape =
++            g [ transform (apply model) ]
++                [ line
++                    [ x1 "0"
++                    , x2 "100"
++                    , y1 "0"
++                    , y2 "0"
++                    , stroke "red"
++                    , strokeWidth "1"
++                    ]
++                    []
++                , circle [ cx "0", cy "0", r "2", fill "red" ] []
++
++                -- , rect [ x "", y "-2", width "1", height "4" ] []
++                ]
++    in
++    shape
++        |> List.singleton
++        |> CartesianPlane.graph 600
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update msg model =
++    case msg of
++        Progress delta ->
++            ( model
++            , Cmd.none
++            )
++
++        Regress delta ->
++            ( model
++            , Cmd.none
++            )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
++
++
++apply : List Transformation -> String
++apply transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
+```
+
+# Expand conspect to slides
+
+
+
+``` diff
+index b6f46a0..c58cd80 100644
+--- a/index.md
++++ b/index.md
+@@ -6,7 +6,9 @@ presentation:
+=
+=<!-- slide -->
+=
+-## Conspect
++## Before the course begins
++
++<!-- slide -->
+=
+=- Setup the development environment
+=
+@@ -20,7 +22,13 @@ presentation:
+=
+=  - Atom
+=
+-Day 1
++<!-- slide -->
++
++## Day 1
++
++Let's make a dot!
++
++<!-- slide -->
+=
+=- Setup the project
+=
+@@ -38,12 +46,17 @@ Day 1
+=
+=  - Web browser
+=
++<!-- slide -->
++
++### Introduce a problem
+=
+-- Let's make a dot!
++We want to have a dot on the screen
+=
+-  - Introduce a problem: We want to have a dot on the screen
++<!-- slide -->
++
++Show complete code
+=
+-  - Show complete code
++<!-- slide -->
+=
+=  - Explain:
+=
+@@ -53,6 +66,7 @@ Day 1
+=
+=  - Introduce SVG
+=
++<!-- slide -->
+=
+=- Give  a color to the dot
+=
+@@ -60,6 +74,7 @@ Day 1
+=
+=  - Everyone can pick a color
+=
++<!-- slide -->
+=
+=- Multiple dots
+=
+@@ -67,7 +82,9 @@ Day 1
+=
+=  - Explain a list, and show there is already a list there (with one item)
+=
+-    Do you see any other lists?
++<!-- slide -->
++
++Do you see any other lists?
+=
+=  - Element takes a list of children
+=
+@@ -79,487 +96,664 @@ Day 1
+=
+=  - Exercise: make 3 more dots (5 total)
+=
++<!-- slide -->
+=
+-Day 2
++## Day 2
+=
+-- Place the dots in a circle
++Place the dots in a circle
+=
+-  - Introduce a problem: We want to place the dots in a circle: show an example in the slides
++<!-- slide -->
+=
+-  - How we will get there: we will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
+=
+-  - How can we figure out the correct cartesian coordinates?
++Introduce a problem: We want to place the dots in a circle: show an example in the slides
++
++How we will get there: we will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
++
++How can we figure out the correct cartesian coordinates?
++
++<!-- slide -->
++
++Story about the flowerpot and the table: two ways to measure relative position, one is distance from a x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
+=
+-    - Story about the flowerpot and the table: two ways to measure relative position, one is distance from a x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
++<!-- slide -->
+=
+-    - What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a "360"). We see that circles and angles are closely related!
++What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a "360"). We see that circles and angles are closely related!
+=
+-    - Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
++<!-- slide -->
+=
+-    - We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
++Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
+=
+-    - Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
++<!-- slide -->
+=
+-    - So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
++We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
+=
+-      Using a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
++<!-- slide -->
+=
+-      Let's use a chart to figure out the sin and cos of our angles
++Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
+=
+-      *It's important to get a chart, otherwise we have to use calculators which work with radians*
++<!-- slide -->
+=
+-    - Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
++So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
+=
+-    - Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
+=
+-      We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
++<!-- slide -->
+=
+-      Sample viewbox program to demonstrate
++Using a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
+=
+-      Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
++<!-- slide -->
+=
+-      ```elm
+-      svg [ viewbox "-100 -100 200 200" ] []
+-      ```
++Let's use a chart to figure out the sin and cos of our angles
+=
+-Day 3
++*It's important to get a chart, otherwise we have to use calculators which work with radians*
+=
+-- Let the computer do the math
++<!-- slide -->
+=
+-  - Introduce the problem: Can we avoid all these repetitive and manual steps?
++Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
+=
+-  - What is a program made of? (in the REPL):
++<!-- slide -->
+=
+-    - Values and names
++Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
+=
+-      ```
+-      5
+-      10.4
+-      "Hello World"
+-      [1, 2, 3, 4, 5]
+-      ```
++We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
+=
+-      Numbers, strings, lists containing numbers and strings are all values. They are self-referential. They simply state what they are. There are other kinds of values that we will see later.
++<!-- slide -->
+=
+-      You can give (or assign) a name to a value, as shown.
++Sample viewbox program to demonstrate
+=
+-      ```
+-      family = 5
+-      price = 10.4
+-      morningGreeting = "Hello World"
+-      fingers = [1, 2, 3, 4, 5]
+-      ```
++<!-- slide -->
+=
+-      Here, `family` is a name and `5` is the value assigned to `family`.
++Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
+=
+-      You can get the value back by calling its name.
++```elm
++svg [ viewbox "-100 -100 200 200" ] []
++```
+=
+-      ```
+-      ---- Elm 0.19.0 ----------------------------------------------------------------
+-      Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+-      --------------------------------------------------------------------------------
+-      > family = 5
+-      5 : number
+-      > family
+-      5 : number
+-      ```
++<!-- slide -->
+=
+-    - Operators (+, -, ++)
++## Day 3
+=
+-      ```
+-      > 2 + 5
+-      7 : number
+-      ```
++Let the computer do the math
+=
+-      ```
+-      > 4 - 6
+-      -2 : number
+-      ```
++<!-- slide -->
+=
+-      ```
+-      > "Hello" ++ " world!"
+-      "Hello world!" : String
+-      ```
++Introduce the problem: Can we avoid all these repetitive and manual steps?
+=
+-      ```
+-      > 1 :: [2, 3, 4, 5]
+-      [1,2,3,4,5] : List number
+-      ```
++<!-- slide -->
+=
+-      Operators take two values and return a new value. We know that names refer to values, so we can use them in place of values:
++What is a program made of? (in the REPL):
+=
+-      ```
+-      > family = 5
+-      5 : number
+-      > family + 2
+-      7 : number
+-      ```
++<!-- slide -->
+=
+-      You can also give a name to the value returned by an operator:
++Values and names
+=
+-      ```
+-      > family = 5
+-      5 : number
+-      > familyAndPets = family + 2
+-      7 : number
+-      > familyAndPets
+-      7 : number
+-      ```
++```
++5
++10.4
++"Hello World"
++[1, 2, 3, 4, 5]
++```
+=
+-      Note that different values have different types.
++* Numbers like `5`, `10.4`
++* strings like `"Hello world"`
++* and lists containing numbers or strings
+=
+-      ```
+-      5 : number
+-      10.4 : Float
+-      "Hello World" : String
+-      [1,2,3,4,5] : List number
+-      ```
+-      Different operators work on different types. Adding a number and a string doesn't make sense. So if you try, Elm will complain (and give a helpful hint):
++are all values
+=
+-      ```
+-      > "Hello " + 5
+-      -- TYPE MISMATCH ----------------------------------------------------------- elm
++<!-- slide -->
+=
+-      I cannot do addition with String values like this one:
++They are self-referential. They simply state what they are. There are other kinds of values that we will see later.
+=
+-      5|   "Hello " + 5
+-           ^^^^^^^^
+-      The (+) operator only works with Int and Float values.
++<!-- slide -->
+=
+-      Hint: Switch to the (++) operator to append strings!
+-      ```
++You can give (or assign) a name to a value, as shown.
+=
+-      (Int and Float are both types representing numbers)
++```
++family = 5
++price = 10.4
++morningGreeting = "Hello World"
++fingers = [1, 2, 3, 4, 5]
++```
+=
++Here, `family` is a name and `5` is the value assigned to `family`.
+=
+-    - Functions
++<!-- slide -->
+=
+-      ```elm
+-      fun something = something ++ " is fun."
+-      ```
++You can get the value back by calling its name.
+=
+-      Explain: a function is a thing that takes some values (called arguments) and return one value. So it's similar to operators. In fact operators are functions!
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++> family = 5
++5 : number
++> family
++5 : number
++```
+=
+-      ```elm
+-      (-) 5 10
+-      5 - 10
+-      ```
++<!-- slide -->
+=
+-      You can think of a function as a machine. You put something in the machine, and it produces something in return. For example think about a machine that produces rubber ducks. You put a bucket of white plastic pellets and a bucket of red paint, and you get a bunch of red rubber ducks!
++Operators (`+`, `-`, `++`, `::`)
+=
+-      What you get will depends on what you put. The color of the ducks depends on the paint you put. Quantity of ducks depends on how much plastic you put in.
++```
++> 2 + 5
++7 : number
++```
+=
+-      ```elm
+-      makeMeSomeDucks color plastic =
+-          String.fromInt plastic ++ " " ++ color ++ " rubber ducks"
+-      ```
++```
++> 4 - 6
++-2 : number
++```
+=
+-      Once you have a function, you can call it like this:
++```
++> "Hello" ++ " world!"
++"Hello world!" : String
++```
+=
+-      ```elm
+-      > makeMeSomeDucks "blue" 12
+-      "12 blue rubber ducks" : String
+-      ```
++```
++> 1 :: [2, 3, 4, 5]
++[1,2,3,4,5] : List number
++```
+=
+-      Note: functions help organize code into nice reusable chunks.
++<!-- slide -->
+=
+-      How do you get functions? There are three ways.
++Operators take two values and return a new value. We know that names refer to values, so we can use them in place of values:
+=
+-        1.  Some functions are always there for you, e.g. `(+)`, `(-)`
++```
++> family = 5
++5 : number
++> family + 2
++7 : number
++```
+=
+-        2.  Some functions you can import using code like this:
++<!-- slide -->
+=
+-            ```elm
+-            import Svg
+-            ```
++You can also give a name to the value returned by an operator:
+=
+-            and then
++```
++> family = 5
++5 : number
++> familyAndPets = family + 2
++7 : number
++> familyAndPets
++7 : number
++```
+=
+-            ```
+-            Svg.circle [ cx "10", cy "10", r "20" ] [ ]
+-            ```
++<!-- slide -->
+=
+-            To call a function that was imported, you have to prefix it with the name of the module (in this example `Svg`).
++Note that different values have different types.
+=
+-        3.  Finally, you will write some functions, just like we saw in the `fun` and `makeMeSomeDucks` examples.
++```
++5 : number
++10.4 : Float
++"Hello World" : String
++[1,2,3,4,5] : List number
++```
+=
++<!-- slide -->
+=
+-      Finally there is one special thing: the first line of the program is a module declaration. For now it's enough for us to know, that it has to be there and it has to match the name of the file.
++Different operators work on different types. Adding a number and a string doesn't make sense. So if you try, Elm will complain (and give a helpful hint):
+=
+-  - Exercise: In our Main.elm, try to identify some values, names and function calls:
++```
++> "Hello " + 5
++-- TYPE MISMATCH ----------------------------------------------------------- elm
+=
+-    Hint: `"darkred"` is a value, `main` is a name, `width` is a function.
++I cannot do addition with String values like this one:
+=
+-    Hint: There is nothing else there now, but we will soon introduce our own functions.
++5|   "Hello " + 5
++     ^^^^^^^^
++The (+) operator only works with Int and Float values.
+=
+-  - Where can we use some functions?
++Hint: Switch to the (++) operator to append strings!
++```
+=
+-    As we said before, functions are good when we have some repetitive operation that can be parametrized (like rubber ducks production).
++(Int and Float are both types representing numbers)
+=
+-    Obviously calculating `x` and `y` coordinates is repetitive and can be parametrized (parameters are `radius` and `angle`).
++<!-- slide -->
+=
+-  - Apply to our trigonometry calculations
++Functions
+=
+-    - Compose and copy and paste the trigonometry functions to calculate cx and cy:
++```elm
++fun something = something ++ " is fun."
++```
+=
+-      ```elm
+-      cx (String.fromFloat (cos (degrees 72) * 100))
+-      cy (String.fromFloat (sin (degrees 72) * 100))
+-      ```
++<!-- slide -->
+=
+-    - Eliminate repetition:
++Explain: a function is a thing that takes some values (called arguments) and return one value. So it's similar to operators. In fact operators are functions!
+=
+-      - define and reuse radius (100)
++```elm
++(-) 5 10
++5 - 10
++```
+=
+-        Assign a value of `100` to a name `radius`:
++<!-- slide -->
+=
+-        ```
+-        radius = 100
+-        ```
++You can think of a function as a machine. You put something in the machine, and it produces something in return. For example think about a machine that produces rubber ducks. You put a bucket of white plastic pellets and a bucket of red paint, and you get a bunch of red rubber ducks!
+=
+-        and plug it to our functions:
++What you get will depends on what you put. The color of the ducks depends on the paint you put. Quantity of ducks depends on how much plastic you put in.
+=
+-        ```elm
+-        cx (String.fromFloat (cos (degrees 72) * radius))
+-        cy (String.fromFloat (sin (degrees 72) * radius))
+-        ```
++<!-- slide -->
+=
+-        As you see the names are good for repetitive things too.
++```elm
++makeMeSomeDucks color plastic =
++    String.fromInt plastic ++ " " ++ color ++ " rubber ducks"
++```
+=
+-      - define and reuse `x : angle -> cx` and `y : angle -> cy`
++Once you have a function, you can call it like this:
+=
++```elm
++> makeMeSomeDucks "blue" 12
++"12 blue rubber ducks" : String
++```
+=
+-        ```elm
+-        x angle =
+-          String.fromFloat (cos (degrees angle) * radius)
+-        ```
++<!-- slide -->
+=
+-        ```elm
+-        y angle =
+-          String.fromFloat (sin (degrees angle) * radius)
+-        ```
++Note: functions help organize code into nice reusable chunks.
+=
+-        Then plug it into the code making circles:
++<!-- slide -->
+=
+-        ```elm
+-        circle
+-          [ cx (x 72)
+-          , cy (y 72)
+-          , r "10"
+-          , fill "darkred"
+-          ]
+-        ```
++How do you get functions? There are three ways.
+=
+-      - define and reuse `dot : angle -> color -> Svg`
++<!-- slide -->
+=
++Some functions are always there for you
+=
+-        ```elm
+-        dot angle color =
+-            circle
+-                [ cx (x angle)
+-                , cy (y angle)
+-                , r "10"
+-                , fill color
+-                ]
+-        ```
++`(+)`, `(-)`
+=
+-        and plug it into our SVG picture:
++<!-- slide -->
+=
++2.  Some functions you can import using code like this:
+=
+-        ```
+-        svg [ viewbox "-100 -100 200 200" ]
+-          [ dot 0 "darkred"
+-          , dot 72 "fuchsia"
+-          , dot 144 "saddlebrown"
+-          , dot 216 "deepskyblue"
+-          , dot 288 "gold"
+-          ]
+-        ```
++```elm
++import Svg
++```
+=
+-        Want some more cool color inspiration. Check out https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords
++and then
+=
+-    - Make it more general
++```
++Svg.circle [ cx "10", cy "10", r "20" ] [ ]
++```
+=
+-      - Show List and list operations (map and indexedMap)
++To call a function that was imported, you have to prefix it with the name of the module (in this example `Svg`).
+=
+-        There are a number of functions that operate on lists, for example `List.length` and `List.map`.
++<!-- slide -->
+=
+-        Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket.
++Finally, you will write some functions, just like we saw in the `fun` and `makeMeSomeDucks` examples.
+=
+-        Demonstrate:
++<!-- slide -->
+=
+-        ```elm
+-        > things = ["Programming", "Swimming", "Dancing", "Saddle brown"]    
+-        ["Programming","Swimming","Dancing","Saddle brown"]
+-            : List String
++Finally there is one special thing: the first line of the program is a module declaration. For now it's enough for us to know, that it has to be there and it has to match the name of the file.
+=
++<!-- slide -->
+=
+-        > List.length things
+-        4 : Int
++Exercise: In our Main.elm, try to identify some values, names and function calls:
+=
+-        > List.map fun things
+-        ["Programming is fun!","Swimming is fun!","Dancing is fun!","Saddle brown is fun!"]
+-            : List String
++Hint: `"darkred"` is a value, `main` is a name, `width` is a function.
+=
+-        > things
+-        ["Programming","Swimming","Dancing","Saddle brown"]
+-            : List String
+-        ```
++Hint: There is nothing else there now, but we will soon introduce our own functions.
+=
+-        Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it.
++<!-- slide -->
+=
+-      - Make a `palette : List color`
++#### Where can we use some functions?
+=
+-        ```elm
+-        palette =
+-          [ "darkred"
+-          , "fuchsia"
+-          , "saddlebrown"
+-          , "deepskyblue"
+-          , "gold"
+-          ]
+-        ```
++As we said before, functions are good when we have some repetitive operation that can be parametrized (like rubber ducks production).
+=
+-      - Use `List.indexedMap dot palette` to generate the dots
++Obviously calculating `x` and `y` coordinates is repetitive and can be parametrized (parameters are `radius` and `angle`).
+=
+-        Another function that operates on lists is `List.indexedMap`. Let's see it at work:
++<!-- slide -->
+=
+-        ```elm
+-        dot index color =
+-            circle
+-                [ cx (x ((360 / (List.length palette)) * index))
+-                , cy (y ((360 / (List.length palette)) * index))
+-                , r "10"
+-                , fill color
+-                ]
++#### Apply to our trigonometry calculations
+=
+-        List.indexedMap dot palette
+-        ```
+-
+-        We can introduce a `let` block to make our code more readable and avoid repetition.
+-
+-        ```elm
+-        dot index color =
+-            let
+-                angle =
+-                    (360 / count) * index
+-
+-                count =
+-                    List.length palette
+-            in
+-            circle
+-                [ cx (x angle)
+-                , cy (y angle)
+-                , r "10"
+-                , fill color
+-                ]
+-        ```
++Compose and copy and paste the trigonometry functions to calculate cx and cy:
++
++```elm
++cx (String.fromFloat (cos (degrees 72) * 100))
++cy (String.fromFloat (sin (degrees 72) * 100))
++```
++
++<!-- slide -->
++
++#### Eliminate the repetition:
++
++Assign a value of `100` to a name `radius`:
++
++```
++radius = 100
++```
++
++and plug it to our functions:
++
++```elm
++cx (String.fromFloat (cos (degrees 72) * radius))
++cy (String.fromFloat (sin (degrees 72) * radius))
++```
++
++<!-- slide -->
++
++As you see the names are good for repetitive things too.
++
++Define and reuse
++
++- `x : angle -> cx`
++
++  ```elm
++  x angle =
++      String.fromFloat (cos (degrees angle) * radius)
++  ```
++
++- `y : angle -> cy`
++
++  ```elm
++  y angle =
++      String.fromFloat (sin (degrees angle) * radius)
++  ```
++
++<!-- slide -->
++
++Then plug it into the code making circles:
++
++```elm
++circle
++  [ cx (x 72)
++  , cy (y 72)
++  , r "10"
++  , fill "darkred"
++  ]
++```
++
++<!-- slide -->
++
++define and reuse `dot : angle -> color -> Svg`
++
++```elm
++dot angle color =
++    circle
++        [ cx (x angle)
++        , cy (y angle)
++        , r "10"
++        , fill color
++        ]
++```
++
++
++<!-- slide -->
++
++and plug it into our SVG picture:
++
++```
++svg [ viewbox "-100 -100 200 200" ]
++  [ dot 0 "darkred"
++  , dot 72 "fuchsia"
++  , dot 144 "saddlebrown"
++  , dot 216 "deepskyblue"
++  , dot 288 "gold"
++  ]
++```
++
++For more cool colors check out [Color keywords page at Mozilla Developer's Network](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords)
++
++<!-- slide -->
++
++- Make it more general
++
++  - Show List and list operations (map and indexedMap)
++
++    There are a number of functions that operate on lists, for example `List.length` and `List.map`.
++
++<!-- slide -->
++
++Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket.
++
++<!-- slide -->
++
++Demonstrate:
++
++```elm
++> things = ["Programming", "Swimming", "Dancing", "Saddle brown"]    
++["Programming","Swimming","Dancing","Saddle brown"]
++    : List String
++
++
++> List.length things
++4 : Int
+=
+-        Take a look at the [documentation for `List.indexedMap`](https://package.elm-lang.org/packages/elm/core/latest/List#indexedMap).
++> List.map fun things
++["Programming is fun!","Swimming is fun!","Dancing is fun!","Saddle brown is fun!"]
++    : List String
+=
++> things
++["Programming","Swimming","Dancing","Saddle brown"]
++    : List String
++```
++
++<!-- slide -->
++
++Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it.
++
++<!-- slide -->
+=
+-Day 4
++Make a `palette : List color`
++
++```elm
++palette =
++  [ "darkred"
++  , "fuchsia"
++  , "saddlebrown"
++  , "deepskyblue"
++  , "gold"
++  ]
++```
++
++<!-- slide -->
++
++Use `List.indexedMap dot palette` to generate the dots
++
++<!-- slide -->
++
++Another function that operates on lists is `List.indexedMap`. Let's see it at work:
++
++```elm
++dot index color =
++    circle
++        [ cx (x ((360 / (List.length palette)) * index))
++        , cy (y ((360 / (List.length palette)) * index))
++        , r "10"
++        , fill color
++        ]
++
++List.indexedMap dot palette
++```
++
++<!-- slide -->
++
++We can introduce a `let` block to make our code more readable and avoid repetition.
++
++```elm
++dot index color =
++    let
++        angle =
++            (360 / count) * index
++
++        count =
++            List.length palette
++    in
++    circle
++        [ cx (x angle)
++        , cy (y angle)
++        , r "10"
++        , fill color
++        ]
++```
++
++<!-- slide -->
++
++Take a look at the [documentation for `List.indexedMap`](https://package.elm-lang.org/packages/elm/core/latest/List#indexedMap).
++
++<!-- slide -->
++
++## Day 4
++
++<!-- slide -->
+=
+=Introduce SVG groups
+=
+-    - We'll need one group for the dots and one group for the lines
++We'll need one group for the dots and one group for the lines
+=
+-    ```elm
+-    svg [ viewBox "-100 -100 200 200" ]
+-        [ Svg.g [] (List.indexedMap dot pallete)
+-        , Svg.g [] [ Svg.line [ x1 "123", y1 "112", x2 "41", y2 "11", stroke "black" ] [] ]
++```elm
++svg [ viewBox "-100 -100 200 200" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.g [] [ Svg.line [ x1 "123", y1 "112", x2 "41", y2 "11", stroke "black" ] [] ]
++    ]
++```
++
++<!-- slide -->
++
++Exercise: add a few lines with different colors
++
++Now we'll learn how to color a line with a gradient (going from one color to another)
++
++<!-- slide -->
++
++```
++svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.defs []
++        [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0" ]
++            [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++            , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
++            ]
++        ]
++    , Svg.g []
++        [ Svg.line [ x1 "0", y1 "0", x2 "100", y2 "100", stroke "url(#MyGradient)" ] []
++        , Svg.line [ x1 "0", y1 "0", x2 "-100", y2 "-100", stroke "url(#MyGradient)" ] []
+=        ]
+-    ```
++    ]
++```
++
++<!-- slide -->
+=
+-    Exercise: add a few lines with different colors
++We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative.
+=
+-    Now we'll learn how to color a line with a gradient (going from one color to another)
++<!-- slide -->
+=
+-    ```
++```
++main =
+=    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
+=        [ Svg.g [] (List.indexedMap dot pallete)
+=        , Svg.defs []
+-            [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0" ]
++            [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
+=                [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
+=                , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
+=                ]
+=            ]
+=        , Svg.g []
+-            [ Svg.line [ x1 "0", y1 "0", x2 "100", y2 "100", stroke "url(#MyGradient)" ] []
+-            , Svg.line [ x1 "0", y1 "0", x2 "-100", y2 "-100", stroke "url(#MyGradient)" ] []
++            [ Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(45),scale(100,1)", stroke "url(#MyGradient)" ] []
++            , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(180),scale(100,1)", stroke "url(#MyGradient)" ] []
++            , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(270),scale(100,1)", stroke "url(#MyGradient)" ] []
+=            ]
+=        ]
+-    ```
+-
+-    We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative.
+-
+-    ```
+-    main =
+-        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
+-            [ Svg.g [] (List.indexedMap dot pallete)
+-            , Svg.defs []
+-                [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
+-                    [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
+-                    , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
+-                    ]
+-                ]
+-            , Svg.g []
+-                [ Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(45),scale(100,1)", stroke "url(#MyGradient)" ] []
+-                , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(180),scale(100,1)", stroke "url(#MyGradient)" ] []
+-                , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(270),scale(100,1)", stroke "url(#MyGradient)" ] []
+-                ]
+-            ]
+-    ```
++```
++
++TODO: We need to fix the viewbox!
+=
+-    We need to fix the viewbox!
++<!-- slide -->
+=
+-    Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here.
+=
+-    If you're interested in what `gradientUnits "userSpaceOnUse"` does, come see me after the lesson.
++Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here.
+=
+-    ```elm
+-    gradient : Int -> String -> Svg msg
+-    gradient index color =
+-        Svg.linearGradient [ Svg.Attributes.id ("Gradient-" ++ String.fromInt index), x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
+-            [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
+-            , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor color ] []
++If you're interested in what `gradientUnits "userSpaceOnUse"` does, come see me after the lesson.
++
++<!-- slide -->
++
++```elm
++gradient : Int -> String -> Svg msg
++gradient index color =
++    Svg.linearGradient
++        [ id ("Gradient-" ++ String.fromInt index)
++        , x1 "0"
++        , y1 "0"
++        , x2 "1"
++        , y2 "0"
++        , gradientUnits "userSpaceOnUse"
++        ]
++        [ Svg.stop
++            [ Svg.Attributes.offset "0"
++            , Svg.Attributes.stopColor "saddlebrown"
++            ]
++            []
++        , Svg.stop
++            [ Svg.Attributes.offset "1"
++            , Svg.Attributes.stopColor color
+=            ]
++            []
++        ]
++```
++
++<!-- slide -->
++
++```
++line : Int -> String -> Svg msg
++line index color =
++    let
++        count =
++            List.length pallete
+=
++        angle =
++            (360 / toFloat count) * toFloat index
+=
+-    line : Int -> String -> Svg msg
+-    line index color =
+-        let
+-            angle =
+-                (360 / toFloat (List.length pallete)) * toFloat index
++        transformation =
++            "rotate("
++                ++ String.fromFloat angle
++                ++ "), scale("
++                ++ String.fromInt radius
++                ++ ", 1)"
+=
+-            transformation =
+-                "rotate(" ++ String.fromFloat angle ++ "),scale(" ++ String.fromInt radius ++ ",1)"
++        url =
++            "url(#Gradient-" ++ String.fromInt index ++ ")"
++    in
++    Svg.line
++        [ x1 "0"
++        , y1 "0"
++        , x2 "1"
++        , y2 "0"
++        , transform transformation
++        , stroke url
++        ]
++        []
++```
+=
+-            url =
+-                "url(#Gradient-" ++ String.fromInt index ++ ")"
+-        in
+-        Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform transformation, stroke url ] []
++<!-- slide -->
+=
++```
++main =
++    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++        [ Svg.g [] (List.indexedMap dot pallete)
++        , Svg.defs [] (List.indexedMap gradient pallete)
++        , Svg.g [] (List.indexedMap line pallete)
++        ]
++```
+=
+-    main =
+-        svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
+-            [ Svg.g [] (List.indexedMap dot pallete)
+-            , Svg.defs [] (List.indexedMap gradient pallete)
+-            , Svg.g [] (List.indexedMap line pallete)
+-            ]
+-    ```
++<!-- slide -->
+=
+=    This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside.
+=
++<!-- slide -->
+=
+-Day 5
++## Day 5
++
++Let's make a beautiful tree!
++
++<!-- slide -->
+=
+=Our project is looking pretty good at this point. We've created some interesting graphical elements, and learned some interesting ways to place them programatically. However, it doesn't take user input like many of the programs we use. We'll be doing some pretty interesting things with user input later in this workshop, but for now we'll start with a deceptively simple step: setting the background color.
+=
+=I say this step is deceptively simple, because we'll need to make some big changes to our program and introduce a number of new concepts.
+=
++<!-- slide -->
++
+=Show complete code.
+=
++<!-- slide -->
++
+=There's a lot going on here, and it's not obvious how it all works.
+=
+=Our `main` value has changed.
+@@ -574,8 +768,15 @@ main =
+=        }
+=```
+=
+-Previously, the value of `main` was an SVG element. Now it contains a call to `Browser.sandbox`. A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element. It also needs a `state`. The state of an application is basically the information used to determine how the application should be rendered. Think about the Atom program we are all running at the moment. We know we are all running the same program, however we are not all looking at exactly the same thing. The text in our neighbors editor might be slightly different than the text in our own. If they opened another file, a letter to a friend for example, the text would be very different. The text input is part of the state of the Atom application. Maybe your neighbor has scrolled to another point in the screen. The scroll point is also part of the state of the Atom application. The cursor might be on another line in their viewport. The cursor position is also part of the state of the Atom application. Atom uses all of this state information to render exactly what you are seeing on your computer screen. Anything that can change will be represented in a state variable. If you input or delete text, move the cursor, scroll up or down, or change any of the application settings, you are updating
+-the state of the Atom application.
++<!-- slide -->
++
++Previously, the value of `main` was an SVG element. Now it contains a call to `Browser.sandbox` function.
++
++<!-- slide -->
++
++A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element.
++
++<!-- slide -->
+=
+=```
+=type alias State =
+@@ -588,8 +789,12 @@ type alias Color =
+=
+=Here we create something called a type alias. We've already discussed types. A type alias is basically a way to give an alternative name for a type. We give the alias `Color` to `String` and the alias `State` to `Color`. So in the end all three names point to the same type!
+=
++<!-- slide -->
++
+=Why are we doing this? All of these types are strings, and Elm will treat them all as strings. But it will make our code more readable to use these aliases. We are writing an application that allows the user to change the background color. That should explain why our `State` is a `Color`.
+=
++<!-- slide -->
++
+=Our `Browser.sandbox` takes something with the name `init`. This is our initial state. Let's take a look at the value of `init`.
+=
+=```
+@@ -598,8 +803,12 @@ init =
+=    "white"
+=```
+=
++<!-- slide -->
++
+=We see that the initial state is `"white"`. We'll see how our application uses this value in a moment.
+=
++<!-- slide -->
++
+=Looking back at our `sandbox`, we see it also takes a `view`. Let's take a look at our view function:
+=
+=```
+@@ -617,14 +826,24 @@ view color =
+=        ]
+=```
+=
++<!-- slide -->
++
+=You'll notice that this `view` function looks very similar to the `main` variable from earlier. Before, our application was only a view. Now the view is only one piece of our interactive application.
+=
++<!-- slide -->
++
+=One important way our `view` function differs from the `main` variable from ealier is that `view` is a function. We see it takes a variable of type `State` (a string), which we have named `color`. In an Elm sandbox project the `view` always takes a variable with the same type as the `init` variable. This is the current state of the application. `view` can use the state to render the application properly, just as Atom uses its state to render text for its user.
+=
++<!-- slide -->
++
+=We see that we've added a line to the svg attributes, `Svg.Attributes.style ("background: " ++ color)`. Here we use the state of the application to set the background color of the svg element.
+=
++<!-- slide -->
++
+=All we need now is some way to update the state (the background color) of the application.
+=
++<!-- slide -->
++
+=Taking a look at our `main` function, we see that it take a third and final function, `update`. Let's look at our update function:
+=
+=```
+@@ -640,58 +859,147 @@ update msg state =
+=
+=```
+=
++<!-- slide -->
++
+=We see our `update` function takes two variables, of type `Msg` and `State`. If we think of update as a machine which takes an old state and spits out a new state, this state variable corresponds to the old state.
+=
++<!-- slide -->
++
+=The `msg` variable is something new. Looking above, we see that we have defined a `Msg` variable type. This is similar to our `State` and `Color` union types, in that we have defined a new type that Elm understands. However, unlike the `type alias` constructor, the `type` constructor does not simply give a new name for an existing type. Instead, here we define a completely unique type. We see values with type Msg must have the form `SetBackground Color`. `SetBackground "white"`, `SetBackground "blue"`, `SetBackground "saddlebrown"` are all valid values of the type `Msg`.
+=
++<!-- slide -->
++
+=What is important to understand is that our `msg` variable is used by `update` to determine how it should update the state. We see that update first checks that `msg` has the form `SetBackground color`, and then returns the value of `color`. This will become our new state. Every time update is called, the sandbox will re-render our view with the new state.
+=
++<!-- slide -->
++
+=So now we see how our application changes the background color of the svg element. However, we haven't seen when it will update the background color. Well, we want to change the background color by clicking a dot. So let's take a look at our `dot` function to see if we can find a hint there.
+=
+-- `Model`:
++<!-- slide -->
+=
+-  the structure of the state of the program
++<dl>
++<dt>Model
++<dd>the structure of the state of the program
++</dl>
+=
+-  TODO: Consistently use `Model` and `model` instead of less standard `State` to make it easier to our participants.
++> TODO: Consistently use `Model` and `model` instead of less standard `State` to make it easier to our participants.
+=
+-- `Msg`
++<!-- slide -->
+=
++<dl>
++<dt>Msg
++<dd>
+=  What events can there be in the program. Currently have only one possible event - setting the background to a given color.
++</dl>
++
++<!-- slide -->
+=
+=- `update`
+=
+=  the instructions of how to change the state when a particular message comes in.
+=
++<!-- slide -->
++
+=- `view`
+=
+=  the instruction of what to display on the screen. It also contains instructions of what messages to send when certain events (like clicks on elements) happen. Every time the state changes the instructions will be applied.
+=
++<!-- slide -->
++
+=- `init`
+=
+=  What is the initial state right after the program is started.
+=
++<!-- slide -->
++
+=- `main`
+=
+=  glues it all together.
+=
+-Next steps:
++<!-- slide -->
+=
+=- How do we model the tree
+=
+-  - introduces the model for later interactive program
++<!-- slide -->
++
++A tree is a composition of segments. A segment can be either a twig or a branch. Twig is a terminal segment of a tree. When the tree is very young it consists of a single twig. Later this twig will evolve into a branch and will grow it's own twigs, that in turn will evolve into branches. So a branch is a segment of a tree that ends with some other  segments (twigs or branches).
++
++<!-- slide -->
++
++Note that a branch can split to several branches that each in turn can split into several branches and so on. This way a tree can be as complex as we want. Note that real trees are like that. It's difficult to say how many generations of branches it can have from the trunk to the smallest little twigs on top of the crown. This kind of structure is called recursive. It repeats the same pattern.
++
++<!-- slide -->
+=
+-  - naturally introduces union types
++Each segment (a twig or a branch) has length, a direction (represented as an angle) and a color (so that it's beautiful despite having no leafs or flowers).
+=
++Here is how we can represent it in code:
+=
+-- Make a tree grow
++```
++type alias Segment =
++    { length : Float
++    , direction : Float
++    , color : Color
++    }
++```
+=
+-  - Make a function that given the age of the tree and rules returns a tree
++<!-- slide -->
+=
+-  - Use time subscription to make the tree grow
++If you consider that a branch can split into more branches or twigs, and then that a trunk of a tree is just a first branch or a twig if the tree is very young, then you can see that each segment separated from the parent branch can be considered a tree.
+=
+=<!-- slide -->
+=
+-``
++So we can reuse the same structure to represent a tree or any of it's segments!
++
++Look:
++
++```
++type Tree
++    = Twig Segment
++    | Branch Segment (List Tree)
++```
++
++<!-- slide -->
++
++Think about it! A tree is a twig (when it's young) or a branch that splits into __a zero, one or many__ __twigs or branches__.
++
++  - zero, one or many: a list
++  - twig or branch: a tree
++
++So a Twig is just a Segment while a branch is a Segment with a list of Trees!
++
++<!-- slide -->
++
++Let's encode a simple tree by hand:
++
++```elm
++tree =
++  Branch { length = 10, direction = degrees -90, color = "brown" }
++      [ Branch { length = 6, direction = degrees -45, color = "lightbrown" }
++          [ Twig { length = 2, direction = degrees -30, color = "green" }
++          , Twig { length = 2, direction = degrees -60, color = "green" }
++          ]
++      , Branch { length = 6, direction = degrees -135, color = "lightbrown" }
++          [ Twig { length = 2, direction = degrees -120, color = "green" }
++          , Twig { length = 2, direction = degrees -150, color = "green" }
++          ]
++      ]
++```
++
++<!-- slide -->
++
++## Day 6
++
++Make the tree grow!
++
++<!-- slide -->
++
++Make a function that given the age of the tree and rules returns a tree
++
++<!-- slide -->
++
++Use time subscription to make the tree grow
++
++<!-- slide -->
+=
+=```
+=dot : Int -> String -> Svg Msg
+@@ -712,12 +1020,18 @@ dot index color =
+=
+=We see there is a new line here:
+=
++<!-- slide -->
++
+=```
+=Svg.Events.onClick (SetBackground color)
+=```
+=
++<!-- slide -->
++
+=`onClick` is an event. It basically tells our svg element to listen for someone to click on it. It will then handle the event by emitting the Msg `SetBackground color`. We know `color` is the color of the dot. We see then that when someone clicks on the dot, the Msg will be emitted, `update` will be called with this Msg and will update our state. Our sandbox will rerender the view with this new state. The user will see the svg element with a new background color.
+=
++<!-- slide -->
++
+=Notice that our `view` function, as well as all the functions that call functions responsible for rendering svg elements, including `gradient`, `line`, and `dot` have return values with type `Svg Msg` or `Html Msg`. This is because svg element can emit Msgs which will be handled by `update`.
+=
+=
+```
+
+# Complete the Tree program using only SVG transformations
+
+No trigonometry anymore!
+
+``` diff
+index 5e5f50b..2ec5008 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -5,6 +5,7 @@ import Browser.Events
+=import Dict exposing (Dict)
+=import Html exposing (Html)
+=import Json.Decode exposing (Decoder)
++import List.Extra as List
+=import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+@@ -50,18 +51,15 @@ type alias Rules =
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( { time = 0
++    ( { time = 20000
+=      , rules =
+=            Dict.fromList
+=                [ ( "red"
+-                  , [ Segment "green" 15 0.2
+-                    , Segment "green" 175 0.1
+-                    ]
++                  , [ Segment "brown" -90 6 ]
+=                  )
+-                , ( "green", [ Segment "blue" 15 0.3 ] )
+-                , ( "blue"
+-                  , [ Segment "red" 15 0.7
+-                    , Segment "purple" 45 0.1
++                , ( "brown"
++                  , [ Segment "brown" 45 3
++                    , Segment "brown" -45 3
+=                    ]
+=                  )
+=                ]
+@@ -72,17 +70,58 @@ init () =
+=
+=view : Model -> Html Msg
+=view model =
++    let
++        gradients : List (Svg Msg)
++        gradients =
++            model.rules
++                |> Dict.map
++                    (\parentColor children ->
++                        children
++                            |> List.map (\child -> ( parentColor, child.color ))
++                    )
++                |> Dict.values
++                |> List.concat
++                |> List.unique
++                |> List.map
++                    (\(( start, end ) as colors) ->
++                        linearGradient
++                            [ id (gradientId colors)
++                            , x1 "0"
++                            , y1 "0"
++                            , x2 "1"
++                            , y2 "0"
++                            , gradientUnits "userSpaceOnUse"
++                            ]
++                            [ stop
++                                [ stopColor start, offset "0.2" ]
++                                []
++                            , stop
++                                [ stopColor end, offset "0.8" ]
++                                []
++                            ]
++                    )
++
++        tree =
++            "red"
++                |> svgTree model.rules (model.time / 5000) (model.time / 5000)
++                |> g
++                    [ transform
++                        ("scale("
++                            ++ String.fromFloat (model.time / 1000)
++                            ++ ")"
++                        )
++                    ]
++    in
+=    Html.div []
+=        [ Html.h1 [] [ Html.text <| String.fromFloat model.time ]
+-        , "red"
+-            |> svgTree model.rules (model.time / 5000) (model.time / 5000)
+-            |> g [ transform ("scale(" ++ String.fromFloat (model.time / 1000) ++ ")") ]
+-            |> List.singleton
+-            |> svg
+-                [ viewBox "-1000 -1000 2000 2000"
+-                , height "800px"
+-                , width "800px"
+-                ]
++        , svg
++            [ viewBox "-1000 -1000 2000 2000"
++            , height "800px"
++            , width "800px"
++            ]
++            [ defs [] gradients
++            , tree
++            ]
+=        ]
+=
+=
+@@ -145,17 +184,41 @@ svgTree rules treeAge branchAge color =
+=                                ++ String.fromFloat segment.angle
+=                                ++ ")"
+=                                ++ ", scale("
+-                                ++ String.fromFloat ((branchAge - 1) / treeAge)
++                                ++ String.fromFloat (branchAge / treeAge)
+=                                ++ ")"
+=                                ++ ", translate("
+-                                ++ String.fromFloat (segment.length * branchAge)
++                                ++ String.fromFloat segment.length
+=                                ++ ", 0)"
+=                    in
+=                    g
+=                        [ transform transformation ]
+-                        (svgTree rules treeAge (branchAge - 1) segment.color)
++                        (line
++                            [ stroke ("url(#" ++ gradientId ( color, segment.color ) ++ ")")
++                            , x1 <| String.fromFloat 0
++                            , x2 <| String.fromFloat 1
++                            , y1 <| String.fromFloat 0
++                            , y2 <| String.fromFloat 0
++                            , strokeWidth "1"
++                            , transform
++                                ("translate("
++                                    ++ String.fromFloat (0 - segment.length)
++                                    ++ ", 0)"
++                                    ++ ", "
++                                    ++ "scale("
++                                    ++ String.fromFloat segment.length
++                                    ++ ", 1)"
++                                )
++                            ]
++                            []
++                            :: svgTree rules treeAge (branchAge - 1) segment.color
++                        )
+=                )
+-            |> (::) (circle [ cx "0", cy "0", fill color, r "1" ] [])
++            |> (::) (circle [ cx "0", cy "0", fill color, r "0.5" ] [])
+=
+=    else
+=        []
++
++
++gradientId : ( Color, Color ) -> String
++gradientId ( start, end ) =
++    "connection-" ++ start ++ "-" ++ end
+```
+
+# Define Transformation as union type and use applyTransformations function to create Svg.Attribute for transformations
+
+
+
+``` diff
+index 2ec5008..711307f 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -51,7 +51,7 @@ type alias Rules =
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( { time = 20000
++    ( { time = 0
+=      , rules =
+=            Dict.fromList
+=                [ ( "red"
+@@ -178,20 +178,13 @@ svgTree rules treeAge branchAge color =
+=            |> Maybe.withDefault []
+=            |> List.map
+=                (\segment ->
+-                    let
+-                        transformation =
+-                            "rotate("
+-                                ++ String.fromFloat segment.angle
+-                                ++ ")"
+-                                ++ ", scale("
+-                                ++ String.fromFloat (branchAge / treeAge)
+-                                ++ ")"
+-                                ++ ", translate("
+-                                ++ String.fromFloat segment.length
+-                                ++ ", 0)"
+-                    in
+=                    g
+-                        [ transform transformation ]
++                        [ applyTransformations
++                            [ Rotate segment.angle
++                            , Scale (branchAge / treeAge) (branchAge / treeAge)
++                            , Translate segment.length 0
++                            ]
++                        ]
+=                        (line
+=                            [ stroke ("url(#" ++ gradientId ( color, segment.color ) ++ ")")
+=                            , x1 <| String.fromFloat 0
+@@ -199,15 +192,10 @@ svgTree rules treeAge branchAge color =
+=                            , y1 <| String.fromFloat 0
+=                            , y2 <| String.fromFloat 0
+=                            , strokeWidth "1"
+-                            , transform
+-                                ("translate("
+-                                    ++ String.fromFloat (0 - segment.length)
+-                                    ++ ", 0)"
+-                                    ++ ", "
+-                                    ++ "scale("
+-                                    ++ String.fromFloat segment.length
+-                                    ++ ", 1)"
+-                                )
++                            , applyTransformations
++                                [ Translate (0 - segment.length) 0
++                                , Scale segment.length 1
++                                ]
+=                            ]
+=                            []
+=                            :: svgTree rules treeAge (branchAge - 1) segment.color
+@@ -219,6 +207,47 @@ svgTree rules treeAge branchAge color =
+=        []
+=
+=
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++applyTransformations : List Transformation -> Svg.Attribute Msg
++applyTransformations transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
++        |> transform
++
++
+=gradientId : ( Color, Color ) -> String
+=gradientId ( start, end ) =
+=    "connection-" ++ start ++ "-" ++ end
+```
+
+# Make subtrees scale linearly
+
+Code reformatting
+
+``` diff
+index 711307f..9f15a49 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -103,7 +103,7 @@ view model =
+=
+=        tree =
+=            "red"
+-                |> svgTree model.rules (model.time / 5000) (model.time / 5000)
++                |> svgTree model.rules (model.time / 5000)
+=                |> g
+=                    [ transform
+=                        ("scale("
+@@ -170,41 +170,52 @@ subscriptions model =
+=        ]
+=
+=
+-svgTree : Rules -> Float -> Float -> Color -> List (Svg Msg)
+-svgTree rules treeAge branchAge color =
+-    if branchAge > 0 then
+-        rules
+-            |> Dict.get color
+-            |> Maybe.withDefault []
+-            |> List.map
+-                (\segment ->
+-                    g
+-                        [ applyTransformations
+-                            [ Rotate segment.angle
+-                            , Scale (branchAge / treeAge) (branchAge / treeAge)
+-                            , Translate segment.length 0
+-                            ]
+-                        ]
+-                        (line
+-                            [ stroke ("url(#" ++ gradientId ( color, segment.color ) ++ ")")
+-                            , x1 <| String.fromFloat 0
+-                            , x2 <| String.fromFloat 1
+-                            , y1 <| String.fromFloat 0
+-                            , y2 <| String.fromFloat 0
+-                            , strokeWidth "1"
+-                            , applyTransformations
+-                                [ Translate (0 - segment.length) 0
+-                                , Scale segment.length 1
+-                                ]
+-                            ]
+-                            []
+-                            :: svgTree rules treeAge (branchAge - 1) segment.color
+-                        )
+-                )
+-            |> (::) (circle [ cx "0", cy "0", fill color, r "0.5" ] [])
++svgTree : Rules -> Float -> Color -> List (Svg Msg)
++svgTree rules age color =
++    if age <= 0 then
++        []
+=
+=    else
+-        []
++        let
++            subtrees =
++                rules
++                    |> Dict.get color
++                    |> Maybe.withDefault []
++                    |> List.map subtree
++
++            subtree segment =
++                let
++                    gradient =
++                        "url(#" ++ gradientId ( color, segment.color ) ++ ")"
++
++                    scale =
++                        age / (age + 1)
++                in
++                g
++                    [ applyTransformations
++                        [ Rotate segment.angle
++                        , Scale scale scale
++                        , Translate segment.length 0
++                        ]
++                    ]
++                    (line
++                        [ stroke gradient
++                        , x1 "0"
++                        , x2 "1"
++                        , y1 "0"
++                        , y2 "0"
++                        , strokeWidth "1"
++                        , applyTransformations
++                            [ Translate (0 - segment.length) 0
++                            , Scale segment.length 1
++                            ]
++                        ]
++                        []
++                        :: svgTree rules (age - 1) segment.color
++                    )
++        in
++        circle [ cx "0", cy "0", fill color, r "0.5" ] []
++            :: subtrees
+=
+=
+=type Transformation
+```
+
+# Remove circles and use round linecap instead.
+
+
+
+``` diff
+index 9f15a49..a7f36d8 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -51,15 +51,25 @@ type alias Rules =
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( { time = 0
++    ( { time = 50000
+=      , rules =
+=            Dict.fromList
+-                [ ( "red"
+-                  , [ Segment "brown" -90 6 ]
++                [ ( "brown"
++                  , [ Segment "green" -115 1
++                    , Segment "green" -65 1
++                    , Segment "saddlebrown" 90 2
++                    ]
++                  )
++                , ( "green"
++                  , [ Segment "green" 20 3
++                    , Segment "green" -20 3
++                    , Segment "red" 90 1
++                    , Segment "red" -90 1
++                    ]
+=                  )
+-                , ( "brown"
+-                  , [ Segment "brown" 45 3
+-                    , Segment "brown" -45 3
++                , ( "saddlebrown"
++                  , [ Segment "saddlebrown" 20 3
++                    , Segment "saddlebrown" -20 3
+=                    ]
+=                  )
+=                ]
+@@ -93,16 +103,16 @@ view model =
+=                            , gradientUnits "userSpaceOnUse"
+=                            ]
+=                            [ stop
+-                                [ stopColor start, offset "0.2" ]
++                                [ stopColor start, offset "0" ]
+=                                []
+=                            , stop
+-                                [ stopColor end, offset "0.8" ]
++                                [ stopColor end, offset "1" ]
+=                                []
+=                            ]
+=                    )
+=
+=        tree =
+-            "red"
++            "brown"
+=                |> svgTree model.rules (model.time / 5000)
+=                |> g
+=                    [ transform
+@@ -204,7 +214,8 @@ svgTree rules age color =
+=                        , x2 "1"
+=                        , y1 "0"
+=                        , y2 "0"
+-                        , strokeWidth "1"
++                        , strokeWidth "0.2"
++                        , strokeLinecap "round"
+=                        , applyTransformations
+=                            [ Translate (0 - segment.length) 0
+=                            , Scale segment.length 1
+@@ -214,8 +225,7 @@ svgTree rules age color =
+=                        :: svgTree rules (age - 1) segment.color
+=                    )
+=        in
+-        circle [ cx "0", cy "0", fill color, r "0.5" ] []
+-            :: subtrees
++        subtrees
+=
+=
+=type Transformation
+```
\ No newline at end of file
new file mode 100644
index 0000000..664b557
--- /dev/null
+++ b/content/devlog/2018-11-19-elm-tree-workshop.md
@@ -0,0 +1,68 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Tree example: Use Elm UI to fill the screen and center the tree
+
+
+
+``` diff
+index a7f36d8..cde6818 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -3,6 +3,7 @@ module Tree exposing (main)
+=import Browser
+=import Browser.Events
+=import Dict exposing (Dict)
++import Element
+=import Html exposing (Html)
+=import Json.Decode exposing (Decoder)
+=import List.Extra as List
+@@ -51,7 +52,7 @@ type alias Rules =
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( { time = 50000
++    ( { time = 0
+=      , rules =
+=            Dict.fromList
+=                [ ( "brown"
+@@ -122,17 +123,20 @@ view model =
+=                        )
+=                    ]
+=    in
+-    Html.div []
+-        [ Html.h1 [] [ Html.text <| String.fromFloat model.time ]
+-        , svg
+-            [ viewBox "-1000 -1000 2000 2000"
+-            , height "800px"
+-            , width "800px"
+-            ]
+-            [ defs [] gradients
+-            , tree
+-            ]
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
+=        ]
++        (Element.html <|
++            svg
++                [ viewBox "-1000 -1000 2000 2000"
++                , height "100%"
++                , width "100%"
++                ]
++                [ defs [] gradients
++                , tree
++                ]
++        )
+=
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+```
\ No newline at end of file
new file mode 100644
index 0000000..fb2fd54
--- /dev/null
+++ b/content/devlog/2018-11-20-elm-tree-workshop.md
@@ -0,0 +1,577 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Write down steps to reproduce the tree, implement or improve some examples
+
+
+
+``` diff
+index c58cd80..47685ff 100644
+--- a/index.md
++++ b/index.md
+@@ -4,6 +4,29 @@ presentation:
+=  theme: solarized.css
+=---
+=
++Steps to reproduce the tree:
++
++Make a dot
++
++  Centered (Elm UI, viewBox, cartesian coordinates)
++
++Make a line
++
++  Play with transformations (union types)
++
++Gradients
++
++Multiple lines
++
++  Rosettes (different kinds)
++
++Groups and transformations
++
++  Spiral (recursion)
++
++Tree
++
++
+=<!-- slide -->
+=
+=## Before the course begins
+```
+
+``` diff
+index 0382141..cbd5f2e 100644
+--- a/src/CenteredDot.elm
++++ b/src/CenteredDot.elm
+@@ -1,4 +1,4 @@
+-module Simplest exposing (main)
++module CenteredDot exposing (main)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+new file mode 100644
+index 0000000..81e07be
+--- /dev/null
++++ b/src/Gradient.elm
+@@ -0,0 +1,45 @@
++module Gradient exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                [ Svg.defs []
++                    [ Svg.linearGradient
++                        [ Svg.Attributes.id "blue-pink-gradient"
++                        , Svg.Attributes.x1 "0"
++                        , Svg.Attributes.y1 "0"
++                        , Svg.Attributes.x2 "1"
++                        , Svg.Attributes.y2 "0"
++                        , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                        ]
++                        [ Svg.stop
++                            [ Svg.Attributes.stopColor "blue"
++                            , Svg.Attributes.offset "0"
++                            ]
++                            []
++                        , Svg.stop
++                            [ Svg.Attributes.stopColor "pink"
++                            , Svg.Attributes.offset "1"
++                            ]
++                            []
++                        ]
++                    ]
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(30) scale(100, 1)"
++                    ]
++                    []
++                ]
++            )
++        )
+```
+
+``` diff
+new file mode 100644
+index 0000000..0eee932
+--- /dev/null
++++ b/src/Line.elm
+@@ -0,0 +1,24 @@
++module Line exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                [ Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "black"
++                    , Svg.Attributes.transform "rotate(30) scale(100, 1)"
++                    ]
++                    []
++                ]
++            )
++        )
+```
+
+``` diff
+new file mode 100644
+index 0000000..f39d91f
+--- /dev/null
++++ b/src/LineTypedTransformations.elm
+@@ -0,0 +1,71 @@
++module LineTypedTransformations exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                [ Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "black"
++                    , transform
++                        [ Rotate 30
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                ]
++            )
++        )
++
++
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++
++-- transform : List Transformation -> Svg.Attribute msg
++
++
++transform transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
++        |> Svg.Attributes.transform
+```
+
+``` diff
+new file mode 100644
+index 0000000..d9d2060
+--- /dev/null
++++ b/src/Rosette.elm
+@@ -0,0 +1,69 @@
++module Rosette exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                [ Svg.defs []
++                    [ Svg.linearGradient
++                        [ Svg.Attributes.id "blue-pink-gradient"
++                        , Svg.Attributes.x1 "0"
++                        , Svg.Attributes.y1 "0"
++                        , Svg.Attributes.x2 "1"
++                        , Svg.Attributes.y2 "0"
++                        , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                        ]
++                        [ Svg.stop
++                            [ Svg.Attributes.stopColor "blue"
++                            , Svg.Attributes.offset "0"
++                            ]
++                            []
++                        , Svg.stop
++                            [ Svg.Attributes.stopColor "pink"
++                            , Svg.Attributes.offset "1"
++                            ]
++                            []
++                        ]
++                    ]
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(0) scale(100, 1)"
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(72) scale(100, 1)"
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(144) scale(100, 1)"
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(216) scale(100, 1)"
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , Svg.Attributes.transform "rotate(288) scale(100, 1)"
++                    ]
++                    []
++                ]
++            )
++        )
+```
+
+``` diff
+new file mode 100644
+index 0000000..dcd20c1
+--- /dev/null
++++ b/src/RosetteTypedTransformations.elm
+@@ -0,0 +1,125 @@
++module RosetteTypedTransformations exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                [ Svg.defs []
++                    [ Svg.linearGradient
++                        [ Svg.Attributes.id "blue-pink-gradient"
++                        , Svg.Attributes.x1 "0"
++                        , Svg.Attributes.y1 "0"
++                        , Svg.Attributes.x2 "1"
++                        , Svg.Attributes.y2 "0"
++                        , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                        ]
++                        [ Svg.stop
++                            [ Svg.Attributes.stopColor "blue"
++                            , Svg.Attributes.offset "0"
++                            ]
++                            []
++                        , Svg.stop
++                            [ Svg.Attributes.stopColor "pink"
++                            , Svg.Attributes.offset "1"
++                            ]
++                            []
++                        ]
++                    ]
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , transform
++                        [ Rotate 0
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , transform
++                        [ Rotate 72
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , transform
++                        [ Rotate 144
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , transform
++                        [ Rotate 216
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                , Svg.line
++                    [ Svg.Attributes.x2 "1"
++                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                    , transform
++                        [ Rotate 288
++                        , Scale 100 1
++                        ]
++                    ]
++                    []
++                ]
++            )
++        )
++
++
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++transform : List Transformation -> Svg.Attribute msg
++transform transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
++        |> Svg.Attributes.transform
+```
+
+``` diff
+index 8710e3c..9686d17 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -6,10 +6,12 @@ import Svg.Attributes
+=
+=main =
+=    Svg.svg [ Svg.Attributes.style "background: pink" ]
+-        [ Svg.circle
+-            [ Svg.Attributes.r "10"
+-            , Svg.Attributes.cx "30"
+-            , Svg.Attributes.cy "30"
++        [ Svg.line
++            [ Svg.Attributes.x1 "0"
++            , Svg.Attributes.x2 "1"
++            , Svg.Attributes.y1 "0"
++            , Svg.Attributes.y2 "0"
++            , Svg.Attributes.stroke "black"
+=            ]
+=            []
+=        ]
+```
+
+``` diff
+new file mode 100644
+index 0000000..f2e2aa0
+--- /dev/null
++++ b/src/Spiral.elm
+@@ -0,0 +1,110 @@
++module Spiral exposing (main)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    let
++        defs =
++            Svg.defs []
++                [ Svg.linearGradient
++                    [ Svg.Attributes.id "blue-pink-gradient"
++                    , Svg.Attributes.x1 "0"
++                    , Svg.Attributes.y1 "0"
++                    , Svg.Attributes.x2 "1"
++                    , Svg.Attributes.y2 "0"
++                    , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                    ]
++                    [ Svg.stop
++                        [ Svg.Attributes.stopColor "blue"
++                        , Svg.Attributes.offset "0"
++                        ]
++                        []
++                    , Svg.stop
++                        [ Svg.Attributes.stopColor "pink"
++                        , Svg.Attributes.offset "1"
++                        ]
++                        []
++                    ]
++                ]
++    in
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (Element.html
++            (Svg.svg
++                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
++                (defs :: spiral 500)
++            )
++        )
++
++
++spiral : Int -> List (Svg.Svg msg)
++spiral age =
++    if age > 0 then
++        let
++            length =
++                500 / toFloat age
++        in
++        [ Svg.line
++            [ Svg.Attributes.x2 "1"
++            , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++            , transform [ Scale length 1 ]
++            ]
++            []
++        , Svg.g
++            [ transform
++                [ Identity
++                , Translate length 0
++                , Rotate 15
++                ]
++            ]
++            (spiral (age - 1))
++        ]
++
++    else
++        []
++
++
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++transform : List Transformation -> Svg.Attribute msg
++transform transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
++        |> Svg.Attributes.transform
+```
\ No newline at end of file
new file mode 100644
index 0000000..f4c5e55
--- /dev/null
+++ b/content/devlog/2018-11-21-elm-tree-workshop.md
@@ -0,0 +1,518 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Make transformations example interactive
+
+Separate axes from CartesianPlane.graph.
+
+``` diff
+index 6e4ad58..6029a39 100644
+--- a/src/CartesianPlane.elm
++++ b/src/CartesianPlane.elm
+@@ -1,25 +1,19 @@
+-module CartesianPlane exposing (graph)
++module CartesianPlane exposing (axes, graph)
+=
++import Direction2d
++import Geometry.Svg
+=import Html exposing (Html)
++import LineSegment2d
++import Point2d exposing (Point2d)
+=import Svg exposing (..)
+=import Svg.Attributes exposing (..)
++import Triangle2d
++import Vector2d
+=
+=
+=graph : Float -> List (Svg msg) -> Html msg
+=graph size shapes =
+=    let
+-        top =
+-            0 - size / 2
+-
+-        left =
+-            0 - size / 2
+-
+-        bottom =
+-            size / 2
+-
+-        right =
+-            size / 2
+-
+=        hairline =
+=            size / 1000
+=
+@@ -27,29 +21,14 @@ graph size shapes =
+=            size / 200
+=
+=        background =
+-            g []
+-                [ line
+-                    [ x1 (String.fromFloat left)
+-                    , y1 "0"
+-                    , x2 (String.fromFloat right)
+-                    , y2 "0"
+-                    , stroke "black"
+-                    , strokeWidth (String.fromFloat hairline)
+-                    ]
+-                    []
+-                , line
+-                    [ x1 "0"
+-                    , x2 "0"
+-                    , y1 (String.fromFloat top)
+-                    , y2 (String.fromFloat bottom)
+-                    , stroke "black"
+-                    , strokeWidth (String.fromFloat hairline)
+-                    ]
+-                    []
++            axes
++                [ stroke "gray"
++                , fill "gray"
+=                ]
++                size
+=    in
+=    svg
+-        [ [ left, top, size, size ]
++        [ [ negate size / 2, negate size / 2, size, size ]
+=            |> List.map String.fromFloat
+=            |> String.join " "
+=            |> viewBox
+@@ -59,3 +38,85 @@ graph size shapes =
+=        , Svg.Attributes.style "width: 100%, height: 100%"
+=        ]
+=        (background :: shapes)
++
++
++axes attributes size =
++    let
++        max =
++            size / 2
++
++        min =
++            negate max
++
++        xAxis =
++            { start = Point2d.fromCoordinates ( min, 0 )
++            , end = Point2d.fromCoordinates ( max, 0 )
++            }
++
++        yAxis =
++            { start = Point2d.fromCoordinates ( 0, min )
++            , end = Point2d.fromCoordinates ( 0, max )
++            }
++    in
++    g []
++        [ arrow
++            attributes
++            xAxis.start
++            xAxis.end
++        , arrow
++            attributes
++            yAxis.start
++            yAxis.end
++        , label [ fontSize "8", color "gray" ] (xAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -135)) 10) "x"
++        , label [ fontSize "8", color "gray" ] (yAxis.end |> Point2d.translateIn (Direction2d.fromAngle (degrees -45)) 10) "y"
++        , label [ fontSize "8", color "gray" ] (Point2d.origin |> Point2d.translateIn (Direction2d.fromAngle (degrees 135)) 10) "O"
++        ]
++
++
++arrow : List (Svg.Attribute msg) -> Point2d -> Point2d -> Svg msg
++arrow attributes start end =
++    let
++        origin =
++            Point2d.fromCoordinates ( 0, 0 )
++
++        direction =
++            Direction2d.from start end
++
++        triangle =
++            Triangle2d.fromVertices
++                ( Point2d.fromCoordinates ( 0, 0 )
++                , Point2d.fromCoordinates ( -4, -2 )
++                , Point2d.fromCoordinates ( -4, 2 )
++                )
++
++        vector =
++            Vector2d.from origin end
++    in
++    case direction of
++        Nothing ->
++            g [] []
++
++        Just dir ->
++            g []
++                [ triangle
++                    |> Triangle2d.rotateAround origin (Direction2d.toAngle dir)
++                    |> Triangle2d.translateBy vector
++                    |> Geometry.Svg.triangle2d ([ strokeWidth "0" ] ++ attributes)
++                , end
++                    |> Point2d.translateIn dir -4
++                    |> LineSegment2d.from start
++                    |> Geometry.Svg.lineSegment2d attributes
++                ]
++
++
++label : List (Svg.Attribute msg) -> Point2d -> String -> Svg msg
++label attributes center content =
++    text_
++        ([ x <| String.fromFloat (Point2d.xCoordinate center)
++         , y <| String.fromFloat (Point2d.yCoordinate center)
++         , dominantBaseline "central"
++         , textAnchor "middle"
++         ]
++            ++ attributes
++        )
++        [ text content ]
+```
+
+``` diff
+index 80a3718..744c43a 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -1,13 +1,20 @@
+=module Transformations exposing (main)
+=
++import Array exposing (Array)
+=import Browser
+=import Browser.Events
+=import CartesianPlane
+=import Dict exposing (Dict)
+-import Element
++import Element exposing (Element)
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Geometry.Svg
+=import Html exposing (Html)
+=import Json.Decode exposing (Decoder)
++import LineSegment2d
+=import List.Extra as List
++import Point2d
+=import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+@@ -26,7 +33,7 @@ type alias Flags =
+=
+=
+=type alias Model =
+-    List Transformation
++    Array Transformation
+=
+=
+=type Transformation
+@@ -37,16 +44,14 @@ type Transformation
+=
+=
+=type Msg
+-    = Progress Float
+-    | Regress Float
++    = AddTransformation Transformation
++    | DeleteTransformation Int
++    | SetTransformation Int Transformation
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( [ Identity
+-      , Scale 2 2
+-      , Translate 20 0
+-      ]
++    ( Array.empty
+=    , Cmd.none
+=    )
+=
+@@ -54,13 +59,23 @@ init () =
+=view : Model -> Html Msg
+=view model =
+=    let
++        transformations =
++            Array.toList model
++
+=        wrapper element =
+-            Element.el
+-                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
+-                (Element.html element)
++            Element.column
++                [ Element.width (Element.maximum 600 Element.fill)
++                , Element.centerX
++                , Element.spacing 20
++                ]
++                [ Element.el
++                    [ Element.width Element.fill ]
++                    (Element.html element)
++                , transformationsUI transformations
++                ]
+=
+=        shape =
+-            g [ transform (apply model) ]
++            g [ transform (apply transformations) ]
+=                [ line
+=                    [ x1 "0"
+=                    , x2 "100"
+@@ -71,13 +86,20 @@ view model =
+=                    ]
+=                    []
+=                , circle [ cx "0", cy "0", r "2", fill "red" ] []
++                , grid
++                    [ stroke "pink"
++                    , fill "pink"
++                    , strokeWidth "0.3"
++                    ]
++                    10
++                    30
+=
+=                -- , rect [ x "", y "-2", width "1", height "4" ] []
+=                ]
+=    in
+=    shape
+=        |> List.singleton
+-        |> CartesianPlane.graph 600
++        |> CartesianPlane.graph 300
+=        |> wrapper
+=        |> Element.layout
+=            [ Element.height Element.fill
+@@ -85,16 +107,187 @@ view model =
+=            ]
+=
+=
++transformationsUI : List Transformation -> Element Msg
++transformationsUI transformations =
++    let
++        addButtons =
++            [ Input.button []
++                { onPress = Just (AddTransformation (Translate 0 0))
++                , label = Element.text "Translate"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Scale 1 1))
++                , label = Element.text "Scale"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Rotate 0))
++                , label = Element.text "Rotate"
++                }
++            ]
++
++        currentTrasformations =
++            transformations
++                |> List.indexedMap transformationUI
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Element.spacing 10
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            addButtons
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            currentTrasformations
++        ]
++
++
++transformationUI : Int -> Transformation -> Element Msg
++transformationUI index transformation =
++    let
++        sliderBackground =
++            Element.el
++                [ Element.width Element.fill
++                , Element.height (Element.px 2)
++                , Element.centerY
++                , Background.color <| Element.rgb 0.7 0.7 0.7
++                , Border.rounded 2
++                ]
++                Element.none
++
++        controls =
++            case transformation of
++                Identity ->
++                    [ Element.text <| Debug.toString transformation ]
++
++                Scale horizontal vertical ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \x ->
++                                SetTransformation index (Scale x vertical)
++                        , label = Input.labelLeft [] (Element.text "horizontal")
++                        , min = 0
++                        , max = 10
++                        , value = horizontal
++                        , thumb = Input.defaultThumb
++                        , step = Nothing
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \y ->
++                                SetTransformation index (Scale horizontal y)
++                        , label = Input.labelLeft [] (Element.text "vertical")
++                        , min = 0
++                        , max = 10
++                        , value = vertical
++                        , thumb = Input.defaultThumb
++                        , step = Nothing
++                        }
++                    ]
++
++                Translate x y ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate value y)
++                        , label = Input.labelLeft [] (Element.text "x")
++                        , min = -100
++                        , max = 100
++                        , value = x
++                        , thumb = Input.defaultThumb
++                        , step = Nothing
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate x value)
++                        , label = Input.labelLeft [] (Element.text "y")
++                        , min = -100
++                        , max = 100
++                        , value = y
++                        , thumb = Input.defaultThumb
++                        , step = Nothing
++                        }
++                    ]
++
++                Rotate angle ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Rotate value)
++                        , label = Input.labelLeft [] (Element.text "angle")
++                        , min = -360
++                        , max = 360
++                        , value = angle
++                        , thumb = Input.defaultThumb
++                        , step = Nothing
++                        }
++                    ]
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Background.color (Element.rgb 0.9 0.9 0.9)
++        , Element.padding 5
++        , Element.spacing 20
++        ]
++        [ Element.row [ Element.width Element.fill ]
++            [ transformation
++                |> List.singleton
++                |> apply
++                |> Element.text
++                |> Element.el [ Element.width Element.fill ]
++            , Input.button []
++                { onPress = Just (DeleteTransformation index)
++                , label = Element.text "X"
++                }
++            ]
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 20
++            ]
++            controls
++        ]
++
++
+=update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+=    case msg of
+-        Progress delta ->
+-            ( model
++        AddTransformation transformation ->
++            ( Array.push transformation model
+=            , Cmd.none
+=            )
+=
+-        Regress delta ->
+-            ( model
++        DeleteTransformation index ->
++            let
++                end =
++                    Array.length model
++
++                front =
++                    Array.slice 0 index model
++
++                back =
++                    Array.slice (index + 1) end model
++            in
++            ( Array.append front back
++            , Cmd.none
++            )
++
++        SetTransformation index transformation ->
++            ( Array.set index transformation model
+=            , Cmd.none
+=            )
+=
+@@ -135,3 +328,43 @@ apply transformations =
+=    transformations
+=        |> List.map toString
+=        |> String.join " "
++
++
++grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
++grid attributes unit size =
++    let
++        positiveValues =
++            size
++                / 2
++                |> floor
++                |> List.range 1
++                |> List.map toFloat
++                |> List.map ((*) unit)
++
++        negativeValues =
++            positiveValues
++                |> List.map negate
++
++        max =
++            unit * size / 2
++
++        min =
++            negate max
++    in
++    ((positiveValues ++ negativeValues)
++        |> List.map
++            (\value ->
++                [ ( Point2d.fromCoordinates ( value, min )
++                  , Point2d.fromCoordinates ( value, max )
++                  )
++                , ( Point2d.fromCoordinates ( min, value )
++                  , Point2d.fromCoordinates ( max, value )
++                  )
++                ]
++            )
++        |> List.concat
++        |> List.map LineSegment2d.fromEndpoints
++        |> List.map (Geometry.Svg.lineSegment2d attributes)
++    )
++        |> (::) (CartesianPlane.axes attributes (size * unit))
++        |> g []
+```
\ No newline at end of file
new file mode 100644
index 0000000..ab103f3
--- /dev/null
+++ b/content/devlog/2018-11-22-elm-tree-workshop.md
@@ -0,0 +1,1361 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Make sliders stepped in Transformations example
+
+Also other minor tweaks.
+
+``` diff
+index 744c43a..0d66cf5 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -51,7 +51,11 @@ type Msg
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init () =
+-    ( Array.empty
++    ( Array.fromList
++        [ Translate 0 0
++        , Rotate 0
++        , Scale 1 1
++        ]
+=    , Cmd.none
+=    )
+=
+@@ -69,9 +73,17 @@ view model =
+=                , Element.spacing 20
+=                ]
+=                [ Element.el
+-                    [ Element.width Element.fill ]
++                    [ Element.width Element.fill
++                    ]
+=                    (Element.html element)
+-                , transformationsUI transformations
++                , Element.row [ Element.width Element.fill ]
++                    [ Element.el
++                        [ Background.color (Element.rgb 1 0.8 0.8)
++                        , Element.padding 10
++                        , Element.width Element.fill
++                        ]
++                        (transformationsUI transformations)
++                    ]
+=                ]
+=
+=        shape =
+@@ -111,7 +123,8 @@ transformationsUI : List Transformation -> Element Msg
+=transformationsUI transformations =
+=    let
+=        addButtons =
+-            [ Input.button []
++            [ Element.text "Add transformation: "
++            , Input.button []
+=                { onPress = Just (AddTransformation (Translate 0 0))
+=                , label = Element.text "Translate"
+=                }
+@@ -176,7 +189,7 @@ transformationUI index transformation =
+=                        , max = 10
+=                        , value = horizontal
+=                        , thumb = Input.defaultThumb
+-                        , step = Nothing
++                        , step = Just 0.1
+=                        }
+=                    , Input.slider
+=                        [ Element.behindContent sliderBackground
+@@ -189,7 +202,7 @@ transformationUI index transformation =
+=                        , max = 10
+=                        , value = vertical
+=                        , thumb = Input.defaultThumb
+-                        , step = Nothing
++                        , step = Just 0.1
+=                        }
+=                    ]
+=
+@@ -205,7 +218,7 @@ transformationUI index transformation =
+=                        , max = 100
+=                        , value = x
+=                        , thumb = Input.defaultThumb
+-                        , step = Nothing
++                        , step = Just 1
+=                        }
+=                    , Input.slider
+=                        [ Element.behindContent sliderBackground
+@@ -218,7 +231,7 @@ transformationUI index transformation =
+=                        , max = 100
+=                        , value = y
+=                        , thumb = Input.defaultThumb
+-                        , step = Nothing
++                        , step = Just 1
+=                        }
+=                    ]
+=
+@@ -234,13 +247,14 @@ transformationUI index transformation =
+=                        , max = 360
+=                        , value = angle
+=                        , thumb = Input.defaultThumb
+-                        , step = Nothing
++                        , step = Just 1
+=                        }
+=                    ]
+=    in
+=    Element.column
+=        [ Element.width Element.fill
+-        , Background.color (Element.rgb 0.9 0.9 0.9)
++        , Border.color (Element.rgb 0.9 0.9 0.9)
++        , Border.width 3
+=        , Element.padding 5
+=        , Element.spacing 20
+=        ]
+```
+
+# Implement NestedTransformations example
+
+
+
+``` diff
+index d17943a..6cfda8a 100644
+--- a/elm.json
++++ b/elm.json
+@@ -11,11 +11,13 @@
+=            "elm/html": "1.0.0",
+=            "elm/json": "1.0.0",
+=            "elm/svg": "1.0.1",
++            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
+=            "elm-explorations/markdown": "1.0.0",
+=            "ianmackenzie/elm-geometry": "1.2.1",
+=            "ianmackenzie/elm-geometry-svg": "1.0.2",
+-            "mdgriffith/elm-ui": "1.1.0"
++            "mdgriffith/elm-ui": "1.1.0",
++            "turboMaCk/any-dict": "1.0.1"
+=        },
+=        "indirect": {
+=            "elm/time": "1.0.0",
+```
+
+``` diff
+new file mode 100644
+index 0000000..30da4f4
+--- /dev/null
++++ b/src/NestedTransformations.elm
+@@ -0,0 +1,437 @@
++module Transformations exposing (main)
++
++import Array exposing (Array)
++import Basics.Extra exposing (..)
++import Browser
++import Browser.Events
++import CartesianPlane
++import Dict.Any as Dict exposing (AnyDict)
++import Element exposing (Element)
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Geometry.Svg
++import Html exposing (Html)
++import Json.Decode exposing (Decoder)
++import LineSegment2d
++import List.Extra as List
++import Point2d
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.element
++        { init = init
++        , view = view
++        , update = update
++        , subscriptions = subscriptions
++        }
++
++
++type alias Flags =
++    ()
++
++
++type alias Model =
++    AnyDict String Group (Array Transformation)
++
++
++type Transformation
++    = Identity
++    | Scale Float Float
++    | Translate Float Float
++    | Rotate Float
++
++
++type Group
++    = Pink
++    | Green
++
++
++type Msg
++    = Msg Group GroupMsg
++
++
++type GroupMsg
++    = AddTransformation Transformation
++    | DeleteTransformation Int
++    | SetTransformation Int Transformation
++
++
++init : Flags -> ( Model, Cmd Msg )
++init () =
++    ( Dict.empty Debug.toString
++        |> Dict.insert Pink
++            (Array.fromList
++                [ Translate 0 0
++                , Rotate 0
++                , Scale 1 1
++                ]
++            )
++        |> Dict.insert Green
++            (Array.fromList
++                [ Translate 0 0
++                , Rotate 0
++                , Scale 1 1
++                ]
++            )
++    , Cmd.none
++    )
++
++
++view : Model -> Html Msg
++view model =
++    let
++        wrapper element =
++            Element.column
++                [ Element.width (Element.maximum 600 Element.fill)
++                , Element.centerX
++                , Element.spacing 20
++                ]
++                [ Element.el
++                    [ Element.width Element.fill
++                    ]
++                    (Element.html element)
++                , Element.row [ Element.width Element.fill ]
++                    (model
++                        |> Dict.toList
++                        |> List.map (uncurry controls)
++                    )
++                ]
++
++        controls : Group -> Array Transformation -> Element Msg
++        controls group transformations =
++            Element.el
++                [ Background.color (toColor group)
++                , Element.padding 10
++                , Element.width Element.fill
++                ]
++                (transformations
++                    |> Array.toList
++                    |> transformationsUI
++                    |> Element.map (Msg group)
++                )
++
++        shape =
++            Dict.foldr
++                nestTransformationsGroup
++                (g [] [])
++                model
++
++        nestTransformationsGroup : Group -> Array Transformation -> Svg Msg -> Svg Msg
++        nestTransformationsGroup group transformations item =
++            let
++                transformation =
++                    transformations |> Array.toList |> apply
++
++                color =
++                    group
++                        |> Debug.toString
++                        |> String.toLower
++            in
++            g [ transform transformation ]
++                [ line
++                    [ x1 "0"
++                    , x2 "100"
++                    , y1 "0"
++                    , y2 "0"
++                    , stroke color
++                    , strokeWidth "1"
++                    ]
++                    []
++                , circle [ cx "0", cy "0", r "2", fill color ] []
++                , item
++                ]
++    in
++    shape
++        |> List.singleton
++        |> CartesianPlane.graph 300
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++
++
++transformationsUI : List Transformation -> Element GroupMsg
++transformationsUI transformations =
++    let
++        addButtons =
++            [ Element.text "Add transformation: "
++            , Input.button []
++                { onPress = Just (AddTransformation (Translate 0 0))
++                , label = Element.text "Translate"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Scale 1 1))
++                , label = Element.text "Scale"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Rotate 0))
++                , label = Element.text "Rotate"
++                }
++            ]
++
++        currentTrasformations =
++            transformations
++                |> List.indexedMap transformationUI
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Element.spacing 10
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            addButtons
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            currentTrasformations
++        ]
++
++
++transformationUI : Int -> Transformation -> Element GroupMsg
++transformationUI index transformation =
++    let
++        sliderBackground =
++            Element.el
++                [ Element.width Element.fill
++                , Element.height (Element.px 2)
++                , Element.centerY
++                , Background.color <| Element.rgb 0.7 0.7 0.7
++                , Border.rounded 2
++                ]
++                Element.none
++
++        controls =
++            case transformation of
++                Identity ->
++                    [ Element.text <| Debug.toString transformation ]
++
++                Scale horizontal vertical ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \x ->
++                                SetTransformation index (Scale x vertical)
++                        , label = Input.labelLeft [] (Element.text "horizontal")
++                        , min = 0
++                        , max = 10
++                        , value = horizontal
++                        , thumb = Input.defaultThumb
++                        , step = Just 0.1
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \y ->
++                                SetTransformation index (Scale horizontal y)
++                        , label = Input.labelLeft [] (Element.text "vertical")
++                        , min = 0
++                        , max = 10
++                        , value = vertical
++                        , thumb = Input.defaultThumb
++                        , step = Just 0.1
++                        }
++                    ]
++
++                Translate x y ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate value y)
++                        , label = Input.labelLeft [] (Element.text "x")
++                        , min = -100
++                        , max = 100
++                        , value = x
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate x value)
++                        , label = Input.labelLeft [] (Element.text "y")
++                        , min = -100
++                        , max = 100
++                        , value = y
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    ]
++
++                Rotate angle ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Rotate value)
++                        , label = Input.labelLeft [] (Element.text "angle")
++                        , min = -360
++                        , max = 360
++                        , value = angle
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    ]
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Border.color (Element.rgb 0.9 0.9 0.9)
++        , Border.width 3
++        , Element.padding 5
++        , Element.spacing 20
++        ]
++        [ Element.row [ Element.width Element.fill ]
++            [ transformation
++                |> List.singleton
++                |> apply
++                |> Element.text
++                |> Element.el [ Element.width Element.fill ]
++            , Input.button []
++                { onPress = Just (DeleteTransformation index)
++                , label = Element.text "X"
++                }
++            ]
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 20
++            ]
++            controls
++        ]
++
++
++update : Msg -> Model -> ( Model, Cmd Msg )
++update (Msg group msg) model =
++    let
++        transformations =
++            model
++                |> Dict.get group
++                |> Maybe.withDefault Array.empty
++                |> (\current ->
++                        case msg of
++                            AddTransformation transformation ->
++                                Array.push transformation current
++
++                            DeleteTransformation index ->
++                                arrayDelete index current
++
++                            SetTransformation index transformation ->
++                                Array.set index transformation current
++                   )
++
++        arrayDelete index array =
++            let
++                end =
++                    Array.length array
++
++                front =
++                    Array.slice 0 index array
++
++                back =
++                    Array.slice (index + 1) end array
++            in
++            Array.append front back
++    in
++    ( Dict.insert group transformations model
++    , Cmd.none
++    )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
++
++
++apply : List Transformation -> String
++apply transformations =
++    let
++        toString : Transformation -> String
++        toString transformation =
++            case transformation of
++                Identity ->
++                    ""
++
++                Scale x y ->
++                    "scale("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Translate x y ->
++                    "translate("
++                        ++ String.fromFloat x
++                        ++ ", "
++                        ++ String.fromFloat y
++                        ++ ")"
++
++                Rotate angle ->
++                    "rotate("
++                        ++ String.fromFloat angle
++                        ++ ")"
++    in
++    transformations
++        |> List.map toString
++        |> String.join " "
++
++
++toColor : Group -> Element.Color
++toColor group =
++    case group of
++        Pink ->
++            Element.rgb 1 0.73 0.8
++
++        Green ->
++            Element.rgb 0.0 0.5 0.0
++
++
++grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
++grid attributes unit size =
++    let
++        positiveValues =
++            size
++                / 2
++                |> floor
++                |> List.range 1
++                |> List.map toFloat
++                |> List.map ((*) unit)
++
++        negativeValues =
++            positiveValues
++                |> List.map negate
++
++        max =
++            unit * size / 2
++
++        min =
++            negate max
++    in
++    ((positiveValues ++ negativeValues)
++        |> List.map
++            (\value ->
++                [ ( Point2d.fromCoordinates ( value, min )
++                  , Point2d.fromCoordinates ( value, max )
++                  )
++                , ( Point2d.fromCoordinates ( min, value )
++                  , Point2d.fromCoordinates ( max, value )
++                  )
++                ]
++            )
++        |> List.concat
++        |> List.map LineSegment2d.fromEndpoints
++        |> List.map (Geometry.Svg.lineSegment2d attributes)
++    )
++        |> (::) (CartesianPlane.axes attributes (size * unit))
++        |> g []
+```
+
+# Reimplement Main with Elm Markup, nest a simple counter program
+
+The idea is that Main will be our website and all the example programs 
+will be nested in it.
+
+``` diff
+index 6cfda8a..d305cfa 100644
+--- a/elm.json
++++ b/elm.json
+@@ -13,13 +13,16 @@
+=            "elm/svg": "1.0.1",
+=            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
++            "elm-community/result-extra": "2.2.1",
+=            "elm-explorations/markdown": "1.0.0",
+=            "ianmackenzie/elm-geometry": "1.2.1",
+=            "ianmackenzie/elm-geometry-svg": "1.0.2",
++            "mdgriffith/elm-markup": "1.0.0",
+=            "mdgriffith/elm-ui": "1.1.0",
+=            "turboMaCk/any-dict": "1.0.1"
+=        },
+=        "indirect": {
++            "elm/parser": "1.1.0",
+=            "elm/time": "1.0.0",
+=            "elm/url": "1.0.0",
+=            "elm/virtual-dom": "1.0.2",
+```
+
+``` diff
+new file mode 100644
+index 0000000..0f0689c
+--- /dev/null
++++ b/src/Counter.elm
+@@ -0,0 +1,71 @@
++module Counter exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , ui
++    , update
++    )
++
++import Browser
++import Element exposing (Element)
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++
++
++main =
++    Browser.sandbox
++        { init = init
++        , view = view
++        , update = update
++        }
++
++
++type alias Model =
++    Int
++
++
++type Msg
++    = Increment
++    | Decrement
++
++
++init =
++    0
++
++
++view : Model -> Html Msg
++view model =
++    model
++        |> ui
++        |> Element.layout []
++
++
++ui : Model -> Element Msg
++ui model =
++    Element.row
++        [ Element.padding 10
++        , Element.spacing 10
++        ]
++        [ Input.button []
++            { onPress = Just Decrement
++            , label = Element.text "-"
++            }
++        , model
++            |> String.fromInt
++            |> Element.text
++        , Input.button []
++            { onPress = Just Increment
++            , label = Element.text "+"
++            }
++        ]
++
++
++update msg model =
++    case msg of
++        Increment ->
++            model + 1
++
++        Decrement ->
++            model - 1
+```
+
+``` diff
+index f18bc73..d1066f3 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,578 +1,134 @@
+=module Main exposing (main)
+=
+-import Browser exposing (Document)
+-import Browser.Events
+-import CartesianCoordinates
+-import Dict
+-import Element
+-import Element.Font as Font
+-import Element.Keyed
+-import Html
+-import Html.Attributes
+-import Json.Decode as Decode exposing (Decoder)
+-import PolarCoordinates
+-import Presentation exposing (Slide, markdown)
++import Browser
++import Counter
++import Element exposing (Element)
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++import Mark
++import Mark.Custom
++import Result.Extra as Result
+=
+=
+=main =
+-    Browser.document
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+-    { currentSlide : Int
+-
+-    -- Nested programs
+-    , cartesianCoordinates : CartesianCoordinates.Model
+-    , polarCoordinates : PolarCoordinates.Model
+-    }
++    { counter : Counter.Model }
+=
+=
+=type Msg
+-    = Next
+-    | Previous
+-      -- Nested programs
+-    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+-    | PolarCoordinatesMsg PolarCoordinates.Msg
++    = CounterMsg Counter.Msg
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init flags =
+-    let
+-        ( cartesianCoordinatesModel, cartesianCoordinatesCmd ) =
+-            CartesianCoordinates.init ()
++init =
++    { counter = Counter.init
++    }
+=
+-        ( polarCoordinatesModel, polarCoordinatesCmd ) =
+-            PolarCoordinates.init ()
+-    in
+-    ( { currentSlide = 0
+-      , cartesianCoordinates = cartesianCoordinatesModel
+-      , polarCoordinates = polarCoordinatesModel
+-      }
+-    , Cmd.batch
+-        [ Cmd.map CartesianCoordinatesMsg cartesianCoordinatesCmd
+-        , Cmd.map PolarCoordinatesMsg polarCoordinatesCmd
+-        ]
+-    )
+-
+-
+-view : Model -> Document Msg
++
++view : Model -> Html Msg
+=view model =
+-    { title = "FP-Art!"
+-    , body =
+-        [ Element.layout
+-            [ Element.width Element.fill
+-            , Element.height Element.fill
+-            ]
+-          <|
+-            Element.column
+-                [ Element.centerX
+-                , Font.center
+-                , Element.height Element.fill
+-                , Element.width (Element.maximum 800 Element.fill)
+-                ]
+-                [ slides model
+-                    |> Dict.get model.currentSlide
+-                    |> Maybe.map
+-                        (Element.column
+-                            [ Element.width Element.fill
+-                            , Element.centerY
+-                            ]
+-                        )
+-                    |> Maybe.withDefault (Element.text "404: Slide not found")
+-                ]
+-        ]
+-    }
++    content
++        |> Mark.parseWith options
++        |> Result.mapError Debug.toString
++        |> Result.map (\fn -> fn model)
++        |> Result.extract Element.text
++        |> Element.layout []
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+=    case msg of
+-        Next ->
+-            ( { model
+-                | currentSlide =
+-                    min (Dict.size (slides model) - 1) (model.currentSlide + 1)
+-              }
+-            , Cmd.none
+-            )
+-
+-        Previous ->
+-            ( { model
+-                | currentSlide =
+-                    max 0 (model.currentSlide - 1)
+-              }
+-            , Cmd.none
+-            )
+-
+-        -- Nested programs
+-        CartesianCoordinatesMsg msg_ ->
+-            let
+-                ( model_, cmd_ ) =
+-                    CartesianCoordinates.update msg_ model.cartesianCoordinates
+-            in
+-            ( { model | cartesianCoordinates = model_ }
+-            , Cmd.map CartesianCoordinatesMsg cmd_
+-            )
+-
+-        PolarCoordinatesMsg msg_ ->
+-            let
+-                ( model_, cmd_ ) =
+-                    PolarCoordinates.update msg_ model.polarCoordinates
+-            in
+-            ( { model | polarCoordinates = model_ }
+-            , Cmd.map PolarCoordinatesMsg cmd_
+-            )
+-
+-
+-subscriptions model =
+-    let
+-        handleKeyPress : Decoder Msg
+-        handleKeyPress =
+-            Decode.field "key" Decode.string
+-                |> Decode.andThen
+-                    (\key ->
+-                        case Debug.log "Key" key of
+-                            "ArrowLeft" ->
+-                                Decode.succeed Previous
+-
+-                            "ArrowRight" ->
+-                                Decode.succeed Next
+-
+-                            "a" ->
+-                                Decode.succeed Previous
+-
+-                            "d" ->
+-                                Decode.succeed Next
+-
+-                            _ ->
+-                                Decode.fail "Unsupported key"
+-                    )
+-    in
+-    Browser.Events.onKeyPress handleKeyPress
+-
+-
+-slides model =
+-    Dict.fromList <|
+-        List.indexedMap Tuple.pair <|
+-            [ markdown """
+-                # Software Garden
+-                ## A functional programming workshop
+-                ### for non-programmers
+-              """
+-            ]
+-                :: [ markdown """
+-                        ## Setup
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        > This setup instructions are based on an assumption that you are using a Mac.
+-                        >
+-                        > If you are using Linux or BSD, then you probably know how to install stuff.
+-                        >
+-                        > If you are using anything else, then... well, good luck.
+-                        >
+-                        > The rest of the instructions should work with any platform.
+-
+-                     """ ]
+-                :: [ markdown """
+-
+-                        We will need:
+-
+-                        - a text editor (I use [Atom][])
+-                        - and the [Elm programming language][]
+-
+-                        [Atom]: https://atom.io/
+-                        [Elm programming language]: https://elm-lang.org/
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        ### Elm Installation
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        To install the Elm programming language, go to the [website][Elm] and follow the installation instructions.
+-
+-                        [Elm]: http://elm-lang.org/
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        Some of the tools we use with Elm require Node.js.
+-
+-                        Go to the [Node.js website][Node.js] and install the current version (the green button on the right).
+-
+-                        [Node.js]: https://nodejs.org/en/
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        We will need to use the terminal a little bit.
+-
+-                        # :fa-terminal:
+-
+-                        Don't be scared. It's easy :)
+-
+-                        <!-- slide data-background-image="images/mac-launchpad-terminal.png" data-background-size="cover" data-background-position="top center"-->
+-
+-                        > <p style="color: white">In Launchpad find a program called <code>terminal</code> and start it.</p>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        You should see a window like this
+-                     """
+-                   , Html.img
+-                        [ Html.Attributes.alt "Mac Terminal app window"
+-                        , Html.Attributes.src "../assets/mac-terminal-window.png"
+-                        ]
+-                        []
+-                        |> Element.html
+-                        |> Element.el
+-                            [ Element.height Element.fill
+-                            , Element.width Element.fill
+-                            ]
+-                   , markdown """
+-                        Here is some more markdown.
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        Now we are going to install few things.
+-
+-                        - Homebrew (to install other things)
+-                        - Elm programming language
+-                        - Atom editor
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        ### Install Homebrew
+-
+-                        Follow instructions on the [Homebrew website](https://brew.sh/) by typing the following in the terminal (you probably want to copy and paste it):
+-
+-                        ```sh
+-                        /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
+-                        ```
+-
+-                        <small>
+-                        Make sure that the command is exactly like the one above, including the `/` character at the beginning, the quotes and parentheses.
+-
+-                        It will ask you to confirm several actions and ask for your password. It may take few minutes to finish, so get your coffee
+-                        </small>
+-
+-                        :fa-coffee:
+-
+-                        Once it's done you should see a command prompt like that:
+-
+-                        ```
+-                        ~ your-name$
+-                        ```
+-
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        ### Install the Elm programming language
+-
+-                        <small>Once we have Homebrew installed, we can use it to install the language.</small>
+-
+-                        Type the following in the terminal.
+-
+-                        ```sh
+-                        brew install node elm
+-                        ```
+-
+-                        and check if it works by typing:
+-
+-                        ```
+-                        elm repl
+-                        ```
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        Note that the command line changes. You should see something like that
+-
+-                        ```
+-                        ---- Elm 0.19.0 ----------------------------------------------------------------
+-                        Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+-                        --------------------------------------------------------------------------------
+-                        >
+-                        ```
+-
+-                        It's the Elm REPL
+-
+-                        <small>Read - Evaluate - Print Loop</small>
+-
+-                        *[REPL]: Read - Evaluate - Print Loop.
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        Inside the REPL type
+-
+-                        ```
+-                        2 + 2
+-                        ```
+-
+-                        And expect to see
+-
+-                        ```
+-                        ---- Elm 0.19.0 ----------------------------------------------------------------
+-                        Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
+-                        --------------------------------------------------------------------------------
+-                        > 2 + 2
+-                        4 : number
+-                        >
+-                        ```
+-
+-                        Easy, huh?
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        We will learn more about REPL later. For now type `:exit` to close it.
++        CounterMsg m ->
++            { model | counter = Counter.update m model.counter }
+=
+-                        The command line should look like before again.
+=
+-                     """
+-                   ]
+-                :: [ markdown """
++type alias Styling =
++    Mark.Styling Msg
+=
+-                        ### Install Atom text editor
+=
+-                        Computer programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use [Atom] here.
++type alias Options =
++    Mark.Options Model Styling Msg
+=
+-                    """
+-                   ]
+-                :: [ markdown """
+=
+-                        Type following in the terminal:
+-
+-                        ```
+-                        brew cask install atom
+-                        ```
+-
+-                        And start it with:
+-
+-                        ```sh
+-                        atom
+-                        ```
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
+-
+-                        ```
+-                        apm install language-elm
+-                        ```
+-
+-                        <small>APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.</small>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        **We are all set!**
+-
+-                        :smile:
+-
+-                    """
+-                   ]
+-                :: [ markdown """
+-
+-
+-                        # First program!
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        As mentioned before, programs are represented as text (called *the source code*).
+-
+-                        The source code is stored in files
+-
+-                        :fa-file:
+-
+-                        and files are organized in directories
+-
+-                        :fa-folder:
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        So the first step is to create a directory for our new program. Let's call it `fpart`.
+-
+-                        In the terminal type
+-
+-                        ```sh
+-                        mkdir fpart/
+-                        ```
+-
+-                        and then
+-
+-                        ```
+-                        cd fpart/
+-                        ```
+-
+-                        <small>This creates a new directory and makes it the current one. Again, don't worry about the details.</small>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        To easily create a new program, we can type
+-
+-                        ```
+-                        elm init
+-                        ```
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        Then to create a file with source code, type
+-
+-                        ```
+-                        atom src/Main.elm
+-                        ```
+-
+-                        <small>This command should open a new Atom window with empty text file.</small>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        ### `main.elm`
+-
+-                        ```elm
+-                        module Main exposing (main)
+-
+-                        import Html
+-
+-
+-                        main =
+-                            Html.text "Hello, Tree!"
+-                        ```
+-
+-                        <small>Type the above in the editor and save the file</small>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        To see the program running type following in the terminal
+-
+-                        ```sh
+-                        elm reactor
+-                        ```
+-
+-                        <small>This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.</small>
+-
+-                     """
+-                   ]
+-                :: [ markdown """
+-
+-                        # Voila!
+-
+-                        <small>Open following address in the web browser</small>
+-
+-                        http://localhost:8000/src/Main.elm
++options : Options
++options =
++    let
++        default =
++            Mark.default
++
++        counterBlock : Mark.Custom.Block Model Styling Msg
++        counterBlock =
++            Mark.Custom.block "counter" counterView
++
++        counterView : Styling -> Model -> Element Msg
++        counterView style model =
++            model.counter
++                |> Counter.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map CounterMsg
++    in
++    { default
++        | blocks =
++            counterBlock :: Mark.defaultBlocks
++    }
+=
+-                        <small>Note that the same address was printed by Elm reactor</small>
+=
+-                     """
+-                   ]
+-                :: [ markdown """
++content =
++    """
++| header
++    To do:
+=
+-                        # Let's make a dot!
+-                        # :fa-circle:
++Steps to reproduce the tree:
+=
+-                     """
+-                   ]
+-                :: [ markdown """
++Make a dot
+=
+-                        We are going to use a technology called SVG
++  Centered (Elm UI, viewBox, cartesian coordinates)
+=
+-                        <small>Scalable Vector Graphics</small>
++Make a line
+=
++  Play with transformations (union types)
+=
+-                        *[SVG]: Scalable Vector Graphics
++Gradients
+=
+-                     """
+-                   ]
+-                :: [ markdown """
++Multiple lines
+=
+-                        Let's install an Elm package to help us work with SVG.
++  Rosettes (different kinds)
+=
+-                        Stop the Reactor running in terminal by pressing
++Groups and transformations
+=
+-                        `CTRL-C`
++  Spiral (recursion)
+=
+-                        and type the following
++Tree
+=
+-                        ```
+-                        elm install elm/svg
+-                        ```
+=
+-                        Then start the reactor again
++| header
++    Before the course begins
+=
+-                        ```
+-                        elm reactor
+-                        ```
++Setup the development environment
+=
+-                        <small>you can press up arrow :fa-arrow-up: on the keyboard to get to previous commands</small>
++| list
++    - Elm
++    - Node.js
+=
+-                     """
+-                   ]
+-                :: [ markdown """
+=
+-                        <iframe id="cartesian" data-src="./CartesianCoordinates.html" class="stretch">
+-                        </iframe>
++| counter
+=
+-                     """
+-                   ]
+-                :: [ markdown """
+-                        Move sliders to change the `x` and `y` coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down should it go.
+-                     """
+-                   , model.cartesianCoordinates
+-                        |> CartesianCoordinates.ui
+-                        |> Element.map CartesianCoordinatesMsg
+-                        |> Element.el
+-                            [ Element.centerX
+-                            , Element.width (Element.maximum 600 Element.fill)
+-                            ]
+-                   ]
+-                :: [ markdown """
+-                        Move sliders to change the `angle` and `length` properties of the line connecting the dot and origin. The x and y coordinates will be calculated like this:
++Last sentence
+=
+-                        ```
+-                        x = cos(angle) * length
++| counter
+=
+-                        y = sin(angle) * length
+-                        ```
+-                     """
+-                   , model.polarCoordinates
+-                        |> PolarCoordinates.ui
+-                        |> Element.map PolarCoordinatesMsg
+-                        |> Element.el
+-                            [ Element.centerX
+-                            , Element.width (Element.maximum 600 Element.fill)
+-                            ]
+-                   ]
+-                :: []
++"""
+```
\ No newline at end of file
new file mode 100644
index 0000000..4b65ce5
--- /dev/null
+++ b/content/devlog/2018-11-23-elm-tree-workshop.md
@@ -0,0 +1,304 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 2
+
+
+# Evolve the Main program into Browser.element
+
+
+
+``` diff
+index d1066f3..939218f 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -12,13 +12,18 @@ import Result.Extra as Result
+=
+=
+=main =
+-    Browser.sandbox
++    Browser.element
+=        { init = init
+=        , view = view
+=        , update = update
++        , subscriptions = subscriptions
+=        }
+=
+=
++type alias Flags =
++    ()
++
++
+=type alias Model =
+=    { counter : Counter.Model }
+=
+@@ -27,9 +32,12 @@ type Msg
+=    = CounterMsg Counter.Msg
+=
+=
+-init =
+-    { counter = Counter.init
+-    }
++init : Flags -> ( Model, Cmd Msg )
++init flags =
++    ( { counter = Counter.init
++      }
++    , Cmd.none
++    )
+=
+=
+=view : Model -> Html Msg
+@@ -42,10 +50,18 @@ view model =
+=        |> Element.layout []
+=
+=
++update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+=    case msg of
+=        CounterMsg m ->
+-            { model | counter = Counter.update m model.counter }
++            ( { model | counter = Counter.update m model.counter }
++            , Cmd.none
++            )
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
+=
+=
+=type alias Styling =
+```
+
+# Separate markup content to own file, fetch it with HTTP
+
+The newly released elm/http 2.0.0 requires updated versions of elm/core 
+and elm/json.
+
+``` diff
+index d305cfa..c770fd4 100644
+--- a/elm.json
++++ b/elm.json
+@@ -7,9 +7,10 @@
+=    "dependencies": {
+=        "direct": {
+=            "elm/browser": "1.0.0",
+-            "elm/core": "1.0.0",
++            "elm/core": "1.0.2",
+=            "elm/html": "1.0.0",
+-            "elm/json": "1.0.0",
++            "elm/http": "2.0.0",
++            "elm/json": "1.1.2",
+=            "elm/svg": "1.0.1",
+=            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
+@@ -22,6 +23,8 @@
+=            "turboMaCk/any-dict": "1.0.1"
+=        },
+=        "indirect": {
++            "elm/bytes": "1.0.7",
++            "elm/file": "1.0.1",
+=            "elm/parser": "1.1.0",
+=            "elm/time": "1.0.0",
+=            "elm/url": "1.0.0",
+@@ -35,4 +38,4 @@
+=        "direct": {},
+=        "indirect": {}
+=    }
+-}
+\ No newline at end of file
++}
+```
+
+``` diff
+new file mode 100644
+index 0000000..5dc347c
+--- /dev/null
++++ b/index.txt
+@@ -0,0 +1,39 @@
++| header
++    To do:
++
++Steps to reproduce the tree:
++
++Make a dot
++
++  Centered (Elm UI, viewBox, cartesian coordinates)
++
++Make a line
++
++  Play with transformations (union types)
++
++Gradients
++
++Multiple lines
++
++  Rosettes (different kinds)
++
++Groups and transformations
++
++  Spiral (recursion)
++
++Tree
++
++
++| header
++    Before the course begins
++
++Setup the development environment
++
++| list
++    - Elm
++    - Node.js
++
++
++| counter
++
++Last sentence
+```
+
+``` diff
+index 939218f..b8450ee 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -6,6 +6,7 @@ import Element exposing (Element)
+=import Element.Border as Border
+=import Element.Input as Input
+=import Html exposing (Html)
++import Http
+=import Mark
+=import Mark.Custom
+=import Result.Extra as Result
+@@ -25,34 +26,64 @@ type alias Flags =
+=
+=
+=type alias Model =
+-    { counter : Counter.Model }
++    { markup : Maybe String
++    , counter : Counter.Model
++    }
+=
+=
+=type Msg
+-    = CounterMsg Counter.Msg
++    = DocumentFetched (Result Http.Error String)
++    | CounterMsg Counter.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init flags =
+-    ( { counter = Counter.init
++    ( { markup = Nothing
++      , counter = Counter.init
+=      }
+-    , Cmd.none
++    , Http.get
++        { url = "/index.txt"
++        , expect = Http.expectString DocumentFetched
++        }
+=    )
+=
+=
+=view : Model -> Html Msg
+=view model =
+-    content
+-        |> Mark.parseWith options
+-        |> Result.mapError Debug.toString
+-        |> Result.map (\fn -> fn model)
+-        |> Result.extract Element.text
+-        |> Element.layout []
++    let
++        content =
++            case model.markup of
++                Nothing ->
++                    Element.el [ Element.centerX, Element.centerY ] <|
++                        Element.text "Loading content..."
++
++                Just markup ->
++                    markup
++                        |> Mark.parseWith options
++                        |> Result.mapError Debug.toString
++                        |> Result.map (\fn -> fn model)
++                        |> Result.extract Element.text
++    in
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        content
+=
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+-    case msg of
++    case Debug.log "update" msg of
++        DocumentFetched (Ok markup) ->
++            ( { model | markup = Just markup }
++            , Cmd.none
++            )
++
++        DocumentFetched (Err markup) ->
++            ( { model | markup = Nothing }
++            , Cmd.none
++            )
++
+=        CounterMsg m ->
+=            ( { model | counter = Counter.update m model.counter }
+=            , Cmd.none
+@@ -101,50 +132,3 @@ options =
+=        | blocks =
+=            counterBlock :: Mark.defaultBlocks
+=    }
+-
+-
+-content =
+-    """
+-| header
+-    To do:
+-
+-Steps to reproduce the tree:
+-
+-Make a dot
+-
+-  Centered (Elm UI, viewBox, cartesian coordinates)
+-
+-Make a line
+-
+-  Play with transformations (union types)
+-
+-Gradients
+-
+-Multiple lines
+-
+-  Rosettes (different kinds)
+-
+-Groups and transformations
+-
+-  Spiral (recursion)
+-
+-Tree
+-
+-
+-| header
+-    Before the course begins
+-
+-Setup the development environment
+-
+-| list
+-    - Elm
+-    - Node.js
+-
+-
+-| counter
+-
+-Last sentence
+-
+-| counter
+-
+-"""
+```
\ No newline at end of file
new file mode 100644
index 0000000..4a45f59
--- /dev/null
+++ b/content/devlog/2018-11-27-elm-tree-workshop.md
@@ -0,0 +1,1486 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 8
+
+
+# Embed Transformations example in Markup document
+
+
+
+``` diff
+index 5dc347c..1a452ee 100644
+--- a/index.txt
++++ b/index.txt
+@@ -36,4 +36,8 @@ Setup the development environment
+=
+=| counter
+=
+-Last sentence
++Embeded transformations example:
++
++| transformations
++
++Finito!
+```
+
+``` diff
+index b8450ee..b7a1bc1 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -3,6 +3,7 @@ module Main exposing (main)
+=import Browser
+=import Counter
+=import Element exposing (Element)
++import Element.Background as Background
+=import Element.Border as Border
+=import Element.Input as Input
+=import Html exposing (Html)
+@@ -10,6 +11,7 @@ import Http
+=import Mark
+=import Mark.Custom
+=import Result.Extra as Result
++import Transformations
+=
+=
+=main =
+@@ -28,18 +30,21 @@ type alias Flags =
+=type alias Model =
+=    { markup : Maybe String
+=    , counter : Counter.Model
++    , transformations : Transformations.Model
+=    }
+=
+=
+=type Msg
+=    = DocumentFetched (Result Http.Error String)
+=    | CounterMsg Counter.Msg
++    | TransformationsMsg Transformations.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+=init flags =
+=    ( { markup = Nothing
+=      , counter = Counter.init
++      , transformations = Transformations.init
+=      }
+=    , Http.get
+=        { url = "/index.txt"
+@@ -60,9 +65,27 @@ view model =
+=                Just markup ->
+=                    markup
+=                        |> Mark.parseWith options
+-                        |> Result.mapError Debug.toString
+=                        |> Result.map (\fn -> fn model)
+-                        |> Result.extract Element.text
++                        |> Result.extract problemsElement
++
++        problemsElement problems =
++            problems
++                |> List.map problemElement
++                |> Element.column
++                    [ Element.centerX
++                    , Element.centerY
++                    , Background.color (Element.rgb 0.7 0 0)
++                    , Element.padding 40
++                    , Element.spacing 20
++                    ]
++
++        problemElement { row, col, problem } =
++            Debug.toString problem
++                ++ " at "
++                ++ String.fromInt row
++                ++ ":"
++                ++ String.fromInt col
++                |> Element.text
+=    in
+=    Element.layout
+=        [ Element.width Element.fill
+@@ -89,6 +112,11 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        TransformationsMsg m ->
++            ( { model | transformations = Transformations.update m model.transformations }
++            , Cmd.none
++            )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+@@ -127,8 +155,29 @@ options =
+=                    , Border.width 2
+=                    ]
+=                |> Element.map CounterMsg
++
++        transformationsBlock : Mark.Custom.Block Model Styling Msg
++        transformationsBlock =
++            Mark.Custom.block "transformations" transformationsView
++
++        transformationsView : Styling -> Model -> Element Msg
++        transformationsView style model =
++            model.transformations
++                |> Transformations.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map TransformationsMsg
+=    in
+=    { default
+=        | blocks =
+-            counterBlock :: Mark.defaultBlocks
++            counterBlock
++                :: transformationsBlock
++                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index 0d66cf5..0c519ab 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -1,4 +1,11 @@
+-module Transformations exposing (main)
++module Transformations exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , ui
++    , update
++    )
+=
+=import Array exposing (Array)
+=import Browser
+@@ -19,19 +26,15 @@ import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+=
++main : Program () Model Msg
+=main =
+-    Browser.element
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+=    Array Transformation
+=
+@@ -49,19 +52,26 @@ type Msg
+=    | SetTransformation Int Transformation
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init () =
+-    ( Array.fromList
++init : Model
++init =
++    Array.fromList
+=        [ Translate 0 0
+=        , Rotate 0
+=        , Scale 1 1
+=        ]
+-    , Cmd.none
+-    )
+=
+=
+=view : Model -> Html Msg
+=view model =
++    Element.layout
++        [ Element.height Element.fill
++        , Element.width Element.fill
++        ]
++        (ui model)
++
++
++ui : Model -> Element Msg
++ui model =
+=    let
+=        transformations =
+=            Array.toList model
+@@ -113,10 +123,6 @@ view model =
+=        |> List.singleton
+=        |> CartesianPlane.graph 300
+=        |> wrapper
+-        |> Element.layout
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
+-            ]
+=
+=
+=transformationsUI : List Transformation -> Element Msg
+@@ -277,13 +283,11 @@ transformationUI index transformation =
+=        ]
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
++update : Msg -> Model -> Model
+=update msg model =
+=    case msg of
+=        AddTransformation transformation ->
+-            ( Array.push transformation model
+-            , Cmd.none
+-            )
++            Array.push transformation model
+=
+=        DeleteTransformation index ->
+=            let
+@@ -296,14 +300,10 @@ update msg model =
+=                back =
+=                    Array.slice (index + 1) end model
+=            in
+-            ( Array.append front back
+-            , Cmd.none
+-            )
++            Array.append front back
+=
+=        SetTransformation index transformation ->
+-            ( Array.set index transformation model
+-            , Cmd.none
+-            )
++            Array.set index transformation model
+=
+=
+=subscriptions : Model -> Sub Msg
+```
+
+# Embed cartesian coordinates example
+
+
+
+``` diff
+index 1a452ee..da39b4c 100644
+--- a/index.txt
++++ b/index.txt
+@@ -36,6 +36,10 @@ Setup the development environment
+=
+=| counter
+=
++Embedded cartesian coordinates example:
++
++| cartesian-coordinates
++
+=Embeded transformations example:
+=
+=| transformations
+```
+
+``` diff
+index 6be4d7b..d9b0f00 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/CartesianCoordinates.elm
+@@ -1,10 +1,8 @@
+=module CartesianCoordinates exposing
+-    ( Flags
+-    , Model
++    ( Model
+=    , Msg
+=    , init
+=    , main
+-    , subscriptions
+=    , ui
+=    , update
+=    , view
+@@ -12,7 +10,7 @@ module CartesianCoordinates exposing
+=
+=import Browser
+=import CartesianPlane exposing (graph)
+-import Element
++import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+=import Element.Input as Input
+@@ -21,19 +19,15 @@ import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+=
++main : Program () Model Msg
+=main =
+-    Browser.element
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+=    { x : Float
+=    , y : Float
+@@ -45,62 +39,47 @@ type Msg
+=    | SetY Float
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init () =
+-    ( { x = 0, y = 0 }
+-    , Cmd.none
+-    )
++init : Model
++init =
++    { x = 0, y = 0 }
+=
+=
+-view : Model -> Html.Html Msg
++view : Model -> Html Msg
+=view model =
+-    let
+-        wrapper element =
+-            Element.el
+-                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
+-                element
+-    in
+-    ui model
+-        |> wrapper
+-        |> Element.layout
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
+-            ]
++    Element.layout
++        [ Element.height Element.fill
++        , Element.width Element.fill
++        ]
++        (ui model)
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
++update : Msg -> Model -> Model
+=update msg model =
+=    case msg of
+=        SetX x ->
+-            ( { model | x = x }, Cmd.none )
++            { model | x = x }
+=
+=        SetY y ->
+-            ( { model | y = y }, Cmd.none )
+-
+-
+-subscriptions : Model -> Sub Msg
+-subscriptions model =
+-    Sub.none
++            { model | y = y }
+=
+=
++ui : Model -> Element Msg
+=ui model =
+=    Element.column
+-        [ Element.width Element.fill
++        [ Element.width (Element.maximum 600 Element.fill)
+=        , Element.centerX
+-        , Element.spacing 30
+-        , Element.padding 30
++        , Element.spacing 20
+=        ]
+=        [ Element.el
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
++            [ Element.width Element.fill
+=            ]
+=          <|
+=            Element.html <|
+-                graph
++                CartesianPlane.graph 300
+=                    [ circle
+=                        [ cx <| String.fromFloat model.x
+=                        , cy <| String.fromFloat model.y
+-                        , r "0.01"
++                        , r "2"
+=                        , fill "magenta"
+=                        ]
+=                        []
+@@ -115,7 +94,7 @@ ui model =
+=        , Input.slider
+=            [ Element.behindContent
+=                (Element.el
+-                    [ Element.width Element.fill
++                    [ Element.width (Element.maximum 600 Element.fill)
+=                    , Element.height (Element.px 2)
+=                    , Element.centerY
+=                    , Background.color <| Element.rgb 0.7 0.7 0.7
+@@ -128,8 +107,8 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("x value: " ++ String.fromFloat model.x)
+-            , min = -1
+-            , max = 1
++            , min = -150
++            , max = 150
+=            , value = model.x
+=            , thumb = Input.defaultThumb
+=            , step = Just 0.01
+@@ -150,8 +129,8 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("y value: " ++ String.fromFloat model.y)
+-            , min = -1
+-            , max = 1
++            , min = -150
++            , max = 150
+=            , value = model.y
+=            , thumb = Input.defaultThumb
+=            , step = Just 0.01
+```
+
+``` diff
+index b7a1bc1..9610763 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,6 +1,7 @@
+=module Main exposing (main)
+=
+=import Browser
++import CartesianCoordinates
+=import Counter
+=import Element exposing (Element)
+=import Element.Background as Background
+@@ -31,6 +32,7 @@ type alias Model =
+=    { markup : Maybe String
+=    , counter : Counter.Model
+=    , transformations : Transformations.Model
++    , cartesianCoordinates : CartesianCoordinates.Model
+=    }
+=
+=
+@@ -38,6 +40,7 @@ type Msg
+=    = DocumentFetched (Result Http.Error String)
+=    | CounterMsg Counter.Msg
+=    | TransformationsMsg Transformations.Msg
++    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+@@ -45,6 +48,7 @@ init flags =
+=    ( { markup = Nothing
+=      , counter = Counter.init
+=      , transformations = Transformations.init
++      , cartesianCoordinates = CartesianCoordinates.init
+=      }
+=    , Http.get
+=        { url = "/index.txt"
+@@ -117,6 +121,11 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        CartesianCoordinatesMsg m ->
++            ( { model | cartesianCoordinates = CartesianCoordinates.update m model.cartesianCoordinates }
++            , Cmd.none
++            )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+@@ -174,10 +183,30 @@ options =
+=                    , Border.width 2
+=                    ]
+=                |> Element.map TransformationsMsg
++
++        cartesianCoordinatesBlock : Mark.Custom.Block Model Styling Msg
++        cartesianCoordinatesBlock =
++            Mark.Custom.block "cartesian-coordinates" cartesianCoordinatesView
++
++        cartesianCoordinatesView : Styling -> Model -> Element Msg
++        cartesianCoordinatesView style model =
++            model.cartesianCoordinates
++                |> CartesianCoordinates.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map CartesianCoordinatesMsg
+=    in
+=    { default
+=        | blocks =
+=            counterBlock
+=                :: transformationsBlock
++                :: cartesianCoordinatesBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+# Embed centered dot example
+
+
+
+``` diff
+index da39b4c..c024bc9 100644
+--- a/index.txt
++++ b/index.txt
+@@ -36,6 +36,10 @@ Setup the development environment
+=
+=| counter
+=
++Embedded centered dot example:
++
++| centered-dot
++
+=Embedded cartesian coordinates example:
+=
+=| cartesian-coordinates
+```
+
+``` diff
+index cbd5f2e..fef1177 100644
+--- a/src/CenteredDot.elm
++++ b/src/CenteredDot.elm
+@@ -1,4 +1,4 @@
+-module CenteredDot exposing (main)
++module CenteredDot exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -10,9 +10,14 @@ main =
+=        [ Element.width Element.fill
+=        , Element.height Element.fill
+=        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.viewBox "-1000 -1000 2000 2000" ]
+-                [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+-            )
++        ui
++
++
++ui =
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.viewBox "-100 -100 200 200"
++            , Svg.Attributes.height "200"
++            ]
++            [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+=        )
+```
+
+``` diff
+index 9610763..7327b7d 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -2,6 +2,7 @@ module Main exposing (main)
+=
+=import Browser
+=import CartesianCoordinates
++import CenteredDot
+=import Counter
+=import Element exposing (Element)
+=import Element.Background as Background
+@@ -165,6 +166,23 @@ options =
+=                    ]
+=                |> Element.map CounterMsg
+=
++        centeredDotBlock : Mark.Custom.Block Model Styling Msg
++        centeredDotBlock =
++            Mark.Custom.block "centered-dot" centeredDotView
++
++        centeredDotView : Styling -> Model -> Element Msg
++        centeredDotView style model =
++            CenteredDot.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
+=        transformationsBlock : Mark.Custom.Block Model Styling Msg
+=        transformationsBlock =
+=            Mark.Custom.block "transformations" transformationsView
+@@ -206,6 +224,7 @@ options =
+=    { default
+=        | blocks =
+=            counterBlock
++                :: centeredDotBlock
+=                :: transformationsBlock
+=                :: cartesianCoordinatesBlock
+=                :: Mark.defaultBlocks
+```
+
+# Embed fill the screen example
+
+
+
+``` diff
+index c024bc9..8d8b39c 100644
+--- a/index.txt
++++ b/index.txt
+@@ -36,6 +36,10 @@ Setup the development environment
+=
+=| counter
+=
++Embedded fill the screen example:
++
++| fill-the-screen
++
+=Embedded centered dot example:
+=
+=| centered-dot
+```
+
+``` diff
+index 753928b..d7cfc83 100644
+--- a/src/FillTheScreen.elm
++++ b/src/FillTheScreen.elm
+@@ -1,4 +1,4 @@
+-module FillTheScreen exposing (main)
++module FillTheScreen exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -10,15 +10,18 @@ main =
+=        [ Element.width Element.fill
+=        , Element.height Element.fill
+=        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
+-                [ Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "30"
+-                    , Svg.Attributes.cy "30"
+-                    ]
+-                    []
++        ui
++
++
++ui =
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
++            [ Svg.circle
++                [ Svg.Attributes.r "10"
++                , Svg.Attributes.cx "30"
++                , Svg.Attributes.cy "30"
+=                ]
+-            )
++                []
++            ]
+=        )
+```
+
+``` diff
+index 7327b7d..e962fd1 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -8,6 +8,7 @@ import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+=import Element.Input as Input
++import FillTheScreen
+=import Html exposing (Html)
+=import Http
+=import Mark
+@@ -166,6 +167,24 @@ options =
+=                    ]
+=                |> Element.map CounterMsg
+=
++        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
++        fillTheScreenBlock =
++            Mark.Custom.block "fill-the-screen" fillTheScreenView
++
++        fillTheScreenView : Styling -> Model -> Element Msg
++        fillTheScreenView style model =
++            FillTheScreen.ui
++                |> Element.el
++                    [ Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
+=        centeredDotBlock : Mark.Custom.Block Model Styling Msg
+=        centeredDotBlock =
+=            Mark.Custom.block "centered-dot" centeredDotView
+@@ -224,6 +243,7 @@ options =
+=    { default
+=        | blocks =
+=            counterBlock
++                :: fillTheScreenBlock
+=                :: centeredDotBlock
+=                :: transformationsBlock
+=                :: cartesianCoordinatesBlock
+```
+
+# Embed gradient example
+
+
+
+``` diff
+index 8d8b39c..b6243e7 100644
+--- a/index.txt
++++ b/index.txt
+@@ -44,6 +44,10 @@ Embedded centered dot example:
+=
+=| centered-dot
+=
++Embedded gradient example:
++
++| gradient
++
+=Embedded cartesian coordinates example:
+=
+=| cartesian-coordinates
+```
+
+``` diff
+index 81e07be..4aa189c 100644
+--- a/src/Gradient.elm
++++ b/src/Gradient.elm
+@@ -1,4 +1,4 @@
+-module Gradient exposing (main)
++module Gradient exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -10,36 +10,41 @@ main =
+=        [ Element.width Element.fill
+=        , Element.height Element.fill
+=        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
+-                [ Svg.defs []
+-                    [ Svg.linearGradient
+-                        [ Svg.Attributes.id "blue-pink-gradient"
+-                        , Svg.Attributes.x1 "0"
+-                        , Svg.Attributes.y1 "0"
+-                        , Svg.Attributes.x2 "1"
+-                        , Svg.Attributes.y2 "0"
+-                        , Svg.Attributes.gradientUnits "userSpaceOnUse"
++        ui
++
++
++ui =
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.height "200"
++            , Svg.Attributes.viewBox "-10 -25 100 100"
++            ]
++            [ Svg.defs []
++                [ Svg.linearGradient
++                    [ Svg.Attributes.id "blue-pink-gradient"
++                    , Svg.Attributes.x1 "0"
++                    , Svg.Attributes.y1 "0"
++                    , Svg.Attributes.x2 "1"
++                    , Svg.Attributes.y2 "0"
++                    , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                    ]
++                    [ Svg.stop
++                        [ Svg.Attributes.stopColor "blue"
++                        , Svg.Attributes.offset "0"
+=                        ]
+-                        [ Svg.stop
+-                            [ Svg.Attributes.stopColor "blue"
+-                            , Svg.Attributes.offset "0"
+-                            ]
+-                            []
+-                        , Svg.stop
+-                            [ Svg.Attributes.stopColor "pink"
+-                            , Svg.Attributes.offset "1"
+-                            ]
+-                            []
++                        []
++                    , Svg.stop
++                        [ Svg.Attributes.stopColor "pink"
++                        , Svg.Attributes.offset "1"
+=                        ]
++                        []
+=                    ]
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , Svg.Attributes.transform "rotate(30) scale(100, 1)"
+-                    ]
+-                    []
+=                ]
+-            )
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , Svg.Attributes.transform "rotate(30) scale(100, 1)"
++                ]
++                []
++            ]
+=        )
+```
+
+``` diff
+index e962fd1..7799c4b 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -9,6 +9,7 @@ import Element.Background as Background
+=import Element.Border as Border
+=import Element.Input as Input
+=import FillTheScreen
++import Gradient
+=import Html exposing (Html)
+=import Http
+=import Mark
+@@ -202,6 +203,23 @@ options =
+=                    , Border.width 2
+=                    ]
+=
++        gradientBlock : Mark.Custom.Block Model Styling Msg
++        gradientBlock =
++            Mark.Custom.block "gradient" gradientView
++
++        gradientView : Styling -> Model -> Element Msg
++        gradientView style model =
++            Gradient.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
+=        transformationsBlock : Mark.Custom.Block Model Styling Msg
+=        transformationsBlock =
+=            Mark.Custom.block "transformations" transformationsView
+@@ -244,6 +262,7 @@ options =
+=        | blocks =
+=            counterBlock
+=                :: fillTheScreenBlock
++                :: gradientBlock
+=                :: centeredDotBlock
+=                :: transformationsBlock
+=                :: cartesianCoordinatesBlock
+```
+
+# Embed line example
+
+
+
+``` diff
+index b6243e7..5481c9e 100644
+--- a/index.txt
++++ b/index.txt
+@@ -44,6 +44,10 @@ Embedded centered dot example:
+=
+=| centered-dot
+=
++Embedded line example:
++
++| line
++
+=Embedded gradient example:
+=
+=| gradient
+```
+
+``` diff
+index 0eee932..53ab314 100644
+--- a/src/Line.elm
++++ b/src/Line.elm
+@@ -1,4 +1,4 @@
+-module Line exposing (main)
++module Line exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -10,15 +10,20 @@ main =
+=        [ Element.width Element.fill
+=        , Element.height Element.fill
+=        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
+-                [ Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "black"
+-                    , Svg.Attributes.transform "rotate(30) scale(100, 1)"
+-                    ]
+-                    []
++        ui
++
++
++ui =
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.height "200"
++            , Svg.Attributes.viewBox "-10 -25 100 100"
++            ]
++            [ Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "black"
++                , Svg.Attributes.transform "rotate(30) scale(100, 1)"
+=                ]
+-            )
++                []
++            ]
+=        )
+```
+
+``` diff
+index 7799c4b..20f8888 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -12,6 +12,7 @@ import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
+=import Http
++import Line
+=import Mark
+=import Mark.Custom
+=import Result.Extra as Result
+@@ -203,6 +204,23 @@ options =
+=                    , Border.width 2
+=                    ]
+=
++        lineBlock : Mark.Custom.Block Model Styling Msg
++        lineBlock =
++            Mark.Custom.block "line" lineView
++
++        lineView : Styling -> Model -> Element Msg
++        lineView style model =
++            Line.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
+=        gradientBlock : Mark.Custom.Block Model Styling Msg
+=        gradientBlock =
+=            Mark.Custom.block "gradient" gradientView
+@@ -262,6 +280,7 @@ options =
+=        | blocks =
+=            counterBlock
+=                :: fillTheScreenBlock
++                :: lineBlock
+=                :: gradientBlock
+=                :: centeredDotBlock
+=                :: transformationsBlock
+```
+
+# Embed nested transformations program
+
+
+
+``` diff
+index 5481c9e..b76690a 100644
+--- a/index.txt
++++ b/index.txt
+@@ -60,4 +60,8 @@ Embeded transformations example:
+=
+=| transformations
+=
++Embeded nested-transformations example:
++
++| nested-transformations
++
+=Finito!
+```
+
+``` diff
+index 20f8888..72898e4 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -15,6 +15,7 @@ import Http
+=import Line
+=import Mark
+=import Mark.Custom
++import NestedTransformations
+=import Result.Extra as Result
+=import Transformations
+=
+@@ -36,6 +37,7 @@ type alias Model =
+=    { markup : Maybe String
+=    , counter : Counter.Model
+=    , transformations : Transformations.Model
++    , nestedTransformations : NestedTransformations.Model
+=    , cartesianCoordinates : CartesianCoordinates.Model
+=    }
+=
+@@ -44,6 +46,7 @@ type Msg
+=    = DocumentFetched (Result Http.Error String)
+=    | CounterMsg Counter.Msg
+=    | TransformationsMsg Transformations.Msg
++    | NestedTransformationsMsg NestedTransformations.Msg
+=    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+=
+=
+@@ -52,6 +55,7 @@ init flags =
+=    ( { markup = Nothing
+=      , counter = Counter.init
+=      , transformations = Transformations.init
++      , nestedTransformations = NestedTransformations.init
+=      , cartesianCoordinates = CartesianCoordinates.init
+=      }
+=    , Http.get
+@@ -125,6 +129,11 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        NestedTransformationsMsg m ->
++            ( { model | nestedTransformations = NestedTransformations.update m model.nestedTransformations }
++            , Cmd.none
++            )
++
+=        CartesianCoordinatesMsg m ->
+=            ( { model | cartesianCoordinates = CartesianCoordinates.update m model.cartesianCoordinates }
+=            , Cmd.none
+@@ -257,6 +266,25 @@ options =
+=                    ]
+=                |> Element.map TransformationsMsg
+=
++        nestedTransformationsBlock : Mark.Custom.Block Model Styling Msg
++        nestedTransformationsBlock =
++            Mark.Custom.block "nested-transformations" nestedTransformationsView
++
++        nestedTransformationsView : Styling -> Model -> Element Msg
++        nestedTransformationsView style model =
++            model.nestedTransformations
++                |> NestedTransformations.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map NestedTransformationsMsg
++
+=        cartesianCoordinatesBlock : Mark.Custom.Block Model Styling Msg
+=        cartesianCoordinatesBlock =
+=            Mark.Custom.block "cartesian-coordinates" cartesianCoordinatesView
+@@ -284,6 +312,7 @@ options =
+=                :: gradientBlock
+=                :: centeredDotBlock
+=                :: transformationsBlock
++                :: nestedTransformationsBlock
+=                :: cartesianCoordinatesBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index 30da4f4..fa27050 100644
+--- a/src/NestedTransformations.elm
++++ b/src/NestedTransformations.elm
+@@ -1,4 +1,11 @@
+-module Transformations exposing (main)
++module NestedTransformations exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , ui
++    , update
++    )
+=
+=import Array exposing (Array)
+=import Basics.Extra exposing (..)
+@@ -9,6 +16,7 @@ import Dict.Any as Dict exposing (AnyDict)
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
++import Element.Font as Font
+=import Element.Input as Input
+=import Geometry.Svg
+=import Html exposing (Html)
+@@ -21,18 +29,13 @@ import Svg.Attributes exposing (..)
+=
+=
+=main =
+-    Browser.element
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+=    AnyDict String Group (Array Transformation)
+=
+@@ -59,9 +62,9 @@ type GroupMsg
+=    | SetTransformation Int Transformation
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init () =
+-    ( Dict.empty Debug.toString
++init : Model
++init =
++    Dict.empty Debug.toString
+=        |> Dict.insert Pink
+=            (Array.fromList
+=                [ Translate 0 0
+@@ -76,12 +79,19 @@ init () =
+=                , Scale 1 1
+=                ]
+=            )
+-    , Cmd.none
+-    )
+=
+=
+=view : Model -> Html Msg
+=view model =
++    Element.layout
++        [ Element.height Element.fill
++        , Element.width Element.fill
++        ]
++        (ui model)
++
++
++ui : Model -> Element Msg
++ui model =
+=    let
+=        wrapper element =
+=            Element.column
+@@ -148,10 +158,6 @@ view model =
+=        |> List.singleton
+=        |> CartesianPlane.graph 300
+=        |> wrapper
+-        |> Element.layout
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
+-            ]
+=
+=
+=transformationsUI : List Transformation -> Element GroupMsg
+@@ -179,11 +185,12 @@ transformationsUI transformations =
+=    in
+=    Element.column
+=        [ Element.width Element.fill
+-        , Element.spacing 10
++        , Font.size 12
++        , Element.spacing 4
+=        ]
+=        [ Element.row
+=            [ Element.width Element.fill
+-            , Element.spacing 10
++            , Element.spacing 2
+=            ]
+=            addButtons
+=        , Element.column
+@@ -301,7 +308,7 @@ transformationUI index transformation =
+=                |> Element.el [ Element.width Element.fill ]
+=            , Input.button []
+=                { onPress = Just (DeleteTransformation index)
+-                , label = Element.text "X"
++                , label = Element.el [] (Element.text "X")
+=                }
+=            ]
+=        , Element.column
+@@ -312,7 +319,7 @@ transformationUI index transformation =
+=        ]
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
++update : Msg -> Model -> Model
+=update (Msg group msg) model =
+=    let
+=        transformations =
+@@ -344,14 +351,7 @@ update (Msg group msg) model =
+=            in
+=            Array.append front back
+=    in
+-    ( Dict.insert group transformations model
+-    , Cmd.none
+-    )
+-
+-
+-subscriptions : Model -> Sub Msg
+-subscriptions model =
+-    Sub.none
++    Dict.insert group transformations model
+=
+=
+=apply : List Transformation -> String
+```
+
+# Embed polar coordinates program
+
+
+
+``` diff
+index b76690a..5dd325d 100644
+--- a/index.txt
++++ b/index.txt
+@@ -56,6 +56,10 @@ Embedded cartesian coordinates example:
+=
+=| cartesian-coordinates
+=
++Embedded polar coordinates example:
++
++| polar-coordinates
++
+=Embeded transformations example:
+=
+=| transformations
+```
+
+``` diff
+index 72898e4..0d3eb5a 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -16,6 +16,7 @@ import Line
+=import Mark
+=import Mark.Custom
+=import NestedTransformations
++import PolarCoordinates
+=import Result.Extra as Result
+=import Transformations
+=
+@@ -39,6 +40,7 @@ type alias Model =
+=    , transformations : Transformations.Model
+=    , nestedTransformations : NestedTransformations.Model
+=    , cartesianCoordinates : CartesianCoordinates.Model
++    , polarCoordinates : PolarCoordinates.Model
+=    }
+=
+=
+@@ -48,6 +50,7 @@ type Msg
+=    | TransformationsMsg Transformations.Msg
+=    | NestedTransformationsMsg NestedTransformations.Msg
+=    | CartesianCoordinatesMsg CartesianCoordinates.Msg
++    | PolarCoordinatesMsg PolarCoordinates.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+@@ -57,6 +60,7 @@ init flags =
+=      , transformations = Transformations.init
+=      , nestedTransformations = NestedTransformations.init
+=      , cartesianCoordinates = CartesianCoordinates.init
++      , polarCoordinates = PolarCoordinates.init
+=      }
+=    , Http.get
+=        { url = "/index.txt"
+@@ -139,6 +143,11 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        PolarCoordinatesMsg m ->
++            ( { model | polarCoordinates = PolarCoordinates.update m model.polarCoordinates }
++            , Cmd.none
++            )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+@@ -303,6 +312,25 @@ options =
+=                    , Border.width 2
+=                    ]
+=                |> Element.map CartesianCoordinatesMsg
++
++        polarCoordinatesBlock : Mark.Custom.Block Model Styling Msg
++        polarCoordinatesBlock =
++            Mark.Custom.block "polar-coordinates" polarCoordinatesView
++
++        polarCoordinatesView : Styling -> Model -> Element Msg
++        polarCoordinatesView style model =
++            model.polarCoordinates
++                |> PolarCoordinates.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map PolarCoordinatesMsg
+=    in
+=    { default
+=        | blocks =
+@@ -314,5 +342,6 @@ options =
+=                :: transformationsBlock
+=                :: nestedTransformationsBlock
+=                :: cartesianCoordinatesBlock
++                :: polarCoordinatesBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index 7a0d79c..5da8133 100644
+--- a/src/PolarCoordinates.elm
++++ b/src/PolarCoordinates.elm
+@@ -1,10 +1,8 @@
+=module PolarCoordinates exposing
+-    ( Flags
+-    , Model
++    ( Model
+=    , Msg
+=    , init
+=    , main
+-    , subscriptions
+=    , ui
+=    , update
+=    , view
+@@ -22,18 +20,13 @@ import Svg.Attributes exposing (..)
+=
+=
+=main =
+-    Browser.element
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+=    { angle : Float
+=    , radius : Float
+@@ -45,27 +38,18 @@ type Msg
+=    | SetRadius Float
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init () =
+-    ( { angle = 0, radius = 0.5 }
+-    , Cmd.none
+-    )
++init : Model
++init =
++    { angle = 0, radius = 75 }
+=
+=
+-view : Model -> Html.Html Msg
++view : Model -> Html Msg
+=view model =
+-    let
+-        wrapper element =
+-            Element.el
+-                [ Element.width (Element.maximum 600 Element.fill), Element.centerX ]
+-                element
+-    in
+-    ui model
+-        |> wrapper
+-        |> Element.layout
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
+-            ]
++    Element.layout
++        [ Element.height Element.fill
++        , Element.width Element.fill
++        ]
++        (ui model)
+=
+=
+=ui : Model -> Element Msg
+@@ -86,11 +70,11 @@ ui model =
+=            ]
+=          <|
+=            Element.html <|
+-                CartesianPlane.graph
++                CartesianPlane.graph 300
+=                    [ circle
+=                        [ cx <| String.fromFloat point.x
+=                        , cy <| String.fromFloat point.y
+-                        , r "0.01"
++                        , r "2"
+=                        , fill "magenta"
+=                        ]
+=                        []
+@@ -100,16 +84,16 @@ ui model =
+=                        , y1 "0"
+=                        , y2 <| String.fromFloat point.y
+=                        , stroke "magenta"
+-                        , strokeWidth "0.001"
++                        , strokeWidth "0.5"
+=                        ]
+=                        []
+=                    , text_
+-                        [ x <| String.fromFloat (point.x + 0.02)
+-                        , y <| String.fromFloat (point.y + 0.01)
+-                        , Svg.Attributes.fontSize "0.03pt"
++                        [ x <| String.fromFloat (point.x + 5)
++                        , y <| String.fromFloat (point.y + 5)
++                        , Svg.Attributes.fontSize "10pt"
+=                        ]
+=                        [ text <|
+-                            Debug.toString ( point.x, point.y )
++                            Debug.toString ( round point.x, round point.y )
+=                        ]
+=                    ]
+=        , Input.slider
+@@ -151,7 +135,7 @@ ui model =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("radius value: " ++ String.fromFloat model.radius)
+=            , min = 0
+-            , max = 1
++            , max = 150
+=            , value = model.radius
+=            , thumb = Input.defaultThumb
+=            , step = Just 0.01
+@@ -159,19 +143,14 @@ ui model =
+=        ]
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
++update : Msg -> Model -> Model
+=update msg model =
+=    case msg of
+=        SetAngle angle ->
+-            ( { model | angle = angle }, Cmd.none )
++            { model | angle = angle }
+=
+=        SetRadius radius ->
+-            ( { model | radius = radius }, Cmd.none )
+-
+-
+-subscriptions : Model -> Sub Msg
+-subscriptions model =
+-    Sub.none
++            { model | radius = radius }
+=
+=
+=cartesian : { angle : Float, radius : Float } -> { x : Float, y : Float }
+```
\ No newline at end of file
new file mode 100644
index 0000000..55f29c6
--- /dev/null
+++ b/content/devlog/2018-11-28-elm-tree-workshop.md
@@ -0,0 +1,1204 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 8
+
+
+# Embed simplest, roestte, and spiral examples
+
+
+
+``` diff
+index 5dd325d..f789f82 100644
+--- a/index.txt
++++ b/index.txt
+@@ -36,6 +36,10 @@ Setup the development environment
+=
+=| counter
+=
++Embedded simplest example:
++
++| simplest
++
+=Embedded fill the screen example:
+=
+=| fill-the-screen
+@@ -64,8 +68,16 @@ Embeded transformations example:
+=
+=| transformations
+=
+-Embeded nested-transformations example:
++Embedded nested-transformations example:
+=
+=| nested-transformations
+=
++Embedded rosette example:
++
++| rosette
++
++Embedded spiral example:
++
++| spiral
++
+=Finito!
+```
+
+``` diff
+index 0d3eb5a..d450539 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -18,6 +18,9 @@ import Mark.Custom
+=import NestedTransformations
+=import PolarCoordinates
+=import Result.Extra as Result
++import RosetteTypedTransformations
++import Simplest
++import Spiral
+=import Transformations
+=
+=
+@@ -187,6 +190,24 @@ options =
+=                    ]
+=                |> Element.map CounterMsg
+=
++        simplestBlock : Mark.Custom.Block Model Styling Msg
++        simplestBlock =
++            Mark.Custom.block "simplest" simplestView
++
++        simplestView : Styling -> Model -> Element Msg
++        simplestView style model =
++            Simplest.ui
++                |> Element.el
++                    [ Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+=        fillTheScreenBlock =
+=            Mark.Custom.block "fill-the-screen" fillTheScreenView
+@@ -331,10 +352,45 @@ options =
+=                    , Border.width 2
+=                    ]
+=                |> Element.map PolarCoordinatesMsg
++
++        rosetteBlock : Mark.Custom.Block Model Styling Msg
++        rosetteBlock =
++            Mark.Custom.block "rosette" rosetteView
++
++        rosetteView : Styling -> Model -> Element Msg
++        rosetteView style model =
++            RosetteTypedTransformations.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++
++        spiralBlock : Mark.Custom.Block Model Styling Msg
++        spiralBlock =
++            Mark.Custom.block "spiral" spiralView
++
++        spiralView : Styling -> Model -> Element Msg
++        spiralView style model =
++            Spiral.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
+=    in
+=    { default
+=        | blocks =
+=            counterBlock
++                :: simplestBlock
+=                :: fillTheScreenBlock
+=                :: lineBlock
+=                :: gradientBlock
+@@ -343,5 +399,7 @@ options =
+=                :: nestedTransformationsBlock
+=                :: cartesianCoordinatesBlock
+=                :: polarCoordinatesBlock
++                :: rosetteBlock
++                :: spiralBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index dcd20c1..2ae161e 100644
+--- a/src/RosetteTypedTransformations.elm
++++ b/src/RosetteTypedTransformations.elm
+@@ -1,4 +1,4 @@
+-module RosetteTypedTransformations exposing (main)
++module RosetteTypedTransformations exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -10,77 +10,82 @@ main =
+=        [ Element.width Element.fill
+=        , Element.height Element.fill
+=        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
+-                [ Svg.defs []
+-                    [ Svg.linearGradient
+-                        [ Svg.Attributes.id "blue-pink-gradient"
+-                        , Svg.Attributes.x1 "0"
+-                        , Svg.Attributes.y1 "0"
+-                        , Svg.Attributes.x2 "1"
+-                        , Svg.Attributes.y2 "0"
+-                        , Svg.Attributes.gradientUnits "userSpaceOnUse"
++        ui
++
++
++ui =
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.height "400"
++            , Svg.Attributes.viewBox "-100 -100 200 200"
++            ]
++            [ Svg.defs []
++                [ Svg.linearGradient
++                    [ Svg.Attributes.id "blue-pink-gradient"
++                    , Svg.Attributes.x1 "0"
++                    , Svg.Attributes.y1 "0"
++                    , Svg.Attributes.x2 "1"
++                    , Svg.Attributes.y2 "0"
++                    , Svg.Attributes.gradientUnits "userSpaceOnUse"
++                    ]
++                    [ Svg.stop
++                        [ Svg.Attributes.stopColor "blue"
++                        , Svg.Attributes.offset "0"
+=                        ]
+-                        [ Svg.stop
+-                            [ Svg.Attributes.stopColor "blue"
+-                            , Svg.Attributes.offset "0"
+-                            ]
+-                            []
+-                        , Svg.stop
+-                            [ Svg.Attributes.stopColor "pink"
+-                            , Svg.Attributes.offset "1"
+-                            ]
+-                            []
++                        []
++                    , Svg.stop
++                        [ Svg.Attributes.stopColor "pink"
++                        , Svg.Attributes.offset "1"
+=                        ]
++                        []
+=                    ]
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , transform
+-                        [ Rotate 0
+-                        , Scale 100 1
+-                        ]
++                ]
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , transform
++                    [ Rotate 0
++                    , Scale 100 1
+=                    ]
+-                    []
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , transform
+-                        [ Rotate 72
+-                        , Scale 100 1
+-                        ]
++                ]
++                []
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , transform
++                    [ Rotate 72
++                    , Scale 100 1
+=                    ]
+-                    []
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , transform
+-                        [ Rotate 144
+-                        , Scale 100 1
+-                        ]
++                ]
++                []
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , transform
++                    [ Rotate 144
++                    , Scale 100 1
+=                    ]
+-                    []
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , transform
+-                        [ Rotate 216
+-                        , Scale 100 1
+-                        ]
++                ]
++                []
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , transform
++                    [ Rotate 216
++                    , Scale 100 1
+=                    ]
+-                    []
+-                , Svg.line
+-                    [ Svg.Attributes.x2 "1"
+-                    , Svg.Attributes.stroke "url(#blue-pink-gradient)"
+-                    , transform
+-                        [ Rotate 288
+-                        , Scale 100 1
+-                        ]
++                ]
++                []
++            , Svg.line
++                [ Svg.Attributes.x2 "1"
++                , Svg.Attributes.stroke "url(#blue-pink-gradient)"
++                , transform
++                    [ Rotate 288
++                    , Scale 100 1
+=                    ]
+-                    []
+=                ]
+-            )
++                []
++            ]
+=        )
+=
+=
+```
+
+``` diff
+index 9686d17..ae4d919 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -1,5 +1,6 @@
+-module Simplest exposing (main)
++module Simplest exposing (main, ui)
+=
++import Element
+=import Svg
+=import Svg.Attributes
+=
+@@ -8,10 +9,15 @@ main =
+=    Svg.svg [ Svg.Attributes.style "background: pink" ]
+=        [ Svg.line
+=            [ Svg.Attributes.x1 "0"
+-            , Svg.Attributes.x2 "1"
++            , Svg.Attributes.x2 "20"
+=            , Svg.Attributes.y1 "0"
+=            , Svg.Attributes.y2 "0"
+=            , Svg.Attributes.stroke "black"
++            , Svg.Attributes.strokeWidth <| String.fromInt <| 5
+=            ]
+=            []
+=        ]
++
++
++ui =
++    Element.html main
+```
+
+``` diff
+index f2e2aa0..09c888e 100644
+--- a/src/Spiral.elm
++++ b/src/Spiral.elm
+@@ -1,4 +1,4 @@
+-module Spiral exposing (main)
++module Spiral exposing (main, ui)
+=
+=import Element
+=import Svg
+@@ -6,6 +6,14 @@ import Svg.Attributes
+=
+=
+=main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        ui
++
++
++ui =
+=    let
+=        defs =
+=            Svg.defs []
+@@ -30,15 +38,12 @@ main =
+=                    ]
+=                ]
+=    in
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-        (Element.html
+-            (Svg.svg
+-                [ Svg.Attributes.viewBox "-100 -100 200 200" ]
+-                (defs :: spiral 500)
+-            )
++    Element.html
++        (Svg.svg
++            [ Svg.Attributes.height "400"
++            , Svg.Attributes.viewBox "-100 -100 200 200"
++            ]
++            (defs :: spiral 500)
+=        )
+=
+=
+```
+
+# (Re-) implement title, note and emphasize blocks  for markup
+
+Title has subtitles (lines following the first one). Note and emphasize 
+are visually separated from the normal content.
+
+``` diff
+index 1a452ee..67d6710 100644
+--- a/index.txt
++++ b/index.txt
+@@ -1,3 +1,50 @@
++| title
++    Software Garden
++
++    ⚘
++
++    A functional programming workshop for non-programmers
++
++
++
++| header
++    Before the course begins
++
++| header
++    Setup
++
++| note
++    This setup instructions are based on an assumption that you are using a Mac.
++
++    If you are using Linux or BSD, then you probably know how to install stuff.
++
++    If you are using anything else, then... well, good luck.
++
++    The rest of the instructions should work with any platform.
++
++
++We will need
++
++| list
++    - a text editor (I use Atom)
++    - and the Elm programming language
++
++
++| header
++    Installing Elm
++
++To install the Elm programming language, go to it's website:
++
++| emphasize
++    [elm-lang.org](http://elm-lang.org/)
++
++
++and follow the installation instructions.
++
++
++
++
++
+=| header
+=    To do:
+=
+@@ -22,22 +69,3 @@ Groups and transformations
+=  Spiral (recursion)
+=
+=Tree
+-
+-
+-| header
+-    Before the course begins
+-
+-Setup the development environment
+-
+-| list
+-    - Elm
+-    - Node.js
+-
+-
+-| counter
+-
+-Embeded transformations example:
+-
+-| transformations
+-
+-Finito!
+```
+
+``` diff
+index b7a1bc1..41b9ce4 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -5,6 +5,7 @@ import Counter
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
++import Element.Font as Font
+=import Element.Input as Input
+=import Html exposing (Html)
+=import Http
+@@ -74,7 +75,7 @@ view model =
+=                |> Element.column
+=                    [ Element.centerX
+=                    , Element.centerY
+-                    , Background.color (Element.rgb 0.7 0 0)
++                    , Background.color colors.maroon
+=                    , Element.padding 40
+=                    , Element.spacing 20
+=                    ]
+@@ -131,12 +132,80 @@ type alias Options =
+=    Mark.Options Model Styling Msg
+=
+=
++colors =
++    { maroon = Element.rgb 0.7 0 0
++    , gray = Element.rgb 0.8 0.8 0.8
++    , pink = Element.rgb 1 0.6 0.6
++    }
++
++
+=options : Options
+=options =
+=    let
+=        default =
+=            Mark.default
+=
++        emphasizeBlock : Mark.Custom.Block Model Styling Msg
++        emphasizeBlock =
++            Mark.Custom.section "emphasize" emphasizeView
++
++        emphasizeView : List (Element Msg) -> Styling -> Model -> Element Msg
++        emphasizeView elements style model =
++            elements
++                |> Element.column
++                    [ Element.spacing 30
++                    , Font.bold
++                    , Font.size 30
++                    , Font.center
++                    , Element.paddingXY 0 40
++                    ]
++
++        titleBlock : Mark.Custom.Block Model Styling Msg
++        titleBlock =
++            Mark.Custom.section "title" titleView
++
++        titleView : List (Element Msg) -> Styling -> Model -> Element Msg
++        titleView elements style model =
++            case elements of
++                title :: subtitles ->
++                    let
++                        titleElement =
++                            Element.el
++                                [ Font.center
++                                , Font.size 60
++                                , Font.extraBold
++                                , Element.width Element.fill
++                                ]
++                                title
++                    in
++                    Element.column
++                        [ Element.spacing 30
++                        , Font.bold
++                        , Font.size 30
++                        , Font.center
++                        , Element.paddingXY 0 80
++                        ]
++                        (titleElement :: subtitles)
++
++                [] ->
++                    Element.none
++
++        noteBlock : Mark.Custom.Block Model Styling Msg
++        noteBlock =
++            Mark.Custom.section "note" noteView
++
++        noteView : List (Element Msg) -> Styling -> Model -> Element Msg
++        noteView elements style model =
++            Element.column
++                [ Element.padding 20
++                , Element.spacing 10
++                , Border.width 1
++                , Border.color colors.gray
++                , Border.rounded 5
++                , Font.italic
++                ]
++                elements
++
+=        counterBlock : Mark.Custom.Block Model Styling Msg
+=        counterBlock =
+=            Mark.Custom.block "counter" counterView
+@@ -150,7 +219,7 @@ options =
+=                    ]
+=                |> Element.el
+=                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.color colors.pink
+=                    , Border.rounded 5
+=                    , Border.width 2
+=                    ]
+@@ -169,7 +238,7 @@ options =
+=                    ]
+=                |> Element.el
+=                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.color colors.pink
+=                    , Border.rounded 5
+=                    , Border.width 2
+=                    ]
+@@ -177,7 +246,10 @@ options =
+=    in
+=    { default
+=        | blocks =
+-            counterBlock
++            titleBlock
++                :: emphasizeBlock
++                :: noteBlock
++                :: counterBlock
+=                :: transformationsBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+# Embed tree example
+
+The embedded example is toggled by clicking on the element
+
+``` diff
+index f789f82..96e0265 100644
+--- a/index.txt
++++ b/index.txt
+@@ -80,4 +80,8 @@ Embedded spiral example:
+=
+=| spiral
+=
++Embedded tree example:
++
++| tree
++
+=Finito!
+```
+
+``` diff
+index d450539..689e5cb 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -7,6 +7,7 @@ import Counter
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
++import Element.Events
+=import Element.Input as Input
+=import FillTheScreen
+=import Gradient
+@@ -22,6 +23,7 @@ import RosetteTypedTransformations
+=import Simplest
+=import Spiral
+=import Transformations
++import Tree
+=
+=
+=main =
+@@ -44,6 +46,7 @@ type alias Model =
+=    , nestedTransformations : NestedTransformations.Model
+=    , cartesianCoordinates : CartesianCoordinates.Model
+=    , polarCoordinates : PolarCoordinates.Model
++    , tree : Maybe Tree.Model
+=    }
+=
+=
+@@ -54,6 +57,8 @@ type Msg
+=    | NestedTransformationsMsg NestedTransformations.Msg
+=    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+=    | PolarCoordinatesMsg PolarCoordinates.Msg
++    | ToggleTree (Maybe Tree.Model)
++    | TreeMsg Tree.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+@@ -64,6 +69,9 @@ init flags =
+=      , nestedTransformations = NestedTransformations.init
+=      , cartesianCoordinates = CartesianCoordinates.init
+=      , polarCoordinates = PolarCoordinates.init
++      , tree = Nothing
++
++      --, tree = Tree.init () |> Tuple.first
+=      }
+=    , Http.get
+=        { url = "/index.txt"
+@@ -151,10 +159,32 @@ update msg model =
+=            , Cmd.none
+=            )
+=
++        ToggleTree tree ->
++            ( { model | tree = tree }, Cmd.none )
++
++        TreeMsg m ->
++            case model.tree of
++                Just tree ->
++                    Tree.update m tree
++                        |> (\( newTree, treeCmd ) ->
++                                ( { model | tree = Just newTree }
++                                , Cmd.map TreeMsg treeCmd
++                                )
++                           )
++
++                Nothing ->
++                    ( model, Cmd.none )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+-    Sub.none
++    case model.tree of
++        Nothing ->
++            Sub.none
++
++        Just tree ->
++            Tree.subscriptions tree
++                |> Sub.map TreeMsg
+=
+=
+=type alias Styling =
+@@ -386,6 +416,48 @@ options =
+=                    , Border.rounded 5
+=                    , Border.width 2
+=                    ]
++
++        treeBlock : Mark.Custom.Block Model Styling Msg
++        treeBlock =
++            Mark.Custom.block "tree" treeView
++
++        treeView : Styling -> Model -> Element Msg
++        treeView style model =
++            let
++                content =
++                    case model.tree of
++                        Nothing ->
++                            Element.text "Click Here to Display Tree"
++                                |> List.singleton
++                                |> Element.paragraph
++                                    [ Element.centerY ]
++                                |> Element.el
++                                    [ Element.centerX
++                                    , Element.height <| Element.minimum 300 <| Element.fill
++                                    , Element.Events.onClick
++                                        (Tree.init ()
++                                            |> Tuple.first
++                                            |> Just
++                                            |> ToggleTree
++                                        )
++                                    ]
++
++                        Just tree ->
++                            tree
++                                |> Tree.ui
++                                |> Element.map TreeMsg
++                                |> Element.el
++                                    [ Element.centerX
++                                    , Element.height <| Element.minimum 300 <| Element.fill
++                                    , Element.Events.onClick (ToggleTree Nothing)
++                                    ]
++            in
++            content
++                |> Element.el
++                    [ Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
+=    in
+=    { default
+=        | blocks =
+@@ -401,5 +473,6 @@ options =
+=                :: polarCoordinatesBlock
+=                :: rosetteBlock
+=                :: spiralBlock
++                :: treeBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index cde6818..44e7a9a 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -1,9 +1,17 @@
+-module Tree exposing (main)
++module Tree exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , subscriptions
++    , ui
++    , update
++    )
+=
+=import Browser
+=import Browser.Events
+=import Dict exposing (Dict)
+-import Element
++import Element exposing (Element)
+=import Html exposing (Html)
+=import Json.Decode exposing (Decoder)
+=import List.Extra as List
+@@ -81,6 +89,15 @@ init () =
+=
+=view : Model -> Html Msg
+=view model =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui model)
++
++
++ui : Model -> Element Msg
++ui model =
+=    let
+=        gradients : List (Svg Msg)
+=        gradients =
+@@ -123,20 +140,15 @@ view model =
+=                        )
+=                    ]
+=    in
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-        (Element.html <|
+-            svg
+-                [ viewBox "-1000 -1000 2000 2000"
+-                , height "100%"
+-                , width "100%"
+-                ]
+-                [ defs [] gradients
+-                , tree
+-                ]
+-        )
++    Element.html <|
++        svg
++            [ viewBox "-1000 -1000 2000 2000"
++            , height "100%"
++            , width "100%"
++            ]
++            [ defs [] gradients
++            , tree
++            ]
+=
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+```
+
+# Embed view box example
+
+
+
+``` diff
+index 96e0265..1a106ac 100644
+--- a/index.txt
++++ b/index.txt
+@@ -84,4 +84,8 @@ Embedded tree example:
+=
+=| tree
+=
++Embedded view box example:
++
++| view-box
++
+=Finito!
+```
+
+``` diff
+index 689e5cb..b88bb06 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -24,6 +24,7 @@ import Simplest
+=import Spiral
+=import Transformations
+=import Tree
++import ViewBox
+=
+=
+=main =
+@@ -47,6 +48,7 @@ type alias Model =
+=    , cartesianCoordinates : CartesianCoordinates.Model
+=    , polarCoordinates : PolarCoordinates.Model
+=    , tree : Maybe Tree.Model
++    , viewBox : ViewBox.Model
+=    }
+=
+=
+@@ -59,6 +61,7 @@ type Msg
+=    | PolarCoordinatesMsg PolarCoordinates.Msg
+=    | ToggleTree (Maybe Tree.Model)
+=    | TreeMsg Tree.Msg
++    | ViewBoxMsg ViewBox.Msg
+=
+=
+=init : Flags -> ( Model, Cmd Msg )
+@@ -70,8 +73,7 @@ init flags =
+=      , cartesianCoordinates = CartesianCoordinates.init
+=      , polarCoordinates = PolarCoordinates.init
+=      , tree = Nothing
+-
+-      --, tree = Tree.init () |> Tuple.first
++      , viewBox = ViewBox.init
+=      }
+=    , Http.get
+=        { url = "/index.txt"
+@@ -175,6 +177,9 @@ update msg model =
+=                Nothing ->
+=                    ( model, Cmd.none )
+=
++        ViewBoxMsg m ->
++            ( { model | viewBox = ViewBox.update m model.viewBox }, Cmd.none )
++
+=
+=subscriptions : Model -> Sub Msg
+=subscriptions model =
+@@ -458,6 +463,25 @@ options =
+=                    , Border.rounded 5
+=                    , Border.width 2
+=                    ]
++
++        viewBoxBlock : Mark.Custom.Block Model Styling Msg
++        viewBoxBlock =
++            Mark.Custom.block "view-box" viewBoxView
++
++        viewBoxView : Styling -> Model -> Element Msg
++        viewBoxView style model =
++            model.viewBox
++                |> ViewBox.ui
++                |> Element.el
++                    [ Element.centerX
++                    ]
++                |> Element.el
++                    [ Element.centerX
++                    , Border.color (Element.rgb 1 0.6 0.6)
++                    , Border.rounded 5
++                    , Border.width 2
++                    ]
++                |> Element.map ViewBoxMsg
+=    in
+=    { default
+=        | blocks =
+@@ -474,5 +498,6 @@ options =
+=                :: rosetteBlock
+=                :: spiralBlock
+=                :: treeBlock
++                :: viewBoxBlock
+=                :: Mark.defaultBlocks
+=    }
+```
+
+``` diff
+index de064d5..fadec31 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -1,10 +1,8 @@
+=module ViewBox exposing
+-    ( Flags
+-    , Model
++    ( Model
+=    , Msg
+=    , init
+=    , main
+-    , subscriptions
+=    , ui
+=    , update
+=    , view
+@@ -22,18 +20,13 @@ import Svg.Attributes exposing (..)
+=
+=
+=main =
+-    Browser.element
++    Browser.sandbox
+=        { init = init
+=        , view = view
+=        , update = update
+-        , subscriptions = subscriptions
+=        }
+=
+=
+-type alias Flags =
+-    ()
+-
+-
+=type alias Model =
+=    { left : Float
+=    , top : Float
+@@ -47,15 +40,13 @@ type Msg
+=    | Resize Float Float
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init () =
+-    ( { left = 0
+-      , top = 0
+-      , width = 400
+-      , height = 400
+-      }
+-    , Cmd.none
+-    )
++init : Model
++init =
++    { left = 0
++    , top = 0
++    , width = 400
++    , height = 400
++    }
+=
+=
+=view : Model -> Html.Html Msg
+@@ -77,19 +68,14 @@ view model =
+=            ]
+=
+=
+-update : Msg -> Model -> ( Model, Cmd Msg )
++update : Msg -> Model -> Model
+=update msg model =
+=    case msg of
+=        Move left top ->
+-            ( { model | left = left, top = top }, Cmd.none )
++            { model | left = left, top = top }
+=
+=        Resize width height ->
+-            ( { model | width = width, height = height }, Cmd.none )
+-
+-
+-subscriptions : Model -> Sub Msg
+-subscriptions model =
+-    Sub.none
++            { model | width = width, height = height }
+=
+=
+=ui model =
+@@ -107,11 +93,14 @@ ui model =
+=            , Element.padding 30
+=            ]
+=            [ Element.el
+-                [ Element.height Element.fill
+-                , Element.width Element.fill
++                [ Element.height <| Element.maximum 500 Element.fill
++                , Element.width <| Element.maximum 500 Element.fill
+=                ]
+=                (Element.html <|
+-                    svg [ viewBox "0 0 1000 1000" ]
++                    svg
++                        [ viewBox "0 0 1000 1000"
++                        , Svg.Attributes.style "width: 100%; height: 100%"
++                        ]
+=                        [ g [] world
+=                        , rect
+=                            [ x (String.fromFloat model.left)
+@@ -126,8 +115,8 @@ ui model =
+=                        ]
+=                )
+=            , Element.el
+-                [ Element.height Element.fill
+-                , Element.width Element.fill
++                [ Element.height <| Element.maximum 170 Element.fill
++                , Element.width <| Element.maximum 170 Element.fill
+=                ]
+=                (Element.html <|
+=                    svg
+@@ -136,7 +125,7 @@ ui model =
+=                            |> String.join " "
+=                            |> viewBox
+=                        , preserveAspectRatio "none"
+-                        , Svg.Attributes.style "width: 100; height: 100%"
++                        , Svg.Attributes.style "width: 100%; height: 100%"
+=                        ]
+=                        [ g [] world
+=                        , rect
+```
+
+# Unify styles of grid examples
+
+
+
+``` diff
+index d9b0f00..7441ca2 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/CartesianCoordinates.elm
+@@ -66,12 +66,14 @@ update msg model =
+=ui : Model -> Element Msg
+=ui model =
+=    Element.column
+-        [ Element.width (Element.maximum 600 Element.fill)
++        [ Element.width Element.fill
+=        , Element.centerX
+-        , Element.spacing 20
++        , Element.spacing 30
++        , Element.padding 30
+=        ]
+=        [ Element.el
+-            [ Element.width Element.fill
++            [ Element.height Element.fill
++            , Element.width Element.fill
+=            ]
+=          <|
+=            Element.html <|
+```
+
+``` diff
+index b88bb06..c295c9b 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -360,6 +360,7 @@ options =
+=                |> CartesianCoordinates.ui
+=                |> Element.el
+=                    [ Element.centerX
++                    , Element.width Element.fill
+=                    ]
+=                |> Element.el
+=                    [ Element.centerX
+@@ -379,6 +380,7 @@ options =
+=                |> PolarCoordinates.ui
+=                |> Element.el
+=                    [ Element.centerX
++                    , Element.width Element.fill
+=                    ]
+=                |> Element.el
+=                    [ Element.centerX
+```
+
+``` diff
+index fa27050..27129df 100644
+--- a/src/NestedTransformations.elm
++++ b/src/NestedTransformations.elm
+@@ -101,6 +101,7 @@ ui model =
+=                ]
+=                [ Element.el
+=                    [ Element.width Element.fill
++                    , Element.paddingEach { top = 0, right = 30, bottom = 0, left = 30 }
+=                    ]
+=                    (Element.html element)
+=                , Element.row [ Element.width Element.fill ]
+```
+
+``` diff
+index 0c519ab..0bcd917 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -86,14 +86,12 @@ ui model =
+=                    [ Element.width Element.fill
+=                    ]
+=                    (Element.html element)
+-                , Element.row [ Element.width Element.fill ]
+-                    [ Element.el
+-                        [ Background.color (Element.rgb 1 0.8 0.8)
+-                        , Element.padding 10
+-                        , Element.width Element.fill
+-                        ]
+-                        (transformationsUI transformations)
++                , Element.el
++                    [ Background.color (Element.rgb 1 0.8 0.8)
++                    , Element.padding 10
++                    , Element.width Element.fill
+=                    ]
++                    (transformationsUI transformations)
+=                ]
+=
+=        shape =
+```
+
+# Fix embedded tree style
+
+
+
+``` diff
+index c295c9b..b4f72e0 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -431,16 +431,23 @@ options =
+=        treeView : Styling -> Model -> Element Msg
+=        treeView style model =
+=            let
++                height =
++                    500
++
+=                content =
+=                    case model.tree of
+=                        Nothing ->
+=                            Element.text "Click Here to Display Tree"
+=                                |> List.singleton
+=                                |> Element.paragraph
+-                                    [ Element.centerY ]
++                                    [ Element.centerY
++                                    ]
+=                                |> Element.el
+=                                    [ Element.centerX
+-                                    , Element.height <| Element.minimum 300 <| Element.fill
++                                    , Element.height <|
++                                        Element.minimum height <|
++                                            Element.maximum height <|
++                                                Element.fill
+=                                    , Element.Events.onClick
+=                                        (Tree.init ()
+=                                            |> Tuple.first
+@@ -455,7 +462,11 @@ options =
+=                                |> Element.map TreeMsg
+=                                |> Element.el
+=                                    [ Element.centerX
+-                                    , Element.height <| Element.minimum 300 <| Element.fill
++                                    , Element.height <|
++                                        Element.minimum height <|
++                                            Element.maximum height <|
++                                                Element.fill
++                                    , Element.width Element.fill
+=                                    , Element.Events.onClick (ToggleTree Nothing)
+=                                    ]
+=            in
+```
+
+# Merge embeded programs with content branch
+
+
+
+
+
+# Merge embeded programs with content branch
+
+
+
new file mode 100644
index 0000000..300922e
--- /dev/null
+++ b/content/devlog/2018-11-29-elm-tree-workshop.md
@@ -0,0 +1,591 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 8
+
+
+# Create browser window element that wraps ui element in a mock browser window
+
+
+
+``` diff
+new file mode 100644
+index 0000000..7902e60
+--- /dev/null
++++ b/src/BrowserWindow.elm
+@@ -0,0 +1,54 @@
++module BrowserWindow exposing (main, window)
++
++import Element exposing (Element)
++import Element.Background as Background
++import Element.Border as Border
++import Element.Font as Font
++import Html exposing (Html)
++
++
++main : Html msg
++main =
++    Element.text "Hello World!"
++        |> window
++        |> Element.layout []
++
++
++window : Element msg -> Element msg
++window content =
++    let
++        circle : Char
++        circle =
++            Char.fromCode 9679
++    in
++    Element.column
++        [ Element.width Element.shrink
++        , Element.height Element.shrink
++        , Border.width 1
++        , Border.rounded 6
++        , Border.color <| Element.rgb 0.4 0.4 0.4
++        , Border.shadow { offset = ( 0, 0 ), size = 2, blur = 0, color = Element.rgb 0.35 0.35 0.35 }
++        ]
++        [ Element.row
++            [ Element.height <| Element.px <| 23
++            , Background.color <| Element.rgb 0.27 0.27 0.27
++            , Element.width Element.fill
++            , Border.widthEach { bottom = 2, left = 0, top = 0, right = 0 }
++            , Border.color <| Element.rgb 0.2 0.2 0.2
++            ]
++            ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
++                |> List.map
++                    (\( red, green, blue ) ->
++                        circle
++                            |> String.fromChar
++                            |> Element.text
++                            |> Element.el
++                                [ Element.rgb red green blue |> Font.color
++                                , Font.size 35
++                                , Element.height <| Element.px 42
++                                , Element.moveRight 6
++                                ]
++                    )
++            )
++        , content
++        ]
+```
+
+``` diff
+index b4f72e0..0da1f44 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,6 +1,7 @@
+=module Main exposing (main)
+=
+=import Browser
++import BrowserWindow
+=import CartesianCoordinates
+=import CenteredDot
+=import Counter
+@@ -217,12 +218,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=                |> Element.map CounterMsg
+=
+=        simplestBlock : Mark.Custom.Block Model Styling Msg
+@@ -236,12 +232,7 @@ options =
+=                    [ Element.width Element.fill
+=                    , Element.height Element.fill
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+=        fillTheScreenBlock =
+@@ -254,12 +245,7 @@ options =
+=                    [ Element.width Element.fill
+=                    , Element.height Element.fill
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        centeredDotBlock : Mark.Custom.Block Model Styling Msg
+=        centeredDotBlock =
+@@ -271,12 +257,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        lineBlock : Mark.Custom.Block Model Styling Msg
+=        lineBlock =
+@@ -288,12 +269,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        gradientBlock : Mark.Custom.Block Model Styling Msg
+=        gradientBlock =
+@@ -305,12 +281,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        transformationsBlock : Mark.Custom.Block Model Styling Msg
+=        transformationsBlock =
+@@ -400,12 +371,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        spiralBlock : Mark.Custom.Block Model Styling Msg
+=        spiralBlock =
+@@ -417,12 +383,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        treeBlock : Mark.Custom.Block Model Styling Msg
+=        treeBlock =
+@@ -471,11 +432,7 @@ options =
+=                                    ]
+=            in
+=            content
+-                |> Element.el
+-                    [ Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
++                |> BrowserWindow.window
+=
+=        viewBoxBlock : Mark.Custom.Block Model Styling Msg
+=        viewBoxBlock =
+```
+
+# Refactor circle in browser window
+
+
+
+``` diff
+index 7902e60..34177eb 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -17,9 +17,15 @@ main =
+=window : Element msg -> Element msg
+=window content =
+=    let
+-        circle : Char
+-        circle =
++        circle : Element.Color -> Element msg
++        circle color =
+=            Char.fromCode 9679
++                |> String.fromChar
++                |> Element.text
++                |> Element.el
++                    [ color |> Font.color
++                    , Font.size 35
++                    ]
+=    in
+=    Element.column
+=        [ Element.width Element.shrink
+@@ -39,14 +45,10 @@ window content =
+=            ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
+=                |> List.map
+=                    (\( red, green, blue ) ->
+-                        circle
+-                            |> String.fromChar
+-                            |> Element.text
++                        circle (Element.rgb red green blue)
+=                            |> Element.el
+-                                [ Element.rgb red green blue |> Font.color
+-                                , Font.size 35
+-                                , Element.height <| Element.px 42
+-                                , Element.moveRight 6
++                                [ Element.moveRight 6
++                                , Element.moveUp 3
+=                                ]
+=                    )
+=            )
+```
+
+# Minor improvements to BrowserWindow layout
+
+
+
+``` diff
+index 34177eb..161f379 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -5,6 +5,7 @@ import Element.Background as Background
+=import Element.Border as Border
+=import Element.Font as Font
+=import Html exposing (Html)
++import Html.Attributes
+=
+=
+=main : Html msg
+@@ -25,32 +26,49 @@ window content =
+=                |> Element.el
+=                    [ color |> Font.color
+=                    , Font.size 35
++                    , Element.width (Element.px 20)
++                    , Element.height (Element.px 35)
++                    , Font.center
+=                    ]
+=    in
+=    Element.column
+=        [ Element.width Element.shrink
+=        , Element.height Element.shrink
+-        , Border.width 1
++        , Border.width 2
+=        , Border.rounded 6
+-        , Border.color <| Element.rgb 0.4 0.4 0.4
+-        , Border.shadow { offset = ( 0, 0 ), size = 2, blur = 0, color = Element.rgb 0.35 0.35 0.35 }
++        , Border.color <| Element.rgb 0.27 0.27 0.27
++        , Background.color <| Element.rgb 0.27 0.27 0.27
+=        ]
+=        [ Element.row
+=            [ Element.height <| Element.px <| 23
+-            , Background.color <| Element.rgb 0.27 0.27 0.27
+=            , Element.width Element.fill
+=            , Border.widthEach { bottom = 2, left = 0, top = 0, right = 0 }
+=            , Border.color <| Element.rgb 0.2 0.2 0.2
++            , Border.roundEach
++                { topLeft = 5
++                , topRight = 5
++                , bottomLeft = 0
++                , bottomRight = 0
++                }
++            , Element.paddingXY 10 15
+=            ]
+=            ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
+=                |> List.map
+=                    (\( red, green, blue ) ->
+=                        circle (Element.rgb red green blue)
+-                            |> Element.el
+-                                [ Element.moveRight 6
+-                                , Element.moveUp 3
+-                                ]
+=                    )
+=            )
+-        , content
++        , Element.el
++            [ Background.color <| Element.rgb 1 1 1
++            , Element.width Element.fill
++            , Element.height Element.fill
++            , Border.roundEach
++                { topLeft = 0
++                , topRight = 0
++                , bottomLeft = 5
++                , bottomRight = 5
++                }
++            , Element.htmlAttribute (Html.Attributes.style "overflow" "hidden")
++            ]
++            content
+=        ]
+```
+
+# Make browser window content not stretch to fill window
+
+
+
+``` diff
+index 161f379..257c30b 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -38,18 +38,13 @@ window content =
+=        , Border.rounded 6
+=        , Border.color <| Element.rgb 0.27 0.27 0.27
+=        , Background.color <| Element.rgb 0.27 0.27 0.27
++        , Element.htmlAttribute (Html.Attributes.style "overflow" "hidden")
+=        ]
+=        [ Element.row
+=            [ Element.height <| Element.px <| 23
+=            , Element.width Element.fill
+=            , Border.widthEach { bottom = 2, left = 0, top = 0, right = 0 }
+=            , Border.color <| Element.rgb 0.2 0.2 0.2
+-            , Border.roundEach
+-                { topLeft = 5
+-                , topRight = 5
+-                , bottomLeft = 0
+-                , bottomRight = 0
+-                }
+=            , Element.paddingXY 10 15
+=            ]
+=            ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
+@@ -59,16 +54,14 @@ window content =
+=                    )
+=            )
+=        , Element.el
+-            [ Background.color <| Element.rgb 1 1 1
++            [ Element.width Element.fill
+=            , Element.width Element.fill
+-            , Element.height Element.fill
+-            , Border.roundEach
+-                { topLeft = 0
+-                , topRight = 0
+-                , bottomLeft = 5
+-                , bottomRight = 5
+-                }
+-            , Element.htmlAttribute (Html.Attributes.style "overflow" "hidden")
++            , Background.color <| Element.rgb 1 1 1
+=            ]
+-            content
++            (Element.el
++                [ Element.width Element.shrink
++                , Element.height Element.shrink
++                ]
++                content
++            )
+=        ]
+```
+
+``` diff
+index 0da1f44..1edee6f 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -118,9 +118,7 @@ view model =
+=                |> Element.text
+=    in
+=    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
++        []
+=        content
+=
+=
+@@ -228,10 +226,6 @@ options =
+=        simplestView : Styling -> Model -> Element Msg
+=        simplestView style model =
+=            Simplest.ui
+-                |> Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height Element.fill
+-                    ]
+=                |> BrowserWindow.window
+=
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+```
+
+# Make only simplest example not fill screen
+
+
+
+``` diff
+index 257c30b..d3b8508 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -58,10 +58,5 @@ window content =
+=            , Element.width Element.fill
+=            , Background.color <| Element.rgb 1 1 1
+=            ]
+-            (Element.el
+-                [ Element.width Element.shrink
+-                , Element.height Element.shrink
+-                ]
+-                content
+-            )
++            content
+=        ]
+```
+
+``` diff
+index 1edee6f..0591a0c 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -226,6 +226,7 @@ options =
+=        simplestView : Styling -> Model -> Element Msg
+=        simplestView style model =
+=            Simplest.ui
++                |> Element.el []
+=                |> BrowserWindow.window
+=
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+```
+
+# Make simplest example display a circle instead of a line
+
+
+
+``` diff
+index ae4d919..27930cc 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -6,14 +6,12 @@ import Svg.Attributes
+=
+=
+=main =
+-    Svg.svg [ Svg.Attributes.style "background: pink" ]
+-        [ Svg.line
+-            [ Svg.Attributes.x1 "0"
+-            , Svg.Attributes.x2 "20"
+-            , Svg.Attributes.y1 "0"
+-            , Svg.Attributes.y2 "0"
+-            , Svg.Attributes.stroke "black"
+-            , Svg.Attributes.strokeWidth <| String.fromInt <| 5
++    Svg.svg
++        [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.cx "30"
++            , Svg.Attributes.cy "30"
+=            ]
+=            []
+=        ]
+```
+
+# Use Element.clip instead of overflow  CSS property in BorwserWindow
+
+
+
+``` diff
+index d3b8508..2714ed3 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -5,7 +5,6 @@ import Element.Background as Background
+=import Element.Border as Border
+=import Element.Font as Font
+=import Html exposing (Html)
+-import Html.Attributes
+=
+=
+=main : Html msg
+@@ -38,7 +37,7 @@ window content =
+=        , Border.rounded 6
+=        , Border.color <| Element.rgb 0.27 0.27 0.27
+=        , Background.color <| Element.rgb 0.27 0.27 0.27
+-        , Element.htmlAttribute (Html.Attributes.style "overflow" "hidden")
++        , Element.clip
+=        ]
+=        [ Element.row
+=            [ Element.height <| Element.px <| 23
+```
+
+# Use BrowserWindow decoration inside ViewBox example
+
+
+
+``` diff
+index fadec31..5de8663 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -9,6 +9,7 @@ module ViewBox exposing
+=    )
+=
+=import Browser
++import BrowserWindow
+=import CartesianPlane exposing (graph)
+=import Element
+=import Element.Background as Background
+@@ -114,32 +115,32 @@ ui model =
+=                            []
+=                        ]
+=                )
+-            , Element.el
+-                [ Element.height <| Element.maximum 170 Element.fill
+-                , Element.width <| Element.maximum 170 Element.fill
++            , svg
++                [ [ model.left, model.top, model.width, model.height ]
++                    |> List.map String.fromFloat
++                    |> String.join " "
++                    |> viewBox
++                , preserveAspectRatio "none"
++                , Svg.Attributes.style "width: 100%; height: 100%"
+=                ]
+-                (Element.html <|
+-                    svg
+-                        [ [ model.left, model.top, model.width, model.height ]
+-                            |> List.map String.fromFloat
+-                            |> String.join " "
+-                            |> viewBox
+-                        , preserveAspectRatio "none"
+-                        , Svg.Attributes.style "width: 100%; height: 100%"
+-                        ]
+-                        [ g [] world
+-                        , rect
+-                            [ x (String.fromFloat model.left)
+-                            , y (String.fromFloat model.top)
+-                            , width (String.fromFloat model.width)
+-                            , height (String.fromFloat model.height)
+-                            , opacity "0.3"
+-                            , stroke "white"
+-                            , fill "pink"
+-                            ]
+-                            []
+-                        ]
+-                )
++                [ g [] world
++                , rect
++                    [ x (String.fromFloat model.left)
++                    , y (String.fromFloat model.top)
++                    , width (String.fromFloat model.width)
++                    , height (String.fromFloat model.height)
++                    , opacity "0.3"
++                    , stroke "white"
++                    , fill "pink"
++                    ]
++                    []
++                ]
++                |> Element.html
++                |> BrowserWindow.window
++                |> Element.el
++                    [ Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
+=            ]
+=        , Input.slider
+=            [ Element.behindContent
+```
\ No newline at end of file
new file mode 100644
index 0000000..2ed43f7
--- /dev/null
+++ b/content/devlog/2018-12-03-elm-tree-workshop.md
@@ -0,0 +1,733 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 5
+
+
+# Make BrowserWindow example (main) bigger
+
+
+
+``` diff
+index 2714ed3..b5db0fa 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -10,6 +10,10 @@ import Html exposing (Html)
+=main : Html msg
+=main =
+=    Element.text "Hello World!"
++        |> Element.el
++            [ Element.width (Element.px 400)
++            , Element.height (Element.px 400)
++            ]
+=        |> window
+=        |> Element.layout []
+=
+```
+
+# Remove the ui helper from Simplest and Centered dot
+
+Instead move this code to Main module.
+
+``` diff
+index fef1177..7c7ef14 100644
+--- a/src/CenteredDot.elm
++++ b/src/CenteredDot.elm
+@@ -6,18 +6,16 @@ import Svg.Attributes
+=
+=
+=main =
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
++    Svg.svg
++        [ Svg.Attributes.style "background: pink"
++        , Svg.Attributes.width "300"
++        , Svg.Attributes.height "150"
++        , Svg.Attributes.viewBox "-100 -100 200 200"
+=        ]
+-        ui
+-
+-
+-ui =
+-    Element.html
+-        (Svg.svg
+-            [ Svg.Attributes.viewBox "-100 -100 200 200"
+-            , Svg.Attributes.height "200"
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.cx "0"
++            , Svg.Attributes.cy "0"
+=            ]
+-            [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+-        )
++            []
++        ]
+```
+
+``` diff
+index 0591a0c..a32f254 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -225,8 +225,17 @@ options =
+=
+=        simplestView : Styling -> Model -> Element Msg
+=        simplestView style model =
+-            Simplest.ui
+-                |> Element.el []
++            Simplest.main
++                |> Element.html
++                |> Element.el
++                    [ Element.width (Element.px 300)
++                    , Element.height (Element.px 150)
++                    ]
++                |> Element.el
++                    [ Element.height (Element.minimum 400 Element.shrink)
++                    , Element.width Element.fill
++                    , Element.padding 10
++                    ]
+=                |> BrowserWindow.window
+=
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+@@ -248,9 +257,11 @@ options =
+=
+=        centeredDotView : Styling -> Model -> Element Msg
+=        centeredDotView style model =
+-            CenteredDot.ui
++            CenteredDot.main
++                |> Element.html
+=                |> Element.el
+-                    [ Element.centerX
++                    [ Element.height (Element.px 400)
++                    , Element.width Element.fill
+=                    ]
+=                |> BrowserWindow.window
+=
+```
+
+``` diff
+index 27930cc..dee7dbd 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -1,4 +1,4 @@
+-module Simplest exposing (main, ui)
++module Simplest exposing (main)
+=
+=import Element
+=import Svg
+@@ -7,7 +7,10 @@ import Svg.Attributes
+=
+=main =
+=    Svg.svg
+-        [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
++        [ Svg.Attributes.style "background: pink"
++        , Svg.Attributes.width "300"
++        , Svg.Attributes.height "150"
++        ]
+=        [ Svg.circle
+=            [ Svg.Attributes.r "10"
+=            , Svg.Attributes.cx "30"
+@@ -15,7 +18,3 @@ main =
+=            ]
+=            []
+=        ]
+-
+-
+-ui =
+-    Element.html main
+```
+
+# Write a comment explaining how to position an object in the center of the viewBox
+
+
+
+``` diff
+index 7c7ef14..59fb692 100644
+--- a/src/CenteredDot.elm
++++ b/src/CenteredDot.elm
+@@ -1,4 +1,23 @@
+-module CenteredDot exposing (main, ui)
++module CenteredDot exposing (main)
++
++{-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
++
++The viewBox property takes a string consisting of four numbers:
++
++    - position of the top edge
++    - position of the left edge
++    - width
++    - and height
++
++If we set the width to 200 and then set the left edge to -100, that will place the right edge at +100. Both edges are 100 away from the 0!
++
++The same way you can control the bottom edge by tweaking the position of the top edge and the height.
++
++In fact it doesnt matter how wide and high the viewBox is. As long as the top will be -1/2 of the width and left will be -1/2 of the width.
++
++That's the trick to position an object (like a dot) exactly in the center of the SVG viewport
++
++-}
+=
+=import Element
+=import Svg
+```
+
+# Fix the embeded programs layouts
+
+Make BrowserWindow.window take a list of attributes, so it's possible to 
+size the window as required.
+
+``` diff
+index b5db0fa..91c211c 100644
+--- a/src/BrowserWindow.elm
++++ b/src/BrowserWindow.elm
+@@ -14,12 +14,12 @@ main =
+=            [ Element.width (Element.px 400)
+=            , Element.height (Element.px 400)
+=            ]
+-        |> window
++        |> window []
+=        |> Element.layout []
+=
+=
+-window : Element msg -> Element msg
+-window content =
++window : List (Element.Attribute msg) -> Element msg -> Element msg
++window attributes content =
+=    let
+=        circle : Element.Color -> Element msg
+=        circle color =
+@@ -34,32 +34,33 @@ window content =
+=                    , Font.center
+=                    ]
+=    in
+-    Element.column
+-        [ Element.width Element.shrink
+-        , Element.height Element.shrink
+-        , Border.width 2
+-        , Border.rounded 6
+-        , Border.color <| Element.rgb 0.27 0.27 0.27
+-        , Background.color <| Element.rgb 0.27 0.27 0.27
+-        , Element.clip
+-        ]
+-        [ Element.row
+-            [ Element.height <| Element.px <| 23
+-            , Element.width Element.fill
+-            , Border.widthEach { bottom = 2, left = 0, top = 0, right = 0 }
+-            , Border.color <| Element.rgb 0.2 0.2 0.2
+-            , Element.paddingXY 10 15
+-            ]
+-            ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
+-                |> List.map
+-                    (\( red, green, blue ) ->
+-                        circle (Element.rgb red green blue)
+-                    )
+-            )
+-        , Element.el
++    Element.el attributes <|
++        Element.column
+=            [ Element.width Element.fill
+-            , Element.width Element.fill
+-            , Background.color <| Element.rgb 1 1 1
++            , Element.height Element.fill
++            , Border.width 2
++            , Border.rounded 6
++            , Border.color <| Element.rgb 0.27 0.27 0.27
++            , Background.color <| Element.rgb 0.27 0.27 0.27
++            , Element.clip
++            ]
++            [ Element.row
++                [ Element.height <| Element.px <| 23
++                , Element.width Element.fill
++                , Border.widthEach { bottom = 2, left = 0, top = 0, right = 0 }
++                , Border.color <| Element.rgb 0.2 0.2 0.2
++                , Element.paddingXY 10 15
++                ]
++                ([ ( 1, 0.4, 0.3 ), ( 1, 0.75, 0.18 ), ( 0.16, 0.79, 0.26 ) ]
++                    |> List.map
++                        (\( red, green, blue ) ->
++                            circle (Element.rgb red green blue)
++                        )
++                )
++            , Element.el
++                [ Element.width Element.fill
++                , Element.height Element.fill
++                , Background.color <| Element.rgb 1 1 1
++                ]
++                content
+=            ]
+-            content
+-        ]
+```
+
+``` diff
+index d7cfc83..56f88d6 100644
+--- a/src/FillTheScreen.elm
++++ b/src/FillTheScreen.elm
+@@ -14,14 +14,15 @@ main =
+=
+=
+=ui =
+-    Element.html
+-        (Svg.svg
+-            [ Svg.Attributes.style "background: pink; height: 100%; width: 100%" ]
+-            [ Svg.circle
+-                [ Svg.Attributes.r "10"
+-                , Svg.Attributes.cx "30"
+-                , Svg.Attributes.cy "30"
+-                ]
+-                []
++    Svg.svg
++        [ Svg.Attributes.style "background: pink"
++        , Svg.Attributes.viewBox "-100 -100 200 200"
++        ]
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.cx "0"
++            , Svg.Attributes.cy "0"
+=            ]
+-        )
++            []
++        ]
++        |> Element.html
+```
+
+``` diff
+index a32f254..acbf627 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -10,6 +10,7 @@ import Element.Background as Background
+=import Element.Border as Border
+=import Element.Events
+=import Element.Input as Input
++import FeatherIcons exposing (icons)
+=import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
+@@ -215,8 +216,13 @@ options =
+=                |> Counter.ui
+=                |> Element.el
+=                    [ Element.centerX
++                    , Element.centerY
++                    ]
++                |> Element.el
++                    [ Element.height (Element.px 400)
++                    , Element.width Element.fill
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=                |> Element.map CounterMsg
+=
+=        simplestBlock : Mark.Custom.Block Model Styling Msg
+@@ -228,15 +234,11 @@ options =
+=            Simplest.main
+=                |> Element.html
+=                |> Element.el
+-                    [ Element.width (Element.px 300)
+-                    , Element.height (Element.px 150)
+-                    ]
+-                |> Element.el
+-                    [ Element.height (Element.minimum 400 Element.shrink)
++                    [ Element.height (Element.px 400)
+=                    , Element.width Element.fill
+=                    , Element.padding 10
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+=        fillTheScreenBlock =
+@@ -246,10 +248,10 @@ options =
+=        fillTheScreenView style model =
+=            FillTheScreen.ui
+=                |> Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height Element.fill
++                    [ Element.height (Element.px 400)
++                    , Element.width Element.fill
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        centeredDotBlock : Mark.Custom.Block Model Styling Msg
+=        centeredDotBlock =
+@@ -262,8 +264,9 @@ options =
+=                |> Element.el
+=                    [ Element.height (Element.px 400)
+=                    , Element.width Element.fill
++                    , Element.padding 10
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        lineBlock : Mark.Custom.Block Model Styling Msg
+=        lineBlock =
+@@ -275,7 +278,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        gradientBlock : Mark.Custom.Block Model Styling Msg
+=        gradientBlock =
+@@ -287,7 +290,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        transformationsBlock : Mark.Custom.Block Model Styling Msg
+=        transformationsBlock =
+@@ -298,7 +301,8 @@ options =
+=            model.transformations
+=                |> Transformations.ui
+=                |> Element.el
+-                    [ Element.centerX
++                    [ Element.height Element.fill
++                    , Element.width Element.fill
+=                    ]
+=                |> Element.el
+=                    [ Element.centerX
+@@ -377,7 +381,7 @@ options =
+=                |> Element.el
+=                    [ Element.centerX
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        spiralBlock : Mark.Custom.Block Model Styling Msg
+=        spiralBlock =
+@@ -388,8 +392,11 @@ options =
+=            Spiral.ui
+=                |> Element.el
+=                    [ Element.centerX
++                    , Element.width Element.fill
++
++                    -- , Element.height Element.fill
+=                    ]
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        treeBlock : Mark.Custom.Block Model Styling Msg
+=        treeBlock =
+@@ -438,7 +445,7 @@ options =
+=                                    ]
+=            in
+=            content
+-                |> BrowserWindow.window
++                |> BrowserWindow.window []
+=
+=        viewBoxBlock : Mark.Custom.Block Model Styling Msg
+=        viewBoxBlock =
+@@ -448,9 +455,6 @@ options =
+=        viewBoxView style model =
+=            model.viewBox
+=                |> ViewBox.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    ]
+=                |> Element.el
+=                    [ Element.centerX
+=                    , Border.color (Element.rgb 1 0.6 0.6)
+```
+
+``` diff
+index 27129df..1f34423 100644
+--- a/src/NestedTransformations.elm
++++ b/src/NestedTransformations.elm
+@@ -95,7 +95,7 @@ ui model =
+=    let
+=        wrapper element =
+=            Element.column
+-                [ Element.width (Element.maximum 600 Element.fill)
++                [ Element.width Element.fill
+=                , Element.centerX
+=                , Element.spacing 20
+=                ]
+```
+
+``` diff
+index 09c888e..7e1ae87 100644
+--- a/src/Spiral.elm
++++ b/src/Spiral.elm
+@@ -40,10 +40,9 @@ ui =
+=    in
+=    Element.html
+=        (Svg.svg
+-            [ Svg.Attributes.height "400"
+-            , Svg.Attributes.viewBox "-100 -100 200 200"
++            [ Svg.Attributes.viewBox "-100 -100 200 200"
+=            ]
+-            (defs :: spiral 500)
++            (defs :: spiral 50)
+=        )
+=
+=
+@@ -52,7 +51,7 @@ spiral age =
+=    if age > 0 then
+=        let
+=            length =
+-                500 / toFloat age
++                50 / toFloat age
+=        in
+=        [ Svg.line
+=            [ Svg.Attributes.x2 "1"
+```
+
+``` diff
+index 5de8663..cfc9865 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -73,49 +73,48 @@ update : Msg -> Model -> Model
+=update msg model =
+=    case msg of
+=        Move left top ->
+-            { model | left = left, top = top }
++            { model
++                | left = left
++                , top = top
++            }
+=
+=        Resize width height ->
+-            { model | width = width, height = height }
++            { model
++                | width = width
++                , height = height
++            }
+=
+=
+=ui model =
+-    Element.column
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        , Element.spacing 30
+-        , Element.padding 30
+-        ]
+-        [ Element.row
+-            [ Element.width Element.fill
+-            , Element.height Element.fill
+-            , Element.centerX
+-            , Element.spacing 30
+-            , Element.padding 30
+-            ]
+-            [ Element.el
+-                [ Element.height <| Element.maximum 500 Element.fill
+-                , Element.width <| Element.maximum 500 Element.fill
++    let
++        viewbox =
++            svg
++                [ viewBox "-500 -500 1000 1000"
++
++                -- , Svg.Attributes.style "width: 100%; height: 100%"
+=                ]
+-                (Element.html <|
+-                    svg
+-                        [ viewBox "0 0 1000 1000"
+-                        , Svg.Attributes.style "width: 100%; height: 100%"
+-                        ]
+-                        [ g [] world
+-                        , rect
+-                            [ x (String.fromFloat model.left)
+-                            , y (String.fromFloat model.top)
+-                            , width (String.fromFloat model.width)
+-                            , height (String.fromFloat model.height)
+-                            , opacity "0.3"
+-                            , stroke "white"
+-                            , fill "pink"
+-                            ]
+-                            []
+-                        ]
+-                )
+-            , svg
++                [ g [] world
++                , rect
++                    [ x (String.fromFloat model.left)
++                    , y (String.fromFloat model.top)
++                    , width (String.fromFloat model.width)
++                    , height (String.fromFloat model.height)
++                    , opacity "0.3"
++                    , stroke "white"
++                    , fill "pink"
++                    ]
++                    []
++                ]
++                |> Element.html
++                |> Element.el
++                    [ Element.centerX
++                    , Element.centerY
++                    , Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++
++        viewport =
++            svg
+=                [ [ model.left, model.top, model.width, model.height ]
+=                    |> List.map String.fromFloat
+=                    |> String.join " "
+@@ -136,11 +135,31 @@ ui model =
+=                    []
+=                ]
+=                |> Element.html
+-                |> BrowserWindow.window
+=                |> Element.el
+=                    [ Element.width Element.fill
+=                    , Element.height Element.fill
+=                    ]
++                |> BrowserWindow.window
++                    [ Element.centerX
++                    , Element.centerY
++                    , Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        , Element.spacing 30
++        , Element.padding 30
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            , Element.centerX
++            , Element.spacing 30
++            ]
++            [ viewbox
++            , viewport
+=            ]
+=        , Input.slider
+=            [ Element.behindContent
+@@ -158,8 +177,8 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("left: " ++ String.fromFloat model.left)
+-            , min = 0
+-            , max = 1000 - model.width
++            , min = -500
++            , max = 500
+=            , value = model.left
+=            , thumb = Input.defaultThumb
+=            , step = Just 1
+@@ -180,8 +199,8 @@ ui model =
+=            , label =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("top: " ++ String.fromFloat model.top)
+-            , min = 0
+-            , max = 1000 - model.width
++            , min = -500
++            , max = 500
+=            , value = model.top
+=            , thumb = Input.defaultThumb
+=            , step = Just 1
+@@ -203,7 +222,7 @@ ui model =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("width: " ++ String.fromFloat model.width)
+=            , min = 1
+-            , max = 1000 - model.left
++            , max = 500
+=            , value = model.width
+=            , thumb = Input.defaultThumb
+=            , step = Just 1
+@@ -225,7 +244,7 @@ ui model =
+=                Input.labelBelow [ Element.centerX ] <|
+=                    Element.text ("height: " ++ String.fromFloat model.height)
+=            , min = 1
+-            , max = 1000 - model.top
++            , max = 500
+=            , value = model.height
+=            , thumb = Input.defaultThumb
+=            , step = Just 1
+@@ -237,5 +256,5 @@ world : List (Svg Msg)
+=world =
+=    [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
+=    , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
+-    , circle [ cx "400", cy "600", r "20", fill "red" ] []
++    , circle [ cx "0", cy "0", r "20", fill "red" ] []
+=    ]
+```
+
+# Add control of aspect ratio preservation to ViewBox example
+
+
+
+``` diff
+index cfc9865..5a4bad5 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -33,12 +33,14 @@ type alias Model =
+=    , top : Float
+=    , width : Float
+=    , height : Float
++    , preserveAspectRatio : Bool
+=    }
+=
+=
+=type Msg
+=    = Move Float Float
+=    | Resize Float Float
++    | PreserveAspectRatio Bool
+=
+=
+=init : Model
+@@ -47,6 +49,7 @@ init =
+=    , top = 0
+=    , width = 400
+=    , height = 400
++    , preserveAspectRatio = False
+=    }
+=
+=
+@@ -84,14 +87,15 @@ update msg model =
+=                , height = height
+=            }
+=
++        PreserveAspectRatio value ->
++            { model | preserveAspectRatio = value }
++
+=
+=ui model =
+=    let
+=        viewbox =
+=            svg
+=                [ viewBox "-500 -500 1000 1000"
+-
+-                -- , Svg.Attributes.style "width: 100%; height: 100%"
+=                ]
+=                [ g [] world
+=                , rect
+@@ -119,7 +123,13 @@ ui model =
+=                    |> List.map String.fromFloat
+=                    |> String.join " "
+=                    |> viewBox
+-                , preserveAspectRatio "none"
++                , preserveAspectRatio <|
++                    case model.preserveAspectRatio of
++                        True ->
++                            "xMidYMid meet"
++
++                        False ->
++                            "none"
+=                , Svg.Attributes.style "width: 100%; height: 100%"
+=                ]
+=                [ g [] world
+@@ -249,6 +259,12 @@ ui model =
+=            , thumb = Input.defaultThumb
+=            , step = Just 1
+=            }
++        , Input.checkbox []
++            { checked = model.preserveAspectRatio
++            , icon = Input.defaultCheckbox
++            , label = Input.labelRight [] (Element.text "Preserve aspect ratio")
++            , onChange = PreserveAspectRatio
++            }
+=        ]
+=
+=
+```
\ No newline at end of file
new file mode 100644
index 0000000..e422b16
--- /dev/null
+++ b/content/devlog/2018-12-04-elm-tree-workshop.md
@@ -0,0 +1,1621 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 9
+
+
+# Remove FeatherIcons import from Main module
+
+It leaked there from another branch.
+
+``` diff
+index acbf627..ac0641b 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -10,7 +10,6 @@ import Element.Background as Background
+=import Element.Border as Border
+=import Element.Events
+=import Element.Input as Input
+-import FeatherIcons exposing (icons)
+=import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
+```
+
+# Make an SVG element representing viewport fill the container in the ViewBox example running on Chrome
+
+
+
+``` diff
+index 5a4bad5..b3f0a44 100644
+--- a/src/ViewBox.elm
++++ b/src/ViewBox.elm
+@@ -130,7 +130,7 @@ ui model =
+=
+=                        False ->
+=                            "none"
+-                , Svg.Attributes.style "width: 100%; height: 100%"
++                , Svg.Attributes.style "width: 100%; height: 100%; flex-grow: 1; flex-basis: 0"
+=                ]
+=                [ g [] world
+=                , rect
+```
+
+# Configure project for GitLab pages
+
+Make git ignore /index.html and /public/
+
+``` diff
+index 1cf77ef..14d21f1 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -2,3 +2,5 @@
+=/**/.DS_Store
+=*.swp
+=.#*
++/index.html
++/public/
+```
+
+``` diff
+new file mode 100644
+index 0000000..0a8a769
+--- /dev/null
++++ b/.gitlab-ci.yml
+@@ -0,0 +1,15 @@
++image: node:7.2.0
++
++before_script:
++  - npm install -g elm@0.19.0-bugfix2
++
++pages:
++  stage: deploy
++  script:
++    - elm make src/Main.elm --output public/index.html
++    - cp index.txt public/
++  artifacts:
++    paths:
++      - public
++  only:
++    - master
+```
+
+# Merge branch 'master' into elm-markup-content
+
+
+
+
+
+# Implement capture script that will generate a static HTML file from Elm program
+
+For users with disabled JS.
+
+``` diff
+index 14d21f1..266c915 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -4,3 +4,4 @@
+=.#*
+=/index.html
+=/public/
++/node_modules/
+```
+
+``` diff
+index 0a8a769..0ecc581 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -1,13 +1,15 @@
+=image: node:7.2.0
+=
+=before_script:
+-  - npm install -g elm@0.19.0-bugfix2
++  - npm install
+=
+=pages:
+=  stage: deploy
+=  script:
+-    - elm make src/Main.elm --output public/index.html
+-    - cp index.txt public/
++    - elm make src/Main.elm --output public/index.js
++    - cp index.txt container.html public/
++    - capture > public/index.html
++
+=  artifacts:
+=    paths:
+=      - public
+```
+
+``` diff
+new file mode 100644
+index 0000000..b30969d
+--- /dev/null
++++ b/container.html
+@@ -0,0 +1,14 @@
++<!DOCTYPE html>
++<html lang="en" dir="ltr">
++  <head>
++    <meta charset="utf-8">
++    <title>Software Garden</title>
++  </head>
++  <body>
++    <div id="app-container"></div>
++    <script src="/index.js"></script>
++    <script>
++      Elm.Main.init({ node: document.querySelector("#app-container") })
++    </script>
++  </body>
++</html>
+```
+
+``` diff
+new file mode 100644
+index 0000000..3125dbc
+--- /dev/null
++++ b/package-lock.json
+@@ -0,0 +1,1295 @@
++{
++  "name": "website",
++  "version": "1.0.0",
++  "lockfileVersion": 1,
++  "requires": true,
++  "dependencies": {
++    "accepts": {
++      "version": "1.3.5",
++      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
++      "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
++      "requires": {
++        "mime-types": "~2.1.18",
++        "negotiator": "0.6.1"
++      }
++    },
++    "agent-base": {
++      "version": "4.2.1",
++      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
++      "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
++      "requires": {
++        "es6-promisify": "^5.0.0"
++      }
++    },
++    "ajv": {
++      "version": "6.6.1",
++      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.1.tgz",
++      "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==",
++      "requires": {
++        "fast-deep-equal": "^2.0.1",
++        "fast-json-stable-stringify": "^2.0.0",
++        "json-schema-traverse": "^0.4.1",
++        "uri-js": "^4.2.2"
++      }
++    },
++    "array-flatten": {
++      "version": "1.1.1",
++      "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
++      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
++    },
++    "asn1": {
++      "version": "0.2.4",
++      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
++      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
++      "requires": {
++        "safer-buffer": "~2.1.0"
++      }
++    },
++    "assert-plus": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
++      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
++    },
++    "async-limiter": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
++      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
++    },
++    "asynckit": {
++      "version": "0.4.0",
++      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
++      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
++    },
++    "aws-sign2": {
++      "version": "0.7.0",
++      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
++      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
++    },
++    "aws4": {
++      "version": "1.8.0",
++      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
++      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
++    },
++    "balanced-match": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
++      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
++    },
++    "bcrypt-pbkdf": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
++      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
++      "requires": {
++        "tweetnacl": "^0.14.3"
++      }
++    },
++    "binary": {
++      "version": "0.3.0",
++      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
++      "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
++      "requires": {
++        "buffers": "~0.1.1",
++        "chainsaw": "~0.1.0"
++      }
++    },
++    "binwrap": {
++      "version": "0.1.4",
++      "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.1.4.tgz",
++      "integrity": "sha1-yh94cDAiElGPoksHcm+cUKFcdVk=",
++      "requires": {
++        "request": "^2.81.0",
++        "request-promise": "^4.2.0",
++        "tar": "^2.2.1",
++        "unzip": "^0.1.11"
++      }
++    },
++    "block-stream": {
++      "version": "0.0.9",
++      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
++      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
++      "requires": {
++        "inherits": "~2.0.0"
++      }
++    },
++    "bluebird": {
++      "version": "3.5.3",
++      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
++      "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
++    },
++    "body-parser": {
++      "version": "1.18.3",
++      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
++      "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
++      "requires": {
++        "bytes": "3.0.0",
++        "content-type": "~1.0.4",
++        "debug": "2.6.9",
++        "depd": "~1.1.2",
++        "http-errors": "~1.6.3",
++        "iconv-lite": "0.4.23",
++        "on-finished": "~2.3.0",
++        "qs": "6.5.2",
++        "raw-body": "2.3.3",
++        "type-is": "~1.6.16"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "brace-expansion": {
++      "version": "1.1.11",
++      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
++      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
++      "requires": {
++        "balanced-match": "^1.0.0",
++        "concat-map": "0.0.1"
++      }
++    },
++    "buffer-from": {
++      "version": "1.1.1",
++      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
++      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
++    },
++    "buffers": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
++      "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
++    },
++    "bytes": {
++      "version": "3.0.0",
++      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
++      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
++    },
++    "caseless": {
++      "version": "0.12.0",
++      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
++      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
++    },
++    "chainsaw": {
++      "version": "0.1.0",
++      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
++      "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
++      "requires": {
++        "traverse": ">=0.3.0 <0.4"
++      }
++    },
++    "coffeescript": {
++      "version": "2.3.2",
++      "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.3.2.tgz",
++      "integrity": "sha512-YObiFDoukx7qPBi/K0kUKyntEZDfBQiqs/DbrR1xzASKOBjGT7auD85/DiPeRr9k++lRj7l3uA9TNMLfyfcD/Q=="
++    },
++    "combined-stream": {
++      "version": "1.0.7",
++      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
++      "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
++      "requires": {
++        "delayed-stream": "~1.0.0"
++      }
++    },
++    "concat-map": {
++      "version": "0.0.1",
++      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
++      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
++    },
++    "concat-stream": {
++      "version": "1.6.2",
++      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
++      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
++      "requires": {
++        "buffer-from": "^1.0.0",
++        "inherits": "^2.0.3",
++        "readable-stream": "^2.2.2",
++        "typedarray": "^0.0.6"
++      }
++    },
++    "content-disposition": {
++      "version": "0.5.2",
++      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
++      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
++    },
++    "content-type": {
++      "version": "1.0.4",
++      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
++      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
++    },
++    "cookie": {
++      "version": "0.3.1",
++      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
++      "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
++    },
++    "cookie-signature": {
++      "version": "1.0.6",
++      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
++      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
++    },
++    "core-util-is": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
++      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
++    },
++    "dashdash": {
++      "version": "1.14.1",
++      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
++      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
++      "requires": {
++        "assert-plus": "^1.0.0"
++      }
++    },
++    "debug": {
++      "version": "4.1.0",
++      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
++      "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
++      "requires": {
++        "ms": "^2.1.1"
++      }
++    },
++    "delayed-stream": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
++      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
++    },
++    "depd": {
++      "version": "1.1.2",
++      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
++      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
++    },
++    "destroy": {
++      "version": "1.0.4",
++      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
++      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
++    },
++    "ecc-jsbn": {
++      "version": "0.1.2",
++      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
++      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
++      "requires": {
++        "jsbn": "~0.1.0",
++        "safer-buffer": "^2.1.0"
++      }
++    },
++    "ee-first": {
++      "version": "1.1.1",
++      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
++      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
++    },
++    "elm": {
++      "version": "0.19.0-bugfix2",
++      "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.0-bugfix2.tgz",
++      "integrity": "sha512-kEbsC7SzTo6B2aq9ZEhdNZnqSehqVZvdXcm2FBKIAp1eRa1pxQr7VG1WXF5oC/XtYoQaQHVT3QMt5/PqA00ygg==",
++      "requires": {
++        "binwrap": "0.1.4"
++      }
++    },
++    "encodeurl": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
++      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
++    },
++    "es6-promise": {
++      "version": "4.2.5",
++      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
++      "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg=="
++    },
++    "es6-promisify": {
++      "version": "5.0.0",
++      "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
++      "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
++      "requires": {
++        "es6-promise": "^4.0.3"
++      }
++    },
++    "escape-html": {
++      "version": "1.0.3",
++      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
++      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
++    },
++    "etag": {
++      "version": "1.8.1",
++      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
++      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
++    },
++    "express": {
++      "version": "4.16.4",
++      "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
++      "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
++      "requires": {
++        "accepts": "~1.3.5",
++        "array-flatten": "1.1.1",
++        "body-parser": "1.18.3",
++        "content-disposition": "0.5.2",
++        "content-type": "~1.0.4",
++        "cookie": "0.3.1",
++        "cookie-signature": "1.0.6",
++        "debug": "2.6.9",
++        "depd": "~1.1.2",
++        "encodeurl": "~1.0.2",
++        "escape-html": "~1.0.3",
++        "etag": "~1.8.1",
++        "finalhandler": "1.1.1",
++        "fresh": "0.5.2",
++        "merge-descriptors": "1.0.1",
++        "methods": "~1.1.2",
++        "on-finished": "~2.3.0",
++        "parseurl": "~1.3.2",
++        "path-to-regexp": "0.1.7",
++        "proxy-addr": "~2.0.4",
++        "qs": "6.5.2",
++        "range-parser": "~1.2.0",
++        "safe-buffer": "5.1.2",
++        "send": "0.16.2",
++        "serve-static": "1.13.2",
++        "setprototypeof": "1.1.0",
++        "statuses": "~1.4.0",
++        "type-is": "~1.6.16",
++        "utils-merge": "1.0.1",
++        "vary": "~1.1.2"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "extend": {
++      "version": "3.0.2",
++      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
++      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
++    },
++    "extract-zip": {
++      "version": "1.6.7",
++      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
++      "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
++      "requires": {
++        "concat-stream": "1.6.2",
++        "debug": "2.6.9",
++        "mkdirp": "0.5.1",
++        "yauzl": "2.4.1"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "extsprintf": {
++      "version": "1.3.0",
++      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
++      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
++    },
++    "fast-deep-equal": {
++      "version": "2.0.1",
++      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
++      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
++    },
++    "fast-json-stable-stringify": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
++      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
++    },
++    "fd-slicer": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
++      "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
++      "requires": {
++        "pend": "~1.2.0"
++      }
++    },
++    "finalhandler": {
++      "version": "1.1.1",
++      "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
++      "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
++      "requires": {
++        "debug": "2.6.9",
++        "encodeurl": "~1.0.2",
++        "escape-html": "~1.0.3",
++        "on-finished": "~2.3.0",
++        "parseurl": "~1.3.2",
++        "statuses": "~1.4.0",
++        "unpipe": "~1.0.0"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "forever-agent": {
++      "version": "0.6.1",
++      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
++      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
++    },
++    "form-data": {
++      "version": "2.3.3",
++      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
++      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
++      "requires": {
++        "asynckit": "^0.4.0",
++        "combined-stream": "^1.0.6",
++        "mime-types": "^2.1.12"
++      }
++    },
++    "forwarded": {
++      "version": "0.1.2",
++      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
++      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
++    },
++    "fresh": {
++      "version": "0.5.2",
++      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
++      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
++    },
++    "fs.realpath": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
++      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
++    },
++    "fstream": {
++      "version": "1.0.11",
++      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
++      "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
++      "requires": {
++        "graceful-fs": "^4.1.2",
++        "inherits": "~2.0.0",
++        "mkdirp": ">=0.5 0",
++        "rimraf": "2"
++      }
++    },
++    "getpass": {
++      "version": "0.1.7",
++      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
++      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
++      "requires": {
++        "assert-plus": "^1.0.0"
++      }
++    },
++    "glob": {
++      "version": "7.1.3",
++      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
++      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
++      "requires": {
++        "fs.realpath": "^1.0.0",
++        "inflight": "^1.0.4",
++        "inherits": "2",
++        "minimatch": "^3.0.4",
++        "once": "^1.3.0",
++        "path-is-absolute": "^1.0.0"
++      }
++    },
++    "graceful-fs": {
++      "version": "4.1.15",
++      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
++      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
++    },
++    "har-schema": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
++      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
++    },
++    "har-validator": {
++      "version": "5.1.3",
++      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
++      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
++      "requires": {
++        "ajv": "^6.5.5",
++        "har-schema": "^2.0.0"
++      }
++    },
++    "http-errors": {
++      "version": "1.6.3",
++      "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
++      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
++      "requires": {
++        "depd": "~1.1.2",
++        "inherits": "2.0.3",
++        "setprototypeof": "1.1.0",
++        "statuses": ">= 1.4.0 < 2"
++      }
++    },
++    "http-signature": {
++      "version": "1.2.0",
++      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
++      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
++      "requires": {
++        "assert-plus": "^1.0.0",
++        "jsprim": "^1.2.2",
++        "sshpk": "^1.7.0"
++      }
++    },
++    "https-proxy-agent": {
++      "version": "2.2.1",
++      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
++      "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
++      "requires": {
++        "agent-base": "^4.1.0",
++        "debug": "^3.1.0"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "3.2.6",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
++          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
++          "requires": {
++            "ms": "^2.1.1"
++          }
++        }
++      }
++    },
++    "iconv-lite": {
++      "version": "0.4.23",
++      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
++      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
++      "requires": {
++        "safer-buffer": ">= 2.1.2 < 3"
++      }
++    },
++    "inflight": {
++      "version": "1.0.6",
++      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
++      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
++      "requires": {
++        "once": "^1.3.0",
++        "wrappy": "1"
++      }
++    },
++    "inherits": {
++      "version": "2.0.3",
++      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
++      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
++    },
++    "ipaddr.js": {
++      "version": "1.8.0",
++      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
++      "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
++    },
++    "is-typedarray": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
++      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
++    },
++    "isarray": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
++      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
++    },
++    "isstream": {
++      "version": "0.1.2",
++      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
++      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
++    },
++    "jsbn": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
++      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
++    },
++    "json-schema": {
++      "version": "0.2.3",
++      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
++      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
++    },
++    "json-schema-traverse": {
++      "version": "0.4.1",
++      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
++      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
++    },
++    "json-stringify-safe": {
++      "version": "5.0.1",
++      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
++      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
++    },
++    "jsprim": {
++      "version": "1.4.1",
++      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
++      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
++      "requires": {
++        "assert-plus": "1.0.0",
++        "extsprintf": "1.3.0",
++        "json-schema": "0.2.3",
++        "verror": "1.10.0"
++      }
++    },
++    "lodash": {
++      "version": "4.17.11",
++      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
++      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
++    },
++    "match-stream": {
++      "version": "0.0.2",
++      "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz",
++      "integrity": "sha1-mesFAJOzTf+t5CG5rAtBCpz6F88=",
++      "requires": {
++        "buffers": "~0.1.1",
++        "readable-stream": "~1.0.0"
++      },
++      "dependencies": {
++        "isarray": {
++          "version": "0.0.1",
++          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
++          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
++        },
++        "readable-stream": {
++          "version": "1.0.34",
++          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
++          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
++          "requires": {
++            "core-util-is": "~1.0.0",
++            "inherits": "~2.0.1",
++            "isarray": "0.0.1",
++            "string_decoder": "~0.10.x"
++          }
++        },
++        "string_decoder": {
++          "version": "0.10.31",
++          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
++          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
++        }
++      }
++    },
++    "media-typer": {
++      "version": "0.3.0",
++      "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
++      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
++    },
++    "merge-descriptors": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
++      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
++    },
++    "methods": {
++      "version": "1.1.2",
++      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
++      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
++    },
++    "mime": {
++      "version": "2.4.0",
++      "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
++      "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w=="
++    },
++    "mime-db": {
++      "version": "1.37.0",
++      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
++      "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
++    },
++    "mime-types": {
++      "version": "2.1.21",
++      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
++      "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
++      "requires": {
++        "mime-db": "~1.37.0"
++      }
++    },
++    "minimatch": {
++      "version": "3.0.4",
++      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
++      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
++      "requires": {
++        "brace-expansion": "^1.1.7"
++      }
++    },
++    "minimist": {
++      "version": "0.0.8",
++      "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
++      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
++    },
++    "mkdirp": {
++      "version": "0.5.1",
++      "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
++      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
++      "requires": {
++        "minimist": "0.0.8"
++      }
++    },
++    "ms": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
++      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
++    },
++    "natives": {
++      "version": "1.1.6",
++      "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
++      "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA=="
++    },
++    "negotiator": {
++      "version": "0.6.1",
++      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
++      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
++    },
++    "oauth-sign": {
++      "version": "0.9.0",
++      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
++      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
++    },
++    "on-finished": {
++      "version": "2.3.0",
++      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
++      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
++      "requires": {
++        "ee-first": "1.1.1"
++      }
++    },
++    "once": {
++      "version": "1.4.0",
++      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
++      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
++      "requires": {
++        "wrappy": "1"
++      }
++    },
++    "over": {
++      "version": "0.0.5",
++      "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz",
++      "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg="
++    },
++    "parseurl": {
++      "version": "1.3.2",
++      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
++      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
++    },
++    "path-is-absolute": {
++      "version": "1.0.1",
++      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
++      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
++    },
++    "path-to-regexp": {
++      "version": "0.1.7",
++      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
++      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
++    },
++    "pend": {
++      "version": "1.2.0",
++      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
++      "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
++    },
++    "performance-now": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
++      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
++    },
++    "process-nextick-args": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
++      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
++    },
++    "progress": {
++      "version": "2.0.2",
++      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.2.tgz",
++      "integrity": "sha512-/OLz5F9beZUWwSHZDreXgap1XShX6W+DCHQCqwCF7uZ88s6uTlD2cR3JBE77SegCmNtb1Idst+NfmwcdU6KVhw=="
++    },
++    "proxy-addr": {
++      "version": "2.0.4",
++      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
++      "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
++      "requires": {
++        "forwarded": "~0.1.2",
++        "ipaddr.js": "1.8.0"
++      }
++    },
++    "proxy-from-env": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
++      "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4="
++    },
++    "psl": {
++      "version": "1.1.29",
++      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
++      "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
++    },
++    "pullstream": {
++      "version": "0.4.1",
++      "resolved": "https://registry.npmjs.org/pullstream/-/pullstream-0.4.1.tgz",
++      "integrity": "sha1-1vs79a7Wl+gxFQ6xACwlo/iuExQ=",
++      "requires": {
++        "over": ">= 0.0.5 < 1",
++        "readable-stream": "~1.0.31",
++        "setimmediate": ">= 1.0.2 < 2",
++        "slice-stream": ">= 1.0.0 < 2"
++      },
++      "dependencies": {
++        "isarray": {
++          "version": "0.0.1",
++          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
++          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
++        },
++        "readable-stream": {
++          "version": "1.0.34",
++          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
++          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
++          "requires": {
++            "core-util-is": "~1.0.0",
++            "inherits": "~2.0.1",
++            "isarray": "0.0.1",
++            "string_decoder": "~0.10.x"
++          }
++        },
++        "string_decoder": {
++          "version": "0.10.31",
++          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
++          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
++        }
++      }
++    },
++    "punycode": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
++      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
++    },
++    "puppeteer": {
++      "version": "1.11.0",
++      "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.11.0.tgz",
++      "integrity": "sha512-iG4iMOHixc2EpzqRV+pv7o3GgmU2dNYEMkvKwSaQO/vMZURakwSOn/EYJ6OIRFYOque1qorzIBvrytPIQB3YzQ==",
++      "requires": {
++        "debug": "^4.1.0",
++        "extract-zip": "^1.6.6",
++        "https-proxy-agent": "^2.2.1",
++        "mime": "^2.0.3",
++        "progress": "^2.0.1",
++        "proxy-from-env": "^1.0.0",
++        "rimraf": "^2.6.1",
++        "ws": "^6.1.0"
++      }
++    },
++    "qs": {
++      "version": "6.5.2",
++      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
++      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
++    },
++    "range-parser": {
++      "version": "1.2.0",
++      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
++      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
++    },
++    "raw-body": {
++      "version": "2.3.3",
++      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
++      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
++      "requires": {
++        "bytes": "3.0.0",
++        "http-errors": "1.6.3",
++        "iconv-lite": "0.4.23",
++        "unpipe": "1.0.0"
++      }
++    },
++    "readable-stream": {
++      "version": "2.3.6",
++      "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
++      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
++      "requires": {
++        "core-util-is": "~1.0.0",
++        "inherits": "~2.0.3",
++        "isarray": "~1.0.0",
++        "process-nextick-args": "~2.0.0",
++        "safe-buffer": "~5.1.1",
++        "string_decoder": "~1.1.1",
++        "util-deprecate": "~1.0.1"
++      }
++    },
++    "request": {
++      "version": "2.88.0",
++      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
++      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
++      "requires": {
++        "aws-sign2": "~0.7.0",
++        "aws4": "^1.8.0",
++        "caseless": "~0.12.0",
++        "combined-stream": "~1.0.6",
++        "extend": "~3.0.2",
++        "forever-agent": "~0.6.1",
++        "form-data": "~2.3.2",
++        "har-validator": "~5.1.0",
++        "http-signature": "~1.2.0",
++        "is-typedarray": "~1.0.0",
++        "isstream": "~0.1.2",
++        "json-stringify-safe": "~5.0.1",
++        "mime-types": "~2.1.19",
++        "oauth-sign": "~0.9.0",
++        "performance-now": "^2.1.0",
++        "qs": "~6.5.2",
++        "safe-buffer": "^5.1.2",
++        "tough-cookie": "~2.4.3",
++        "tunnel-agent": "^0.6.0",
++        "uuid": "^3.3.2"
++      }
++    },
++    "request-promise": {
++      "version": "4.2.2",
++      "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
++      "integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
++      "requires": {
++        "bluebird": "^3.5.0",
++        "request-promise-core": "1.1.1",
++        "stealthy-require": "^1.1.0",
++        "tough-cookie": ">=2.3.3"
++      }
++    },
++    "request-promise-core": {
++      "version": "1.1.1",
++      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
++      "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
++      "requires": {
++        "lodash": "^4.13.1"
++      }
++    },
++    "rimraf": {
++      "version": "2.6.2",
++      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
++      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
++      "requires": {
++        "glob": "^7.0.5"
++      }
++    },
++    "safe-buffer": {
++      "version": "5.1.2",
++      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
++      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
++    },
++    "safer-buffer": {
++      "version": "2.1.2",
++      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
++      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
++    },
++    "send": {
++      "version": "0.16.2",
++      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
++      "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
++      "requires": {
++        "debug": "2.6.9",
++        "depd": "~1.1.2",
++        "destroy": "~1.0.4",
++        "encodeurl": "~1.0.2",
++        "escape-html": "~1.0.3",
++        "etag": "~1.8.1",
++        "fresh": "0.5.2",
++        "http-errors": "~1.6.2",
++        "mime": "1.4.1",
++        "ms": "2.0.0",
++        "on-finished": "~2.3.0",
++        "range-parser": "~1.2.0",
++        "statuses": "~1.4.0"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "mime": {
++          "version": "1.4.1",
++          "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
++          "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "serve-static": {
++      "version": "1.13.2",
++      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
++      "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
++      "requires": {
++        "encodeurl": "~1.0.2",
++        "escape-html": "~1.0.3",
++        "parseurl": "~1.3.2",
++        "send": "0.16.2"
++      }
++    },
++    "setimmediate": {
++      "version": "1.0.5",
++      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
++      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
++    },
++    "setprototypeof": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
++      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
++    },
++    "slice-stream": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz",
++      "integrity": "sha1-WzO9ZvATsaf4ZGCwPUY97DmtPqA=",
++      "requires": {
++        "readable-stream": "~1.0.31"
++      },
++      "dependencies": {
++        "isarray": {
++          "version": "0.0.1",
++          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
++          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
++        },
++        "readable-stream": {
++          "version": "1.0.34",
++          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
++          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
++          "requires": {
++            "core-util-is": "~1.0.0",
++            "inherits": "~2.0.1",
++            "isarray": "0.0.1",
++            "string_decoder": "~0.10.x"
++          }
++        },
++        "string_decoder": {
++          "version": "0.10.31",
++          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
++          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
++        }
++      }
++    },
++    "sshpk": {
++      "version": "1.15.2",
++      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz",
++      "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==",
++      "requires": {
++        "asn1": "~0.2.3",
++        "assert-plus": "^1.0.0",
++        "bcrypt-pbkdf": "^1.0.0",
++        "dashdash": "^1.12.0",
++        "ecc-jsbn": "~0.1.1",
++        "getpass": "^0.1.1",
++        "jsbn": "~0.1.0",
++        "safer-buffer": "^2.0.2",
++        "tweetnacl": "~0.14.0"
++      }
++    },
++    "statuses": {
++      "version": "1.4.0",
++      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
++      "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
++    },
++    "stealthy-require": {
++      "version": "1.1.1",
++      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
++      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
++    },
++    "string_decoder": {
++      "version": "1.1.1",
++      "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
++      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
++      "requires": {
++        "safe-buffer": "~5.1.0"
++      }
++    },
++    "tar": {
++      "version": "2.2.1",
++      "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
++      "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
++      "requires": {
++        "block-stream": "*",
++        "fstream": "^1.0.2",
++        "inherits": "2"
++      }
++    },
++    "tough-cookie": {
++      "version": "2.4.3",
++      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
++      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
++      "requires": {
++        "psl": "^1.1.24",
++        "punycode": "^1.4.1"
++      },
++      "dependencies": {
++        "punycode": {
++          "version": "1.4.1",
++          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
++          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
++        }
++      }
++    },
++    "traverse": {
++      "version": "0.3.9",
++      "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
++      "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
++    },
++    "tunnel-agent": {
++      "version": "0.6.0",
++      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
++      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
++      "requires": {
++        "safe-buffer": "^5.0.1"
++      }
++    },
++    "tweetnacl": {
++      "version": "0.14.5",
++      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
++      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
++    },
++    "type-is": {
++      "version": "1.6.16",
++      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
++      "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
++      "requires": {
++        "media-typer": "0.3.0",
++        "mime-types": "~2.1.18"
++      }
++    },
++    "typedarray": {
++      "version": "0.0.6",
++      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
++      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
++    },
++    "unpipe": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
++      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
++    },
++    "unzip": {
++      "version": "0.1.11",
++      "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz",
++      "integrity": "sha1-iXScY7BY19kNYZ+GuYqhU107l/A=",
++      "requires": {
++        "binary": ">= 0.3.0 < 1",
++        "fstream": ">= 0.1.30 < 1",
++        "match-stream": ">= 0.0.2 < 1",
++        "pullstream": ">= 0.4.1 < 1",
++        "readable-stream": "~1.0.31",
++        "setimmediate": ">= 1.0.1 < 2"
++      },
++      "dependencies": {
++        "fstream": {
++          "version": "0.1.31",
++          "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz",
++          "integrity": "sha1-czfwWPu7vvqMn1YaKMqwhJICyYg=",
++          "requires": {
++            "graceful-fs": "~3.0.2",
++            "inherits": "~2.0.0",
++            "mkdirp": "0.5",
++            "rimraf": "2"
++          }
++        },
++        "graceful-fs": {
++          "version": "3.0.11",
++          "resolved": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
++          "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
++          "requires": {
++            "natives": "^1.1.0"
++          }
++        },
++        "isarray": {
++          "version": "0.0.1",
++          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
++          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
++        },
++        "readable-stream": {
++          "version": "1.0.34",
++          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
++          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
++          "requires": {
++            "core-util-is": "~1.0.0",
++            "inherits": "~2.0.1",
++            "isarray": "0.0.1",
++            "string_decoder": "~0.10.x"
++          }
++        },
++        "string_decoder": {
++          "version": "0.10.31",
++          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
++          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
++        }
++      }
++    },
++    "uri-js": {
++      "version": "4.2.2",
++      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
++      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
++      "requires": {
++        "punycode": "^2.1.0"
++      }
++    },
++    "util-deprecate": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
++      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
++    },
++    "utils-merge": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
++      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
++    },
++    "uuid": {
++      "version": "3.3.2",
++      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
++      "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
++    },
++    "vary": {
++      "version": "1.1.2",
++      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
++      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
++    },
++    "verror": {
++      "version": "1.10.0",
++      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
++      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
++      "requires": {
++        "assert-plus": "^1.0.0",
++        "core-util-is": "1.0.2",
++        "extsprintf": "^1.2.0"
++      }
++    },
++    "wrappy": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
++      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
++    },
++    "ws": {
++      "version": "6.1.2",
++      "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.2.tgz",
++      "integrity": "sha512-rfUqzvz0WxmSXtJpPMX2EeASXabOrSMk1ruMOV3JBTBjo4ac2lDjGGsbQSyxj8Odhw5fBib8ZKEjDNvgouNKYw==",
++      "requires": {
++        "async-limiter": "~1.0.0"
++      }
++    },
++    "yauzl": {
++      "version": "2.4.1",
++      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
++      "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
++      "requires": {
++        "fd-slicer": "~1.0.1"
++      }
++    }
++  }
++}
+```
+
+``` diff
+new file mode 100644
+index 0000000..3432d08
+--- /dev/null
++++ b/package.json
+@@ -0,0 +1,25 @@
++{
++  "name": "website",
++  "version": "1.0.0",
++  "description": "A website for Software Garden project",
++  "main": "built/index.js",
++  "scripts": {
++    "test": "echo \"Error: no test specified\" && exit 1"
++  },
++  "repository": {
++    "type": "git",
++    "url": "git+ssh://git@gitlab.com/software-garden/website.git"
++  },
++  "author": "",
++  "license": "ISC",
++  "bugs": {
++    "url": "https://gitlab.com/software-garden/website/issues"
++  },
++  "homepage": "https://gitlab.com/software-garden/website#README",
++  "dependencies": {
++    "coffeescript": "^2.3.2",
++    "elm": "^0.19.0-bugfix2",
++    "express": "^4.16.4",
++    "puppeteer": "^1.11.0"
++  }
++}
+```
+
+``` diff
+index ce96065..29bbd46 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -14,6 +14,7 @@ import Element.Input as Input
+=import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
++import Html.Attributes
+=import Http
+=import Line
+=import Mark
+@@ -119,7 +120,9 @@ view model =
+=                |> Element.text
+=    in
+=    Element.layout
+-        []
++        [ -- Below is a hack that enables static site generation
++          Element.htmlAttribute (Html.Attributes.id "app-container")
++        ]
+=        content
+=
+=
+```
+
+``` diff
+new file mode 100644
+index 0000000..adc6353
+--- /dev/null
++++ b/src/capture.coffee
+@@ -0,0 +1,20 @@
++puppeteer = require "puppeteer"
++express = require "express"
++
++do () =>
++  app = express ``
++  app.use express.static "public/"
++  server = app.listen 8000
++
++  browser = await puppeteer.launch ``
++  page = await browser.newPage ``
++
++  await page.goto "http://localhost:8000/container.html",
++    waitUntil: "networkidle0"
++
++  html = await page.content ``
++
++  console.log html
++
++  await browser.close ``
++  server.close ``
+```
+
+# Fix GitLab CI config
+
+
+
+``` diff
+index 0ecc581..438ae7e 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -6,9 +6,9 @@ before_script:
+=pages:
+=  stage: deploy
+=  script:
+-    - elm make src/Main.elm --output public/index.js
++    - npx elm make src/Main.elm --output public/index.js
+=    - cp index.txt container.html public/
+-    - capture > public/index.html
++    - npx coffee src/capture > public/index.html
+=
+=  artifacts:
+=    paths:
+```
+
+# CI try newer version of Node.js container
+
+
+
+``` diff
+index 438ae7e..b1c5166 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -1,4 +1,4 @@
+-image: node:7.2.0
++image: node:10.9.0
+=
+=before_script:
+=  - npm install
+```
+
+# CI: Fix path to capture.coffee script
+
+
+
+``` diff
+index b1c5166..c2011f5 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -8,7 +8,7 @@ pages:
+=  script:
+=    - npx elm make src/Main.elm --output public/index.js
+=    - cp index.txt container.html public/
+-    - npx coffee src/capture > public/index.html
++    - npx coffee src/capture.coffee > public/index.html
+=
+=  artifacts:
+=    paths:
+```
+
+# Temporarily disable capture script in CI
+
+The puppeteer does not work in a container. To be fixed later.
+
+``` diff
+index c2011f5..8687ffe 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -8,7 +8,9 @@ pages:
+=  script:
+=    - npx elm make src/Main.elm --output public/index.js
+=    - cp index.txt container.html public/
+-    - npx coffee src/capture.coffee > public/index.html
++    ## FIXME: Make puppeteer work in the container
++    - cp container.html public/index.html
++    # - npx coffee src/capture.coffee > public/index.html
+=
+=  artifacts:
+=    paths:
+```
\ No newline at end of file
new file mode 100644
index 0000000..884427a
--- /dev/null
+++ b/content/devlog/2018-12-05-elm-tree-workshop.md
@@ -0,0 +1,71 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Use alekzonder/puppeteer:1.0.0 docker image in gitlab CI/CD
+
+
+
+``` diff
+index 8687ffe..ad4f592 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -1,4 +1,4 @@
+-image: node:10.9.0
++image: alekzonder/puppeteer:1.0.0
+=
+=before_script:
+=  - npm install
+```
+
+# Enable puppeteer in CI/CD
+
+Use capture.coffee script to render HTML
+
+``` diff
+index ad4f592..74b4280 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -9,8 +9,8 @@ pages:
+=    - npx elm make src/Main.elm --output public/index.js
+=    - cp index.txt container.html public/
+=    ## FIXME: Make puppeteer work in the container
+-    - cp container.html public/index.html
+-    # - npx coffee src/capture.coffee > public/index.html
++    #- cp container.html public/index.html
++    - npx coffee src/capture.coffee > public/index.html
+=
+=  artifacts:
+=    paths:
+```
+
+# Add options required by puppeteer container image to `puppeteer.launch`
+
+
+
+``` diff
+index adc6353..8764395 100644
+--- a/src/capture.coffee
++++ b/src/capture.coffee
+@@ -6,7 +6,12 @@ do () =>
+=  app.use express.static "public/"
+=  server = app.listen 8000
+=
+-  browser = await puppeteer.launch ``
++  browser = await puppeteer.launch
++    args: [
++      "--no-sandbox"
++      "--disable-setuid-sandbox"
++    ]
++
+=  page = await browser.newPage ``
+=
+=  await page.goto "http://localhost:8000/container.html",
+```
\ No newline at end of file
new file mode 100644
index 0000000..1f6ab25
--- /dev/null
+++ b/content/devlog/2018-12-11-elm-tree-workshop.md
@@ -0,0 +1,43 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 1
+
+
+# Add terminal image
+
+
+
+``` diff
+index 138e422..e9d30d1 100644
+--- a/index.txt
++++ b/index.txt
+@@ -40,9 +40,22 @@ To install the Elm programming language, go to it's website:
+=
+=
+=and follow the installation instructions.
++Some of the tools we use with Elm require [Node.js.](https://nodejs.org/en/)
++Go to the [Node.js](https://nodejs.org/en/) website and install the current version (the green button on the right)
+=
++We will need to use the terminal a little bit.
+=
+=
++| emphasize
++    >_
++
++
++Don't be scared. It's easy.
++
++
++| image "/assets/mac-launchpad-terminal.png"
++    "Here is a picture of terminal"
++
+=
+=
+=| header
+```
\ No newline at end of file
new file mode 100644
index 0000000..9eaf8af
--- /dev/null
+++ b/content/devlog/2018-12-13-elm-tree-workshop.md
@@ -0,0 +1,2512 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 7
+
+
+# Update Elm Markup to 2.0.2
+
+Fixe slider length and remove Debug.toString from CartesianCordinates.
+
+``` diff
+index c770fd4..2bb88f6 100644
+--- a/elm.json
++++ b/elm.json
+@@ -11,21 +11,22 @@
+=            "elm/html": "1.0.0",
+=            "elm/http": "2.0.0",
+=            "elm/json": "1.1.2",
++            "elm/parser": "1.1.0",
+=            "elm/svg": "1.0.1",
+=            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
+=            "elm-community/result-extra": "2.2.1",
+=            "elm-explorations/markdown": "1.0.0",
++            "feathericons/elm-feather": "1.2.0",
+=            "ianmackenzie/elm-geometry": "1.2.1",
+=            "ianmackenzie/elm-geometry-svg": "1.0.2",
+-            "mdgriffith/elm-markup": "1.0.0",
++            "mdgriffith/elm-markup": "2.0.2",
+=            "mdgriffith/elm-ui": "1.1.0",
+=            "turboMaCk/any-dict": "1.0.1"
+=        },
+=        "indirect": {
+=            "elm/bytes": "1.0.7",
+=            "elm/file": "1.0.1",
+-            "elm/parser": "1.1.0",
+=            "elm/time": "1.0.0",
+=            "elm/url": "1.0.0",
+=            "elm/virtual-dom": "1.0.2",
+```
+
+``` diff
+index 138e422..2afd30d 100644
+--- a/index.txt
++++ b/index.txt
+@@ -1,19 +1,21 @@
+-| title
++| Title
+=    Software Garden
+=
++| Emphasize
+=    ⚘
+=
++| Emphasize
+=    A functional programming workshop for non-programmers
+=
+=
+=
+-| header
++| Header
+=    Before the course begins
+=
+-| header
++| Header
+=    Setup
+=
+-| note
++| Note
+=    This setup instructions are based on an assumption that you are using a Mac.
+=
+=    If you are using Linux or BSD, then you probably know how to install stuff.
+@@ -25,114 +27,97 @@
+=
+=We will need
+=
+-| list
++| List
+=    - a text editor (I use Atom)
+=    - and the Elm programming language
+=
+-
+-| header
++| Header
+=    Installing Elm
+=
+=To install the Elm programming language, go to it's website:
+=
+-| emphasize
+-    [elm-lang.org](http://elm-lang.org/)
+-
++| Emphasize
++    {Link|elm-lang.org|url=http://elm-lang.org/}
+=
+=and follow the installation instructions.
+=
+-
+-
+-
+-
+-| header
++| Header
+=    To do:
+=
+=Steps to reproduce the tree:
+=
+-Make a dot
+-
+-  Centered (Elm UI, viewBox, cartesian coordinates)
+-
+-Make a line
+-
+-  Play with transformations (union types)
+-
+-Gradients
+-
+-Multiple lines
+-
+-  Rosettes (different kinds)
+-
+-Groups and transformations
+-
+-  Spiral (recursion)
+-
+-Tree
+-
+-
+-| header
++| List
++    - Make a dot (Centered (Elm UI, viewBox, cartesian coordinates))
++    - Make a line (Play with transformations (union types))
++    - Gradients
++    - Multiple lines
++    - Rosettes (different kinds)
++    - Groups and transformations
++    - Spiral (recursion)
++    - Tree
++
++| Header
+=    Before the course begins
+=
+=Setup the development environment
+=
+-| list
++| List
+=    - Elm
+=    - Node.js
+=
+=
+-| counter
++| Counter
+=
+=Embedded simplest example:
+=
+-| simplest
++| Simplest
+=
+=Embedded fill the screen example:
+=
+-| fill-the-screen
++| FillTheScreen
+=
+=Embedded centered dot example:
+=
+-| centered-dot
++| CenteredDot
+=
+=Embedded line example:
+=
+-| line
++| Line
+=
+=Embedded gradient example:
+=
+-| gradient
++| Gradient
+=
+=Embedded cartesian coordinates example:
+=
+-| cartesian-coordinates
++| CartesianCoordinates
+=
+=Embedded polar coordinates example:
+=
+-| polar-coordinates
++| PolarCoordinates
+=
+=Embeded transformations example:
+=
+-| transformations
++| Transformations
+=
+=Embedded nested-transformations example:
+=
+-| nested-transformations
++| NestedTransformations
+=
+=Embedded rosette example:
+=
+-| rosette
++| Rosette
+=
+=Embedded spiral example:
+=
+-| spiral
++| Spiral
+=
+=Embedded tree example:
+=
+-| tree
++| Tree
+=
+=Embedded view box example:
+=
+-| view-box
++| ViewBox
+=
+=Finito!
+```
+
+``` diff
+index 7441ca2..fc29be3 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/CartesianCoordinates.elm
+@@ -91,12 +91,18 @@ ui model =
+=                        , fontSize "0.05"
+=                        , dominantBaseline "central"
+=                        ]
+-                        [ text <| Debug.toString ( model.x, model.y ) ]
++                        [ text <|
++                            "("
++                                ++ String.fromFloat model.x
++                                ++ ", "
++                                ++ String.fromFloat model.y
++                                ++ ")"
++                        ]
+=                    ]
+=        , Input.slider
+=            [ Element.behindContent
+=                (Element.el
+-                    [ Element.width (Element.maximum 600 Element.fill)
++                    [ Element.width Element.fill
+=                    , Element.height (Element.px 2)
+=                    , Element.centerY
+=                    , Background.color <| Element.rgb 0.7 0.7 0.7
+```
+
+``` diff
+index 29bbd46..31f964d 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -1,16 +1,25 @@
+=module Main exposing (main)
+=
++{-| This program is responsible for rendering the website.
++
++It fetches the Elm Markup file at /index.txt and renders it. There are a number of embeded programs in the markup.
++
++-}
++
++import Basics.Extra exposing (curry)
+=import Browser
+=import BrowserWindow
+=import CartesianCoordinates
+=import CenteredDot
+=import Counter
++import Dict
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+=import Element.Events
+=import Element.Font as Font
+=import Element.Input as Input
++import FeatherIcons exposing (icons)
+=import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
+@@ -18,8 +27,10 @@ import Html.Attributes
+=import Http
+=import Line
+=import Mark
+-import Mark.Custom
++import Mark.Default
+=import NestedTransformations
++import Parser
++import Parser.Advanced
+=import PolarCoordinates
+=import Result.Extra as Result
+=import RosetteTypedTransformations
+@@ -30,6 +41,7 @@ import Tree
+=import ViewBox
+=
+=
++main : Program Flags Model Msg
+=main =
+=    Browser.element
+=        { init = init
+@@ -88,6 +100,7 @@ init flags =
+=view : Model -> Html Msg
+=view model =
+=    let
++        content : Element Msg
+=        content =
+=            case model.markup of
+=                Nothing ->
+@@ -96,13 +109,14 @@ view model =
+=
+=                Just markup ->
+=                    markup
+-                        |> Mark.parseWith options
+-                        |> Result.map (\fn -> fn model)
+-                        |> Result.extract problemsElement
+-
+-        problemsElement problems =
+-            problems
+-                |> List.map problemElement
++                        |> Mark.parse document
++                        |> Result.map (\render -> render model)
++                        |> Result.extract deadEndsElement
++
++        deadEndsElement : List DeadEnd -> Element Msg
++        deadEndsElement deadEnds =
++            deadEnds
++                |> List.map deadEndElement
+=                |> Element.column
+=                    [ Element.centerX
+=                    , Element.centerY
+@@ -111,13 +125,117 @@ view model =
+=                    , Element.spacing 20
+=                    ]
+=
+-        problemElement { row, col, problem } =
+-            Debug.toString problem
+-                ++ " at "
+-                ++ String.fromInt row
+-                ++ ":"
+-                ++ String.fromInt col
+-                |> Element.text
++        deadEndElement : DeadEnd -> Element Msg
++        deadEndElement { row, col, problem, contextStack } =
++            let
++                message =
++                    case problem of
++                        Mark.ExpectingIndent level ->
++                            "Expecting indentation level "
++                                ++ String.fromInt level
++
++                        Mark.InlineStart ->
++                            "Inline start"
++
++                        Mark.InlineEnd ->
++                            "Inline end"
++
++                        Mark.BlockStart ->
++                            "Block start"
++
++                        Mark.Expecting what ->
++                            "Expecting " ++ what
++
++                        Mark.ExpectingBlockName name ->
++                            "Expecting a block name " ++ name
++
++                        Mark.ExpectingInlineName name ->
++                            "Expecting an inline name" ++ name
++
++                        Mark.ExpectingFieldName name ->
++                            "Expecting a field name" ++ name
++
++                        Mark.NonMatchingFields { expecting, found } ->
++                            "Fields don't match. Expecting one of [ "
++                                ++ String.join ", " expecting
++                                ++ " ], but got [ "
++                                ++ String.join ", " found
++                                ++ " ]"
++
++                        Mark.MissingField name ->
++                            "A field is missing from the record: "
++                                ++ name
++
++                        Mark.RecordError ->
++                            "A record error"
++
++                        Mark.Escape ->
++                            "Escape"
++
++                        Mark.EscapedChar ->
++                            "Escaped character"
++
++                        Mark.Newline ->
++                            "Expecting newline"
++
++                        Mark.Space ->
++                            "Space"
++
++                        Mark.End ->
++                            "End"
++
++                        Mark.Integer ->
++                            "Integer"
++
++                        Mark.FloatingPoint ->
++                            "Floating point"
++
++                        Mark.InvalidNumber ->
++                            "Invalid number"
++
++                        Mark.UnexpectedEnd ->
++                            "Unexpected end"
++
++                        Mark.CantStartTextWithSpace ->
++                            "Can't start text with a space"
++
++                        Mark.UnclosedStyles styles ->
++                            let
++                                styleName : Mark.Style -> String
++                                styleName style =
++                                    case style of
++                                        Mark.Bold ->
++                                            "Bold"
++
++                                        Mark.Italic ->
++                                            "Italic"
++
++                                        Mark.Strike ->
++                                            "Strike"
++                            in
++                            "Unclosed styles: [ "
++                                ++ (styles
++                                        |> List.map styleName
++                                        |> String.join ", "
++                                   )
++                                ++ "]"
++
++                        Mark.UnexpectedField { found, options, recordName } ->
++                            "Unexpected field: "
++                                ++ found
++                                ++ ". Valid fields for "
++                                ++ recordName
++                                ++ " are: [ "
++                                ++ String.join ", " options
++                                ++ " ]"
++            in
++            Element.text
++                (message
++                    ++ " at "
++                    ++ String.fromInt row
++                    ++ ":"
++                    ++ String.fromInt col
++                )
+=    in
+=    Element.layout
+=        [ -- Below is a hack that enables static site generation
+@@ -128,7 +246,7 @@ view model =
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+-    case Debug.log "update" msg of
++    case msg of
+=        DocumentFetched (Ok markup) ->
+=            ( { model | markup = Just markup }
+=            , Cmd.none
+@@ -195,363 +313,438 @@ subscriptions model =
+=                |> Sub.map TreeMsg
+=
+=
+-type alias Styling =
+-    Mark.Styling Msg
+-
+-
+-type alias Options =
+-    Mark.Options Model Styling Msg
+-
+-
+-colors =
+-    { maroon = Element.rgb 0.7 0 0
+-    , gray = Element.rgb 0.8 0.8 0.8
+-    , pink = Element.rgb 1 0.6 0.6
+-    }
++type alias DeadEnd =
++    Parser.Advanced.DeadEnd Mark.Context Mark.Problem
+=
+=
+-options : Options
+-options =
++document : Mark.Document (Model -> Element Msg)
++document =
+=    let
+-        default =
+-            Mark.default
+-
+-        emphasizeBlock : Mark.Custom.Block Model Styling Msg
+-        emphasizeBlock =
+-            Mark.Custom.section "emphasize" emphasizeView
++        title =
++            Mark.Default.title
++                [ Element.width Element.fill
++                , Element.paddingXY 0 32
++                , Font.center
++                , Font.size 86
++                , Font.extraBold
++                ]
++                text
+=
+-        emphasizeView : List (Element Msg) -> Styling -> Model -> Element Msg
+-        emphasizeView elements style model =
+-            elements
+-                |> Element.column
+-                    [ Element.spacing 30
+-                    , Font.bold
+-                    , Font.size 30
+-                    , Font.center
+-                    , Element.paddingXY 0 40
+-                    ]
++        header =
++            Mark.Default.header
++                [ Font.size 24
++                , Font.underline
++                , Element.paddingXY 0 24
++                ]
++                text
+=
+-        titleBlock : Mark.Custom.Block Model Styling Msg
+-        titleBlock =
+-            Mark.Custom.section "title" titleView
+-
+-        titleView : List (Element Msg) -> Styling -> Model -> Element Msg
+-        titleView elements style model =
+-            case elements of
+-                title :: subtitles ->
+-                    let
+-                        titleElement =
+-                            Element.el
+-                                [ Font.center
+-                                , Font.size 60
+-                                , Font.extraBold
+-                                , Element.width Element.fill
+-                                ]
+-                                title
+-                    in
+-                    Element.column
+-                        [ Element.spacing 30
+-                        , Font.bold
+-                        , Font.size 30
+-                        , Font.center
+-                        , Element.paddingXY 0 80
++        paragraph : Mark.Block (Model -> Element Msg)
++        paragraph =
++            let
++                render content model =
++                    Element.paragraph
++                        [ Element.paddingXY 0 24
+=                        ]
+-                        (titleElement :: subtitles)
+-
+-                [] ->
+-                    Element.none
+-
+-        noteBlock : Mark.Custom.Block Model Styling Msg
+-        noteBlock =
+-            Mark.Custom.section "note" noteView
++                        (content model)
++            in
++            Mark.map
++                render
++                text
+=
+-        noteView : List (Element Msg) -> Styling -> Model -> Element Msg
+-        noteView elements style model =
+-            Element.column
++        monospace =
++            Mark.Default.monospace
+=                [ Element.padding 20
+-                , Element.spacing 10
+-                , Border.width 1
++                , Element.width Element.fill
+=                , Border.color colors.gray
++                , Border.width 3
+=                , Border.rounded 5
+-                , Font.italic
++                , Font.family [ Font.monospace ]
++                , Element.scrollbarY
+=                ]
+-                elements
+=
+-        counterBlock : Mark.Custom.Block Model Styling Msg
+-        counterBlock =
+-            Mark.Custom.block "counter" counterView
++        css : String -> String -> Element.Attribute msg
++        css property value =
++            Element.htmlAttribute
++                (Html.Attributes.style
++                    property
++                    value
++                )
+=
+-        counterView : Styling -> Model -> Element Msg
+-        counterView style model =
+-            model.counter
+-                |> Counter.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Element.centerY
+-                    ]
+-                |> Element.el
+-                    [ Element.height (Element.px 400)
+-                    , Element.width Element.fill
+-                    ]
+-                |> BrowserWindow.window []
+-                |> Element.map CounterMsg
+-
+-        simplestBlock : Mark.Custom.Block Model Styling Msg
+-        simplestBlock =
+-            Mark.Custom.block "simplest" simplestView
+-
+-        simplestView : Styling -> Model -> Element Msg
+-        simplestView style model =
+-            Simplest.main
+-                |> Element.html
+-                |> Element.el
+-                    [ Element.height (Element.px 400)
+-                    , Element.width Element.fill
+-                    , Element.padding 10
+-                    ]
+-                |> BrowserWindow.window []
+-
+-        fillTheScreenBlock : Mark.Custom.Block Model Styling Msg
+-        fillTheScreenBlock =
+-            Mark.Custom.block "fill-the-screen" fillTheScreenView
+-
+-        fillTheScreenView : Styling -> Model -> Element Msg
+-        fillTheScreenView style model =
+-            FillTheScreen.ui
+-                |> Element.el
+-                    [ Element.height (Element.px 400)
+-                    , Element.width Element.fill
+-                    ]
+-                |> BrowserWindow.window []
+-
+-        centeredDotBlock : Mark.Custom.Block Model Styling Msg
+-        centeredDotBlock =
+-            Mark.Custom.block "centered-dot" centeredDotView
+-
+-        centeredDotView : Styling -> Model -> Element Msg
+-        centeredDotView style model =
+-            CenteredDot.main
+-                |> Element.html
+-                |> Element.el
+-                    [ Element.height (Element.px 400)
+-                    , Element.width Element.fill
+-                    , Element.padding 10
+-                    ]
+-                |> BrowserWindow.window []
+-
+-        lineBlock : Mark.Custom.Block Model Styling Msg
+-        lineBlock =
+-            Mark.Custom.block "line" lineView
++        code =
++            let
++                render string model =
++                    string
++                        |> String.split "\n"
++                        |> List.indexedMap
++                            (\n loc ->
++                                Element.row [ Element.spacing 10 ]
++                                    [ Element.el
++                                        [ Font.color colors.gray
++                                        , Font.size 20
++                                        , Element.width (Element.px 40)
++                                        , css "user-select" "none"
++                                        , css "-webkit-user-select" "none"
++                                        , css "-ms-user-select" "none"
++                                        , css "-webkit-touch-callout" "none"
++                                        , css "-o-user-select" "none"
++                                        , css "-moz-user-select" "none"
++                                        ]
++                                        (Element.text (String.fromInt n ++ "."))
++                                    , Element.el
++                                        [ Element.width Element.fill
++                                        ]
++                                        (Element.text loc)
++                                    ]
++                            )
++                        |> Element.column
++                            [ Element.padding 20
++                            , Element.spacing 10
++                            , Element.width Element.fill
++                            , Border.color colors.gray
++                            , Border.width 3
++                            , Border.rounded 5
++                            , Font.family [ Font.monospace ]
++                            , Element.scrollbarY
++                            ]
++            in
++            Mark.block "Code"
++                render
++                Mark.multiline
+=
+-        lineView : Styling -> Model -> Element Msg
+-        lineView style model =
+-            Line.ui
+-                |> Element.el
+-                    [ Element.centerX
++        note : Mark.Block (Model -> Element Msg)
++        note =
++            let
++                render elements model =
++                    elements
++                        |> List.map (\element -> element model)
++                        |> Element.textColumn
++                            [ Element.padding 20
++                            , Element.spacing 10
++                            , Element.width Element.fill
++                            , Border.width 1
++                            , Border.color colors.gray
++                            , Font.color colors.gray
++                            , Border.rounded 5
++                            ]
++            in
++            Mark.block "Note"
++                render
++                (Mark.manyOf
++                    [ paragraph
++                    , header
+=                    ]
+-                |> BrowserWindow.window []
++                )
+=
+-        gradientBlock : Mark.Custom.Block Model Styling Msg
+-        gradientBlock =
+-            Mark.Custom.block "gradient" gradientView
++        emphasize : Mark.Block (Model -> Element Msg)
++        emphasize =
++            let
++                render element model =
++                    model
++                        |> element
++                        |> Element.el
++                            [ Element.spacing 30
++                            , Font.bold
++                            , Font.size 30
++                            , Font.center
++                            , Element.paddingXY 0 40
++                            , Element.width Element.fill
++                            ]
++            in
++            Mark.block "Emphasize"
++                render
++                paragraph
+=
+-        gradientView : Styling -> Model -> Element Msg
+-        gradientView style model =
+-            Gradient.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    ]
+-                |> BrowserWindow.window []
+-
+-        transformationsBlock : Mark.Custom.Block Model Styling Msg
+-        transformationsBlock =
+-            Mark.Custom.block "transformations" transformationsView
+-
+-        transformationsView : Styling -> Model -> Element Msg
+-        transformationsView style model =
+-            model.transformations
+-                |> Transformations.ui
+-                |> Element.el
+-                    [ Element.height Element.fill
+-                    , Element.width Element.fill
+-                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color colors.pink
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
+-                |> Element.map TransformationsMsg
++        image =
++            Mark.Default.image
++                [ Element.width Element.fill
++                ]
+=
+-        nestedTransformationsBlock : Mark.Custom.Block Model Styling Msg
+-        nestedTransformationsBlock =
+-            Mark.Custom.block "nested-transformations" nestedTransformationsView
++        list =
++            Mark.Default.list
++                { icon = Mark.Default.listIcon
++                , style =
++                    \cursor ->
++                        case List.length cursor of
++                            0 ->
++                                -- top level element
++                                [ Element.spacing 16 ]
++
++                            1 ->
++                                [ Element.spacing 16 ]
++
++                            2 ->
++                                [ Element.spacing 16 ]
++
++                            _ ->
++                                [ Element.spacing 8 ]
++                }
++                text
++
++        text : Mark.Block (Model -> List (Element Msg))
++        text =
++            let
++                defaultTextStyle =
++                    Mark.Default.defaultTextStyle
++            in
++            Mark.Default.textWith
++                { defaultTextStyle
++                    | inlines = defaultTextStyle.inlines ++ [ icon ]
++                }
++
++        icon : Mark.Inline (Model -> Element Msg)
++        icon =
++            Mark.inline "Icon"
++                (\name model ->
++                    icons
++                        |> Dict.get name
++                        |> Maybe.map (FeatherIcons.toHtml [])
++                        |> Maybe.map Element.html
++                        |> Maybe.withDefault
++                            (Element.text
++                                ("Icon not found: '" ++ name ++ "'")
++                            )
++                )
++                |> Mark.inlineString "name"
++
++        -- Embeded programs' blocks
++        counter : Mark.Block (Model -> Element Msg)
++        counter =
++            let
++                render model =
++                    model.counter
++                        |> Counter.ui
++                        |> Element.el
++                            [ Element.centerX
++                            , Element.centerY
++                            ]
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map CounterMsg
++            in
++            Mark.stub "Counter" render
+=
+-        nestedTransformationsView : Styling -> Model -> Element Msg
+-        nestedTransformationsView style model =
+-            model.nestedTransformations
+-                |> NestedTransformations.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
+-                |> Element.map NestedTransformationsMsg
++        simplest : Mark.Block (Model -> Element Msg)
++        simplest =
++            let
++                render model =
++                    Simplest.main
++                        |> Element.html
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "Simplest" render
+=
+-        cartesianCoordinatesBlock : Mark.Custom.Block Model Styling Msg
+-        cartesianCoordinatesBlock =
+-            Mark.Custom.block "cartesian-coordinates" cartesianCoordinatesView
++        fillTheScreen : Mark.Block (Model -> Element Msg)
++        fillTheScreen =
++            let
++                render model =
++                    FillTheScreen.ui
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "FillTheScreen" render
+=
+-        cartesianCoordinatesView : Styling -> Model -> Element Msg
+-        cartesianCoordinatesView style model =
+-            model.cartesianCoordinates
+-                |> CartesianCoordinates.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Element.width Element.fill
+-                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
+-                |> Element.map CartesianCoordinatesMsg
++        centeredDot : Mark.Block (Model -> Element Msg)
++        centeredDot =
++            let
++                render model =
++                    CenteredDot.main
++                        |> Element.html
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "CenteredDot" render
+=
+-        polarCoordinatesBlock : Mark.Custom.Block Model Styling Msg
+-        polarCoordinatesBlock =
+-            Mark.Custom.block "polar-coordinates" polarCoordinatesView
++        line : Mark.Block (Model -> Element Msg)
++        line =
++            let
++                render model =
++                    Line.ui
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "Line" render
+=
+-        polarCoordinatesView : Styling -> Model -> Element Msg
+-        polarCoordinatesView style model =
+-            model.polarCoordinates
+-                |> PolarCoordinates.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Element.width Element.fill
+-                    ]
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
+-                |> Element.map PolarCoordinatesMsg
++        gradient : Mark.Block (Model -> Element Msg)
++        gradient =
++            let
++                render model =
++                    Gradient.ui
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "Gradient" render
+=
+-        rosetteBlock : Mark.Custom.Block Model Styling Msg
+-        rosetteBlock =
+-            Mark.Custom.block "rosette" rosetteView
++        transformations : Mark.Block (Model -> Element Msg)
++        transformations =
++            let
++                render model =
++                    model.transformations
++                        |> Transformations.ui
++                        |> Element.el
++                            [ Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map TransformationsMsg
++            in
++            Mark.stub "Transformations" render
+=
+-        rosetteView : Styling -> Model -> Element Msg
+-        rosetteView style model =
+-            RosetteTypedTransformations.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    ]
+-                |> BrowserWindow.window []
++        nestedTransformations : Mark.Block (Model -> Element Msg)
++        nestedTransformations =
++            let
++                render model =
++                    model.nestedTransformations
++                        |> NestedTransformations.ui
++                        |> Element.el
++                            [ Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map NestedTransformationsMsg
++            in
++            Mark.stub "NestedTransformations" render
+=
+-        spiralBlock : Mark.Custom.Block Model Styling Msg
+-        spiralBlock =
+-            Mark.Custom.block "spiral" spiralView
++        cartesianCoordinates : Mark.Block (Model -> Element Msg)
++        cartesianCoordinates =
++            let
++                render model =
++                    model.cartesianCoordinates
++                        |> CartesianCoordinates.ui
++                        |> Element.el
++                            [ Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map CartesianCoordinatesMsg
++            in
++            Mark.stub "CartesianCoordinates" render
+=
+-        spiralView : Styling -> Model -> Element Msg
+-        spiralView style model =
+-            Spiral.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Element.width Element.fill
++        polarCoordinates : Mark.Block (Model -> Element Msg)
++        polarCoordinates =
++            let
++                render model =
++                    model.polarCoordinates
++                        |> PolarCoordinates.ui
++                        |> Element.el
++                            [ Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map PolarCoordinatesMsg
++            in
++            Mark.stub "PolarCoordinates" render
+=
+-                    -- , Element.height Element.fill
+-                    ]
+-                |> BrowserWindow.window []
++        rosette : Mark.Block (Model -> Element Msg)
++        rosette =
++            let
++                render model =
++                    RosetteTypedTransformations.ui
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "Rosette" render
+=
+-        treeBlock : Mark.Custom.Block Model Styling Msg
+-        treeBlock =
+-            Mark.Custom.block "tree" treeView
++        spiral : Mark.Block (Model -> Element Msg)
++        spiral =
++            let
++                render model =
++                    Spiral.ui
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.stub "Spiral" render
+=
+-        treeView : Styling -> Model -> Element Msg
+-        treeView style model =
++        tree : Mark.Block (Model -> Element Msg)
++        tree =
+=            let
+-                height =
+-                    500
+-
+-                content =
+-                    case model.tree of
+-                        Nothing ->
+-                            Element.text "Click Here to Display Tree"
+-                                |> List.singleton
+-                                |> Element.paragraph
+-                                    [ Element.centerY
+-                                    ]
+-                                |> Element.el
+-                                    [ Element.centerX
+-                                    , Element.height <|
+-                                        Element.minimum height <|
+-                                            Element.maximum height <|
+-                                                Element.fill
+-                                    , Element.Events.onClick
+-                                        (Tree.init ()
+-                                            |> Tuple.first
+-                                            |> Just
+-                                            |> ToggleTree
+-                                        )
+-                                    ]
++                render model =
++                    model.tree
++                        |> Maybe.map Tree.ui
++                        |> Maybe.withDefault Element.none
++                        |> Element.el
++                            [ Element.height (Element.px 600)
++                            , Element.width Element.fill
++                            ]
++                        |> BrowserWindow.window []
++                        |> Element.map TreeMsg
++            in
++            Mark.stub "Tree" render
+=
+-                        Just tree ->
+-                            tree
+-                                |> Tree.ui
+-                                |> Element.map TreeMsg
+-                                |> Element.el
+-                                    [ Element.centerX
+-                                    , Element.height <|
+-                                        Element.minimum height <|
+-                                            Element.maximum height <|
+-                                                Element.fill
+-                                    , Element.width Element.fill
+-                                    , Element.Events.onClick (ToggleTree Nothing)
+-                                    ]
++        viewBox : Mark.Block (Model -> Element Msg)
++        viewBox =
++            let
++                render model =
++                    model.viewBox
++                        |> ViewBox.ui
++                        |> Element.el
++                            [ Element.width Element.fill
++                            ]
++                        |> Element.map ViewBoxMsg
+=            in
+-            content
+-                |> BrowserWindow.window []
+-
+-        viewBoxBlock : Mark.Custom.Block Model Styling Msg
+-        viewBoxBlock =
+-            Mark.Custom.block "view-box" viewBoxView
+-
+-        viewBoxView : Styling -> Model -> Element Msg
+-        viewBoxView style model =
+-            model.viewBox
+-                |> ViewBox.ui
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Border.color (Element.rgb 1 0.6 0.6)
+-                    , Border.rounded 5
+-                    , Border.width 2
+-                    ]
+-                |> Element.map ViewBoxMsg
++            Mark.stub "ViewBox" render
+=    in
+-    { default
+-        | blocks =
+-            titleBlock
+-                :: emphasizeBlock
+-                :: noteBlock
+-                :: counterBlock
+-                :: simplestBlock
+-                :: fillTheScreenBlock
+-                :: lineBlock
+-                :: gradientBlock
+-                :: centeredDotBlock
+-                :: transformationsBlock
+-                :: nestedTransformationsBlock
+-                :: cartesianCoordinatesBlock
+-                :: polarCoordinatesBlock
+-                :: rosetteBlock
+-                :: spiralBlock
+-                :: treeBlock
+-                :: viewBoxBlock
+-                :: Mark.defaultBlocks
++    Mark.document
++        (\children model ->
++            Element.textColumn
++                [ Element.centerX
++                , Element.width (Element.px 800)
++                , Element.spacing 24
++                ]
++                (List.map (\render -> render model) children)
++        )
++        (Mark.manyOf
++            [ title
++            , header
++            , paragraph
++            , monospace
++            , code
++            , note
++            , emphasize
++            , list
++            , image
++
++            -- Embeded programs
++            , counter
++            , simplest
++            , fillTheScreen
++            , centeredDot
++            , line
++            , gradient
++            , transformations
++            , nestedTransformations
++            , cartesianCoordinates
++            , polarCoordinates
++            , rosette
++            , spiral
++            , tree
++            , viewBox
++            ]
++        )
++
++
++colors =
++    { maroon = Element.rgb 0.7 0 0
++    , gray = Element.rgb 0.8 0.8 0.8
++    , pink = Element.rgb 1 0.6 0.6
+=    }
+```
+
+# Remove calls to Debug functions
+
+This enables the optimized compilation.
+
+``` diff
+index 1f34423..e0aaded 100644
+--- a/src/NestedTransformations.elm
++++ b/src/NestedTransformations.elm
+@@ -64,7 +64,7 @@ type GroupMsg
+=
+=init : Model
+=init =
+-    Dict.empty Debug.toString
++    Dict.empty groupName
+=        |> Dict.insert Pink
+=            (Array.fromList
+=                [ Translate 0 0
+@@ -138,7 +138,7 @@ ui model =
+=
+=                color =
+=                    group
+-                        |> Debug.toString
++                        |> groupName
+=                        |> String.toLower
+=            in
+=            g [ transform transformation ]
+@@ -218,7 +218,7 @@ transformationUI index transformation =
+=        controls =
+=            case transformation of
+=                Identity ->
+-                    [ Element.text <| Debug.toString transformation ]
++                    [ Element.text "Identity" ]
+=
+=                Scale horizontal vertical ->
+=                    [ Input.slider
+@@ -388,6 +388,16 @@ apply transformations =
+=        |> String.join " "
+=
+=
++groupName : Group -> String
++groupName group =
++    case group of
++        Pink ->
++            "Pink"
++
++        Green ->
++            "Green"
++
++
+=toColor : Group -> Element.Color
+=toColor group =
+=    case group of
+```
+
+``` diff
+index 5da8133..3a476cd 100644
+--- a/src/PolarCoordinates.elm
++++ b/src/PolarCoordinates.elm
+@@ -93,7 +93,17 @@ ui model =
+=                        , Svg.Attributes.fontSize "10pt"
+=                        ]
+=                        [ text <|
+-                            Debug.toString ( round point.x, round point.y )
++                            "( "
++                                ++ (point.x
++                                        |> round
++                                        |> String.fromInt
++                                   )
++                                ++ " , "
++                                ++ (point.x
++                                        |> round
++                                        |> String.fromInt
++                                   )
++                                ++ " )"
+=                        ]
+=                    ]
+=        , Input.slider
+```
+
+``` diff
+index 0bcd917..295c1ef 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -179,7 +179,7 @@ transformationUI index transformation =
+=        controls =
+=            case transformation of
+=                Identity ->
+-                    [ Element.text <| Debug.toString transformation ]
++                    [ Element.text "Identity" ]
+=
+=                Scale horizontal vertical ->
+=                    [ Input.slider
+```
+
+``` diff
+index 44e7a9a..e2925a8 100644
+--- a/src/Tree.elm
++++ b/src/Tree.elm
+@@ -173,7 +173,7 @@ subscriptions model =
+=            Json.Decode.field "key" Json.Decode.string
+=                |> Json.Decode.andThen
+=                    (\key ->
+-                        case Debug.log "Key" key of
++                        case key of
+=                            "," ->
+=                                Json.Decode.succeed (Regress 10)
+=
+```
+
+# Remove background color and position of the dot from Simplest
+
+
+
+``` diff
+index dee7dbd..9f90116 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -7,14 +7,11 @@ import Svg.Attributes
+=
+=main =
+=    Svg.svg
+-        [ Svg.Attributes.style "background: pink"
+-        , Svg.Attributes.width "300"
++        [ Svg.Attributes.width "300"
+=        , Svg.Attributes.height "150"
+=        ]
+=        [ Svg.circle
+=            [ Svg.Attributes.r "10"
+-            , Svg.Attributes.cx "30"
+-            , Svg.Attributes.cy "30"
+=            ]
+=            []
+=        ]
+```
+
+# Create DotAtTheCenterOfTheScreen program
+
+It's the same as the FillTheScreen but without background color
+
+``` diff
+new file mode 100644
+index 0000000..83f0d1d
+--- /dev/null
++++ b/src/DotAtTheCenterOfTheScreen.elm
+@@ -0,0 +1,27 @@
++module DotAtTheCenterOfTheScreen exposing (main, ui)
++
++import Element
++import Svg
++import Svg.Attributes
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        ui
++
++
++ui =
++    Svg.svg
++        [ Svg.Attributes.viewBox "-100 -100 200 200"
++        ]
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.cx "0"
++            , Svg.Attributes.cy "0"
++            ]
++            []
++        ]
++        |> Element.html
+```
+
+``` diff
+index 29bbd46..82bc5a7 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -5,6 +5,7 @@ import BrowserWindow
+=import CartesianCoordinates
+=import CenteredDot
+=import Counter
++import DotAtTheCenterOfTheScreen
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+@@ -324,6 +325,19 @@ options =
+=                    ]
+=                |> BrowserWindow.window []
+=
++        dotAtTheCenterOfTheScreenBlock : Mark.Custom.Block Model Styling Msg
++        dotAtTheCenterOfTheScreenBlock =
++            Mark.Custom.block "dot-at-the-center-of-the-screen" dotAtTheCenterOfTheScreenView
++
++        dotAtTheCenterOfTheScreenView : Styling -> Model -> Element Msg
++        dotAtTheCenterOfTheScreenView style model =
++            DotAtTheCenterOfTheScreen.ui
++                |> Element.el
++                    [ Element.height (Element.px 400)
++                    , Element.width Element.fill
++                    ]
++                |> BrowserWindow.window []
++
+=        centeredDotBlock : Mark.Custom.Block Model Styling Msg
+=        centeredDotBlock =
+=            Mark.Custom.block "centered-dot" centeredDotView
+@@ -542,6 +556,7 @@ options =
+=                :: counterBlock
+=                :: simplestBlock
+=                :: fillTheScreenBlock
++                :: dotAtTheCenterOfTheScreenBlock
+=                :: lineBlock
+=                :: gradientBlock
+=                :: centeredDotBlock
+```
+
+# Add the content from paper slides
+
+
+
+``` diff
+index e9d30d1..7deafce 100644
+--- a/index.txt
++++ b/index.txt
+@@ -33,15 +33,18 @@ We will need
+=| header
+=    Installing Elm
+=
++
+=To install the Elm programming language, go to it's website:
+=
++
+=| emphasize
+=    [elm-lang.org](http://elm-lang.org/)
+=
+=
++
+=and follow the installation instructions.
+-Some of the tools we use with Elm require [Node.js.](https://nodejs.org/en/)
+-Go to the [Node.js](https://nodejs.org/en/) website and install the current version (the green button on the right)
++
++Some of the tools we use with Elm require Node.js. Go to the [Node.js](https://nodejs.org/en/) website and install the current version (the green button on the right)
+=
+=We will need to use the terminal a little bit.
+=
+@@ -57,6 +60,1021 @@ Don't be scared. It's easy.
+=    "Here is a picture of terminal"
+=
+=
++Mac Terminal app window
++You should see a window like this
++
++
++| image "/assets/mac-terminal-window.png"
++    "A picture of Mac Terminal"
++
++Type the following in the terminal.
++
++
++| monospace
++    elm repl
++
++Inside the REPL type
++
++
++| monospace
++    2+2
++
++And expect to see
++
++
++| monospace
++    ---- Elm 0.19.0 ----------------
++    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
++    --------------------------------
++    > 2 + 2
++    4 : number
++    >
++
++Easy, huh?
++We will learn more about REPL later. For now type
++:exit to close it.
++The command line should get back to its initial state.
++
++| header
++    Install Atom Text Editor
++
++Computer Programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use Atom here.
++
++Go to
++[atom.io](https://atom.io)
++and download the editor.
++After installation on Mac, open it and from Atom menu choose Install Shell Commands.
++
++One last thing we need is Elm support for Atom editor.
++You can install it by typing in the terminal:
++
++| monospace
++    apm install language-elm
++
++APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.
++We are all set.
++
++| header
++    Day 1 - let's make a dot!
++
++| note
++    Today we are going to learn about
++
++    | list
++        -> Scalable Vector Graphics
++        -> cartesian Coordinates System
++        -> Layouts with ELM UI
++
++
++| header
++    First Program!
++
++As mentioned before, programs are represented as text (called the /source code/).
++The source code is stored in files
++
++| emphasize
++    * icon|name=file *
++
++
++and files are organized in directories
++
++
++| emphasize
++    * icon|name=directory *
++
++
++So the first step is to create a directory for our new program. Lets call it fpart.
++In the terminal type
++
++| monospace
++    mkdir fpart/
++
++and then
++
++| monospace
++    cd fpart/
++
++This creates a new directory and makes it the current one. Again, don't worry about the details.
++
++To easily create a new program, we can type
++
++| monospace
++    elm init
++
++Then to create a file with source code, type
++
++| monospace
++    atom src/Main.elm
++
++This command should open a new Atom window with empty text file.
++
++| header
++    Main.elm
++
++| monospace
++    module Main exposing (main)
++    import Html
++    main=
++       Html.text "Hello, Tree"
++
++Type the above in the editor and save the file.
++To see the program running type the following in the terminal
++
++| monospace
++    elm reactor
++
++This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
++
++
++| emphasize
++    Voila!
++
++
++Open following address in the web browser
++[localhost](http://localhost:8000/src/Main.elm)
++Note that the same address was printed by Elm reactor
++
++| header
++    The problem
++
++We want to have a dot at the center of the screen, like this
++
++| dot-at-the-center-of-the-screen
++
++We are going to use a technology called SVG (Scalable Vector Graphics)
++Its all about drawing shapes on the screen
++
++* to be filled with the complete code *
++
++Let's install an Elm Package to help us work with SVG. Stop the reactor running in terminal by pressing `CTRL + C`
++and type the following
++
++| monospace
++    elm install elm/svg
++
++Then start the reactor again
++
++| monospace
++    elm reactor
++
++you can press up arrow  *icon up-arrow*  on the keyboard to get to the previous commands
++
++* simplest elm should be displayed here *
++
++| simplest
++
++Why is the dot at the corner?
++
++| simplest
++
++It's because its center is at point (0, 0)
++But what does it mean ?
++
++* a picture of a vase on a table should be here *
++
++Move sliders to change the x and y coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down it should go.
++
++| cartesian-coordinates
++
++Note that the command line changes. You should see something like this
++
++| monospace
++    ---- Elm 0.19.0 --------------------------------------------------
++    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
++    ---------------------------------------------------------------------
++    >
++
++It's the Elm REPL
++Read-Evaluate-Print Loop
++
++To change where the dot is we can use cx and cy attributes of the circle
++
++* simplest with cx and cy should be here *
++
++* Module main exposing main should be here *
++
++Note that we have a complete program that draws a dot at the center of the screen , lets take a moment to understand it.
++
++Think about this ( * a picture of eggs and a box of eggs should be here * )
++
++| note
++    An egg is a thing.
++    Six eggs are six things.
++    A box of six eggs is a thing.
++
++
++
++Now, let's make the SVG element fill the screen
++
++
++| fill-the-screen
++
++
++For that we will need a package called [mdgriffith](https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/)
++
++Install it using terminal
++
++`CTRL + C` to stop elm-reactor and type `elm install mdgriffith/elm-ui`
++
++Then in `src/Main.elm` add import `import Element` and change `Main` to look like this :
++
++Main = * the code of Element.layout should be here*
++
++Note that our scene fills the screen, it's time to put the dot at the center of the scene.
++
++* a picture of cx and cy with width and height should be here *
++
++If we would know the height and width of the screen , we could calculate its position as
++
++`cx = width/2`
++
++`cy = height/2`
++
++There is a problem though, we don't know the height and width.
++☹️
++
++But we don't need to!
++
++*Scalable icon should be here*
++
++S for Scalable
++
++Instead of moving the dot, we can set the boundaries of the scene so that the dot is at the center using a property called Viewbox.
++
++| view-box
++
++Here is how the viewbox works
++
++* a picture of viewbox should be here*
++
++viewbox = 0 0 100 100 (left, top, width and height respectively)
++
++* a picture of periscope looking table should be here*
++
++The trick is to set the width and height to any arbitrary value (say 1000) and top and left to `- width/2`!
++
++That way point (0,0) will always be right in the middle (the distance to the left and to the right is the same, likewise the distance to the top and bottom).
++
++First, lets see where the SVG boundaries are by giving it a background color
++
++
++| centered-dot
++
++
++`Svg [background "pink"]
++[...
++]`
++
++Give a color to the dot
++
++
++| list
++    - Explain SVG attributes ( svg elements take a list of attributes)
++    - Everyone can pick a color
++
++
++Multiple dots
++
++
++| list
++    -> Introduce a problem: We want to have two dots on the screen
++    -> Explain a list, and show there is already a list there (with one item)
++
++
++| header
++    Do you see any other lists?
++
++
++| list
++    -> Element takes a list of children
++    -> Mention , how can we have a list with one or zero elements? Shopping list with one item. Empty spoon drawer( list of spoons)
++    -> Give your new dot a color and different coordinates
++    -> Show the code for 2 dots
++    -> Exercise: make 3 more dots (5 total)
++
++
++| header
++    Day 2 - Place the dots in a circle
++
++Introduce a problem: We want to place the dots in a circle, show an example on the slides
++
++How we will get there: We will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
++
++How can we figure out the correct cartesian coordinates?
++
++| note
++    Story about the flowerpot and the table: two ways to measure relative position, one is distance from an x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
++
++
++What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a '360'). We see that circles and angles are closely related!
++
++Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
++
++We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
++
++Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
++
++So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
++
++sing a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
++
++Let's use a chart to figure out the sin and cos of our angles
++
++/It's important to get a chart, otherwise we have to use calculators which work with radians/
++
++Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
++
++Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
++
++We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
++
++Sample viewbox program to demonstrate
++
++Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
++
++`svg [viewbox "-100 -100 200 200 " ] []`
++
++| header
++    Day 3 - Let the computer do the math
++
++Introduce the problem: Can we avoid all these repetitive and manual steps?
++
++What is a program made of? (in the REPL):
++
++Values and names
++
++| monospace
++    5
++    10.4
++    "Hello World"
++    [1, 2, 3, 4, 5]
++
++
++| list
++    -> Numbers like 5, 10.4
++    -> Strings like "Hello World"
++    -> and lists containing numbers or strings
++
++
++are all values
++
++They are self-referential. They simply state what they are. There are other kinds of values that we will see later.
++
++You can give (or assign) a name to a value, as shown.
++
++| monospace
++    family = 5
++    price = 10.4
++    morningGreeting = "Hello World"
++    fingers = [1, 2, 3, 4, 5]
++
++Here, `family` is a name and `5` is the value assigned to `family`.
++
++You can get the value back by calling its name.
++
++```
++---- Elm 0.19.0 ----------------------------------------------------------------
++Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
++--------------------------------------------------------------------------------
++> family = 5
++5 : number
++> family
++5 : number
++```
++
++Operators (`+`, `-`, `++`, `::`)
++
++| monospace
++    > 2 + 5
++    7 : number
++
++| monospace
++    > 4 - 6
++    -2 : number
++
++| monospace
++    > "Hello" ++ " world!"
++    "Hello world!" : String
++
++
++| monospace
++    > 1 :: [2, 3, 4, 5]
++    [1,2,3,4,5] : List number
++
++
++Operators take two values and return a new value. We know that names refer to values, so we can use them in place of values:
++
++| monospace
++    > family = 5
++    5 : number
++    > family + 2
++    7 : number
++
++
++You can also give a name to the value returned by an operator:
++
++| monospace
++    > family = 5
++    5 : number
++    > familyAndPets = family + 2
++    7 : number
++    > familyAndPets
++    7 : number
++
++Note that different values have different types.
++
++| monospace
++    5 : number
++    10.4 : Float
++    "Hello World" : String
++    [1,2,3,4,5] : List number
++
++Different operators work on different types. Adding a number and a string doesn't make sense. So if you try, Elm will complain (and give a helpful hint):
++
++| monospace
++    > "Hello " + 5
++    -- TYPE MISMATCH ----------------------------------------------------------- elm
++    I cannot do addition with String values like this one:
++    5|   "Hello " + 5
++         ^^^^^^^^
++    The (+) operator only works with Int and Float values.
++    Hint: Switch to the (++) operator to append strings!
++
++
++(Int and Float are both types representing numbers)
++
++Functions
++
++`fun something = something ++ " is fun."`
++
++Explain: a function is a thing that takes some values (called arguments) and return one value. So it's similar to operators. In fact operators are functions!
++
++| monospace
++    (-) 5 10
++    5 - 10
++
++
++You can think of a function as a machine. You put something in the machine, and it produces something in return. For example think about a machine that produces rubber ducks. You put a bucket of white plastic pellets and a bucket of red paint, and you get a bunch of red rubber ducks!
++
++What you get will depends on what you put. The color of the ducks depends on the paint you put. Quantity of ducks depends on how much plastic you put in.
++
++| monospace
++    makeMeSomeDucks color plastic =
++    String.fromInt plastic ++ " " ++ color ++ " rubber ducks"
++
++
++Once you have a function, you can call it like this:
++
++| monospace
++    > makeMeSomeDucks "blue" 12
++    "12 blue rubber ducks" : String
++
++
++Note: functions help organize code into nice reusable chunks.
++
++How do you get functions? There are three ways.
++
++Some functions are always there for you
++
++`(+)`, `(-)`
++
++Some functions you can import using code like this:
++
++`import Svg`
++
++
++and then
++
++| monospace
++    Svg.circle [ cx "10", cy "10", r "20" ] [ ]
++
++
++To call a function that was imported, you have to prefix it with the name of the module (in this example `Svg`).
++
++
++Finally, you will write some functions, just like we saw in the `fun` and `makeMeSomeDucks` examples.
++
++Finally there is one special thing: the first line of the program is a module declaration. For now it's enough for us to know, that it has to be there and it has to match the name of the file.
++
++Exercise: In our Main.elm, try to identify some values, names and function calls:
++
++Hint: `"darkred"` is a value, `main` is a name, `width` is a function.
++
++Hint: There is nothing else there now, but we will soon introduce our own functions.
++
++* Where can we use some functions?*
++
++As we said before, functions are good when we have some repetitive operation that can be parametrized (like rubber ducks production).
++
++Obviously calculating `x` and `y` coordinates is repetitive and can be parametrized (parameters are `radius` and `angle`).
++
++*Apply to our trigonometry calculations*
++
++Compose and copy and paste the trigonometry functions to calculate cx and cy:
++
++| monospace
++    cx (String.fromFloat (cos (degrees 72) * 100))
++    cy (String.fromFloat (sin (degrees 72) * 100))
++
++*Eliminate the repetition:*
++
++Assign a value of `100` to a name `radius`:
++
++| monospace
++    radius = 100
++
++and plug it to our functions:
++
++| monospace
++    cx (String.fromFloat (cos (degrees 72) * radius))
++    cy (String.fromFloat (sin (degrees 72) * radius))
++
++As you see the names are good for repetitive things too.
++
++Define and reuse
++
++`x : angle -> cx`
++
++| monospace
++    x angle =
++    String.fromFloat (cos (degrees angle) * radius)
++
++`y : angle -> cy`
++
++| monospace
++    y angle =
++    String.fromFloat (sin (degrees angle) * radius)
++
++Then plug it into the code making circles:
++| monospace
++    circle
++    [ cx (x 72)
++    , cy (y 72)
++    , r "10"
++    , fill "darkred"
++    ]
++
++define and reuse `dot : angle -> color -> Svg`
++
++| monospace
++    dot angle color =
++    circle
++    [ cx (x angle)
++    , cy (y angle)
++    , r "10"
++    , fill color
++    ]
++
++and plug it into our SVG picture:
++
++| monospace
++    svg [ viewbox "-100 -100 200 200" ]
++    [ dot 0 "darkred"
++    , dot 72 "fuchsia"
++    , dot 144 "saddlebrown"
++    , dot 216 "deepskyblue"
++    , dot 288 "gold"
++    ]
++
++For more cool colors check out [Color keywords page at Mozilla Developer's Network](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords)
++
++*Make it more general*
++
++| list
++    - Show List and list operations (map and indexedMap)
++    - There are a number of functions that operate on lists, for example `List.length` and `List.map`.
++
++
++Examples of a map: a shopping list, after you find each item and place it in your basket, you are 'mapping' from a list of needed items to a list of items in your basket. Now you have two lists, and they are related. One is your original shopping list, the other the list of items in the basket.
++
++Demonstrate:
++
++| monospace
++    > things = ["Programming", "Swimming", "Dancing", "Saddle brown"]
++    ["Programming","Swimming","Dancing","Saddle brown"]
++    : List String
++
++| monospace
++    > List.length things
++    4 : Int
++
++| monospace
++    > List.map fun things
++    ["Programming is fun!","Swimming is fun!","Dancing is fun!","Saddle brown is fun!"]
++    : List String
++
++| monospace
++    > things
++    ["Programming","Swimming","Dancing","Saddle brown"]
++    : List String
++
++
++Notice our original `things` list is unchanged. This is different from our rubber duck machine. The rubber duck turns the plastic and paint into rubber ducks. A function on the other hand 'creates' the value it gives you. You don't loose the original value given to it.
++
++Make a `palette : List color`
++
++| monospace
++    palette =
++    [ "darkred"
++    , "fuchsia"
++    , "saddlebrown"
++    , "deepskyblue"
++    , "gold"
++    ]
++
++
++Use `List.indexedMap dot palette` to generate the dots
++
++
++Another function that operates on lists is `List.indexedMap`. Let's see it at work:
++
++| monospace
++    dot index color =
++    circle
++    [ cx (x ((360 / (List.length palette)) * index))
++    , cy (y ((360 / (List.length palette)) * index))
++    , r "10"
++    , fill color
++    ]
++
++`List.indexedMap dot palette`
++
++We can introduce a `let` block to make our code more readable and avoid repetition.
++
++| monospace
++    dot index color =
++    let
++    angle =
++    (360 / count) * index
++    count =
++    List.length palette
++    in
++    circle
++    [ cx (x angle)
++    , cy (y angle)
++    , r "10"
++    , fill color
++    ]
++
++
++Take a look at the documentation for [List.indexedMap](https://package.elm-lang.org/packages/elm/core/latest/List#indexedMap).
++
++
++| header
++    Day 4 - Introduce SVG groups
++
++We'll need one group for the dots and one group for the lines
++
++| monospace
++    svg [ viewBox "-100 -100 200 200" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.g [] [ Svg.line [ x1 "123", y1 "112", x2 "41", y2 "11", stroke "black" ] [] ]
++    ]
++
++
++Exercise: add a few lines with different colors
++
++Now we'll learn how to color a line with a gradient (going from one color to another)
++
++| monospace
++    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.defs []
++    [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0" ]
++    [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++    , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
++    ]
++    ]
++    , Svg.g []
++    [ Svg.line [ x1 "0", y1 "0", x2 "100", y2 "100", stroke "url(#MyGradient)" ] []
++    , Svg.line [ x1 "0", y1 "0", x2 "-100", y2 "-100", stroke "url(#MyGradient)" ] []
++    ]
++    ]
++
++
++We see a problem here. The gradient always goes from saddlebrown to pink from top left to bottom right. If we want to use the same gradient to connect different points, we'll need to be creative.
++
++| monospace
++    main =
++    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.defs []
++    [ Svg.linearGradient [ Svg.Attributes.id "MyGradient", x1 "0", y1 "0", x2 "1", y2 "0", gradientUnits "userSpaceOnUse" ]
++    [ Svg.stop [ Svg.Attributes.offset "0", Svg.Attributes.stopColor "saddlebrown" ] []
++    , Svg.stop [ Svg.Attributes.offset "1", Svg.Attributes.stopColor "pink" ] []
++    ]
++    ]
++    , Svg.g []
++    [ Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(45),scale(100,1)", stroke "url(#MyGradient)" ] []
++    , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(180),scale(100,1)", stroke "url(#MyGradient)" ] []
++    , Svg.line [ x1 "0", y1 "0", x2 "1", y2 "0", transform "rotate(270),scale(100,1)", stroke "url(#MyGradient)" ] []
++    ]
++    ]
++
++
++*TODO:* We need to fix the viewbox!
++
++
++Here's the trick: we have the lines initially match the gradient. We're using the same x and y values for all the lines and the gradient. Then we transform the lines to position them where we'd like them. We're actually using our old friend polar coordinates here.
++
++If you're interested in what `gradientUnits "userSpaceOnUse"` does, come see me after the lesson.
++
++
++| monospace
++    gradient : Int -> String -> Svg msg
++    gradient index color =
++    Svg.linearGradient
++    [ id ("Gradient-" ++ String.fromInt index)
++    , x1 "0"
++    , y1 "0"
++    , x2 "1"
++    , y2 "0"
++    , gradientUnits "userSpaceOnUse"
++    ]
++    [ Svg.stop
++    [ Svg.Attributes.offset "0"
++    , Svg.Attributes.stopColor "saddlebrown"
++    ]
++    []
++    , Svg.stop
++    [ Svg.Attributes.offset "1"
++    , Svg.Attributes.stopColor color
++    ]
++    []
++    ]
++
++| monospace
++    line : Int -> String -> Svg msg
++    line index color =
++    let
++    count =
++    List.length pallete
++
++    angle =
++    (360 / toFloat count) * toFloat index
++
++    transformation =
++    "rotate("
++    ++ String.fromFloat angle
++    ++ "), scale("
++    ++ String.fromInt radius
++    ++ ", 1)"
++
++| monospace
++    url =
++    "url(#Gradient-" ++ String.fromInt index ++ ")"
++    in
++    Svg.line
++    [ x1 "0"
++    , y1 "0"
++    , x2 "1"
++    , y2 "0"
++    , transform transformation
++    , stroke url
++    ]
++    []
++
++
++| monospace
++    main =
++    svg [ width "600", height "600", viewBox "-300 -300 600 600" ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.defs [] (List.indexedMap gradient pallete)
++    , Svg.g [] (List.indexedMap line pallete)
++    ]
++
++
++This may look like a lot, but our `gradient` and `line` functions are very similar to our `dot` function from earlier. In fact we see an essential principle of good programming design here. We spent a lot of time banging our heads over gradients. Now that we've done that work, we can hide the implementation in our gradient and line functions. Now, if we want to draw a new dot, line, and gradient going from the center to the dot, we only need to add a new item to our palette. Our functions do the rest for us. Our functions conceal our complexity. We can forget about the implementation, so long as we know how to operate them. They're like black boxes, we know what goes in and what comes out, but we don't have to know how they work on the inside.
++
++
++| header
++    Day 5 - Let's make a beautiful tree!
++
++The Problem:
++Describe A tree using data
++
++A tree is a composition of segments. A segment can be either a twig or a branch. Twig is a terminal segment of a tree. When the tree is very young it consists of a single twig. Later this twig will evolve into a branch and will grow it's own twigs, that in turn will evolve into branches. So a branch is a segment of a tree that ends with some other  segments (twigs or branches).
++
++
++Note that a branch can split to several branches that each in turn can split into several branches and so on. This way a tree can be as complex as we want. Note that real trees are like that. It's difficult to say how many generations of branches it can have from the trunk to the smallest little twigs on top of the crown. This kind of structure is called recursive. It repeats the same pattern.
++
++
++Each segment (a twig or a branch) has length, a direction (represented as an angle) and a color (so that it's beautiful despite having no leafs or flowers).
++
++Here is how we can represent it in code:
++
++| monospace
++    type alias Segment =
++    { length : Float
++    , direction : Float
++    , color : Color
++    }
++
++
++Consider  the following
++
++| list
++    - A branch can split into more branches or twigs,
++    - And then that a trunk of a tree is just a first branch or a twig if the tree is very      young, then you can see that each segment separated from the parent branch can be considered a tree.
++
++
++So we can reuse the same structure to represent a tree or any of it's segments!
++
++Look:
++
++| monospace
++    type Tree
++    = Twig Segment
++    | Branch Segment (List Tree)
++
++
++Think about it! A tree is a twig (when it's young) or a branch that splits into __a zero, one or many__ __twigs or branches__.
++
++| list
++    -> zero, one or many: a list
++    -> twig or branch: a tree
++
++
++So a Twig is just a Segment while a branch is a Segment with a list of Trees!
++
++Let's encode a simple tree by hand:
++
++| monospace
++    tree =
++    Branch { length = 10, direction = degrees -90, color = "brown" }
++    [ Branch { length = 6, direction = degrees -45, color = "lightbrown" }
++    [ Twig { length = 2, direction = degrees -30, color = "green" }
++    , Twig { length = 2, direction = degrees -60, color = "green" }
++    ]
++    , Branch { length = 6, direction = degrees -135, color = "lightbrown" }
++    [ Twig { length = 2, direction = degrees -120, color = "green" }
++    , Twig { length = 2, direction = degrees -150, color = "green" }
++    ]
++    ]
++
++
++| header
++    Day 6 - Make the tree grow!
++
++Make a function that given the age of the tree and rules returns a tree
++
++Use time subscription to make the tree grow
++
++Show complete code.
++There is a lot going on here, and it's not obvious how it all works.
++
++Our main value has changed.
++
++| monospace
++    main : Program () State Msg
++    main =
++    Browser.sandbox
++    { init = init
++    , view = view
++    , update = update
++    }
++
++Previously, the value of `main` was an SVG element. Now it contains a call to `Browser.sandbox` function.
++
++A sandbox is a basic interactive program. For a program to be interactive, it need more than just a SVG element.
++
++| monospace
++    type alias State =
++    Color
++    type alias Color =
++    String
++
++
++Here we create something called a type alias. We've already discussed types. A type alias is basically a way to give an alternative name for a type. We give the alias `Color` to `String` and the alias `State` to `Color`. So in the end all three names point to the same type!
++
++
++Why are we doing this? All of these types are strings, and Elm will treat them all as strings. But it will make our code more readable to use these aliases. We are writing an application that allows the user to change the background color. That should explain why our `State` is a `Color`.
++
++
++Our `Browser.sandbox` takes something with the name `init`. This is our initial state. Let's take a look at the value of `init`.
++
++| monospace
++    init : State
++    init =
++    "white"
++
++
++We see that the initial state is `"white"`. We'll see how our application uses this value in a moment.
++
++
++Looking back at our `sandbox`, we see it also takes a `view`. Let's take a look at our view function:
++
++| monospace
++    view : State -> Html Msg
++    view color =
++    svg
++    [ width "600"
++    , height "600"
++    , viewBox "-300 -300 600 600"
++    , Svg.Attributes.style ("background: " ++ color)
++    ]
++    [ Svg.g [] (List.indexedMap dot pallete)
++    , Svg.defs [] (List.indexedMap gradient pallete)
++    , Svg.g [] (List.indexedMap line pallete)
++    ]
++
++
++You'll notice that this `view` function looks very similar to the `main` variable from earlier. Before, our application was only a view. Now the view is only one piece of our interactive application.
++
++
++One important way our `view` function differs from the `main` variable from ealier is that `view` is a function. We see it takes a variable of type `State` (a string), which we have named `color`. In an Elm sandbox project the `view` always takes a variable with the same type as the `init` variable. This is the current state of the application. `view` can use the state to render the application properly, just as Atom uses its state to render text for its user.
++
++
++We see that we've added a line to the svg attributes, `Svg.Attributes.style ("background: " ++ color)`. Here we use the state of the application to set the background color of the svg element.
++
++
++All we need now is some way to update the state (the background color) of the application.
++
++
++Taking a look at our `main` function, we see that it take a third and final function, `update`. Let's look at our update function:
++
++| monospace
++    type Msg
++    = SetBackground Color
++    update : Msg -> State -> State
++    update msg state =
++    case msg of
++    SetBackground color ->
++    color
++
++
++
++We see our `update` function takes two variables, of type `Msg` and `State`. If we think of update as a machine which takes an old state and spits out a new state, this state variable corresponds to the old state.
++
++
++The `msg` variable is something new. Looking above, we see that we have defined a `Msg` variable type. This is similar to our `State` and `Color` union types, in that we have defined a new type that Elm understands. However, unlike the `type alias` constructor, the `type` constructor does not simply give a new name for an existing type. Instead, here we define a completely unique type. We see values with type Msg must have the form `SetBackground Color`. `SetBackground "white"`, `SetBackground "blue"`, `SetBackground "saddlebrown"` are all valid values of the type `Msg`.
++
++
++What is important to understand is that our `msg` variable is used by `update` to determine how it should update the state. We see that update first checks that `msg` has the form `SetBackground color`, and then returns the value of `color`. This will become our new state. Every time update is called, the sandbox will re-render our view with the new state.
++
++
++So now we see how our application changes the background color of the svg element. However, we haven't seen when it will update the background color. Well, we want to change the background color by clicking a dot. So let's take a look at our `dot` function to see if we can find a hint there.
++
++
++*Model*
++The structure of the state of the program
++
++| note
++    *TO DO* /Consistently use Model and model instead of less standard state to make it easier to our participants.
++
++
++
++*Msg*
++What events can there be in the program? Currently have only one possible event- setting the background to a given color.
++
++
++`update`
++
++The instructions of how to change the state when a particular message comes in.
++
++`view`
++
++The instruction of what to display on the screen. It also contains instructions of what messages to send when certain events (like clicks on elements) happen. Every time the state changes the instructions will be applied.
++
++`init`
++
++What is the initial state right after the program is started.
++
++`main`
++
++Glues it all together.
++
++| monospace
++    dot : Int -> String -> Svg Msg
++    dot index color =
++    let
++    angle =
++    (360 / toFloat (List.length pallete)) * toFloat index
++    in
++    circle
++    [ cx (x angle)
++    , cy (y angle)
++    , r "40"
++    , fill color
++    , Svg.Events.onClick (SetBackground color)
++    ]
++    []
++
++
++
++We see there is a new line here:
++
++
++| monospace
++    Svg.Events.onClick (SetBackground color)
++
++
++
++
++`onClick` is an event. It basically tells our svg element to listen for someone to click on it. It will then handle the event by emitting the Msg `SetBackground color`. We know `color` is the color of the dot. We see then that when someone clicks on the dot, the Msg will be emitted, `update` will be called with this Msg and will update our state. Our sandbox will rerender the view with this new state. The user will see the svg element with a new background color.
++
++
++Notice that our `view` function, as well as all the functions that call functions responsible for rendering svg elements, including `gradient`, `line`, and `dot` have return values with type `Svg Msg` or `Html Msg`. This is because svg element can emit Msgs which will be handled by `update`.
+=
+=| header
+=    To do:
+```
+
+# Merge remote-tracking branch 'origin/master' into elm-markup-2
+
+
+
+
+
+# Merge branch 'elm-markup-2' into 'master'
+
+Update Elm Markup package to 2.0.2
+
+See merge request software-garden/software-garden.gitlab.io!1
+
new file mode 100644
index 0000000..caec6c9
--- /dev/null
+++ b/content/devlog/2018-12-14-elm-tree-workshop.md
@@ -0,0 +1,4917 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 9
+
+
+# Merge remote-tracking branch 'origin/master' into content
+
+
+
+
+
+# Use Source Code Pro 18px font in Code and Monospace blocks
+
+Make line numbers extra light and remove the period.
+
+Remove spacing between blocks.
+
+``` diff
+index 36923a4..0a40895 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -355,11 +355,18 @@ document =
+=        monospace =
+=            Mark.Default.monospace
+=                [ Element.padding 20
++                , Font.size 18
+=                , Element.width Element.fill
+=                , Border.color colors.gray
+=                , Border.width 3
+=                , Border.rounded 5
+-                , Font.family [ Font.monospace ]
++                , Font.family
++                    [ Font.external
++                        { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&amp;subset=latin-ext"
++                        , name = "Source Code Pro"
++                        }
++                    , Font.monospace
++                    ]
+=                , Element.scrollbarY
+=                ]
+=
+@@ -381,7 +388,7 @@ document =
+=                                Element.row [ Element.spacing 10 ]
+=                                    [ Element.el
+=                                        [ Font.color colors.gray
+-                                        , Font.size 20
++                                        , Font.extraLight
+=                                        , Element.width (Element.px 40)
+=                                        , css "user-select" "none"
+=                                        , css "-webkit-user-select" "none"
+@@ -390,7 +397,7 @@ document =
+=                                        , css "-o-user-select" "none"
+=                                        , css "-moz-user-select" "none"
+=                                        ]
+-                                        (Element.text (String.fromInt n ++ "."))
++                                        (Element.text (String.fromInt n))
+=                                    , Element.el
+=                                        [ Element.width Element.fill
+=                                        ]
+@@ -404,7 +411,14 @@ document =
+=                            , Border.color colors.gray
+=                            , Border.width 3
+=                            , Border.rounded 5
+-                            , Font.family [ Font.monospace ]
++                            , Font.size 18
++                            , Font.family
++                                [ Font.external
++                                    { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&amp;subset=latin-ext"
++                                    , name = "Source Code Pro"
++                                    }
++                                , Font.monospace
++                                ]
+=                            , Element.scrollbarY
+=                            ]
+=            in
+@@ -724,7 +738,6 @@ document =
+=            Element.textColumn
+=                [ Element.centerX
+=                , Element.width (Element.px 800)
+-                , Element.spacing 24
+=                ]
+=                (List.map (\render -> render model) children)
+=        )
+```
+
+# Merge branch 'content' into 'master'
+
+Add the content from paper slides
+
+See merge request software-garden/software-garden.gitlab.io!2
+
+
+
+# Evolve Main into Browser.document program
+
+The title is extracted from the markup document.
+
+All the blocks and in-lines that do not depend on Model and do not 
+produce Msg are extracted to the Mark.Custom module. This is mainly to 
+avoid name collisions, as their names are pretty common terms like 
+"title" or "link".
+
+``` diff
+index 0a40895..0a89a20 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -28,6 +28,7 @@ import Html.Attributes
+=import Http
+=import Line
+=import Mark
++import Mark.Custom
+=import Mark.Default
+=import NestedTransformations
+=import Parser
+@@ -44,7 +45,7 @@ import ViewBox
+=
+=main : Program Flags Model Msg
+=main =
+-    Browser.element
++    Browser.document
+=        { init = init
+=        , view = view
+=        , update = update
+@@ -98,33 +99,45 @@ init flags =
+=    )
+=
+=
+-view : Model -> Html Msg
++view : Model -> Browser.Document Msg
+=view model =
+=    let
+-        content : Element Msg
++        content : View
+=        content =
+=            case model.markup of
+=                Nothing ->
+-                    Element.el [ Element.centerX, Element.centerY ] <|
+-                        Element.text "Loading content..."
++                    loadingView
+=
+=                Just markup ->
+=                    markup
+=                        |> Mark.parse document
+=                        |> Result.map (\render -> render model)
+-                        |> Result.extract deadEndsElement
++                        |> Result.extract deadEndsView
++
++        loadingView : View
++        loadingView =
++            { title = "Software Garden"
++            , body =
++                "Loading content..."
++                    |> Element.text
++                    |> Element.el
++                        [ Element.centerX
++                        , Element.centerY
++                        ]
++            }
+=
+-        deadEndsElement : List DeadEnd -> Element Msg
+-        deadEndsElement deadEnds =
++        deadEndsView : List DeadEnd -> View
++        deadEndsView deadEnds =
+=            deadEnds
+=                |> List.map deadEndElement
+=                |> Element.column
+=                    [ Element.centerX
+=                    , Element.centerY
+-                    , Background.color colors.maroon
++                    , Background.color Mark.Custom.colors.maroon
+=                    , Element.padding 40
+=                    , Element.spacing 20
+=                    ]
++                |> View "Software Garden : Parsing Errors"
+=
+=        deadEndElement : DeadEnd -> Element Msg
+=        deadEndElement { row, col, problem, contextStack } =
+@@ -238,11 +251,15 @@ view model =
+=                    ++ String.fromInt col
+=                )
+=    in
+-    Element.layout
+-        [ -- Below is a hack that enables static site generation
+-          Element.htmlAttribute (Html.Attributes.id "app-container")
++    { title = content.title
++    , body =
++        [ Element.layout
++            [ -- Below is a hack that enables static site generation
++              Element.htmlAttribute (Html.Attributes.id "app-container")
++            ]
++            content.body
+=        ]
+-        content
++    }
+=
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+@@ -318,210 +335,86 @@ type alias DeadEnd =
+=    Parser.Advanced.DeadEnd Mark.Context Mark.Problem
+=
+=
+-document : Mark.Document (Model -> Element Msg)
++type alias View =
++    { title : String
++    , body : Element Msg
++    }
++
++
++document : Mark.Document (Model -> View)
+=document =
+=    let
+-        title =
+-            Mark.Default.title
+-                [ Element.width Element.fill
+-                , Element.paddingXY 0 32
+-                , Font.center
+-                , Font.size 86
+-                , Font.extraBold
+-                ]
+-                text
+-
+-        header =
+-            Mark.Default.header
+-                [ Font.size 24
+-                , Font.underline
+-                , Element.paddingXY 0 24
+-                ]
+-                text
+-
+-        paragraph : Mark.Block (Model -> Element Msg)
+-        paragraph =
+-            let
+-                render content model =
+-                    Element.paragraph
+-                        [ Element.paddingXY 0 24
+-                        ]
+-                        (content model)
+-            in
+-            Mark.map
+-                render
+-                text
+-
+-        monospace =
+-            Mark.Default.monospace
+-                [ Element.padding 20
+-                , Font.size 18
+-                , Element.width Element.fill
+-                , Border.color colors.gray
+-                , Border.width 3
+-                , Border.rounded 5
+-                , Font.family
+-                    [ Font.external
+-                        { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&amp;subset=latin-ext"
+-                        , name = "Source Code Pro"
+-                        }
+-                    , Font.monospace
++        content :
++            { title : String
++            , children : List (Model -> Element Msg)
++            }
++            -> Model
++            -> View
++        content { title, children } model =
++            children
++                |> List.map (\child -> child model)
++                |> Element.textColumn
++                    [ Element.centerX
++                    , Element.width (Element.maximum 800 Element.fill)
+=                    ]
+-                , Element.scrollbarY
+-                ]
+-
+-        css : String -> String -> Element.Attribute msg
+-        css property value =
+-            Element.htmlAttribute
+-                (Html.Attributes.style
+-                    property
+-                    value
+-                )
+-
+-        code =
+-            let
+-                render string model =
+-                    string
+-                        |> String.split "\n"
+-                        |> List.indexedMap
+-                            (\n loc ->
+-                                Element.row [ Element.spacing 10 ]
+-                                    [ Element.el
+-                                        [ Font.color colors.gray
+-                                        , Font.extraLight
+-                                        , Element.width (Element.px 40)
+-                                        , css "user-select" "none"
+-                                        , css "-webkit-user-select" "none"
+-                                        , css "-ms-user-select" "none"
+-                                        , css "-webkit-touch-callout" "none"
+-                                        , css "-o-user-select" "none"
+-                                        , css "-moz-user-select" "none"
+-                                        ]
+-                                        (Element.text (String.fromInt n))
+-                                    , Element.el
+-                                        [ Element.width Element.fill
+-                                        ]
+-                                        (Element.text loc)
++                |> View title
++
++        {- The document has to start with a Title block containing a String (i.e. single line of unforamtted text). This String will be used in two ways:
++
++           It will be turned into an Element and appended to list of children that are passed to the view (so it can be rendered in the body of the page).
++
++           It will also serve as a title for a Browser.Document.
++        -}
++        structure =
++            Mark.startWith
++                (\title rest ->
++                    let
++                        titleElement model =
++                            title
++                                |> Element.text
++                                |> List.singleton
++                                |> Element.paragraph
++                                    [ Element.width Element.fill
++                                    , Element.paddingXY 0 32
++                                    , Font.center
++                                    , Font.size 86
++                                    , Font.extraBold
+=                                    ]
+-                            )
+-                        |> Element.column
+-                            [ Element.padding 20
+-                            , Element.spacing 10
+-                            , Element.width Element.fill
+-                            , Border.color colors.gray
+-                            , Border.width 3
+-                            , Border.rounded 5
+-                            , Font.size 18
+-                            , Font.family
+-                                [ Font.external
+-                                    { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&amp;subset=latin-ext"
+-                                    , name = "Source Code Pro"
+-                                    }
+-                                , Font.monospace
+-                                ]
+-                            , Element.scrollbarY
+-                            ]
+-            in
+-            Mark.block "Code"
+-                render
+-                Mark.multiline
+-
+-        note : Mark.Block (Model -> Element Msg)
+-        note =
+-            let
+-                render elements model =
+-                    elements
+-                        |> List.map (\element -> element model)
+-                        |> Element.textColumn
+-                            [ Element.padding 20
+-                            , Element.spacing 10
+-                            , Element.width Element.fill
+-                            , Border.width 1
+-                            , Border.color colors.gray
+-                            , Font.color colors.gray
+-                            , Border.rounded 5
+-                            ]
+-            in
+-            Mark.block "Note"
+-                render
++                    in
++                    { title = title
++                    , children = titleElement :: rest
++                    }
++                )
++                Mark.Custom.title
+=                (Mark.manyOf
+-                    [ paragraph
+-                    , header
+-                    , list
++                    [ Mark.Custom.header
++                    , Mark.Custom.paragraph
++                    , Mark.Custom.monospace
++                    , Mark.Custom.code
++                    , Mark.Custom.note
++                    , Mark.Custom.emphasize
++                    , Mark.Custom.list
++                    , Mark.Custom.image
++
++                    -- Embeded programs
++                    , counter
++                    , simplest
++                    , fillTheScreen
++                    , dotAtTheCenterOfTheScreen
++                    , centeredDot
++                    , line
++                    , gradient
++                    , transformations
++                    , nestedTransformations
++                    , cartesianCoordinates
++                    , polarCoordinates
++                    , rosette
++                    , spiral
++                    , tree
++                    , viewBox
+=                    ]
+=                )
+=
+-        emphasize : Mark.Block (Model -> Element Msg)
+-        emphasize =
+-            let
+-                render element model =
+-                    model
+-                        |> element
+-                        |> Element.el
+-                            [ Element.spacing 30
+-                            , Font.bold
+-                            , Font.size 30
+-                            , Font.center
+-                            , Element.paddingXY 0 40
+-                            , Element.width Element.fill
+-                            ]
+-            in
+-            Mark.block "Emphasize"
+-                render
+-                paragraph
+-
+-        image =
+-            Mark.Default.image
+-                [ Element.width Element.fill
+-                ]
+-
+-        list =
+-            Mark.Default.list
+-                { icon = Mark.Default.listIcon
+-                , style =
+-                    \cursor ->
+-                        case List.length cursor of
+-                            0 ->
+-                                -- top level element
+-                                [ Element.spacing 16 ]
+-
+-                            1 ->
+-                                [ Element.spacing 16 ]
+-
+-                            2 ->
+-                                [ Element.spacing 16 ]
+-
+-                            _ ->
+-                                [ Element.spacing 8 ]
+-                }
+-                text
+-
+-        text : Mark.Block (Model -> List (Element Msg))
+-        text =
+-            let
+-                defaultTextStyle =
+-                    Mark.Default.defaultTextStyle
+-            in
+-            Mark.Default.textWith
+-                { defaultTextStyle
+-                    | inlines = defaultTextStyle.inlines ++ [ icon ]
+-                }
+-
+-        icon : Mark.Inline (Model -> Element Msg)
+-        icon =
+-            Mark.inline "Icon"
+-                (\name model ->
+-                    icons
+-                        |> Dict.get name
+-                        |> Maybe.map (FeatherIcons.toHtml [])
+-                        |> Maybe.map Element.html
+-                        |> Maybe.withDefault
+-                            (Element.text
+-                                ("Icon not found: '" ++ name ++ "'")
+-                            )
+-                )
+-                |> Mark.inlineString "name"
+-
+=        -- Embeded programs' blocks
+=        counter : Mark.Block (Model -> Element Msg)
+=        counter =
+@@ -734,46 +627,5 @@ document =
+=            Mark.stub "ViewBox" render
+=    in
+=    Mark.document
+-        (\children model ->
+-            Element.textColumn
+-                [ Element.centerX
+-                , Element.width (Element.px 800)
+-                ]
+-                (List.map (\render -> render model) children)
+-        )
+-        (Mark.manyOf
+-            [ title
+-            , header
+-            , paragraph
+-            , monospace
+-            , code
+-            , note
+-            , emphasize
+-            , list
+-            , image
+-
+-            -- Embeded programs
+-            , counter
+-            , simplest
+-            , fillTheScreen
+-            , dotAtTheCenterOfTheScreen
+-            , centeredDot
+-            , line
+-            , gradient
+-            , transformations
+-            , nestedTransformations
+-            , cartesianCoordinates
+-            , polarCoordinates
+-            , rosette
+-            , spiral
+-            , tree
+-            , viewBox
+-            ]
+-        )
+-
+-
+-colors =
+-    { maroon = Element.rgb 0.7 0 0
+-    , gray = Element.rgb 0.8 0.8 0.8
+-    , pink = Element.rgb 1 0.6 0.6
+-    }
++        content
++        structure
+```
+
+``` diff
+new file mode 100644
+index 0000000..3e50354
+--- /dev/null
++++ b/src/Mark/Custom.elm
+@@ -0,0 +1,240 @@
++module Mark.Custom exposing (code, colors, css, emphasize, header, icon, image, list, monospace, note, paragraph, text, title)
++
++import Dict
++import Element exposing (Element)
++import Element.Background as Background
++import Element.Border as Border
++import Element.Font as Font
++import FeatherIcons exposing (icons)
++import Html exposing (Html)
++import Html.Attributes
++import Mark
++import Mark.Default
++
++
++title : Mark.Block String
++title =
++    Mark.block "Title"
++        identity
++        Mark.string
++
++
++header : Mark.Block (model -> Element msg)
++header =
++    Mark.Default.header
++        [ Font.size 24
++        , Font.underline
++        , Element.paddingXY 0 24
++        ]
++        text
++
++
++paragraph : Mark.Block (model -> Element msg)
++paragraph =
++    let
++        render content model =
++            Element.paragraph
++                [ Element.paddingXY 0 24
++                ]
++                (content model)
++    in
++    Mark.map
++        render
++        text
++
++
++monospace : Mark.Block (model -> Element msg)
++monospace =
++    Mark.Default.monospace
++        [ Element.padding 20
++        , Font.size 18
++        , Element.width Element.fill
++        , Border.color colors.gray
++        , Border.width 3
++        , Border.rounded 5
++        , Font.family
++            [ sourceCodePro
++            , Font.monospace
++            ]
++        , Element.scrollbarY
++        ]
++
++
++code : Mark.Block (a -> Element msg)
++code =
++    let
++        render string model =
++            string
++                |> String.split "\n"
++                |> List.indexedMap
++                    (\n loc ->
++                        Element.row [ Element.spacing 10 ]
++                            [ Element.el
++                                [ Font.color colors.gray
++                                , Font.extraLight
++                                , Element.width (Element.px 40)
++                                , css "user-select" "none"
++                                , css "-webkit-user-select" "none"
++                                , css "-ms-user-select" "none"
++                                , css "-webkit-touch-callout" "none"
++                                , css "-o-user-select" "none"
++                                , css "-moz-user-select" "none"
++                                ]
++                                (Element.text (String.fromInt n))
++                            , Element.el
++                                [ Element.width Element.fill
++                                ]
++                                (Element.text loc)
++                            ]
++                    )
++                |> Element.column
++                    [ Element.padding 20
++                    , Element.spacing 10
++                    , Element.width Element.fill
++                    , Border.color colors.gray
++                    , Border.width 3
++                    , Border.rounded 5
++                    , Font.size 18
++                    , Font.family
++                        [ sourceCodePro
++                        , Font.monospace
++                        ]
++                    , Element.scrollbarY
++                    ]
++    in
++    Mark.block "Code"
++        render
++        Mark.multiline
++
++
++note : Mark.Block (model -> Element msg)
++note =
++    let
++        render elements model =
++            elements
++                |> List.map (\element -> element model)
++                |> Element.textColumn
++                    [ Element.padding 20
++                    , Element.spacing 10
++                    , Element.width Element.fill
++                    , Border.width 1
++                    , Border.color colors.gray
++                    , Font.color colors.gray
++                    , Border.rounded 5
++                    ]
++    in
++    Mark.block "Note"
++        render
++        (Mark.manyOf
++            [ paragraph
++            , header
++            , list
++            ]
++        )
++
++
++emphasize : Mark.Block (model -> Element msg)
++emphasize =
++    let
++        render element model =
++            model
++                |> element
++                |> Element.el
++                    [ Element.spacing 30
++                    , Font.bold
++                    , Font.size 30
++                    , Font.center
++                    , Element.paddingXY 0 40
++                    , Element.width Element.fill
++                    ]
++    in
++    Mark.block "Emphasize"
++        render
++        paragraph
++
++
++image : Mark.Block (model -> Element msg)
++image =
++    Mark.Default.image
++        [ Element.width Element.fill
++        ]
++
++
++list : Mark.Block (model -> Element msg)
++list =
++    Mark.Default.list
++        { icon = Mark.Default.listIcon
++        , style =
++            \cursor ->
++                case List.length cursor of
++                    0 ->
++                        -- top level element
++                        [ Element.spacing 16 ]
++
++                    1 ->
++                        [ Element.spacing 16 ]
++
++                    2 ->
++                        [ Element.spacing 16 ]
++
++                    _ ->
++                        [ Element.spacing 8 ]
++        }
++        text
++
++
++text : Mark.Block (model -> List (Element msg))
++text =
++    let
++        defaultTextStyle =
++            Mark.Default.defaultTextStyle
++    in
++    Mark.Default.textWith
++        { defaultTextStyle
++            | inlines = defaultTextStyle.inlines ++ [ icon ]
++        }
++
++
++icon : Mark.Inline (model -> Element msg)
++icon =
++    Mark.inline "Icon"
++        (\name model ->
++            icons
++                |> Dict.get name
++                |> Maybe.map (FeatherIcons.toHtml [])
++                |> Maybe.map Element.html
++                |> Maybe.withDefault
++                    (Element.text
++                        ("Icon not found: '" ++ name ++ "'")
++                    )
++        )
++        |> Mark.inlineString "name"
++
++
++
++-- Helpers
++
++
++css : String -> String -> Element.Attribute msg
++css property value =
++    Element.htmlAttribute
++        (Html.Attributes.style
++            property
++            value
++        )
++
++
++sourceCodePro : Font.Font
++sourceCodePro =
++    Font.external
++        { url = "https://fonts.googleapis.com/css?family=Source+Code+Pro:200,400&amp;subset=latin-ext"
++        , name = "Source Code Pro"
++        }
++
++
++colors : { gray : Element.Color, maroon : Element.Color, pink : Element.Color }
++colors =
++    { maroon = Element.rgb 0.7 0 0
++    , gray = Element.rgb 0.8 0.8 0.8
++    , pink = Element.rgb 1 0.6 0.6
++    }
+```
+
+# Evolve Main program into SPA, split content into pages
+
+Create scripts (develop and capture).
+
+Temporarily enable static pages CI pipeline for all branches to test it 
+before merging.
+
+``` diff
+index 266c915..8359570 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -4,4 +4,5 @@
+=.#*
+=/index.html
+=/public/
++/built/
+=/node_modules/
+```
+
+``` diff
+index 74b4280..aae0419 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -6,14 +6,10 @@ before_script:
+=pages:
+=  stage: deploy
+=  script:
+-    - npx elm make src/Main.elm --output public/index.js
+-    - cp index.txt container.html public/
+-    ## FIXME: Make puppeteer work in the container
+-    #- cp container.html public/index.html
+-    - npx coffee src/capture.coffee > public/index.html
++    - scripts/capture
+=
+=  artifacts:
+=    paths:
+=      - public
+-  only:
+-    - master
++  # only:
++  #   - master
+```
+
+``` diff
+index b30969d..fb384fe 100644
+--- a/container.html
++++ b/container.html
+@@ -5,10 +5,10 @@
+=    <title>Software Garden</title>
+=  </head>
+=  <body>
+-    <div id="app-container"></div>
+-    <script src="/index.js"></script>
++    <!-- <div id="app-container"></div> -->
++    <script src="/assets/index.js"></script>
+=    <script>
+-      Elm.Main.init({ node: document.querySelector("#app-container") })
++      Elm.Main.init()
+=    </script>
+=  </body>
+=</html>
+```
+
+``` diff
+new file mode 100644
+index 0000000..3cd7b9e
+--- /dev/null
++++ b/content/day-1.txt
+@@ -0,0 +1,250 @@
++| Title
++    Day 1
++
++| Emphasize
++    Let's make a Dot!
++
++
++| Note
++    Today we are going to learn about
++
++    | List
++        -> Scalable Vector Graphics
++        -> cartesian Coordinates System
++        -> Layouts with ELM UI
++
++
++| Header
++    First Program!
++
++As mentioned before, programs are represented as text (called the /source code/). The source code is stored in files {Icon|name=file} and files are organized in directories {Icon|name=folder}.
++
++
++So the first step is to create a directory for our new program. Lets call it fpart.
++In the terminal type
++
++| Monospace
++    mkdir fpart/
++
++and then
++
++| Monospace
++    cd fpart/
++
++This creates a new directory and makes it the current one. Again, don't worry about the details.
++
++To easily create a new program, we can type
++
++| Monospace
++    elm init
++
++Then to create a file with source code, type
++
++| Monospace
++    atom src/Main.elm
++
++This command should open a new Atom window with empty text file.
++
++| Header
++    Main.elm
++
++| Code
++    module Main exposing (main)
++
++
++    import Html
++
++    main=
++       Html.text "Hello, Tree"
++
++Type the above in the editor and save the file.
++To see the program running type the following in the terminal
++
++| Monospace
++    elm reactor
++
++This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
++
++
++| Emphasize
++    Voila!
++
++
++Open following address in the web browser
++
++| Emphasize
++    {Link|http://localhost:8000/src/Main.elm|url=http://localhost:8000/src/Main.elm}
++
++| Note
++    *TODO*: Make the link display // characters
++
++Note that the same address was printed by Elm reactor
++
++| Header
++    The problem
++
++We want to have a dot at the center of the screen, like this
++
++| DotAtTheCenterOfTheScreen
++
++Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate together it step by step.
++
++| Code
++    module Main exposing (main)
++
++    import Element
++    import Svg
++    import Svg.Attributes
++
++
++    main =
++        Svg.svg
++            [ Svg.Attributes.viewBox "-100 -100 200 200"
++            ]
++            [ Svg.circle
++                [ Svg.Attributes.r "10"
++                , Svg.Attributes.cx "0"
++                , Svg.Attributes.cy "0"
++                ]
++                []
++            ]
++            |> Element.html
++            |> Element.layout
++                [ Element.width Element.fill
++                , Element.height Element.fill
++                ]
++
++
++
++We are going to use a technology called SVG (Scalable Vector Graphics). Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in terminal by pressing {Code|CTRL} + {Code|C} and type the following:
++
++| Monospace
++    elm install elm/svg
++
++Then start the reactor again:
++
++| Monospace
++    elm reactor
++
++| Note
++    You can press up arrow  {Icon|name=arrow-up}  on the keyboard to get to the previous commands.
++
++Reload the browser. You should see something like this:
++
++| Simplest
++
++Why is the dot at the corner?
++
++It's because its center is at point called {Code|origin} or {Code|(0, 0)}. But what does it mean ?
++
++* a picture of a vase on a table should be here *
++
++Move sliders to change the x and y coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down it should go.
++
++| CartesianCoordinates
++
++To change where the dot is we can use cx and cy attributes of the circle
++
++* simplest with cx and cy should be here *
++
++* Module main exposing main should be here *
++
++Note that we have a complete program that draws a dot at the center of the screen , lets take a moment to understand it.
++
++Think about this ( * a picture of eggs and a box of eggs should be here * )
++
++| Note
++    An egg is a thing.
++    Six eggs are six things.
++    A box of six eggs is a thing.
++
++
++
++Now, let's make the SVG element fill the screen
++
++
++| FillTheScreen
++
++
++For that we will need a package called {Link|mdgriffith//elm-ui|url=https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/}
++
++Install it using terminal
++
++{Code|CTRL + C } to stop elm-reactor and type {Code|elm install mdgriffith//elm-ui}
++
++Then in {Code|src//Main.elm} add import {Code|import Element} and change {Code|Main} to look like this :
++
++Main = * the code of Element.layout should be here*
++
++Note that our scene fills the screen, it's time to put the dot at the center of the scene.
++
++* a picture of cx and cy with width and height should be here *
++
++If we would know the height and width of the screen , we could calculate its position as
++
++| Monospace
++    cx = width/2
++    cy = height/2
++
++There is a problem though, we don't know the height and width.
++☹️
++
++But we don't need to!
++
++*Scalable icon should be here*
++
++S for Scalable
++
++Instead of moving the dot, we can set the boundaries of the scene so that the dot is at the center using a property called Viewbox.
++
++| ViewBox
++
++Here is how the viewbox works
++
++* a picture of viewbox should be here*
++
++viewbox = 0 0 100 100 (left, top, width and height respectively)
++
++* a picture of periscope looking table should be here*
++
++The trick is to set the width and height to any arbitrary value (say 1000) and top and left to {Code|- width//2}!
++
++That way point (0,0) will always be right in the middle (the distance to the left and to the right is the same, likewise the distance to the top and bottom).
++
++First, lets see where the SVG boundaries are by giving it a background color
++
++
++| CenteredDot
++
++
++| Code
++    Svg [background "pink"]
++        [...
++        ]
++
++Give a color to the dot
++
++
++| List
++    - Explain SVG attributes ( svg elements take a list of attributes)
++    - Everyone can pick a color
++
++
++Multiple dots
++
++
++| List
++    -> Introduce a problem: We want to have two dots on the screen
++    -> Explain a list, and show there is already a list there (with one item)
++
++
++| Header
++    Do you see any other lists?
++
++
++| List
++    -> Element takes a list of children
++    -> Mention , how can we have a list with one or zero elements? Shopping list with one item. Empty spoon drawer( list of spoons)
++    -> Give your new dot a color and different coordinates
++    -> Show the code for 2 dots
++    -> Exercise: make 3 more dots (5 total)
+```
+
+``` diff
+new file mode 100644
+index 0000000..58f6798
+--- /dev/null
++++ b/content/index.txt
+@@ -0,0 +1,13 @@
++| Title
++    Software Garden
++
++| Emphasize
++    ⚘
++
++| Emphasize
++    A functional programming workshop for non-programmers
++
++| List
++    # {Link|Before the course begins|url=/preparation.html}
++    # {Link|Day 1 - Let's Make a Dot|url=/day-1.html}
++    # {Link|The rest - Work in progress|url=/rest.html}
+```
+
+``` diff
+new file mode 100644
+index 0000000..33c56c2
+--- /dev/null
++++ b/content/preparation.txt
+@@ -0,0 +1,103 @@
++| Title
++    Before the course begins
++
++| Note
++    This setup instructions are based on an assumption that you are using a Mac.
++
++    If you are using Linux or BSD, then you probably know how to install stuff.
++
++    If you are using anything else, then... well, good luck.
++
++    The rest of the instructions should work with any platform.
++
++| Header
++    Setup
++
++We will need
++
++| List
++    - a text editor (I use Atom)
++    - and the Elm programming language
++
++| Header
++    Installing Elm
++
++
++To install the Elm programming language, go to it's website:
++
++| Emphasize
++    {Link|elm-lang.org|url=http://elm-lang.org/}
++
++
++and follow the installation instructions.
++
++Some of the tools we use with Elm require Node.js. Go to the {Link|Node.js|url=https://nodejs.org/en/} website and install the current version (the green button on the right)
++
++We will need to use the terminal {Icon|name=terminal} a little bit. Don't be scared. It's easy.
++
++| Image
++    src = /assets/mac-launchpad-terminal.png
++    description = Here is a picture of terminal
++
++
++Mac Terminal app window
++You should see a window like this
++
++
++| Image
++    src = /assets/mac-terminal-window.png
++    description = A picture of Mac Terminal
++
++Type the following in the terminal.
++
++
++| Monospace
++    elm repl
++
++Note that the command line changes. You should see something like this
++
++| Monospace
++    ---- Elm 0.19.0 --------------------------------------------------
++    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
++    ------------------------------------------------------------------
++    >
++
++It's the Elm REPL (Read-Evaluate-Print Loop). Inside the REPL type.
++
++| Monospace
++    2+2
++
++And expect to see
++
++| Monospace
++    ---- Elm 0.19.0 ----------------
++    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
++    --------------------------------
++    > 2 + 2
++    4 : number
++    >
++
++Easy, huh?
++We will learn more about REPL later. For now type
++:exit to close it.
++The command line should get back to its initial state.
++
++| Header
++    Install Atom Text Editor
++
++Computer Programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use Atom here.
++
++Go to {Link|atom.io|url=https://atom.io} and download the editor. After installation on Mac, open it and from Atom menu choose Install Shell Commands.
++
++One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
++
++| Monospace
++    apm install language-elm
++
++APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.
++We are all set.
++
++Got so far? Congratulations!
++
++| Emphasize
++    You are ready for {Link|Day 1|url=/day-1.html}!
+```
+
+``` diff
+similarity index 74%
+rename from index.txt
+rename to content/rest.txt
+index b241d7f..250aed1 100644
+--- a/index.txt
++++ b/content/rest.txt
+@@ -1,361 +1,8 @@
+=| Title
+-    Software Garden
++    The rest
+=
+=| Emphasize
+-    ⚘
+-
+-| Emphasize
+-    A functional programming workshop for non-programmers
+-
+-
+-
+-| Header
+-    Before the course begins
+-
+-| Header
+-    Setup
+-
+-| Note
+-    This setup instructions are based on an assumption that you are using a Mac.
+-
+-    If you are using Linux or BSD, then you probably know how to install stuff.
+-
+-    If you are using anything else, then... well, good luck.
+-
+-    The rest of the instructions should work with any platform.
+-
+-
+-We will need
+-
+-| List
+-    - a text editor (I use Atom)
+-    - and the Elm programming language
+-
+-| Header
+-    Installing Elm
+-
+-
+-To install the Elm programming language, go to it's website:
+-
+-| Emphasize
+-    {Link|elm-lang.org|url=http://elm-lang.org/}
+-
+-
+-and follow the installation instructions.
+-
+-Some of the tools we use with Elm require Node.js. Go to the {Link|Node.js|url=https://nodejs.org/en/} website and install the current version (the green button on the right)
+-
+-We will need to use the terminal {Icon|name=terminal} a little bit. Don't be scared. It's easy.
+-
+-| Image
+-    src = /assets/mac-launchpad-terminal.png
+-    description = Here is a picture of terminal
+-
+-
+-Mac Terminal app window
+-You should see a window like this
+-
+-
+-| Image
+-    src = /assets/mac-terminal-window.png
+-    description = A picture of Mac Terminal
+-
+-Type the following in the terminal.
+-
+-
+-| Monospace
+-    elm repl
+-
+-Note that the command line changes. You should see something like this
+-
+-| Monospace
+-    ---- Elm 0.19.0 --------------------------------------------------
+-    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
+-    ------------------------------------------------------------------
+-    >
+-
+-It's the Elm REPL (Read-Evaluate-Print Loop). Inside the REPL type.
+-
+-| Monospace
+-    2+2
+-
+-And expect to see
+-
+-| Monospace
+-    ---- Elm 0.19.0 ----------------
+-    Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help
+-    --------------------------------
+-    > 2 + 2
+-    4 : number
+-    >
+-
+-Easy, huh?
+-We will learn more about REPL later. For now type
+-:exit to close it.
+-The command line should get back to its initial state.
+-
+-| Header
+-    Install Atom Text Editor
+-
+-Computer Programs are represented as text, so the text editor is the most fundamental tool of a programmer. There is a lot of good text editors, but to get you started, we will use Atom here.
+-
+-Go to {Link|atom.io|url=https://atom.io} and download the editor. After installation on Mac, open it and from Atom menu choose Install Shell Commands.
+-
+-One last thing we need is Elm support for Atom editor. You can install it by typing in the terminal:
+-
+-| Monospace
+-    apm install language-elm
+-
+-APM is the Atom Package Manager, but don't pay much attention to it. We won't be using it again.
+-We are all set.
+-
+-| Header
+-    Day 1 - let's make a dot!
+-
+-| Note
+-    Today we are going to learn about
+-
+-    | List
+-        -> Scalable Vector Graphics
+-        -> cartesian Coordinates System
+-        -> Layouts with ELM UI
+-
+-
+-| Header
+-    First Program!
+-
+-As mentioned before, programs are represented as text (called the /source code/).
+-The source code is stored in files {Icon|name=file} and files are organized in directories {Icon|name=folder}.
+-
+-
+-So the first step is to create a directory for our new program. Lets call it fpart.
+-In the terminal type
+-
+-| Monospace
+-    mkdir fpart/
+-
+-and then
+-
+-| Monospace
+-    cd fpart/
+-
+-This creates a new directory and makes it the current one. Again, don't worry about the details.
+-
+-To easily create a new program, we can type
+-
+-| Monospace
+-    elm init
+-
+-Then to create a file with source code, type
+-
+-| Monospace
+-    atom src/Main.elm
+-
+-This command should open a new Atom window with empty text file.
+-
+-| Header
+-    Main.elm
+-
+-| Code
+-    module Main exposing (main)
+-
+-
+-    import Html
+-
+-    main=
+-       Html.text "Hello, Tree"
+-
+-Type the above in the editor and save the file.
+-To see the program running type the following in the terminal
+-
+-| Monospace
+-    elm reactor
+-
+-This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
+-
+-
+-| Emphasize
+-    Voila!
+-
+-
+-Open following address in the web browser
+-
+-| Emphasize
+-    {Link|http://localhost:8000/src/Main.elm|url=http://localhost:8000/src/Main.elm}
+-
+-| Note
+-    *TODO*: Make the link display // characters
+-
+-Note that the same address was printed by Elm reactor
+-
+-| Header
+-    The problem
+-
+-We want to have a dot at the center of the screen, like this
+-
+-| DotAtTheCenterOfTheScreen
+-
+-Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate together it step by step.
+-
+-| Code
+-    module Main exposing (main)
+-
+-    import Element
+-    import Svg
+-    import Svg.Attributes
+-
+-
+-    main =
+-        Svg.svg
+-            [ Svg.Attributes.viewBox "-100 -100 200 200"
+-            ]
+-            [ Svg.circle
+-                [ Svg.Attributes.r "10"
+-                , Svg.Attributes.cx "0"
+-                , Svg.Attributes.cy "0"
+-                ]
+-                []
+-            ]
+-            |> Element.html
+-            |> Element.layout
+-                [ Element.width Element.fill
+-                , Element.height Element.fill
+-                ]
+-
+-
+-
+-We are going to use a technology called SVG (Scalable Vector Graphics). Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in terminal by pressing {Code|CTRL} + {Code|C} and type the following:
+-
+-| Monospace
+-    elm install elm/svg
+-
+-Then start the reactor again:
+-
+-| Monospace
+-    elm reactor
+-
+-| Note
+-    You can press up arrow  {Icon|name=arrow-up}  on the keyboard to get to the previous commands.
+-
+-Reload the browser. You should see something like this:
+-
+-| Simplest
+-
+-Why is the dot at the corner?
+-
+-It's because its center is at point called {Code|origin} or {Code|(0, 0)}. But what does it mean ?
+-
+-* a picture of a vase on a table should be here *
+-
+-Move sliders to change the x and y coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down it should go.
+-
+-| CartesianCoordinates
+-
+-To change where the dot is we can use cx and cy attributes of the circle
+-
+-* simplest with cx and cy should be here *
+-
+-* Module main exposing main should be here *
+-
+-Note that we have a complete program that draws a dot at the center of the screen , lets take a moment to understand it.
+-
+-Think about this ( * a picture of eggs and a box of eggs should be here * )
+-
+-| Note
+-    An egg is a thing.
+-    Six eggs are six things.
+-    A box of six eggs is a thing.
+-
+-
+-
+-Now, let's make the SVG element fill the screen
+-
+-
+-| FillTheScreen
+-
+-
+-For that we will need a package called {Link|mdgriffith//elm-ui|url=https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/}
+-
+-Install it using terminal
+-
+-{Code|CTRL + C } to stop elm-reactor and type {Code|elm install mdgriffith//elm-ui}
+-
+-Then in {Code|src//Main.elm} add import {Code|import Element} and change {Code|Main} to look like this :
+-
+-Main = * the code of Element.layout should be here*
+-
+-Note that our scene fills the screen, it's time to put the dot at the center of the scene.
+-
+-* a picture of cx and cy with width and height should be here *
+-
+-If we would know the height and width of the screen , we could calculate its position as
+-
+-| Monospace
+-    cx = width/2
+-    cy = height/2
+-
+-There is a problem though, we don't know the height and width.
+-☹️
+-
+-But we don't need to!
+-
+-*Scalable icon should be here*
+-
+-S for Scalable
+-
+-Instead of moving the dot, we can set the boundaries of the scene so that the dot is at the center using a property called Viewbox.
+-
+-| ViewBox
+-
+-Here is how the viewbox works
+-
+-* a picture of viewbox should be here*
+-
+-viewbox = 0 0 100 100 (left, top, width and height respectively)
+-
+-* a picture of periscope looking table should be here*
+-
+-The trick is to set the width and height to any arbitrary value (say 1000) and top and left to {Code|- width//2}!
+-
+-That way point (0,0) will always be right in the middle (the distance to the left and to the right is the same, likewise the distance to the top and bottom).
+-
+-First, lets see where the SVG boundaries are by giving it a background color
+-
+-
+-| CenteredDot
+-
+-
+-| Code
+-    Svg [background "pink"]
+-        [...
+-        ]
+-
+-Give a color to the dot
+-
+-
+-| List
+-    - Explain SVG attributes ( svg elements take a list of attributes)
+-    - Everyone can pick a color
+-
+-
+-Multiple dots
+-
+-
+-| List
+-    -> Introduce a problem: We want to have two dots on the screen
+-    -> Explain a list, and show there is already a list there (with one item)
+-
+-
+-| Header
+-    Do you see any other lists?
+-
+-
+-| List
+-    -> Element takes a list of children
+-    -> Mention , how can we have a list with one or zero elements? Shopping list with one item. Empty spoon drawer( list of spoons)
+-    -> Give your new dot a color and different coordinates
+-    -> Show the code for 2 dots
+-    -> Exercise: make 3 more dots (5 total)
++    This is a work in progress. We need to split this part into days.
+=
+=
+=| Header
+```
+
+``` diff
+index 2bb88f6..54d5902 100644
+--- a/elm.json
++++ b/elm.json
+@@ -13,6 +13,7 @@
+=            "elm/json": "1.1.2",
+=            "elm/parser": "1.1.0",
+=            "elm/svg": "1.0.1",
++            "elm/url": "1.0.0",
+=            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
+=            "elm-community/result-extra": "2.2.1",
+@@ -28,7 +29,6 @@
+=            "elm/bytes": "1.0.7",
+=            "elm/file": "1.0.1",
+=            "elm/time": "1.0.0",
+-            "elm/url": "1.0.0",
+=            "elm/virtual-dom": "1.0.2",
+=            "ianmackenzie/elm-float-extra": "1.0.1",
+=            "ianmackenzie/elm-interval": "1.0.1",
+@@ -39,4 +39,4 @@
+=        "direct": {},
+=        "indirect": {}
+=    }
+-}
++}
+\ No newline at end of file
+```
+
+``` diff
+index 3125dbc..19dd474 100644
+--- a/package-lock.json
++++ b/package-lock.json
+@@ -32,11 +32,53 @@
+=        "uri-js": "^4.2.2"
+=      }
+=    },
++    "ansi-regex": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
++      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
++    },
++    "ansi-styles": {
++      "version": "2.2.1",
++      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
++      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
++    },
++    "anymatch": {
++      "version": "1.3.2",
++      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz",
++      "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==",
++      "requires": {
++        "micromatch": "^2.1.5",
++        "normalize-path": "^2.0.0"
++      }
++    },
++    "arr-diff": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
++      "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=",
++      "requires": {
++        "arr-flatten": "^1.0.1"
++      }
++    },
++    "arr-flatten": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
++      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
++    },
++    "arr-union": {
++      "version": "3.1.0",
++      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
++      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
++    },
+=    "array-flatten": {
+=      "version": "1.1.1",
+=      "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+=      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+=    },
++    "array-unique": {
++      "version": "0.2.1",
++      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
++      "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM="
++    },
+=    "asn1": {
+=      "version": "0.2.4",
+=      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+@@ -50,6 +92,16 @@
+=      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+=      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+=    },
++    "assign-symbols": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
++      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
++    },
++    "async-each": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
++      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0="
++    },
+=    "async-limiter": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+@@ -60,6 +112,11 @@
+=      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+=      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+=    },
++    "atob": {
++      "version": "2.1.2",
++      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
++      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
++    },
+=    "aws-sign2": {
+=      "version": "0.7.0",
+=      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+@@ -75,6 +132,66 @@
+=      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+=      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+=    },
++    "base": {
++      "version": "0.11.2",
++      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
++      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
++      "requires": {
++        "cache-base": "^1.0.1",
++        "class-utils": "^0.3.5",
++        "component-emitter": "^1.2.1",
++        "define-property": "^1.0.0",
++        "isobject": "^3.0.1",
++        "mixin-deep": "^1.2.0",
++        "pascalcase": "^0.1.1"
++      },
++      "dependencies": {
++        "define-property": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
++          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
++          "requires": {
++            "is-descriptor": "^1.0.0"
++          }
++        },
++        "is-accessor-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
++          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-data-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
++          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-descriptor": {
++          "version": "1.0.2",
++          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
++          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
++          "requires": {
++            "is-accessor-descriptor": "^1.0.0",
++            "is-data-descriptor": "^1.0.0",
++            "kind-of": "^6.0.2"
++          }
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        }
++      }
++    },
+=    "bcrypt-pbkdf": {
+=      "version": "1.0.2",
+=      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+@@ -92,6 +209,11 @@
+=        "chainsaw": "~0.1.0"
+=      }
+=    },
++    "binary-extensions": {
++      "version": "1.12.0",
++      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz",
++      "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg=="
++    },
+=    "binwrap": {
+=      "version": "0.1.4",
+=      "resolved": "https://registry.npmjs.org/binwrap/-/binwrap-0.1.4.tgz",
+@@ -157,6 +279,16 @@
+=        "concat-map": "0.0.1"
+=      }
+=    },
++    "braces": {
++      "version": "1.8.5",
++      "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz",
++      "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=",
++      "requires": {
++        "expand-range": "^1.8.1",
++        "preserve": "^0.2.0",
++        "repeat-element": "^1.1.2"
++      }
++    },
+=    "buffer-from": {
+=      "version": "1.1.1",
+=      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+@@ -172,6 +304,29 @@
+=      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+=      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+=    },
++    "cache-base": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
++      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
++      "requires": {
++        "collection-visit": "^1.0.0",
++        "component-emitter": "^1.2.1",
++        "get-value": "^2.0.6",
++        "has-value": "^1.0.0",
++        "isobject": "^3.0.1",
++        "set-value": "^2.0.0",
++        "to-object-path": "^0.3.0",
++        "union-value": "^1.0.0",
++        "unset-value": "^1.0.0"
++      },
++      "dependencies": {
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
+=    "caseless": {
+=      "version": "0.12.0",
+=      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+@@ -185,11 +340,92 @@
+=        "traverse": ">=0.3.0 <0.4"
+=      }
+=    },
++    "chalk": {
++      "version": "1.1.3",
++      "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
++      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
++      "requires": {
++        "ansi-styles": "^2.2.1",
++        "escape-string-regexp": "^1.0.2",
++        "has-ansi": "^2.0.0",
++        "strip-ansi": "^3.0.0",
++        "supports-color": "^2.0.0"
++      }
++    },
++    "charenc": {
++      "version": "0.0.2",
++      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
++      "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
++    },
++    "chokidar": {
++      "version": "1.6.0",
++      "resolved": "http://registry.npmjs.org/chokidar/-/chokidar-1.6.0.tgz",
++      "integrity": "sha1-kMMq1IApAddxPeUy3ChOlqY60Fg=",
++      "requires": {
++        "anymatch": "^1.3.0",
++        "async-each": "^1.0.0",
++        "fsevents": "^1.0.0",
++        "glob-parent": "^2.0.0",
++        "inherits": "^2.0.1",
++        "is-binary-path": "^1.0.0",
++        "is-glob": "^2.0.0",
++        "path-is-absolute": "^1.0.0",
++        "readdirp": "^2.0.0"
++      }
++    },
++    "class-utils": {
++      "version": "0.3.6",
++      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
++      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
++      "requires": {
++        "arr-union": "^3.1.0",
++        "define-property": "^0.2.5",
++        "isobject": "^3.0.0",
++        "static-extend": "^0.1.1"
++      },
++      "dependencies": {
++        "define-property": {
++          "version": "0.2.5",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
++          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
++          "requires": {
++            "is-descriptor": "^0.1.0"
++          }
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
++    "cli-color": {
++      "version": "1.2.0",
++      "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz",
++      "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=",
++      "requires": {
++        "ansi-regex": "^2.1.1",
++        "d": "1",
++        "es5-ext": "^0.10.12",
++        "es6-iterator": "2",
++        "memoizee": "^0.4.3",
++        "timers-ext": "0.1"
++      }
++    },
+=    "coffeescript": {
+=      "version": "2.3.2",
+=      "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.3.2.tgz",
+=      "integrity": "sha512-YObiFDoukx7qPBi/K0kUKyntEZDfBQiqs/DbrR1xzASKOBjGT7auD85/DiPeRr9k++lRj7l3uA9TNMLfyfcD/Q=="
+=    },
++    "collection-visit": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
++      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
++      "requires": {
++        "map-visit": "^1.0.0",
++        "object-visit": "^1.0.0"
++      }
++    },
+=    "combined-stream": {
+=      "version": "1.0.7",
+=      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+@@ -198,6 +434,16 @@
+=        "delayed-stream": "~1.0.0"
+=      }
+=    },
++    "commander": {
++      "version": "2.17.1",
++      "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
++      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
++    },
++    "component-emitter": {
++      "version": "1.2.1",
++      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
++      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
++    },
+=    "concat-map": {
+=      "version": "0.0.1",
+=      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+@@ -214,6 +460,11 @@
+=        "typedarray": "^0.0.6"
+=      }
+=    },
++    "connect-pushstate": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/connect-pushstate/-/connect-pushstate-1.1.0.tgz",
++      "integrity": "sha1-vKsiQnHEOWBKD7D2FMCl9WPojiQ="
++    },
+=    "content-disposition": {
+=      "version": "0.5.2",
+=      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+@@ -234,11 +485,39 @@
+=      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+=      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+=    },
++    "copy-descriptor": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
++      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
++    },
+=    "core-util-is": {
+=      "version": "1.0.2",
+=      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+=      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+=    },
++    "cross-spawn": {
++      "version": "5.0.1",
++      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.0.1.tgz",
++      "integrity": "sha1-o7uzAtsil8vqPATt82lB9GE6o5k=",
++      "requires": {
++        "lru-cache": "^4.0.1",
++        "shebang-command": "^1.2.0",
++        "which": "^1.2.9"
++      }
++    },
++    "crypt": {
++      "version": "0.0.2",
++      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
++      "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
++    },
++    "d": {
++      "version": "1.0.0",
++      "resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
++      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
++      "requires": {
++        "es5-ext": "^0.10.9"
++      }
++    },
+=    "dashdash": {
+=      "version": "1.14.1",
+=      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+@@ -255,6 +534,67 @@
+=        "ms": "^2.1.1"
+=      }
+=    },
++    "decode-uri-component": {
++      "version": "0.2.0",
++      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
++      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
++    },
++    "default-gateway": {
++      "version": "2.7.2",
++      "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz",
++      "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==",
++      "requires": {
++        "execa": "^0.10.0",
++        "ip-regex": "^2.1.0"
++      }
++    },
++    "define-property": {
++      "version": "2.0.2",
++      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
++      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
++      "requires": {
++        "is-descriptor": "^1.0.2",
++        "isobject": "^3.0.1"
++      },
++      "dependencies": {
++        "is-accessor-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
++          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-data-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
++          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-descriptor": {
++          "version": "1.0.2",
++          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
++          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
++          "requires": {
++            "is-accessor-descriptor": "^1.0.0",
++            "is-data-descriptor": "^1.0.0",
++            "kind-of": "^6.0.2"
++          }
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        }
++      }
++    },
+=    "delayed-stream": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+@@ -292,11 +632,86 @@
+=        "binwrap": "0.1.4"
+=      }
+=    },
++    "elm-live": {
++      "version": "3.4.0",
++      "resolved": "https://registry.npmjs.org/elm-live/-/elm-live-3.4.0.tgz",
++      "integrity": "sha512-t/pdd6yvFsft7cOysiV0ilmlE6TgCzDL6JQq+lG0TyL5ZjjTAdaYt5evJdyMliRfGY768zXKKQll4clM4VQyCA==",
++      "requires": {
++        "chalk": "^1.1.1",
++        "chokidar": "1.6.0",
++        "commander": "2.17.1",
++        "cross-spawn": "5.0.1",
++        "elm-serve": "0.4.0"
++      }
++    },
++    "elm-serve": {
++      "version": "0.4.0",
++      "resolved": "https://registry.npmjs.org/elm-serve/-/elm-serve-0.4.0.tgz",
++      "integrity": "sha512-NYXzzaJT/zw8v7jzDWGXuvX3/soj+5NTLHxX0n/T6DICbmyDj8kO7rlI2wSKs9UTNjXhZ7quFQEKcgcf/SZksw==",
++      "requires": {
++        "cli-color": "1.2.0",
++        "commander": "2.9.0",
++        "connect-pushstate": "1.1.0",
++        "finalhandler": "1.1.1",
++        "http-proxy": "1.17.0",
++        "internal-ip": "3.0.1",
++        "minimist": "1.2.0",
++        "opn": "5.3.0",
++        "pem": "1.13.2",
++        "serve-static": "1.13.2",
++        "supervisor": "0.12.0",
++        "url-parse": "1.4.3",
++        "ws": "5.2.0"
++      },
++      "dependencies": {
++        "commander": {
++          "version": "2.9.0",
++          "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
++          "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
++          "requires": {
++            "graceful-readlink": ">= 1.0.0"
++          }
++        },
++        "minimist": {
++          "version": "1.2.0",
++          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
++          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
++        },
++        "ws": {
++          "version": "5.2.0",
++          "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.0.tgz",
++          "integrity": "sha512-c18dMeW+PEQdDFzkhDsnBAlS4Z8KGStBQQUcQ5mf7Nf689jyGk0594L+i9RaQuf4gog6SvWLJorz2NfSaqxZ7w==",
++          "requires": {
++            "async-limiter": "~1.0.0"
++          }
++        }
++      }
++    },
+=    "encodeurl": {
+=      "version": "1.0.2",
+=      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+=      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+=    },
++    "es5-ext": {
++      "version": "0.10.46",
++      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
++      "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
++      "requires": {
++        "es6-iterator": "~2.0.3",
++        "es6-symbol": "~3.1.1",
++        "next-tick": "1"
++      }
++    },
++    "es6-iterator": {
++      "version": "2.0.3",
++      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
++      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
++      "requires": {
++        "d": "1",
++        "es5-ext": "^0.10.35",
++        "es6-symbol": "^3.1.1"
++      }
++    },
+=    "es6-promise": {
+=      "version": "4.2.5",
+=      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+@@ -310,16 +725,99 @@
+=        "es6-promise": "^4.0.3"
+=      }
+=    },
++    "es6-symbol": {
++      "version": "3.1.1",
++      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
++      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
++      "requires": {
++        "d": "1",
++        "es5-ext": "~0.10.14"
++      }
++    },
++    "es6-weak-map": {
++      "version": "2.0.2",
++      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
++      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
++      "requires": {
++        "d": "1",
++        "es5-ext": "^0.10.14",
++        "es6-iterator": "^2.0.1",
++        "es6-symbol": "^3.1.1"
++      }
++    },
+=    "escape-html": {
+=      "version": "1.0.3",
+=      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+=      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+=    },
++    "escape-string-regexp": {
++      "version": "1.0.5",
++      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
++      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
++    },
+=    "etag": {
+=      "version": "1.8.1",
+=      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+=      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+=    },
++    "event-emitter": {
++      "version": "0.3.5",
++      "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
++      "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
++      "requires": {
++        "d": "1",
++        "es5-ext": "~0.10.14"
++      }
++    },
++    "eventemitter3": {
++      "version": "3.1.0",
++      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
++      "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA=="
++    },
++    "execa": {
++      "version": "0.10.0",
++      "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
++      "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
++      "requires": {
++        "cross-spawn": "^6.0.0",
++        "get-stream": "^3.0.0",
++        "is-stream": "^1.1.0",
++        "npm-run-path": "^2.0.0",
++        "p-finally": "^1.0.0",
++        "signal-exit": "^3.0.0",
++        "strip-eof": "^1.0.0"
++      },
++      "dependencies": {
++        "cross-spawn": {
++          "version": "6.0.5",
++          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
++          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
++          "requires": {
++            "nice-try": "^1.0.4",
++            "path-key": "^2.0.1",
++            "semver": "^5.5.0",
++            "shebang-command": "^1.2.0",
++            "which": "^1.2.9"
++          }
++        }
++      }
++    },
++    "expand-brackets": {
++      "version": "0.1.5",
++      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz",
++      "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=",
++      "requires": {
++        "is-posix-bracket": "^0.1.0"
++      }
++    },
++    "expand-range": {
++      "version": "1.8.2",
++      "resolved": "http://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz",
++      "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=",
++      "requires": {
++        "fill-range": "^2.1.0"
++      }
++    },
+=    "express": {
+=      "version": "4.16.4",
+=      "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+@@ -377,6 +875,33 @@
+=      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+=      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+=    },
++    "extend-shallow": {
++      "version": "3.0.2",
++      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
++      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
++      "requires": {
++        "assign-symbols": "^1.0.0",
++        "is-extendable": "^1.0.1"
++      },
++      "dependencies": {
++        "is-extendable": {
++          "version": "1.0.1",
++          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
++          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
++          "requires": {
++            "is-plain-object": "^2.0.4"
++          }
++        }
++      }
++    },
++    "extglob": {
++      "version": "0.3.2",
++      "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz",
++      "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=",
++      "requires": {
++        "is-extglob": "^1.0.0"
++      }
++    },
+=    "extract-zip": {
+=      "version": "1.6.7",
+=      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+@@ -426,6 +951,23 @@
+=        "pend": "~1.2.0"
+=      }
+=    },
++    "filename-regex": {
++      "version": "2.0.1",
++      "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
++      "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY="
++    },
++    "fill-range": {
++      "version": "2.2.4",
++      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
++      "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==",
++      "requires": {
++        "is-number": "^2.1.0",
++        "isobject": "^2.0.0",
++        "randomatic": "^3.0.0",
++        "repeat-element": "^1.1.2",
++        "repeat-string": "^1.5.2"
++      }
++    },
+=    "finalhandler": {
+=      "version": "1.1.1",
+=      "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+@@ -455,6 +997,42 @@
+=        }
+=      }
+=    },
++    "follow-redirects": {
++      "version": "1.5.10",
++      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
++      "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
++      "requires": {
++        "debug": "=3.1.0"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "3.1.0",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
++          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "for-in": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
++      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
++    },
++    "for-own": {
++      "version": "0.1.5",
++      "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz",
++      "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=",
++      "requires": {
++        "for-in": "^1.0.1"
++      }
++    },
+=    "forever-agent": {
+=      "version": "0.6.1",
+=      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+@@ -475,6 +1053,14 @@
+=      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+=      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+=    },
++    "fragment-cache": {
++      "version": "0.2.1",
++      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
++      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
++      "requires": {
++        "map-cache": "^0.2.2"
++      }
++    },
+=    "fresh": {
+=      "version": "0.5.2",
+=      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+@@ -485,57 +1071,613 @@
+=      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+=      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+=    },
+-    "fstream": {
+-      "version": "1.0.11",
+-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
+-      "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
+-      "requires": {
+-        "graceful-fs": "^4.1.2",
+-        "inherits": "~2.0.0",
+-        "mkdirp": ">=0.5 0",
+-        "rimraf": "2"
+-      }
+-    },
+-    "getpass": {
+-      "version": "0.1.7",
+-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+-      "requires": {
+-        "assert-plus": "^1.0.0"
+-      }
+-    },
+-    "glob": {
+-      "version": "7.1.3",
+-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+-      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+-      "requires": {
+-        "fs.realpath": "^1.0.0",
+-        "inflight": "^1.0.4",
+-        "inherits": "2",
+-        "minimatch": "^3.0.4",
+-        "once": "^1.3.0",
+-        "path-is-absolute": "^1.0.0"
+-      }
+-    },
+-    "graceful-fs": {
+-      "version": "4.1.15",
+-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+-      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
+-    },
+-    "har-schema": {
+-      "version": "2.0.0",
+-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+-    },
+-    "har-validator": {
+-      "version": "5.1.3",
+-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
++    "fsevents": {
++      "version": "1.2.4",
++      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz",
++      "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==",
++      "optional": true,
+=      "requires": {
+-        "ajv": "^6.5.5",
+-        "har-schema": "^2.0.0"
+-      }
+-    },
++        "nan": "^2.9.2",
++        "node-pre-gyp": "^0.10.0"
++      },
++      "dependencies": {
++        "abbrev": {
++          "version": "1.1.1",
++          "bundled": true,
++          "optional": true
++        },
++        "ansi-regex": {
++          "version": "2.1.1",
++          "bundled": true
++        },
++        "aproba": {
++          "version": "1.2.0",
++          "bundled": true,
++          "optional": true
++        },
++        "are-we-there-yet": {
++          "version": "1.1.4",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "delegates": "^1.0.0",
++            "readable-stream": "^2.0.6"
++          }
++        },
++        "balanced-match": {
++          "version": "1.0.0",
++          "bundled": true
++        },
++        "brace-expansion": {
++          "version": "1.1.11",
++          "bundled": true,
++          "requires": {
++            "balanced-match": "^1.0.0",
++            "concat-map": "0.0.1"
++          }
++        },
++        "chownr": {
++          "version": "1.0.1",
++          "bundled": true,
++          "optional": true
++        },
++        "code-point-at": {
++          "version": "1.1.0",
++          "bundled": true
++        },
++        "concat-map": {
++          "version": "0.0.1",
++          "bundled": true
++        },
++        "console-control-strings": {
++          "version": "1.1.0",
++          "bundled": true
++        },
++        "core-util-is": {
++          "version": "1.0.2",
++          "bundled": true,
++          "optional": true
++        },
++        "debug": {
++          "version": "2.6.9",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "deep-extend": {
++          "version": "0.5.1",
++          "bundled": true,
++          "optional": true
++        },
++        "delegates": {
++          "version": "1.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "detect-libc": {
++          "version": "1.0.3",
++          "bundled": true,
++          "optional": true
++        },
++        "fs-minipass": {
++          "version": "1.2.5",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "minipass": "^2.2.1"
++          }
++        },
++        "fs.realpath": {
++          "version": "1.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "gauge": {
++          "version": "2.7.4",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "aproba": "^1.0.3",
++            "console-control-strings": "^1.0.0",
++            "has-unicode": "^2.0.0",
++            "object-assign": "^4.1.0",
++            "signal-exit": "^3.0.0",
++            "string-width": "^1.0.1",
++            "strip-ansi": "^3.0.1",
++            "wide-align": "^1.1.0"
++          }
++        },
++        "glob": {
++          "version": "7.1.2",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "fs.realpath": "^1.0.0",
++            "inflight": "^1.0.4",
++            "inherits": "2",
++            "minimatch": "^3.0.4",
++            "once": "^1.3.0",
++            "path-is-absolute": "^1.0.0"
++          }
++        },
++        "has-unicode": {
++          "version": "2.0.1",
++          "bundled": true,
++          "optional": true
++        },
++        "iconv-lite": {
++          "version": "0.4.21",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "safer-buffer": "^2.1.0"
++          }
++        },
++        "ignore-walk": {
++          "version": "3.0.1",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "minimatch": "^3.0.4"
++          }
++        },
++        "inflight": {
++          "version": "1.0.6",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "once": "^1.3.0",
++            "wrappy": "1"
++          }
++        },
++        "inherits": {
++          "version": "2.0.3",
++          "bundled": true
++        },
++        "ini": {
++          "version": "1.3.5",
++          "bundled": true,
++          "optional": true
++        },
++        "is-fullwidth-code-point": {
++          "version": "1.0.0",
++          "bundled": true,
++          "requires": {
++            "number-is-nan": "^1.0.0"
++          }
++        },
++        "isarray": {
++          "version": "1.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "minimatch": {
++          "version": "3.0.4",
++          "bundled": true,
++          "requires": {
++            "brace-expansion": "^1.1.7"
++          }
++        },
++        "minimist": {
++          "version": "0.0.8",
++          "bundled": true
++        },
++        "minipass": {
++          "version": "2.2.4",
++          "bundled": true,
++          "requires": {
++            "safe-buffer": "^5.1.1",
++            "yallist": "^3.0.0"
++          }
++        },
++        "minizlib": {
++          "version": "1.1.0",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "minipass": "^2.2.1"
++          }
++        },
++        "mkdirp": {
++          "version": "0.5.1",
++          "bundled": true,
++          "requires": {
++            "minimist": "0.0.8"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "needle": {
++          "version": "2.2.0",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "debug": "^2.1.2",
++            "iconv-lite": "^0.4.4",
++            "sax": "^1.2.4"
++          }
++        },
++        "node-pre-gyp": {
++          "version": "0.10.0",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "detect-libc": "^1.0.2",
++            "mkdirp": "^0.5.1",
++            "needle": "^2.2.0",
++            "nopt": "^4.0.1",
++            "npm-packlist": "^1.1.6",
++            "npmlog": "^4.0.2",
++            "rc": "^1.1.7",
++            "rimraf": "^2.6.1",
++            "semver": "^5.3.0",
++            "tar": "^4"
++          }
++        },
++        "nopt": {
++          "version": "4.0.1",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "abbrev": "1",
++            "osenv": "^0.1.4"
++          }
++        },
++        "npm-bundled": {
++          "version": "1.0.3",
++          "bundled": true,
++          "optional": true
++        },
++        "npm-packlist": {
++          "version": "1.1.10",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "ignore-walk": "^3.0.1",
++            "npm-bundled": "^1.0.1"
++          }
++        },
++        "npmlog": {
++          "version": "4.1.2",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "are-we-there-yet": "~1.1.2",
++            "console-control-strings": "~1.1.0",
++            "gauge": "~2.7.3",
++            "set-blocking": "~2.0.0"
++          }
++        },
++        "number-is-nan": {
++          "version": "1.0.1",
++          "bundled": true
++        },
++        "object-assign": {
++          "version": "4.1.1",
++          "bundled": true,
++          "optional": true
++        },
++        "once": {
++          "version": "1.4.0",
++          "bundled": true,
++          "requires": {
++            "wrappy": "1"
++          }
++        },
++        "os-homedir": {
++          "version": "1.0.2",
++          "bundled": true,
++          "optional": true
++        },
++        "os-tmpdir": {
++          "version": "1.0.2",
++          "bundled": true,
++          "optional": true
++        },
++        "osenv": {
++          "version": "0.1.5",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "os-homedir": "^1.0.0",
++            "os-tmpdir": "^1.0.0"
++          }
++        },
++        "path-is-absolute": {
++          "version": "1.0.1",
++          "bundled": true,
++          "optional": true
++        },
++        "process-nextick-args": {
++          "version": "2.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "rc": {
++          "version": "1.2.7",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "deep-extend": "^0.5.1",
++            "ini": "~1.3.0",
++            "minimist": "^1.2.0",
++            "strip-json-comments": "~2.0.1"
++          },
++          "dependencies": {
++            "minimist": {
++              "version": "1.2.0",
++              "bundled": true,
++              "optional": true
++            }
++          }
++        },
++        "readable-stream": {
++          "version": "2.3.6",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "core-util-is": "~1.0.0",
++            "inherits": "~2.0.3",
++            "isarray": "~1.0.0",
++            "process-nextick-args": "~2.0.0",
++            "safe-buffer": "~5.1.1",
++            "string_decoder": "~1.1.1",
++            "util-deprecate": "~1.0.1"
++          }
++        },
++        "rimraf": {
++          "version": "2.6.2",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "glob": "^7.0.5"
++          }
++        },
++        "safe-buffer": {
++          "version": "5.1.1",
++          "bundled": true
++        },
++        "safer-buffer": {
++          "version": "2.1.2",
++          "bundled": true,
++          "optional": true
++        },
++        "sax": {
++          "version": "1.2.4",
++          "bundled": true,
++          "optional": true
++        },
++        "semver": {
++          "version": "5.5.0",
++          "bundled": true,
++          "optional": true
++        },
++        "set-blocking": {
++          "version": "2.0.0",
++          "bundled": true,
++          "optional": true
++        },
++        "signal-exit": {
++          "version": "3.0.2",
++          "bundled": true,
++          "optional": true
++        },
++        "string-width": {
++          "version": "1.0.2",
++          "bundled": true,
++          "requires": {
++            "code-point-at": "^1.0.0",
++            "is-fullwidth-code-point": "^1.0.0",
++            "strip-ansi": "^3.0.0"
++          }
++        },
++        "string_decoder": {
++          "version": "1.1.1",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "safe-buffer": "~5.1.0"
++          }
++        },
++        "strip-ansi": {
++          "version": "3.0.1",
++          "bundled": true,
++          "requires": {
++            "ansi-regex": "^2.0.0"
++          }
++        },
++        "strip-json-comments": {
++          "version": "2.0.1",
++          "bundled": true,
++          "optional": true
++        },
++        "tar": {
++          "version": "4.4.1",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "chownr": "^1.0.1",
++            "fs-minipass": "^1.2.5",
++            "minipass": "^2.2.4",
++            "minizlib": "^1.1.0",
++            "mkdirp": "^0.5.0",
++            "safe-buffer": "^5.1.1",
++            "yallist": "^3.0.2"
++          }
++        },
++        "util-deprecate": {
++          "version": "1.0.2",
++          "bundled": true,
++          "optional": true
++        },
++        "wide-align": {
++          "version": "1.1.2",
++          "bundled": true,
++          "optional": true,
++          "requires": {
++            "string-width": "^1.0.2"
++          }
++        },
++        "wrappy": {
++          "version": "1.0.2",
++          "bundled": true
++        },
++        "yallist": {
++          "version": "3.0.2",
++          "bundled": true
++        }
++      }
++    },
++    "fstream": {
++      "version": "1.0.11",
++      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
++      "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
++      "requires": {
++        "graceful-fs": "^4.1.2",
++        "inherits": "~2.0.0",
++        "mkdirp": ">=0.5 0",
++        "rimraf": "2"
++      }
++    },
++    "get-stream": {
++      "version": "3.0.0",
++      "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
++      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
++    },
++    "get-value": {
++      "version": "2.0.6",
++      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
++      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
++    },
++    "getpass": {
++      "version": "0.1.7",
++      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
++      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
++      "requires": {
++        "assert-plus": "^1.0.0"
++      }
++    },
++    "glob": {
++      "version": "7.1.3",
++      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
++      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
++      "requires": {
++        "fs.realpath": "^1.0.0",
++        "inflight": "^1.0.4",
++        "inherits": "2",
++        "minimatch": "^3.0.4",
++        "once": "^1.3.0",
++        "path-is-absolute": "^1.0.0"
++      }
++    },
++    "glob-base": {
++      "version": "0.3.0",
++      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
++      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
++      "requires": {
++        "glob-parent": "^2.0.0",
++        "is-glob": "^2.0.0"
++      }
++    },
++    "glob-parent": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
++      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
++      "requires": {
++        "is-glob": "^2.0.0"
++      }
++    },
++    "graceful-fs": {
++      "version": "4.1.15",
++      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
++      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
++    },
++    "graceful-readlink": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
++      "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
++    },
++    "har-schema": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
++      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
++    },
++    "har-validator": {
++      "version": "5.1.3",
++      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
++      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
++      "requires": {
++        "ajv": "^6.5.5",
++        "har-schema": "^2.0.0"
++      }
++    },
++    "has-ansi": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
++      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
++      "requires": {
++        "ansi-regex": "^2.0.0"
++      }
++    },
++    "has-value": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
++      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
++      "requires": {
++        "get-value": "^2.0.6",
++        "has-values": "^1.0.0",
++        "isobject": "^3.0.0"
++      },
++      "dependencies": {
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
++    "has-values": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
++      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
++      "requires": {
++        "is-number": "^3.0.0",
++        "kind-of": "^4.0.0"
++      },
++      "dependencies": {
++        "is-number": {
++          "version": "3.0.0",
++          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
++          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
++          "requires": {
++            "kind-of": "^3.0.2"
++          },
++          "dependencies": {
++            "kind-of": {
++              "version": "3.2.2",
++              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
++              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
++              "requires": {
++                "is-buffer": "^1.1.5"
++              }
++            }
++          }
++        },
++        "kind-of": {
++          "version": "4.0.0",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
++          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
++          "requires": {
++            "is-buffer": "^1.1.5"
++          }
++        }
++      }
++    },
+=    "http-errors": {
+=      "version": "1.6.3",
+=      "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+@@ -547,6 +1689,16 @@
+=        "statuses": ">= 1.4.0 < 2"
+=      }
+=    },
++    "http-proxy": {
++      "version": "1.17.0",
++      "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
++      "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
++      "requires": {
++        "eventemitter3": "^3.0.0",
++        "follow-redirects": "^1.0.0",
++        "requires-port": "^1.0.0"
++      }
++    },
+=    "http-signature": {
+=      "version": "1.2.0",
+=      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+@@ -598,21 +1750,178 @@
+=      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+=      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+=    },
++    "internal-ip": {
++      "version": "3.0.1",
++      "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz",
++      "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==",
++      "requires": {
++        "default-gateway": "^2.6.0",
++        "ipaddr.js": "^1.5.2"
++      }
++    },
++    "ip-regex": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
++      "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
++    },
+=    "ipaddr.js": {
+=      "version": "1.8.0",
+=      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
+=      "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
+=    },
++    "is-accessor-descriptor": {
++      "version": "0.1.6",
++      "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
++      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
++      "requires": {
++        "kind-of": "^3.0.2"
++      }
++    },
++    "is-binary-path": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
++      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
++      "requires": {
++        "binary-extensions": "^1.0.0"
++      }
++    },
++    "is-buffer": {
++      "version": "1.1.6",
++      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
++      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
++    },
++    "is-data-descriptor": {
++      "version": "0.1.4",
++      "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
++      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
++      "requires": {
++        "kind-of": "^3.0.2"
++      }
++    },
++    "is-descriptor": {
++      "version": "0.1.6",
++      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
++      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
++      "requires": {
++        "is-accessor-descriptor": "^0.1.6",
++        "is-data-descriptor": "^0.1.4",
++        "kind-of": "^5.0.0"
++      },
++      "dependencies": {
++        "kind-of": {
++          "version": "5.1.0",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
++          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
++        }
++      }
++    },
++    "is-dotfile": {
++      "version": "1.0.3",
++      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
++      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE="
++    },
++    "is-equal-shallow": {
++      "version": "0.1.3",
++      "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz",
++      "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=",
++      "requires": {
++        "is-primitive": "^2.0.0"
++      }
++    },
++    "is-extendable": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
++      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
++    },
++    "is-extglob": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
++      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
++    },
++    "is-glob": {
++      "version": "2.0.1",
++      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
++      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
++      "requires": {
++        "is-extglob": "^1.0.0"
++      }
++    },
++    "is-number": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz",
++      "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=",
++      "requires": {
++        "kind-of": "^3.0.2"
++      }
++    },
++    "is-plain-object": {
++      "version": "2.0.4",
++      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
++      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
++      "requires": {
++        "isobject": "^3.0.1"
++      },
++      "dependencies": {
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
++    "is-posix-bracket": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
++      "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q="
++    },
++    "is-primitive": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
++      "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU="
++    },
++    "is-promise": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
++      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
++    },
++    "is-stream": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
++      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
++    },
+=    "is-typedarray": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+=      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+=    },
++    "is-windows": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
++      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
++    },
++    "is-wsl": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
++      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
++    },
+=    "isarray": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+=      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+=    },
++    "isexe": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
++      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
++    },
++    "isobject": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
++      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
++      "requires": {
++        "isarray": "1.0.0"
++      }
++    },
+=    "isstream": {
+=      "version": "0.1.2",
+=      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+@@ -649,11 +1958,49 @@
+=        "verror": "1.10.0"
+=      }
+=    },
++    "kind-of": {
++      "version": "3.2.2",
++      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
++      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
++      "requires": {
++        "is-buffer": "^1.1.5"
++      }
++    },
+=    "lodash": {
+=      "version": "4.17.11",
+=      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+=      "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+=    },
++    "lru-cache": {
++      "version": "4.1.5",
++      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
++      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
++      "requires": {
++        "pseudomap": "^1.0.2",
++        "yallist": "^2.1.2"
++      }
++    },
++    "lru-queue": {
++      "version": "0.1.0",
++      "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
++      "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
++      "requires": {
++        "es5-ext": "~0.10.2"
++      }
++    },
++    "map-cache": {
++      "version": "0.2.2",
++      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
++      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
++    },
++    "map-visit": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
++      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
++      "requires": {
++        "object-visit": "^1.0.0"
++      }
++    },
+=    "match-stream": {
+=      "version": "0.0.2",
+=      "resolved": "https://registry.npmjs.org/match-stream/-/match-stream-0.0.2.tgz",
+@@ -686,11 +2033,41 @@
+=        }
+=      }
+=    },
++    "math-random": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
++      "integrity": "sha1-izqsWIuKZuSXXjzepn97sylgH6w="
++    },
++    "md5": {
++      "version": "2.2.1",
++      "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
++      "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
++      "requires": {
++        "charenc": "~0.0.1",
++        "crypt": "~0.0.1",
++        "is-buffer": "~1.1.1"
++      }
++    },
+=    "media-typer": {
+=      "version": "0.3.0",
+=      "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+=      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+=    },
++    "memoizee": {
++      "version": "0.4.14",
++      "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz",
++      "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==",
++      "requires": {
++        "d": "1",
++        "es5-ext": "^0.10.45",
++        "es6-weak-map": "^2.0.2",
++        "event-emitter": "^0.3.5",
++        "is-promise": "^2.1",
++        "lru-queue": "0.1",
++        "next-tick": "1",
++        "timers-ext": "^0.1.5"
++      }
++    },
+=    "merge-descriptors": {
+=      "version": "1.0.1",
+=      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+@@ -701,6 +2078,26 @@
+=      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+=      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+=    },
++    "micromatch": {
++      "version": "2.3.11",
++      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
++      "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=",
++      "requires": {
++        "arr-diff": "^2.0.0",
++        "array-unique": "^0.2.1",
++        "braces": "^1.8.2",
++        "expand-brackets": "^0.1.4",
++        "extglob": "^0.3.1",
++        "filename-regex": "^2.0.0",
++        "is-extglob": "^1.0.0",
++        "is-glob": "^2.0.1",
++        "kind-of": "^3.0.2",
++        "normalize-path": "^2.0.1",
++        "object.omit": "^2.0.0",
++        "parse-glob": "^3.0.4",
++        "regex-cache": "^0.4.2"
++      }
++    },
+=    "mime": {
+=      "version": "2.4.0",
+=      "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+@@ -732,6 +2129,25 @@
+=      "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+=      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+=    },
++    "mixin-deep": {
++      "version": "1.3.1",
++      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
++      "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
++      "requires": {
++        "for-in": "^1.0.2",
++        "is-extendable": "^1.0.1"
++      },
++      "dependencies": {
++        "is-extendable": {
++          "version": "1.0.1",
++          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
++          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
++          "requires": {
++            "is-plain-object": "^2.0.4"
++          }
++        }
++      }
++    },
+=    "mkdirp": {
+=      "version": "0.5.1",
+=      "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+@@ -745,6 +2161,47 @@
+=      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+=      "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+=    },
++    "nan": {
++      "version": "2.11.1",
++      "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
++      "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
++      "optional": true
++    },
++    "nanomatch": {
++      "version": "1.2.13",
++      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
++      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
++      "requires": {
++        "arr-diff": "^4.0.0",
++        "array-unique": "^0.3.2",
++        "define-property": "^2.0.2",
++        "extend-shallow": "^3.0.2",
++        "fragment-cache": "^0.2.1",
++        "is-windows": "^1.0.2",
++        "kind-of": "^6.0.2",
++        "object.pick": "^1.3.0",
++        "regex-not": "^1.0.0",
++        "snapdragon": "^0.8.1",
++        "to-regex": "^3.0.1"
++      },
++      "dependencies": {
++        "arr-diff": {
++          "version": "4.0.0",
++          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
++          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
++        },
++        "array-unique": {
++          "version": "0.3.2",
++          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
++          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        }
++      }
++    },
+=    "natives": {
+=      "version": "1.1.6",
+=      "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz",
+@@ -755,11 +2212,96 @@
+=      "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+=      "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
+=    },
++    "next-tick": {
++      "version": "1.0.0",
++      "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
++      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
++    },
++    "nice-try": {
++      "version": "1.0.5",
++      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
++      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
++    },
++    "normalize-path": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
++      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
++      "requires": {
++        "remove-trailing-separator": "^1.0.1"
++      }
++    },
++    "npm-run-path": {
++      "version": "2.0.2",
++      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
++      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
++      "requires": {
++        "path-key": "^2.0.0"
++      }
++    },
+=    "oauth-sign": {
+=      "version": "0.9.0",
+=      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+=      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
+=    },
++    "object-copy": {
++      "version": "0.1.0",
++      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
++      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
++      "requires": {
++        "copy-descriptor": "^0.1.0",
++        "define-property": "^0.2.5",
++        "kind-of": "^3.0.3"
++      },
++      "dependencies": {
++        "define-property": {
++          "version": "0.2.5",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
++          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
++          "requires": {
++            "is-descriptor": "^0.1.0"
++          }
++        }
++      }
++    },
++    "object-visit": {
++      "version": "1.0.1",
++      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
++      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
++      "requires": {
++        "isobject": "^3.0.0"
++      },
++      "dependencies": {
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
++    "object.omit": {
++      "version": "2.0.1",
++      "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
++      "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=",
++      "requires": {
++        "for-own": "^0.1.4",
++        "is-extendable": "^0.1.1"
++      }
++    },
++    "object.pick": {
++      "version": "1.3.0",
++      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
++      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
++      "requires": {
++        "isobject": "^3.0.1"
++      },
++      "dependencies": {
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
+=    "on-finished": {
+=      "version": "2.3.0",
+=      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+@@ -776,26 +2318,83 @@
+=        "wrappy": "1"
+=      }
+=    },
++    "opn": {
++      "version": "5.3.0",
++      "resolved": "http://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
++      "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
++      "requires": {
++        "is-wsl": "^1.1.0"
++      }
++    },
++    "os-tmpdir": {
++      "version": "1.0.2",
++      "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
++      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
++    },
+=    "over": {
+=      "version": "0.0.5",
+=      "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz",
+=      "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg="
+=    },
++    "p-finally": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
++      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
++    },
++    "parse-glob": {
++      "version": "3.0.4",
++      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
++      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
++      "requires": {
++        "glob-base": "^0.3.0",
++        "is-dotfile": "^1.0.0",
++        "is-extglob": "^1.0.0",
++        "is-glob": "^2.0.0"
++      }
++    },
+=    "parseurl": {
+=      "version": "1.3.2",
+=      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+=      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+=    },
++    "pascalcase": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
++      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
++    },
+=    "path-is-absolute": {
+=      "version": "1.0.1",
+=      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+=      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+=    },
++    "path-key": {
++      "version": "2.0.1",
++      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
++      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
++    },
+=    "path-to-regexp": {
+=      "version": "0.1.7",
+=      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+=      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+=    },
++    "pem": {
++      "version": "1.13.2",
++      "resolved": "https://registry.npmjs.org/pem/-/pem-1.13.2.tgz",
++      "integrity": "sha512-MPJWuEb/r6AG+GpZi2JnfNtGAZDeL/8+ERKwXEWRuST5i+4lq/Uy36B352OWIUSPQGH+HR1HEDcIDi+8cKxXNg==",
++      "requires": {
++        "es6-promisify": "^6.0.0",
++        "md5": "^2.2.1",
++        "os-tmpdir": "^1.0.1",
++        "which": "^1.3.1"
++      },
++      "dependencies": {
++        "es6-promisify": {
++          "version": "6.0.1",
++          "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-6.0.1.tgz",
++          "integrity": "sha512-J3ZkwbEnnO+fGAKrjVpeUAnZshAdfZvbhQpqfIH9kSAspReRC4nJnu8ewm55b4y9ElyeuhCTzJD0XiH8Tsbhlw=="
++        }
++      }
++    },
+=    "pend": {
+=      "version": "1.2.0",
+=      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+@@ -806,6 +2405,16 @@
+=      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+=      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+=    },
++    "posix-character-classes": {
++      "version": "0.1.1",
++      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
++      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
++    },
++    "preserve": {
++      "version": "0.2.0",
++      "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
++      "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
++    },
+=    "process-nextick-args": {
+=      "version": "2.0.0",
+=      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+@@ -830,6 +2439,11 @@
+=      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+=      "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4="
+=    },
++    "pseudomap": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
++      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
++    },
+=    "psl": {
+=      "version": "1.1.29",
+=      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
+@@ -894,6 +2508,33 @@
+=      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+=      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+=    },
++    "querystringify": {
++      "version": "2.1.0",
++      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
++      "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg=="
++    },
++    "randomatic": {
++      "version": "3.1.1",
++      "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
++      "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==",
++      "requires": {
++        "is-number": "^4.0.0",
++        "kind-of": "^6.0.0",
++        "math-random": "^1.0.1"
++      },
++      "dependencies": {
++        "is-number": {
++          "version": "4.0.0",
++          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
++          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        }
++      }
++    },
+=    "range-parser": {
+=      "version": "1.2.0",
+=      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+@@ -924,6 +2565,311 @@
+=        "util-deprecate": "~1.0.1"
+=      }
+=    },
++    "readdirp": {
++      "version": "2.2.1",
++      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
++      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
++      "requires": {
++        "graceful-fs": "^4.1.11",
++        "micromatch": "^3.1.10",
++        "readable-stream": "^2.0.2"
++      },
++      "dependencies": {
++        "arr-diff": {
++          "version": "4.0.0",
++          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
++          "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
++        },
++        "array-unique": {
++          "version": "0.3.2",
++          "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
++          "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
++        },
++        "braces": {
++          "version": "2.3.2",
++          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
++          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
++          "requires": {
++            "arr-flatten": "^1.1.0",
++            "array-unique": "^0.3.2",
++            "extend-shallow": "^2.0.1",
++            "fill-range": "^4.0.0",
++            "isobject": "^3.0.1",
++            "repeat-element": "^1.1.2",
++            "snapdragon": "^0.8.1",
++            "snapdragon-node": "^2.0.1",
++            "split-string": "^3.0.2",
++            "to-regex": "^3.0.1"
++          },
++          "dependencies": {
++            "extend-shallow": {
++              "version": "2.0.1",
++              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++              "requires": {
++                "is-extendable": "^0.1.0"
++              }
++            }
++          }
++        },
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "expand-brackets": {
++          "version": "2.1.4",
++          "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
++          "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
++          "requires": {
++            "debug": "^2.3.3",
++            "define-property": "^0.2.5",
++            "extend-shallow": "^2.0.1",
++            "posix-character-classes": "^0.1.0",
++            "regex-not": "^1.0.0",
++            "snapdragon": "^0.8.1",
++            "to-regex": "^3.0.1"
++          },
++          "dependencies": {
++            "define-property": {
++              "version": "0.2.5",
++              "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
++              "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
++              "requires": {
++                "is-descriptor": "^0.1.0"
++              }
++            },
++            "extend-shallow": {
++              "version": "2.0.1",
++              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++              "requires": {
++                "is-extendable": "^0.1.0"
++              }
++            },
++            "is-accessor-descriptor": {
++              "version": "0.1.6",
++              "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
++              "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
++              "requires": {
++                "kind-of": "^3.0.2"
++              },
++              "dependencies": {
++                "kind-of": {
++                  "version": "3.2.2",
++                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
++                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
++                  "requires": {
++                    "is-buffer": "^1.1.5"
++                  }
++                }
++              }
++            },
++            "is-data-descriptor": {
++              "version": "0.1.4",
++              "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
++              "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
++              "requires": {
++                "kind-of": "^3.0.2"
++              },
++              "dependencies": {
++                "kind-of": {
++                  "version": "3.2.2",
++                  "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
++                  "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
++                  "requires": {
++                    "is-buffer": "^1.1.5"
++                  }
++                }
++              }
++            },
++            "is-descriptor": {
++              "version": "0.1.6",
++              "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
++              "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
++              "requires": {
++                "is-accessor-descriptor": "^0.1.6",
++                "is-data-descriptor": "^0.1.4",
++                "kind-of": "^5.0.0"
++              }
++            },
++            "kind-of": {
++              "version": "5.1.0",
++              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
++              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="
++            }
++          }
++        },
++        "extglob": {
++          "version": "2.0.4",
++          "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
++          "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
++          "requires": {
++            "array-unique": "^0.3.2",
++            "define-property": "^1.0.0",
++            "expand-brackets": "^2.1.4",
++            "extend-shallow": "^2.0.1",
++            "fragment-cache": "^0.2.1",
++            "regex-not": "^1.0.0",
++            "snapdragon": "^0.8.1",
++            "to-regex": "^3.0.1"
++          },
++          "dependencies": {
++            "define-property": {
++              "version": "1.0.0",
++              "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
++              "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
++              "requires": {
++                "is-descriptor": "^1.0.0"
++              }
++            },
++            "extend-shallow": {
++              "version": "2.0.1",
++              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++              "requires": {
++                "is-extendable": "^0.1.0"
++              }
++            }
++          }
++        },
++        "fill-range": {
++          "version": "4.0.0",
++          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
++          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
++          "requires": {
++            "extend-shallow": "^2.0.1",
++            "is-number": "^3.0.0",
++            "repeat-string": "^1.6.1",
++            "to-regex-range": "^2.1.0"
++          },
++          "dependencies": {
++            "extend-shallow": {
++              "version": "2.0.1",
++              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++              "requires": {
++                "is-extendable": "^0.1.0"
++              }
++            }
++          }
++        },
++        "is-accessor-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
++          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-data-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
++          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-descriptor": {
++          "version": "1.0.2",
++          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
++          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
++          "requires": {
++            "is-accessor-descriptor": "^1.0.0",
++            "is-data-descriptor": "^1.0.0",
++            "kind-of": "^6.0.2"
++          }
++        },
++        "is-number": {
++          "version": "3.0.0",
++          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
++          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
++          "requires": {
++            "kind-of": "^3.0.2"
++          },
++          "dependencies": {
++            "kind-of": {
++              "version": "3.2.2",
++              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
++              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
++              "requires": {
++                "is-buffer": "^1.1.5"
++              }
++            }
++          }
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        },
++        "micromatch": {
++          "version": "3.1.10",
++          "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
++          "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
++          "requires": {
++            "arr-diff": "^4.0.0",
++            "array-unique": "^0.3.2",
++            "braces": "^2.3.1",
++            "define-property": "^2.0.2",
++            "extend-shallow": "^3.0.2",
++            "extglob": "^2.0.4",
++            "fragment-cache": "^0.2.1",
++            "kind-of": "^6.0.2",
++            "nanomatch": "^1.2.9",
++            "object.pick": "^1.3.0",
++            "regex-not": "^1.0.0",
++            "snapdragon": "^0.8.1",
++            "to-regex": "^3.0.2"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "regex-cache": {
++      "version": "0.4.4",
++      "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz",
++      "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==",
++      "requires": {
++        "is-equal-shallow": "^0.1.3"
++      }
++    },
++    "regex-not": {
++      "version": "1.0.2",
++      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
++      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
++      "requires": {
++        "extend-shallow": "^3.0.2",
++        "safe-regex": "^1.1.0"
++      }
++    },
++    "remove-trailing-separator": {
++      "version": "1.1.0",
++      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
++      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
++    },
++    "repeat-element": {
++      "version": "1.1.3",
++      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
++      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
++    },
++    "repeat-string": {
++      "version": "1.6.1",
++      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
++      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
++    },
+=    "request": {
+=      "version": "2.88.0",
+=      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+@@ -970,6 +2916,21 @@
+=        "lodash": "^4.13.1"
+=      }
+=    },
++    "requires-port": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
++      "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
++    },
++    "resolve-url": {
++      "version": "0.2.1",
++      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
++      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
++    },
++    "ret": {
++      "version": "0.1.15",
++      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
++      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="
++    },
+=    "rimraf": {
+=      "version": "2.6.2",
+=      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+@@ -983,11 +2944,24 @@
+=      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+=      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+=    },
++    "safe-regex": {
++      "version": "1.1.0",
++      "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
++      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
++      "requires": {
++        "ret": "~0.1.10"
++      }
++    },
+=    "safer-buffer": {
+=      "version": "2.1.2",
+=      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+=      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+=    },
++    "semver": {
++      "version": "5.6.0",
++      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
++      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
++    },
+=    "send": {
+=      "version": "0.16.2",
+=      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+@@ -1039,6 +3013,27 @@
+=        "send": "0.16.2"
+=      }
+=    },
++    "set-value": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
++      "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
++      "requires": {
++        "extend-shallow": "^2.0.1",
++        "is-extendable": "^0.1.1",
++        "is-plain-object": "^2.0.3",
++        "split-string": "^3.0.1"
++      },
++      "dependencies": {
++        "extend-shallow": {
++          "version": "2.0.1",
++          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++          "requires": {
++            "is-extendable": "^0.1.0"
++          }
++        }
++      }
++    },
+=    "setimmediate": {
+=      "version": "1.0.5",
+=      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+@@ -1049,6 +3044,24 @@
+=      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+=      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+=    },
++    "shebang-command": {
++      "version": "1.2.0",
++      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
++      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
++      "requires": {
++        "shebang-regex": "^1.0.0"
++      }
++    },
++    "shebang-regex": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
++      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
++    },
++    "signal-exit": {
++      "version": "3.0.2",
++      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
++      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
++    },
+=    "slice-stream": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz",
+@@ -1080,6 +3093,146 @@
+=        }
+=      }
+=    },
++    "snapdragon": {
++      "version": "0.8.2",
++      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
++      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
++      "requires": {
++        "base": "^0.11.1",
++        "debug": "^2.2.0",
++        "define-property": "^0.2.5",
++        "extend-shallow": "^2.0.1",
++        "map-cache": "^0.2.2",
++        "source-map": "^0.5.6",
++        "source-map-resolve": "^0.5.0",
++        "use": "^3.1.0"
++      },
++      "dependencies": {
++        "debug": {
++          "version": "2.6.9",
++          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
++          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
++          "requires": {
++            "ms": "2.0.0"
++          }
++        },
++        "define-property": {
++          "version": "0.2.5",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
++          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
++          "requires": {
++            "is-descriptor": "^0.1.0"
++          }
++        },
++        "extend-shallow": {
++          "version": "2.0.1",
++          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++          "requires": {
++            "is-extendable": "^0.1.0"
++          }
++        },
++        "ms": {
++          "version": "2.0.0",
++          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
++          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
++        }
++      }
++    },
++    "snapdragon-node": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
++      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
++      "requires": {
++        "define-property": "^1.0.0",
++        "isobject": "^3.0.0",
++        "snapdragon-util": "^3.0.1"
++      },
++      "dependencies": {
++        "define-property": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
++          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
++          "requires": {
++            "is-descriptor": "^1.0.0"
++          }
++        },
++        "is-accessor-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
++          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-data-descriptor": {
++          "version": "1.0.0",
++          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
++          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
++          "requires": {
++            "kind-of": "^6.0.0"
++          }
++        },
++        "is-descriptor": {
++          "version": "1.0.2",
++          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
++          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
++          "requires": {
++            "is-accessor-descriptor": "^1.0.0",
++            "is-data-descriptor": "^1.0.0",
++            "kind-of": "^6.0.2"
++          }
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        },
++        "kind-of": {
++          "version": "6.0.2",
++          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
++          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
++        }
++      }
++    },
++    "snapdragon-util": {
++      "version": "3.0.1",
++      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
++      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
++      "requires": {
++        "kind-of": "^3.2.0"
++      }
++    },
++    "source-map": {
++      "version": "0.5.7",
++      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
++      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
++    },
++    "source-map-resolve": {
++      "version": "0.5.2",
++      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
++      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
++      "requires": {
++        "atob": "^2.1.1",
++        "decode-uri-component": "^0.2.0",
++        "resolve-url": "^0.2.1",
++        "source-map-url": "^0.4.0",
++        "urix": "^0.1.0"
++      }
++    },
++    "source-map-url": {
++      "version": "0.4.0",
++      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
++      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
++    },
++    "split-string": {
++      "version": "3.1.0",
++      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
++      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
++      "requires": {
++        "extend-shallow": "^3.0.0"
++      }
++    },
+=    "sshpk": {
+=      "version": "1.15.2",
+=      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz",
+@@ -1096,6 +3249,25 @@
+=        "tweetnacl": "~0.14.0"
+=      }
+=    },
++    "static-extend": {
++      "version": "0.1.2",
++      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
++      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
++      "requires": {
++        "define-property": "^0.2.5",
++        "object-copy": "^0.1.0"
++      },
++      "dependencies": {
++        "define-property": {
++          "version": "0.2.5",
++          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
++          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
++          "requires": {
++            "is-descriptor": "^0.1.0"
++          }
++        }
++      }
++    },
+=    "statuses": {
+=      "version": "1.4.0",
+=      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+@@ -1114,6 +3286,29 @@
+=        "safe-buffer": "~5.1.0"
+=      }
+=    },
++    "strip-ansi": {
++      "version": "3.0.1",
++      "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
++      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
++      "requires": {
++        "ansi-regex": "^2.0.0"
++      }
++    },
++    "strip-eof": {
++      "version": "1.0.0",
++      "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
++      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
++    },
++    "supervisor": {
++      "version": "0.12.0",
++      "resolved": "https://registry.npmjs.org/supervisor/-/supervisor-0.12.0.tgz",
++      "integrity": "sha1-3n5jNwFbKRhRwQ81OMSn8EkX7ME="
++    },
++    "supports-color": {
++      "version": "2.0.0",
++      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
++      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
++    },
+=    "tar": {
+=      "version": "2.2.1",
+=      "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+@@ -1124,6 +3319,53 @@
+=        "inherits": "2"
+=      }
+=    },
++    "timers-ext": {
++      "version": "0.1.7",
++      "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz",
++      "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==",
++      "requires": {
++        "es5-ext": "~0.10.46",
++        "next-tick": "1"
++      }
++    },
++    "to-object-path": {
++      "version": "0.3.0",
++      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
++      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
++      "requires": {
++        "kind-of": "^3.0.2"
++      }
++    },
++    "to-regex": {
++      "version": "3.0.2",
++      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
++      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
++      "requires": {
++        "define-property": "^2.0.2",
++        "extend-shallow": "^3.0.2",
++        "regex-not": "^1.0.2",
++        "safe-regex": "^1.1.0"
++      }
++    },
++    "to-regex-range": {
++      "version": "2.1.1",
++      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
++      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
++      "requires": {
++        "is-number": "^3.0.0",
++        "repeat-string": "^1.6.1"
++      },
++      "dependencies": {
++        "is-number": {
++          "version": "3.0.0",
++          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
++          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
++          "requires": {
++            "kind-of": "^3.0.2"
++          }
++        }
++      }
++    },
+=    "tough-cookie": {
+=      "version": "2.4.3",
+=      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+@@ -1172,11 +3414,84 @@
+=      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+=      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+=    },
++    "union-value": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
++      "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
++      "requires": {
++        "arr-union": "^3.1.0",
++        "get-value": "^2.0.6",
++        "is-extendable": "^0.1.1",
++        "set-value": "^0.4.3"
++      },
++      "dependencies": {
++        "extend-shallow": {
++          "version": "2.0.1",
++          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
++          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
++          "requires": {
++            "is-extendable": "^0.1.0"
++          }
++        },
++        "set-value": {
++          "version": "0.4.3",
++          "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
++          "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
++          "requires": {
++            "extend-shallow": "^2.0.1",
++            "is-extendable": "^0.1.1",
++            "is-plain-object": "^2.0.1",
++            "to-object-path": "^0.3.0"
++          }
++        }
++      }
++    },
+=    "unpipe": {
+=      "version": "1.0.0",
+=      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+=      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+=    },
++    "unset-value": {
++      "version": "1.0.0",
++      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
++      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
++      "requires": {
++        "has-value": "^0.3.1",
++        "isobject": "^3.0.0"
++      },
++      "dependencies": {
++        "has-value": {
++          "version": "0.3.1",
++          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
++          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
++          "requires": {
++            "get-value": "^2.0.3",
++            "has-values": "^0.1.4",
++            "isobject": "^2.0.0"
++          },
++          "dependencies": {
++            "isobject": {
++              "version": "2.1.0",
++              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
++              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
++              "requires": {
++                "isarray": "1.0.0"
++              }
++            }
++          }
++        },
++        "has-values": {
++          "version": "0.1.4",
++          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
++          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
++        },
++        "isobject": {
++          "version": "3.0.1",
++          "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
++          "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
++        }
++      }
++    },
+=    "unzip": {
+=      "version": "0.1.11",
+=      "resolved": "https://registry.npmjs.org/unzip/-/unzip-0.1.11.tgz",
+@@ -1240,6 +3555,25 @@
+=        "punycode": "^2.1.0"
+=      }
+=    },
++    "urix": {
++      "version": "0.1.0",
++      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
++      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="
++    },
++    "url-parse": {
++      "version": "1.4.3",
++      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
++      "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==",
++      "requires": {
++        "querystringify": "^2.0.0",
++        "requires-port": "^1.0.0"
++      }
++    },
++    "use": {
++      "version": "3.1.1",
++      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
++      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="
++    },
+=    "util-deprecate": {
+=      "version": "1.0.2",
+=      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+@@ -1270,6 +3604,14 @@
+=        "extsprintf": "^1.2.0"
+=      }
+=    },
++    "which": {
++      "version": "1.3.1",
++      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
++      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
++      "requires": {
++        "isexe": "^2.0.0"
++      }
++    },
+=    "wrappy": {
+=      "version": "1.0.2",
+=      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+@@ -1283,6 +3625,11 @@
+=        "async-limiter": "~1.0.0"
+=      }
+=    },
++    "yallist": {
++      "version": "2.1.2",
++      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
++      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
++    },
+=    "yauzl": {
+=      "version": "2.4.1",
+=      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+```
+
+``` diff
+index 3432d08..0ce2b31 100644
+--- a/package.json
++++ b/package.json
+@@ -19,6 +19,7 @@
+=  "dependencies": {
+=    "coffeescript": "^2.3.2",
+=    "elm": "^0.19.0-bugfix2",
++    "elm-live": "^3.4.0",
+=    "express": "^4.16.4",
+=    "puppeteer": "^1.11.0"
+=  }
+```
+
+``` diff
+new file mode 100755
+index 0000000..2952960
+--- /dev/null
++++ b/scripts/capture
+@@ -0,0 +1,14 @@
++#! /usr/bin/env bash -euo pipefail
++
++rm -rf built/*
++rm -rf public/*
++
++npx elm make src/Main.elm --output built/index.js
++
++cp -r content/ public/content/
++cp -r assets/ public/assets/
++cp -r built/* public/assets/
++
++mkdir -p built/captured/
++npx coffee src/capture.coffee
++cp -r built/captured/* public/
+```
+
+``` diff
+new file mode 100755
+index 0000000..b8fc9a4
+--- /dev/null
++++ b/scripts/develop
+@@ -0,0 +1,3 @@
++#! /usr/bin/env bash -euo pipefail
++
++npx elm-live src/Main.elm --pushstate -- --debug
+```
+
+``` diff
+index 0a89a20..d542d78 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -8,6 +8,7 @@ It fetches the Elm Markup file at /index.txt and renders it. There are a number
+=
+=import Basics.Extra exposing (curry)
+=import Browser
++import Browser.Navigation as Navigation
+=import BrowserWindow
+=import CartesianCoordinates
+=import CenteredDot
+@@ -36,20 +37,24 @@ import Parser.Advanced
+=import PolarCoordinates
+=import Result.Extra as Result
+=import RosetteTypedTransformations
++import Routes exposing (Route)
+=import Simplest
+=import Spiral
+=import Transformations
+=import Tree
++import Url exposing (Url)
+=import ViewBox
+=
+=
+=main : Program Flags Model Msg
+=main =
+-    Browser.document
++    Browser.application
+=        { init = init
+=        , view = view
+=        , update = update
+=        , subscriptions = subscriptions
++        , onUrlChange = UrlChanged
++        , onUrlRequest = UrlRequested
+=        }
+=
+=
+@@ -58,7 +63,9 @@ type alias Flags =
+=
+=
+=type alias Model =
+-    { markup : Maybe String
++    { url : Url
++    , key : Navigation.Key
++    , markup : Maybe String
+=    , counter : Counter.Model
+=    , transformations : Transformations.Model
+=    , nestedTransformations : NestedTransformations.Model
+@@ -70,7 +77,9 @@ type alias Model =
+=
+=
+=type Msg
+-    = DocumentFetched (Result Http.Error String)
++    = UrlRequested Browser.UrlRequest
++    | UrlChanged Url
++    | ContentFetched (Result Http.Error String)
+=    | CounterMsg Counter.Msg
+=    | TransformationsMsg Transformations.Msg
+=    | NestedTransformationsMsg NestedTransformations.Msg
+@@ -81,9 +90,11 @@ type Msg
+=    | ViewBoxMsg ViewBox.Msg
+=
+=
+-init : Flags -> ( Model, Cmd Msg )
+-init flags =
+-    ( { markup = Nothing
++init : Flags -> Url -> Navigation.Key -> ( Model, Cmd Msg )
++init flags url key =
++    ( { url = url
++      , key = key
++      , markup = Nothing
+=      , counter = Counter.init
+=      , transformations = Transformations.init
+=      , nestedTransformations = NestedTransformations.init
+@@ -92,10 +103,9 @@ init flags =
+=      , tree = Nothing
+=      , viewBox = ViewBox.init
+=      }
+-    , Http.get
+-        { url = "/index.txt"
+-        , expect = Http.expectString DocumentFetched
+-        }
++    , url
++        |> Routes.parse
++        |> loadContent
+=    )
+=
+=
+@@ -264,13 +274,33 @@ view model =
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+-    case msg of
+-        DocumentFetched (Ok markup) ->
++    case Debug.log "update" msg of
++        UrlRequested (Browser.Internal url) ->
++            ( model
++            , url
++                |> Debug.log "New URL"
++                |> Url.toString
++                |> Navigation.pushUrl model.key
++            )
++
++        UrlRequested (Browser.External url) ->
++            ( model
++            , Navigation.load url
++            )
++
++        UrlChanged url ->
++            ( { model | url = url }
++            , url
++                |> Routes.parse
++                |> loadContent
++            )
++
++        ContentFetched (Ok markup) ->
+=            ( { model | markup = Just markup }
+=            , Cmd.none
+=            )
+=
+-        DocumentFetched (Err markup) ->
++        ContentFetched (Err err) ->
+=            ( { model | markup = Nothing }
+=            , Cmd.none
+=            )
+@@ -331,6 +361,25 @@ subscriptions model =
+=                |> Sub.map TreeMsg
+=
+=
++loadContent : Route -> Cmd Msg
++loadContent route =
++    case Debug.log "Route" route of
++        Routes.Home ->
++            Http.get
++                { url = "/content/index.txt"
++                , expect = Http.expectString ContentFetched
++                }
++
++        Routes.Content base ->
++            Http.get
++                { url = "/content/" ++ base ++ ".txt"
++                , expect = Http.expectString ContentFetched
++                }
++
++        Routes.NotFound ->
++            Cmd.none
++
++
+=type alias DeadEnd =
+=    Parser.Advanced.DeadEnd Mark.Context Mark.Problem
+=
+```
+
+``` diff
+new file mode 100644
+index 0000000..f76545a
+--- /dev/null
++++ b/src/Routes.elm
+@@ -0,0 +1,37 @@
++module Routes exposing
++    ( Route(..)
++    , parse
++    , parser
++    )
++
++import Url exposing (Url)
++import Url.Parser as Parser exposing (..)
++
++
++type Route
++    = Home
++    | Content String
++    | NotFound
++
++
++parser : Parser (Route -> b) b
++parser =
++    Parser.oneOf
++        [ Parser.map Home Parser.top
++        , Parser.custom "Content"
++            (\path ->
++                case String.split "." path of
++                    [ base, "html" ] ->
++                        Just (Content base)
++
++                    _ ->
++                        Nothing
++            )
++        ]
++
++
++parse : Url -> Route
++parse url =
++    url
++        |> Parser.parse parser
++        |> Maybe.withDefault NotFound
+```
+
+``` diff
+index 8764395..15eb598 100644
+--- a/src/capture.coffee
++++ b/src/capture.coffee
+@@ -1,9 +1,14 @@
+=puppeteer = require "puppeteer"
+=express = require "express"
++{ promises: fs  } = require "fs"
+=
+=do () =>
+=  app = express ``
+=  app.use express.static "public/"
++
++  app.get '*', (req, res) ->
++    res.sendFile "container.html", root: process.cwd ``
++
+=  server = app.listen 8000
+=
+=  browser = await puppeteer.launch
+@@ -14,12 +19,34 @@ do () =>
+=
+=  page = await browser.newPage ``
+=
+-  await page.goto "http://localhost:8000/container.html",
+-    waitUntil: "networkidle0"
++  # TODO: Scan the directory and make this list dynamic!
++  for base in [
++    "index",
++    "preparation",
++    "day-1",
++    "rest"
++  ]
++    await do (base) ->
++      url = "http://localhost:8000/#{base}.html"
++      path = "built/captured/#{base}.html"
++      console.log "#{url} -> #{path}"
++
++
++      await page.goto url, waitUntil: "networkidle2"
++      console.log "Ready"
+=
+-  html = await page.content ``
++      html =
++        (await page.content ``).replace "</body>", """
++            <script src="/assets/index.js"></script>
++            <script>
++              Elm.Main.init()
++            </script>
++          </body>
++        """
+=
+-  console.log html
++      output = await fs.open path, "w"
++      await output.writeFile html
++      await output.close ``
+=
+=  await browser.close ``
+=  server.close ``
+```
+
+# Upgrade CI configuration to use alekzonder/puppeteer:1.8.0-0
+
+
+
+``` diff
+index aae0419..8ae0061 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -1,4 +1,4 @@
+-image: alekzonder/puppeteer:1.0.0
++image: alekzonder/puppeteer:1.8.0-0
+=
+=before_script:
+=  - npm install
+```
+
+# Use set instead of command line options to bash in scripts
+
+The previous syntax doesn't work in the Docker container.
+
+``` diff
+index 2952960..f710c3c 100755
+--- a/scripts/capture
++++ b/scripts/capture
+@@ -1,4 +1,6 @@
+-#! /usr/bin/env bash -euo pipefail
++#! /usr/bin/env bash
++
++set -euo pipefail
+=
+=rm -rf built/*
+=rm -rf public/*
+```
+
+``` diff
+index b8fc9a4..568384b 100755
+--- a/scripts/develop
++++ b/scripts/develop
+@@ -1,3 +1,5 @@
+-#! /usr/bin/env bash -euo pipefail
++#! /usr/bin/env bash
++
++set -euo pipefail
+=
+=npx elm-live src/Main.elm --pushstate -- --debug
+```
+
+# Make capture script ensure that public/ and built/ directories exists
+
+
+
+``` diff
+index f710c3c..4236c08 100755
+--- a/scripts/capture
++++ b/scripts/capture
+@@ -2,11 +2,15 @@
+=
+=set -euo pipefail
+=
+-rm -rf built/*
+-rm -rf public/*
++rm -rf built/
++rm -rf public/
++mkdir -p built/
++mkdir -p public/
++
+=
+=npx elm make src/Main.elm --output built/index.js
+=
++
+=cp -r content/ public/content/
+=cp -r assets/ public/assets/
+=cp -r built/* public/assets/
+```
+
+# Use classic (callback) API of FS module in capture.coffee
+
+The Promise API is not available in Node v. 8, which is used in 
+alekzonder/puppeteer Docker image.
+
+``` diff
+index 15eb598..46eae85 100644
+--- a/src/capture.coffee
++++ b/src/capture.coffee
+@@ -1,6 +1,6 @@
+=puppeteer = require "puppeteer"
+=express = require "express"
+-{ promises: fs  } = require "fs"
++fs = require "fs"
+=
+=do () =>
+=  app = express ``
+@@ -29,11 +29,8 @@ do () =>
+=    await do (base) ->
+=      url = "http://localhost:8000/#{base}.html"
+=      path = "built/captured/#{base}.html"
+-      console.log "#{url} -> #{path}"
+-
+=
+=      await page.goto url, waitUntil: "networkidle2"
+-      console.log "Ready"
+=
+=      html =
+=        (await page.content ``).replace "</body>", """
+@@ -44,9 +41,10 @@ do () =>
+=          </body>
+=        """
+=
+-      output = await fs.open path, "w"
+-      await output.writeFile html
+-      await output.close ``
++      await new Promise (resolve, reject) ->
++        fs.writeFile path, html, (error) ->
++          if error then return reject error
++          do resolve
+=
+=  await browser.close ``
+=  server.close ``
+```
\ No newline at end of file
new file mode 100644
index 0000000..cdba38f
--- /dev/null
+++ b/content/devlog/2018-12-16-elm-tree-workshop.md
@@ -0,0 +1,147 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Rename capture script to build
+
+This name better reflects it's function.
+
+``` diff
+index 8ae0061..801b12d 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -1,3 +1,4 @@
++
+=image: alekzonder/puppeteer:1.8.0-0
+=
+=before_script:
+@@ -6,7 +7,7 @@ before_script:
+=pages:
+=  stage: deploy
+=  script:
+-    - scripts/capture
++    - scripts/build
+=
+=  artifacts:
+=    paths:
+```
+
+``` diff
+similarity index 99%
+rename from scripts/capture
+rename to scripts/build
+index 4236c08..d9def3c 100755
+--- a/scripts/capture
++++ b/scripts/build
+@@ -2,6 +2,7 @@
+=
+=set -euo pipefail
+=
++
+=rm -rf built/
+=rm -rf public/
+=mkdir -p built/
+@@ -15,6 +16,7 @@ cp -r content/ public/content/
+=cp -r assets/ public/assets/
+=cp -r built/* public/assets/
+=
++
+=mkdir -p built/captured/
+=npx coffee src/capture.coffee
+=cp -r built/captured/* public/
+```
+
+# Make capture.coffee automatically scan for content/*.txt files
+
+
+
+``` diff
+index 0ce2b31..b00bfa5 100644
+--- a/package.json
++++ b/package.json
+@@ -21,6 +21,7 @@
+=    "elm": "^0.19.0-bugfix2",
+=    "elm-live": "^3.4.0",
+=    "express": "^4.16.4",
++    "glob": "^7.1.3",
+=    "puppeteer": "^1.11.0"
+=  }
+=}
+```
+
+``` diff
+index 46eae85..bdbd6c6 100644
+--- a/src/capture.coffee
++++ b/src/capture.coffee
+@@ -1,6 +1,14 @@
+=puppeteer = require "puppeteer"
+=express = require "express"
+-fs = require "fs"
++FS = require "fs"
++glob = require "glob"
++Path = require "path"
++
++match = (pattern, options) =>
++  new Promise (resolve, reject) =>
++    glob pattern, options, (error, paths) =>
++      if error then return reject errro
++      resolve paths
+=
+=do () =>
+=  app = express ``
+@@ -19,14 +27,11 @@ do () =>
+=
+=  page = await browser.newPage ``
+=
+-  # TODO: Scan the directory and make this list dynamic!
+-  for base in [
+-    "index",
+-    "preparation",
+-    "day-1",
+-    "rest"
+-  ]
+-    await do (base) ->
++  for match in await match "*.txt", cwd: "content/"
++    await do (match) ->
++      { dir, name } = Path.parse match
++      base = if dir is "" then name else "#{dir}/#{name}"
++
+=      url = "http://localhost:8000/#{base}.html"
+=      path = "built/captured/#{base}.html"
+=
+@@ -42,7 +47,7 @@ do () =>
+=        """
+=
+=      await new Promise (resolve, reject) ->
+-        fs.writeFile path, html, (error) ->
++        FS.writeFile path, html, (error) ->
+=          if error then return reject error
+=          do resolve
+=
+```
+
+# Disable pages CI task on branches different than master
+
+
+
+``` diff
+index 801b12d..4472935 100644
+--- a/.gitlab-ci.yml
++++ b/.gitlab-ci.yml
+@@ -12,5 +12,6 @@ pages:
+=  artifacts:
+=    paths:
+=      - public
+-  # only:
+-  #   - master
++
++  only:
++    - master
+```
\ No newline at end of file
new file mode 100644
index 0000000..83a52e3
--- /dev/null
+++ b/content/devlog/2018-12-18-elm-tree-workshop.md
@@ -0,0 +1,2341 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 3
+
+
+# Edits to the day-1 text
+
+Modify simplest embedded example to take background and fill config
+
+``` diff
+index 3cd7b9e..83cc9bf 100644
+--- a/content/day-1.txt
++++ b/content/day-1.txt
+@@ -10,40 +10,40 @@
+=
+=    | List
+=        -> Scalable Vector Graphics
+-        -> cartesian Coordinates System
++        -> Cartesian Coordinates Systems
+=        -> Layouts with ELM UI
+=
+=
+=| Header
+-    First Program!
++    Our First Program!
+=
+=As mentioned before, programs are represented as text (called the /source code/). The source code is stored in files {Icon|name=file} and files are organized in directories {Icon|name=folder}.
+=
+=
+=So the first step is to create a directory for our new program. Lets call it fpart.
+-In the terminal type
++In the terminal type:
+=
+=| Monospace
+=    mkdir fpart/
+=
+-and then
++and then:
+=
+=| Monospace
+=    cd fpart/
+=
+-This creates a new directory and makes it the current one. Again, don't worry about the details.
++The first command will create our new directory and the second will make it the current directory. Again, don't worry about the details.
+=
+-To easily create a new program, we can type
++We can easily create a new program by typing:
+=
+=| Monospace
+=    elm init
+=
+-Then to create a file with source code, type
++Then to create the file which will contain our source code, type:
+=
+=| Monospace
+=    atom src/Main.elm
+=
+-This command should open a new Atom window with empty text file.
++This command should open a new Atom window with an empty text file.
+=
+=| Header
+=    Main.elm
+@@ -58,19 +58,19 @@ This command should open a new Atom window with empty text file.
+=       Html.text "Hello, Tree"
+=
+=Type the above in the editor and save the file.
+-To see the program running type the following in the terminal
++To run the program, type the following command in the terminal:
+=
+=| Monospace
+=    elm reactor
+=
+-This command starts the Elm reactor, which will let you run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
++This command will start the Elm reactor, which will allow you to run your program in the web browser. Note that it won't give you the command line back - it will run as long as you don't stop it.
+=
+=
+=| Emphasize
+=    Voila!
+=
+=
+-Open following address in the web browser
++Open following address in the web browser:
+=
+=| Emphasize
+=    {Link|http://localhost:8000/src/Main.elm|url=http://localhost:8000/src/Main.elm}
+@@ -78,16 +78,16 @@ Open following address in the web browser
+=| Note
+=    *TODO*: Make the link display // characters
+=
+-Note that the same address was printed by Elm reactor
++Note that the same address was printed by the Elm reactor.
+=
+=| Header
+=    The problem
+=
+-We want to have a dot at the center of the screen, like this
++We want to display a dot at the center of the screen, like this:
+=
+=| DotAtTheCenterOfTheScreen
+=
+-Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate together it step by step.
++Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
+=
+=| Code
+=    module Main exposing (main)
+@@ -114,113 +114,124 @@ Below is the complete code to draw a dot like this, but don't type it in your ed
+=                , Element.height Element.fill
+=                ]
+=
++We are going to use a technology called SVG (Scalable Vector Graphics).
+=
++*Scalable icon should be here*
++
++S for Scalable
+=
+-We are going to use a technology called SVG (Scalable Vector Graphics). Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in terminal by pressing {Code|CTRL} + {Code|C} and type the following:
++Its all about drawing shapes on the screen. Let's install an Elm Package to help us work with SVG. Stop the reactor running in the terminal by pressing {Code|CTRL} + {Code|C} and then type the following:
+=
+=| Monospace
+=    elm install elm/svg
+=
+-Then start the reactor again:
++Now that we have {Code|elm//svg} installed, we can write the code that will draw our dot:
++
++| Code
++    module Simplest exposing (main)
++
++    import Element
++    import Svg
++    import Svg.Attributes
++
++
++    main =
++        Svg.svg []
++            [ Svg.circle
++                [ Svg.Attributes.r "10"
++                ]
++                []
++            ]
++
++Start the reactor again:
+=
+=| Monospace
+=    elm reactor
+=
+=| Note
+-    You can press up arrow  {Icon|name=arrow-up}  on the keyboard to get to the previous commands.
++    You can press the up arrow  {Icon|name=arrow-up}  on the keyboard to retrieve commands entered previously.
+=
+=Reload the browser. You should see something like this:
+=
+=| Simplest
++    background=none
++    fill=black
+=
+-Why is the dot at the corner?
++Something isn't quite right here. Why is the dot in the corner?
+=
+-It's because its center is at point called {Code|origin} or {Code|(0, 0)}. But what does it mean ?
++There are two reasons. First, because the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. We'll explore exactly what this means later.
+=
+-* a picture of a vase on a table should be here *
++Second, it's because the SVG space doesn't fill the browser window. Don't believe me? We can see that the space doesn't fill the window by changing the background color of the SVG element.
+=
+-Move sliders to change the x and y coordinates of the dot. It's like moving a thing on a table by telling how far left, right, up or down it should go.
+-
+-| CartesianCoordinates
++| Note
++    Code sample for simplest with pink background should be here.
+=
+-To change where the dot is we can use cx and cy attributes of the circle
++When you reload the browser, you'll see this:
+=
+-* simplest with cx and cy should be here *
++| Note
++    Example of simplest with pink background should be here.
+=
+-* Module main exposing main should be here *
++| Simplest
++    background=pink
++    fill=black
+=
+-Note that we have a complete program that draws a dot at the center of the screen , lets take a moment to understand it.
++Now we can clearly see that our SVG element (which is pink) does not fill the screen.
+=
+-Think about this ( * a picture of eggs and a box of eggs should be here * )
++We can easily correct this with the help of a very handy elm package, {Code|mdgriffith//elm-ui}.
+=
+-| Note
+-    An egg is a thing.
+-    Six eggs are six things.
+-    A box of six eggs is a thing.
++Install it with the terminal.
+=
++Press {Code|CTRL + C} to stop the elm-reactor, type {Code|elm install mdgriffith//elm-ui} and press {Code|Enter}.
+=
++Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
+=
+-Now, let's make the SVG element fill the screen
++Main = * the code of Element.layout should be here*
+=
++Take a wild guess: what do you think {Code|Element.height Element.fill} and {Code|Element.height Element.width} do?
+=
+-| FillTheScreen
++Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen. It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+=
++Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What eactly does this mean?
+=
+-For that we will need a package called {Link|mdgriffith//elm-ui|url=https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/}
++* a picture of a vase on a table should be here *
+=
+-Install it using terminal
++Adjust the sliders below to change the x and y coordinates of the dot. It's like moving an object on a table by describing how far left, right, up or down it should be.
+=
+-{Code|CTRL + C } to stop elm-reactor and type {Code|elm install mdgriffith//elm-ui}
++| CartesianCoordinates
+=
+-Then in {Code|src//Main.elm} add import {Code|import Element} and change {Code|Main} to look like this :
++Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)} of our plain, we can see that by default the origin of an SVG space is at the upper left corner. If we want to help our dot get out of hiding, we'll have to place the center of the dot at a point with positive {Code|x} and {Code|y} values.
+=
+-Main = * the code of Element.layout should be here*
++To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
+=
+-Note that our scene fills the screen, it's time to put the dot at the center of the scene.
++* simplest with cx and cy should be here *
+=
+-* a picture of cx and cy with width and height should be here *
++Now, if we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG space.
+=
+-If we would know the height and width of the screen , we could calculate its position as
++We can calculate the position of the circle as:
+=
+=| Monospace
+=    cx = width/2
+=    cy = height/2
+=
+-There is a problem though, we don't know the height and width.
++There is a problem though! We don't actually know the width and height of the SVG space. All we know is that its {Code|width} and {Code|height} will {Code|fill} the viewport, whatever its size.
+=☹️
+=
+-But we don't need to!
++Fortunately, we don't need to know the width and height of the SVG space!
+=
+-*Scalable icon should be here*
++This is a bit tricky. Instead of moving the dot in the SVG space, we can set the boundaries of the {Code|scene} so that the dot is at the center using a property called {Code|Viewbox}.
+=
+-S for Scalable
++To use an anology, imagine you've decided to build a holiday home with your family or friends in the Alps. Sounds pretty nice, right? Now, your budget isn't unlimited, so you've had to make a few compromises. You're in a beautiful area with a lovely view of the mountains, but yours isn't the only house in the area, and some of the others aren't the nicest to look at. Of course, you don't want your view of the beautiful snow-capped mountains to be blocked by a big pile of concrete. So what are you going to do about this? You can move your neighbours' homes, and the mountains while you're at it, so they are directly in front of your windows. Or, you can move the windows as you design your house so that you get a full view of the beautiful surroundings. Imagine you could move the windows around, as you decide the best place for them. As the window moves, you it will look as though the surroundings as moving, to the right, the left, up and down, as you shift the window. But in reality it is only your perspective that is changing.
+=
+-Instead of moving the dot, we can set the boundaries of the scene so that the dot is at the center using a property called Viewbox.
++A videwbox is like a window into an SVG space. It allows us to change the point of view of the scene.
+=
+=| ViewBox
+=
+-Here is how the viewbox works
+-
+-* a picture of viewbox should be here*
+-
+-viewbox = 0 0 100 100 (left, top, width and height respectively)
+-
+-* a picture of periscope looking table should be here*
++In this example, we can set the width and height of the viewbox to an arbitrary value. As we see in the interactive example, the width and height of the viewbox will effect how big the dot appears, but that's not so important to us at the moment. Let's set them both to 1000.
+=
+-The trick is to set the width and height to any arbitrary value (say 1000) and top and left to {Code|- width//2}!
++The top and left values of the viewbox are a bit trickier. We saw ealier that if we knew the width and height of our SVG scene, we could have set the cx and cy values of the dot to half the width and height of the scene to center the dot. We will have to use similar reasoning here. If we set the top and left values of the viewbox to 0, the dot will still still appear in the top left corner of the scene. If we want to see the dot moved to the right and downward within the scene, we will need the top and left values of the viewbox to be negative. If we want the origin of the SVG space to appear directly in the center of the scene, we will need to set the top and left values of the viewbox to {Code|- height//2} and {Code|- width//2}. In this case, they will both have the value 500.
+=
+-That way point (0,0) will always be right in the middle (the distance to the left and to the right is the same, likewise the distance to the top and bottom).
++Refresh the broswer. We should now see the dot in the center of the screen.
+=
+-First, lets see where the SVG boundaries are by giving it a background color
+-
+-
+-| CenteredDot
+-
+-
+-| Code
+-    Svg [background "pink"]
+-        [...
+-        ]
+=
+=Give a color to the dot
+=
+```
+
+``` diff
+index d542d78..019ba58 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -487,16 +487,23 @@ document =
+=        simplest : Mark.Block (Model -> Element Msg)
+=        simplest =
+=            let
+-                render model =
+-                    Simplest.main
++                render : Simplest.Config -> Model -> Element Msg
++                render config model =
++                    Simplest.ui config
+=                        |> Element.html
++                        |> Element.el []
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
++                            , Element.padding 5
+=                            ]
+=                        |> BrowserWindow.window []
+=            in
+-            Mark.stub "Simplest" render
++            Mark.record2 "Simplest"
++                Simplest.Config
++                (Mark.field "background" Mark.string)
++                (Mark.field "fill" Mark.string)
++                |> Mark.map render
+=
+=        fillTheScreen : Mark.Block (Model -> Element Msg)
+=        fillTheScreen =
+```
+
+``` diff
+index 9f90116..2e65fa4 100644
+--- a/src/Simplest.elm
++++ b/src/Simplest.elm
+@@ -1,17 +1,32 @@
+-module Simplest exposing (main)
++module Simplest exposing (Config, defaults, ui)
+=
+-import Element
++import Html exposing (Html)
+=import Svg
+=import Svg.Attributes
+=
+=
++type alias Config =
++    { background : String
++    , fill : String
++    }
++
++
++defaults : Config
++defaults =
++    Config "none" "black"
++
++
++main : Html msg
+=main =
+-    Svg.svg
+-        [ Svg.Attributes.width "300"
+-        , Svg.Attributes.height "150"
+-        ]
++    ui defaults
++
++
++ui : Config -> Html msg
++ui { background, fill } =
++    Svg.svg [ Svg.Attributes.style <| "background: " ++ background ]
+=        [ Svg.circle
+=            [ Svg.Attributes.r "10"
++            , Svg.Attributes.fill fill
+=            ]
+=            []
+=        ]
+```
+
+# Separate day 2, add TransformationsCircle example
+
+Expose Transformation(..) and Apply from Transformations module.
+
+``` diff
+new file mode 100644
+index 0000000..32d4a6c
+--- /dev/null
++++ b/content/day-2.txt
+@@ -0,0 +1,75 @@
++| Title
++    Day 2
++
++| Emphasize
++    Place the dots in a circle
++
++
++| Note
++    Today we are going to lear about
++
++    | List
++        - SVG transformations
++        - SVG groups (?)
++
++| Header
++    The Problem
++
++We want to place the dots in a circle, like that:
++
++| TransformationsCircle
++    dots = True
++    circle = False
++    angle = False
++    center = False
++
++As you can see, we will make the dot's have different colors, so it's more fun!
++
++How we will get there?
++
++We will need more than one dot. Let's make it 5. Then let's think what does it mean to be placed on a circle.
++
++First of all we have to realize that a circle has a center. It's a point that lays exactly in the middle:
++
++
++Then we can say that several things lay on a circle if the distance between each of them and the center is the same.
++
++In other words, you can put a thing in the center and then move it in any direction, let's say 1 meter. If you do the same to several things, you will make them lay on a circle.
++
++Of course the direction must be different each time. Otherwise the things would just stack one on top of another! We wouldn't call it a circle, right?
++
++We will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
++
++How can we figure out the correct cartesian coordinates?
++
++| Note
++    Story about the flowerpot and the table: two ways to measure relative position, one is distance from an x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
++
++
++What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a '360'). We see that circles and angles are closely related!
++
++Exercise with compass and 5 objects, place objects evenly along compass. How many degrees apart are they? (observe if we multiply the result by 5, we're back to 360)
++
++We have one part of it (angle), we now need the distance. Notice they are all the same distance from the origin (the center dot) of the compass. Definition of circle: points that are an equal distance from a center. Actually, the distance doesn't matter, so long as they all have the same distance. You can have a big circle or a small circle, they're both circles
++
++Ok great, we're done! Now, does anyone know how to give an angle and distance to svg? Oh... no? We don't either... you can't. You can only give x and y values relative to the origin
++
++So we already know that angle and length are just another way of describing x and y. But we need some way of translating between the two
++
++sing a visual demonstration, if you draw a line from your object to the x axis, you have a triangle. Same if you draw a line to the y axis. You can figure out the point on the axis using sin and cos functions and multiplying the result by the length.
++
++Let's use a chart to figure out the sin and cos of our angles
++
++/It's important to get a chart, otherwise we have to use calculators which work with radians/
++
++Now we are done... we can plug in our x and y values, and presto, our dots are arranged in a circle. But we don't see most of them...
++
++Our origin is in the top left corner. This means any dots with negative x or y values are off the screen.
++
++We can shift the dots so they are on the screen, or shift the screen so that it covers the dots. We will be shifting the screen
++
++Sample viewbox program to demonstrate
++
++Create a viewbox with correct perimeters (must be more than the radius of the circle plus the radius of a dot in each direction, and width and height are circumference)
++
++{Code|svg [viewbox "-100 -100 200 200 " ] []}
+```
+
+``` diff
+index 54d5902..07ece92 100644
+--- a/elm.json
++++ b/elm.json
+@@ -16,6 +16,7 @@
+=            "elm/url": "1.0.0",
+=            "elm-community/basics-extra": "4.0.0",
+=            "elm-community/list-extra": "8.1.0",
++            "elm-community/maybe-extra": "5.0.0",
+=            "elm-community/result-extra": "2.2.1",
+=            "elm-explorations/markdown": "1.0.0",
+=            "feathericons/elm-feather": "1.2.0",
+```
+
+``` diff
+index 019ba58..ab8ac60 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -41,6 +41,7 @@ import Routes exposing (Route)
+=import Simplest
+=import Spiral
+=import Transformations
++import TransformationsCircle
+=import Tree
+=import Url exposing (Url)
+=import ViewBox
+@@ -451,6 +452,7 @@ document =
+=                    , fillTheScreen
+=                    , dotAtTheCenterOfTheScreen
+=                    , centeredDot
++                    , transformationsCircle
+=                    , line
+=                    , gradient
+=                    , transformations
+@@ -545,6 +547,27 @@ document =
+=            in
+=            Mark.stub "CenteredDot" render
+=
++        transformationsCircle : Mark.Block (Model -> Element Msg)
++        transformationsCircle =
++            let
++                render : TransformationsCircle.Config -> Model -> Element Msg
++                render config model =
++                    TransformationsCircle.ui config
++                        |> Element.el
++                            [ Element.height (Element.px 400)
++                            , Element.width Element.fill
++                            , Element.padding 5
++                            ]
++                        |> BrowserWindow.window []
++            in
++            Mark.record4 "TransformationsCircle"
++                TransformationsCircle.Config
++                (Mark.field "circle" Mark.bool)
++                (Mark.field "center" Mark.bool)
++                (Mark.field "angle" Mark.bool)
++                (Mark.field "dots" Mark.bool)
++                |> Mark.map render
++
+=        line : Mark.Block (Model -> Element Msg)
+=        line =
+=            let
+```
+
+``` diff
+index 295c1ef..1b3937d 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -1,6 +1,8 @@
+=module Transformations exposing
+=    ( Model
+=    , Msg
++    , Transformation(..)
++    , apply
+=    , init
+=    , main
+=    , ui
+```
+
+``` diff
+new file mode 100644
+index 0000000..96264ff
+--- /dev/null
++++ b/src/TransformationsCircle.elm
+@@ -0,0 +1,135 @@
++module TransformationsCircle exposing (Config, defaults, main, ui)
++
++import Element exposing (Element)
++import Html exposing (Html)
++import Maybe.Extra as Maybe
++import Svg exposing (Svg)
++import Svg.Attributes
++import Transformations exposing (Transformation(..))
++
++
++main : Html.Html msg
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui defaults)
++
++
++ui : Config -> Element msg
++ui config =
++    let
++        present : Bool -> Svg msg -> Maybe (Svg msg)
++        present flag shape =
++            if flag then
++                Just shape
++
++            else
++                Nothing
++
++        shapes =
++            dots
++                ++ Maybe.values
++                    [ present config.circle circle
++                    ]
++
++        dots =
++            if config.dots then
++                [ Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "0"
++                    , Svg.Attributes.cy "0"
++                    , Svg.Attributes.fill "skyblue"
++                    , Svg.Attributes.transform <|
++                        Transformations.apply
++                            [ Rotate 0
++                            , Translate 80 0
++                            ]
++                    ]
++                    []
++                , Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "0"
++                    , Svg.Attributes.cy "0"
++                    , Svg.Attributes.fill "pink"
++                    , Svg.Attributes.transform <|
++                        Transformations.apply
++                            [ Rotate 72
++                            , Translate 80 0
++                            ]
++                    ]
++                    []
++                , Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "0"
++                    , Svg.Attributes.cy "0"
++                    , Svg.Attributes.fill "yellow"
++                    , Svg.Attributes.transform <|
++                        Transformations.apply
++                            [ Rotate 144
++                            , Translate 80 0
++                            ]
++                    ]
++                    []
++                , Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "0"
++                    , Svg.Attributes.cy "0"
++                    , Svg.Attributes.fill "lime"
++                    , Svg.Attributes.transform <|
++                        Transformations.apply
++                            [ Rotate 216
++                            , Translate 80 0
++                            ]
++                    ]
++                    []
++                , Svg.circle
++                    [ Svg.Attributes.r "10"
++                    , Svg.Attributes.cx "0"
++                    , Svg.Attributes.cy "0"
++                    , Svg.Attributes.fill "maroon"
++                    , Svg.Attributes.transform <|
++                        Transformations.apply
++                            [ Rotate 288
++                            , Translate 80 0
++                            ]
++                    ]
++                    []
++                ]
++
++            else
++                []
++
++        circle =
++            Svg.circle
++                [ Svg.Attributes.r "80"
++                , Svg.Attributes.stroke "silver"
++                , Svg.Attributes.strokeWidth "1"
++                , Svg.Attributes.strokeDasharray "5 5"
++                , Svg.Attributes.fill "none"
++                ]
++                []
++    in
++    shapes
++        |> Svg.svg
++            [ Svg.Attributes.viewBox "-100 -100 200 200"
++            ]
++        |> Element.html
++
++
++defaults : Config
++defaults =
++    { circle = False
++    , center = False
++    , angle = False
++    , dots = False
++    }
++
++
++type alias Config =
++    { circle : Bool
++    , center : Bool
++    , angle : Bool
++    , dots : Bool
++    }
+```
+
+# Make develop script build all examples to public/
+
+It's a quick and dirty solution.
+
+All examples are now in src/Examples/ directory.
+
+There is a new Transformations module that exposes Transformation type 
+and it's constructors and apply function.
+
+``` diff
+index 568384b..3b0e90e 100755
+--- a/scripts/develop
++++ b/scripts/develop
+@@ -2,4 +2,10 @@
+=
+=set -euo pipefail
+=
++# Build examples
++for example in src/Examples/*.elm
++do
++  npx elm make --output "public/${example}.html" "${example}"
++done
++
+=npx elm-live src/Main.elm --pushstate -- --debug
+```
+
+``` diff
+similarity index 98%
+rename from src/CartesianCoordinates.elm
+rename to src/Examples/CartesianCoordinates.elm
+index fc29be3..1c4266c 100644
+--- a/src/CartesianCoordinates.elm
++++ b/src/Examples/CartesianCoordinates.elm
+@@ -1,4 +1,4 @@
+-module CartesianCoordinates exposing
++module Examples.CartesianCoordinates exposing
+=    ( Model
+=    , Msg
+=    , init
+```
+
+``` diff
+similarity index 96%
+rename from src/CenteredDot.elm
+rename to src/Examples/CenteredDot.elm
+index 59fb692..9f00631 100644
+--- a/src/CenteredDot.elm
++++ b/src/Examples/CenteredDot.elm
+@@ -1,4 +1,4 @@
+-module CenteredDot exposing (main)
++module Examples.CenteredDot exposing (main)
+=
+={-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
+=
+```
+
+``` diff
+similarity index 97%
+rename from src/Counter.elm
+rename to src/Examples/Counter.elm
+index 0f0689c..66d1400 100644
+--- a/src/Counter.elm
++++ b/src/Examples/Counter.elm
+@@ -1,4 +1,4 @@
+-module Counter exposing
++module Examples.Counter exposing
+=    ( Model
+=    , Msg
+=    , init
+```
+
+``` diff
+similarity index 87%
+rename from src/DotAtTheCenterOfTheScreen.elm
+rename to src/Examples/DotAtTheCenterOfTheScreen.elm
+index 83f0d1d..cc1875e 100644
+--- a/src/DotAtTheCenterOfTheScreen.elm
++++ b/src/Examples/DotAtTheCenterOfTheScreen.elm
+@@ -1,4 +1,4 @@
+-module DotAtTheCenterOfTheScreen exposing (main, ui)
++module Examples.DotAtTheCenterOfTheScreen exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 90%
+rename from src/FillTheScreen.elm
+rename to src/Examples/FillTheScreen.elm
+index 56f88d6..cef2371 100644
+--- a/src/FillTheScreen.elm
++++ b/src/Examples/FillTheScreen.elm
+@@ -1,4 +1,4 @@
+-module FillTheScreen exposing (main, ui)
++module Examples.FillTheScreen exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 96%
+rename from src/Gradient.elm
+rename to src/Examples/Gradient.elm
+index 4aa189c..1d7a0c9 100644
+--- a/src/Gradient.elm
++++ b/src/Examples/Gradient.elm
+@@ -1,4 +1,4 @@
+-module Gradient exposing (main, ui)
++module Examples.Gradient exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 93%
+rename from src/Line.elm
+rename to src/Examples/Line.elm
+index 53ab314..d105ef5 100644
+--- a/src/Line.elm
++++ b/src/Examples/Line.elm
+@@ -1,4 +1,4 @@
+-module Line exposing (main, ui)
++module Examples.Line exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 96%
+rename from src/LineTypedTransformations.elm
+rename to src/Examples/LineTypedTransformations.elm
+index f39d91f..46899d3 100644
+--- a/src/LineTypedTransformations.elm
++++ b/src/Examples/LineTypedTransformations.elm
+@@ -1,4 +1,4 @@
+-module LineTypedTransformations exposing (main)
++module Examples.LineTypedTransformations exposing (main)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 98%
+rename from src/NestedTransformations.elm
+rename to src/Examples/NestedTransformations.elm
+index e0aaded..b1bc6f5 100644
+--- a/src/NestedTransformations.elm
++++ b/src/Examples/NestedTransformations.elm
+@@ -1,4 +1,4 @@
+-module NestedTransformations exposing
++module Examples.NestedTransformations exposing
+=    ( Model
+=    , Msg
+=    , init
+@@ -26,8 +26,10 @@ import List.Extra as List
+=import Point2d
+=import Svg exposing (..)
+=import Svg.Attributes exposing (..)
++import Transformations exposing (Transformation(..))
+=
+=
++main : Program () Model Msg
+=main =
+=    Browser.sandbox
+=        { init = init
+@@ -40,13 +42,6 @@ type alias Model =
+=    AnyDict String Group (Array Transformation)
+=
+=
+-type Transformation
+-    = Identity
+-    | Scale Float Float
+-    | Translate Float Float
+-    | Rotate Float
+-
+-
+=type Group
+=    = Pink
+=    | Green
+@@ -134,7 +129,9 @@ ui model =
+=        nestTransformationsGroup group transformations item =
+=            let
+=                transformation =
+-                    transformations |> Array.toList |> apply
++                    transformations
++                        |> Array.toList
++                        |> Transformations.apply
+=
+=                color =
+=                    group
+```
+
+``` diff
+similarity index 99%
+rename from src/PolarCoordinates.elm
+rename to src/Examples/PolarCoordinates.elm
+index 3a476cd..4fd1e93 100644
+--- a/src/PolarCoordinates.elm
++++ b/src/Examples/PolarCoordinates.elm
+@@ -1,4 +1,4 @@
+-module PolarCoordinates exposing
++module Examples.PolarCoordinates exposing
+=    ( Model
+=    , Msg
+=    , init
+```
+
+``` diff
+similarity index 98%
+rename from src/RosetteTypedTransformations.elm
+rename to src/Examples/RosetteTypedTransformations.elm
+index 2ae161e..e83eb99 100644
+--- a/src/RosetteTypedTransformations.elm
++++ b/src/Examples/RosetteTypedTransformations.elm
+@@ -1,4 +1,4 @@
+-module RosetteTypedTransformations exposing (main, ui)
++module Examples.RosetteTypedTransformations exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+similarity index 89%
+rename from src/Simplest.elm
+rename to src/Examples/Simplest.elm
+index 2e65fa4..74f75dc 100644
+--- a/src/Simplest.elm
++++ b/src/Examples/Simplest.elm
+@@ -1,4 +1,4 @@
+-module Simplest exposing (Config, defaults, ui)
++module Examples.Simplest exposing (Config, defaults, ui)
+=
+=import Html exposing (Html)
+=import Svg
+```
+
+``` diff
+similarity index 98%
+rename from src/Spiral.elm
+rename to src/Examples/Spiral.elm
+index 7e1ae87..3b52178 100644
+--- a/src/Spiral.elm
++++ b/src/Examples/Spiral.elm
+@@ -1,4 +1,4 @@
+-module Spiral exposing (main, ui)
++module Examples.Spiral exposing (main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+new file mode 100644
+index 0000000..3321826
+--- /dev/null
++++ b/src/Examples/Transformations.elm
+@@ -0,0 +1,343 @@
++module Examples.Transformations exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , ui
++    , update
++    )
++
++import Array exposing (Array)
++import Browser
++import Browser.Events
++import CartesianPlane
++import Dict exposing (Dict)
++import Element exposing (Element)
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Geometry.Svg
++import Html exposing (Html)
++import Json.Decode exposing (Decoder)
++import LineSegment2d
++import List.Extra as List
++import Point2d
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++import Transformations exposing (Transformation(..))
++
++
++main : Program () Model Msg
++main =
++    Browser.sandbox
++        { init = init
++        , view = view
++        , update = update
++        }
++
++
++type alias Model =
++    Array Transformation
++
++
++type Msg
++    = AddTransformation Transformation
++    | DeleteTransformation Int
++    | SetTransformation Int Transformation
++
++
++init : Model
++init =
++    Array.fromList
++        [ Translate 0 0
++        , Rotate 0
++        , Scale 1 1
++        ]
++
++
++view : Model -> Html Msg
++view model =
++    Element.layout
++        [ Element.height Element.fill
++        , Element.width Element.fill
++        ]
++        (ui model)
++
++
++ui : Model -> Element Msg
++ui model =
++    let
++        transformations =
++            Array.toList model
++
++        wrapper element =
++            Element.column
++                [ Element.width (Element.maximum 600 Element.fill)
++                , Element.centerX
++                , Element.spacing 20
++                ]
++                [ Element.el
++                    [ Element.width Element.fill
++                    ]
++                    (Element.html element)
++                , Element.el
++                    [ Background.color (Element.rgb 1 0.8 0.8)
++                    , Element.padding 10
++                    , Element.width Element.fill
++                    ]
++                    (transformationsUI transformations)
++                ]
++
++        shape =
++            g [ transform (Transformations.apply transformations) ]
++                [ line
++                    [ x1 "0"
++                    , x2 "100"
++                    , y1 "0"
++                    , y2 "0"
++                    , stroke "red"
++                    , strokeWidth "1"
++                    ]
++                    []
++                , circle [ cx "0", cy "0", r "2", fill "red" ] []
++                , grid
++                    [ stroke "pink"
++                    , fill "pink"
++                    , strokeWidth "0.3"
++                    ]
++                    10
++                    30
++
++                -- , rect [ x "", y "-2", width "1", height "4" ] []
++                ]
++    in
++    shape
++        |> List.singleton
++        |> CartesianPlane.graph 300
++        |> wrapper
++
++
++transformationsUI : List Transformation -> Element Msg
++transformationsUI transformations =
++    let
++        addButtons =
++            [ Element.text "Add transformation: "
++            , Input.button []
++                { onPress = Just (AddTransformation (Translate 0 0))
++                , label = Element.text "Translate"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Scale 1 1))
++                , label = Element.text "Scale"
++                }
++            , Input.button []
++                { onPress = Just (AddTransformation (Rotate 0))
++                , label = Element.text "Rotate"
++                }
++            ]
++
++        currentTrasformations =
++            transformations
++                |> List.indexedMap transformationUI
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Element.spacing 10
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            addButtons
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 10
++            ]
++            currentTrasformations
++        ]
++
++
++transformationUI : Int -> Transformation -> Element Msg
++transformationUI index transformation =
++    let
++        sliderBackground =
++            Element.el
++                [ Element.width Element.fill
++                , Element.height (Element.px 2)
++                , Element.centerY
++                , Background.color <| Element.rgb 0.7 0.7 0.7
++                , Border.rounded 2
++                ]
++                Element.none
++
++        controls =
++            case transformation of
++                Identity ->
++                    [ Element.text "Identity" ]
++
++                Scale horizontal vertical ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \x ->
++                                SetTransformation index (Scale x vertical)
++                        , label = Input.labelLeft [] (Element.text "horizontal")
++                        , min = 0
++                        , max = 10
++                        , value = horizontal
++                        , thumb = Input.defaultThumb
++                        , step = Just 0.1
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \y ->
++                                SetTransformation index (Scale horizontal y)
++                        , label = Input.labelLeft [] (Element.text "vertical")
++                        , min = 0
++                        , max = 10
++                        , value = vertical
++                        , thumb = Input.defaultThumb
++                        , step = Just 0.1
++                        }
++                    ]
++
++                Translate x y ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate value y)
++                        , label = Input.labelLeft [] (Element.text "x")
++                        , min = -100
++                        , max = 100
++                        , value = x
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    , Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Translate x value)
++                        , label = Input.labelLeft [] (Element.text "y")
++                        , min = -100
++                        , max = 100
++                        , value = y
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    ]
++
++                Rotate angle ->
++                    [ Input.slider
++                        [ Element.behindContent sliderBackground
++                        ]
++                        { onChange =
++                            \value ->
++                                SetTransformation index (Rotate value)
++                        , label = Input.labelLeft [] (Element.text "angle")
++                        , min = -360
++                        , max = 360
++                        , value = angle
++                        , thumb = Input.defaultThumb
++                        , step = Just 1
++                        }
++                    ]
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Border.color (Element.rgb 0.9 0.9 0.9)
++        , Border.width 3
++        , Element.padding 5
++        , Element.spacing 20
++        ]
++        [ Element.row [ Element.width Element.fill ]
++            [ transformation
++                |> List.singleton
++                |> Transformations.apply
++                |> Element.text
++                |> Element.el [ Element.width Element.fill ]
++            , Input.button []
++                { onPress = Just (DeleteTransformation index)
++                , label = Element.text "X"
++                }
++            ]
++        , Element.column
++            [ Element.width Element.fill
++            , Element.spacing 20
++            ]
++            controls
++        ]
++
++
++update : Msg -> Model -> Model
++update msg model =
++    case msg of
++        AddTransformation transformation ->
++            Array.push transformation model
++
++        DeleteTransformation index ->
++            let
++                end =
++                    Array.length model
++
++                front =
++                    Array.slice 0 index model
++
++                back =
++                    Array.slice (index + 1) end model
++            in
++            Array.append front back
++
++        SetTransformation index transformation ->
++            Array.set index transformation model
++
++
++subscriptions : Model -> Sub Msg
++subscriptions model =
++    Sub.none
++
++
++grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
++grid attributes unit size =
++    let
++        positiveValues =
++            size
++                / 2
++                |> floor
++                |> List.range 1
++                |> List.map toFloat
++                |> List.map ((*) unit)
++
++        negativeValues =
++            positiveValues
++                |> List.map negate
++
++        max =
++            unit * size / 2
++
++        min =
++            negate max
++    in
++    ((positiveValues ++ negativeValues)
++        |> List.map
++            (\value ->
++                [ ( Point2d.fromCoordinates ( value, min )
++                  , Point2d.fromCoordinates ( value, max )
++                  )
++                , ( Point2d.fromCoordinates ( min, value )
++                  , Point2d.fromCoordinates ( max, value )
++                  )
++                ]
++            )
++        |> List.concat
++        |> List.map LineSegment2d.fromEndpoints
++        |> List.map (Geometry.Svg.lineSegment2d attributes)
++    )
++        |> (::) (CartesianPlane.axes attributes (size * unit))
++        |> g []
+```
+
+``` diff
+similarity index 98%
+rename from src/TransformationsCircle.elm
+rename to src/Examples/TransformationsCircle.elm
+index 96264ff..a3a507f 100644
+--- a/src/TransformationsCircle.elm
++++ b/src/Examples/TransformationsCircle.elm
+@@ -1,4 +1,4 @@
+-module TransformationsCircle exposing (Config, defaults, main, ui)
++module Examples.TransformationsCircle exposing (Config, defaults, main, ui)
+=
+=import Element exposing (Element)
+=import Html exposing (Html)
+```
+
+``` diff
+similarity index 99%
+rename from src/Tree.elm
+rename to src/Examples/Tree.elm
+index e2925a8..eaed7b3 100644
+--- a/src/Tree.elm
++++ b/src/Examples/Tree.elm
+@@ -1,4 +1,4 @@
+-module Tree exposing
++module Examples.Tree exposing
+=    ( Model
+=    , Msg
+=    , init
+@@ -19,6 +19,7 @@ import Svg exposing (..)
+=import Svg.Attributes exposing (..)
+=
+=
++main : Program Flags Model Msg
+=main =
+=    Browser.element
+=        { init = init
+```
+
+``` diff
+new file mode 100644
+index 0000000..222da7a
+--- /dev/null
++++ b/src/Examples/ViewBox.elm
+@@ -0,0 +1,276 @@
++module Examples.ViewBox exposing
++    ( Model
++    , Msg
++    , init
++    , main
++    , ui
++    , update
++    , view
++    )
++
++import Browser
++import BrowserWindow
++import CartesianPlane exposing (graph)
++import Element
++import Element.Background as Background
++import Element.Border as Border
++import Element.Input as Input
++import Html exposing (Html)
++import Svg exposing (..)
++import Svg.Attributes exposing (..)
++
++
++main =
++    Browser.sandbox
++        { init = init
++        , view = view
++        , update = update
++        }
++
++
++type alias Model =
++    { left : Float
++    , top : Float
++    , width : Float
++    , height : Float
++    , preserveAspectRatio : Bool
++    }
++
++
++type Msg
++    = Move Float Float
++    | Resize Float Float
++    | PreserveAspectRatio Bool
++
++
++init : Model
++init =
++    { left = 0
++    , top = 0
++    , width = 400
++    , height = 400
++    , preserveAspectRatio = False
++    }
++
++
++view : Model -> Html.Html Msg
++view model =
++    let
++        wrapper element =
++            Element.el
++                [ Element.width (Element.maximum 1200 Element.fill)
++                , Element.height Element.fill
++                , Element.centerX
++                ]
++                element
++    in
++    ui model
++        |> wrapper
++        |> Element.layout
++            [ Element.height Element.fill
++            , Element.width Element.fill
++            ]
++
++
++update : Msg -> Model -> Model
++update msg model =
++    case msg of
++        Move left top ->
++            { model
++                | left = left
++                , top = top
++            }
++
++        Resize width height ->
++            { model
++                | width = width
++                , height = height
++            }
++
++        PreserveAspectRatio value ->
++            { model | preserveAspectRatio = value }
++
++
++ui model =
++    let
++        viewbox =
++            svg
++                [ viewBox "-500 -500 1000 1000"
++                ]
++                [ g [] world
++                , rect
++                    [ x (String.fromFloat model.left)
++                    , y (String.fromFloat model.top)
++                    , width (String.fromFloat model.width)
++                    , height (String.fromFloat model.height)
++                    , opacity "0.3"
++                    , stroke "white"
++                    , fill "pink"
++                    ]
++                    []
++                ]
++                |> Element.html
++                |> Element.el
++                    [ Element.centerX
++                    , Element.centerY
++                    , Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++
++        viewport =
++            svg
++                [ [ model.left, model.top, model.width, model.height ]
++                    |> List.map String.fromFloat
++                    |> String.join " "
++                    |> viewBox
++                , preserveAspectRatio <|
++                    case model.preserveAspectRatio of
++                        True ->
++                            "xMidYMid meet"
++
++                        False ->
++                            "none"
++                , Svg.Attributes.style "width: 100%; height: 100%; flex-grow: 1; flex-basis: 0"
++                ]
++                [ g [] world
++                , rect
++                    [ x (String.fromFloat model.left)
++                    , y (String.fromFloat model.top)
++                    , width (String.fromFloat model.width)
++                    , height (String.fromFloat model.height)
++                    , opacity "0.3"
++                    , stroke "white"
++                    , fill "pink"
++                    ]
++                    []
++                ]
++                |> Element.html
++                |> Element.el
++                    [ Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++                |> BrowserWindow.window
++                    [ Element.centerX
++                    , Element.centerY
++                    , Element.width Element.fill
++                    , Element.height Element.fill
++                    ]
++    in
++    Element.column
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        , Element.spacing 30
++        , Element.padding 30
++        ]
++        [ Element.row
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            , Element.centerX
++            , Element.spacing 30
++            ]
++            [ viewbox
++            , viewport
++            ]
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \left -> Move left model.top
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("left: " ++ String.fromFloat model.left)
++            , min = -500
++            , max = 500
++            , value = model.left
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \top -> Move model.left top
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("top: " ++ String.fromFloat model.top)
++            , min = -500
++            , max = 500
++            , value = model.top
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \width -> Resize width model.height
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("width: " ++ String.fromFloat model.width)
++            , min = 1
++            , max = 500
++            , value = model.width
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.slider
++            [ Element.behindContent
++                (Element.el
++                    [ Element.width Element.fill
++                    , Element.height (Element.px 2)
++                    , Element.centerY
++                    , Background.color <| Element.rgb 0.7 0.7 0.7
++                    , Border.rounded 2
++                    ]
++                    Element.none
++                )
++            ]
++            { onChange = \height -> Resize model.width height
++            , label =
++                Input.labelBelow [ Element.centerX ] <|
++                    Element.text ("height: " ++ String.fromFloat model.height)
++            , min = 1
++            , max = 500
++            , value = model.height
++            , thumb = Input.defaultThumb
++            , step = Just 1
++            }
++        , Input.checkbox []
++            { checked = model.preserveAspectRatio
++            , icon = Input.defaultCheckbox
++            , label = Input.labelRight [] (Element.text "Preserve aspect ratio")
++            , onChange = PreserveAspectRatio
++            }
++        ]
++
++
++world : List (Svg Msg)
++world =
++    [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
++    , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
++    , circle [ cx "0", cy "0", r "20", fill "red" ] []
++    ]
+```
+
+``` diff
+index ab8ac60..99d1276 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -10,41 +10,41 @@ import Basics.Extra exposing (curry)
+=import Browser
+=import Browser.Navigation as Navigation
+=import BrowserWindow
+-import CartesianCoordinates
+-import CenteredDot
+-import Counter
+=import Dict
+-import DotAtTheCenterOfTheScreen
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+=import Element.Events
+=import Element.Font as Font
+=import Element.Input as Input
++import Examples.CartesianCoordinates
++import Examples.CenteredDot
++import Examples.Counter
++import Examples.DotAtTheCenterOfTheScreen
++import Examples.FillTheScreen
++import Examples.Gradient
++import Examples.Line
++import Examples.NestedTransformations
++import Examples.PolarCoordinates
++import Examples.RosetteTypedTransformations
++import Examples.Simplest
++import Examples.Spiral
++import Examples.Transformations
++import Examples.TransformationsCircle
++import Examples.Tree
++import Examples.ViewBox
+=import FeatherIcons exposing (icons)
+-import FillTheScreen
+-import Gradient
+=import Html exposing (Html)
+=import Html.Attributes
+=import Http
+-import Line
+=import Mark
+=import Mark.Custom
+=import Mark.Default
+-import NestedTransformations
+=import Parser
+=import Parser.Advanced
+-import PolarCoordinates
+=import Result.Extra as Result
+-import RosetteTypedTransformations
+=import Routes exposing (Route)
+-import Simplest
+-import Spiral
+-import Transformations
+-import TransformationsCircle
+-import Tree
+=import Url exposing (Url)
+-import ViewBox
+=
+=
+=main : Program Flags Model Msg
+@@ -67,13 +67,13 @@ type alias Model =
+=    { url : Url
+=    , key : Navigation.Key
+=    , markup : Maybe String
+-    , counter : Counter.Model
+-    , transformations : Transformations.Model
+-    , nestedTransformations : NestedTransformations.Model
+-    , cartesianCoordinates : CartesianCoordinates.Model
+-    , polarCoordinates : PolarCoordinates.Model
+-    , tree : Maybe Tree.Model
+-    , viewBox : ViewBox.Model
++    , counter : Examples.Counter.Model
++    , transformations : Examples.Transformations.Model
++    , nestedTransformations : Examples.NestedTransformations.Model
++    , cartesianCoordinates : Examples.CartesianCoordinates.Model
++    , polarCoordinates : Examples.PolarCoordinates.Model
++    , tree : Maybe Examples.Tree.Model
++    , viewBox : Examples.ViewBox.Model
+=    }
+=
+=
+@@ -81,14 +81,14 @@ type Msg
+=    = UrlRequested Browser.UrlRequest
+=    | UrlChanged Url
+=    | ContentFetched (Result Http.Error String)
+-    | CounterMsg Counter.Msg
+-    | TransformationsMsg Transformations.Msg
+-    | NestedTransformationsMsg NestedTransformations.Msg
+-    | CartesianCoordinatesMsg CartesianCoordinates.Msg
+-    | PolarCoordinatesMsg PolarCoordinates.Msg
+-    | ToggleTree (Maybe Tree.Model)
+-    | TreeMsg Tree.Msg
+-    | ViewBoxMsg ViewBox.Msg
++    | CounterMsg Examples.Counter.Msg
++    | TransformationsMsg Examples.Transformations.Msg
++    | NestedTransformationsMsg Examples.NestedTransformations.Msg
++    | CartesianCoordinatesMsg Examples.CartesianCoordinates.Msg
++    | PolarCoordinatesMsg Examples.PolarCoordinates.Msg
++    | ToggleTree (Maybe Examples.Tree.Model)
++    | TreeMsg Examples.Tree.Msg
++    | ViewBoxMsg Examples.ViewBox.Msg
+=
+=
+=init : Flags -> Url -> Navigation.Key -> ( Model, Cmd Msg )
+@@ -96,13 +96,13 @@ init flags url key =
+=    ( { url = url
+=      , key = key
+=      , markup = Nothing
+-      , counter = Counter.init
+-      , transformations = Transformations.init
+-      , nestedTransformations = NestedTransformations.init
+-      , cartesianCoordinates = CartesianCoordinates.init
+-      , polarCoordinates = PolarCoordinates.init
++      , counter = Examples.Counter.init
++      , transformations = Examples.Transformations.init
++      , nestedTransformations = Examples.NestedTransformations.init
++      , cartesianCoordinates = Examples.CartesianCoordinates.init
++      , polarCoordinates = Examples.PolarCoordinates.init
+=      , tree = Nothing
+-      , viewBox = ViewBox.init
++      , viewBox = Examples.ViewBox.init
+=      }
+=    , url
+=        |> Routes.parse
+@@ -307,27 +307,39 @@ update msg model =
+=            )
+=
+=        CounterMsg m ->
+-            ( { model | counter = Counter.update m model.counter }
++            ( { model | counter = Examples.Counter.update m model.counter }
+=            , Cmd.none
+=            )
+=
+=        TransformationsMsg m ->
+-            ( { model | transformations = Transformations.update m model.transformations }
++            ( { model
++                | transformations =
++                    Examples.Transformations.update m model.transformations
++              }
+=            , Cmd.none
+=            )
+=
+=        NestedTransformationsMsg m ->
+-            ( { model | nestedTransformations = NestedTransformations.update m model.nestedTransformations }
++            ( { model
++                | nestedTransformations =
++                    Examples.NestedTransformations.update m model.nestedTransformations
++              }
+=            , Cmd.none
+=            )
+=
+=        CartesianCoordinatesMsg m ->
+-            ( { model | cartesianCoordinates = CartesianCoordinates.update m model.cartesianCoordinates }
++            ( { model
++                | cartesianCoordinates =
++                    Examples.CartesianCoordinates.update m model.cartesianCoordinates
++              }
+=            , Cmd.none
+=            )
+=
+=        PolarCoordinatesMsg m ->
+-            ( { model | polarCoordinates = PolarCoordinates.update m model.polarCoordinates }
++            ( { model
++                | polarCoordinates =
++                    Examples.PolarCoordinates.update m model.polarCoordinates
++              }
+=            , Cmd.none
+=            )
+=
+@@ -337,7 +349,7 @@ update msg model =
+=        TreeMsg m ->
+=            case model.tree of
+=                Just tree ->
+-                    Tree.update m tree
++                    Examples.Tree.update m tree
+=                        |> (\( newTree, treeCmd ) ->
+=                                ( { model | tree = Just newTree }
+=                                , Cmd.map TreeMsg treeCmd
+@@ -348,7 +360,7 @@ update msg model =
+=                    ( model, Cmd.none )
+=
+=        ViewBoxMsg m ->
+-            ( { model | viewBox = ViewBox.update m model.viewBox }, Cmd.none )
++            ( { model | viewBox = Examples.ViewBox.update m model.viewBox }, Cmd.none )
+=
+=
+=subscriptions : Model -> Sub Msg
+@@ -358,7 +370,7 @@ subscriptions model =
+=            Sub.none
+=
+=        Just tree ->
+-            Tree.subscriptions tree
++            Examples.Tree.subscriptions tree
+=                |> Sub.map TreeMsg
+=
+=
+@@ -472,7 +484,7 @@ document =
+=            let
+=                render model =
+=                    model.counter
+-                        |> Counter.ui
++                        |> Examples.Counter.ui
+=                        |> Element.el
+=                            [ Element.centerX
+=                            , Element.centerY
+@@ -489,9 +501,9 @@ document =
+=        simplest : Mark.Block (Model -> Element Msg)
+=        simplest =
+=            let
+-                render : Simplest.Config -> Model -> Element Msg
++                render : Examples.Simplest.Config -> Model -> Element Msg
+=                render config model =
+-                    Simplest.ui config
++                    Examples.Simplest.ui config
+=                        |> Element.html
+=                        |> Element.el []
+=                        |> Element.el
+@@ -502,7 +514,7 @@ document =
+=                        |> BrowserWindow.window []
+=            in
+=            Mark.record2 "Simplest"
+-                Simplest.Config
++                Examples.Simplest.Config
+=                (Mark.field "background" Mark.string)
+=                (Mark.field "fill" Mark.string)
+=                |> Mark.map render
+@@ -511,7 +523,7 @@ document =
+=        fillTheScreen =
+=            let
+=                render model =
+-                    FillTheScreen.ui
++                    Examples.FillTheScreen.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -524,7 +536,7 @@ document =
+=        dotAtTheCenterOfTheScreen =
+=            let
+=                render model =
+-                    DotAtTheCenterOfTheScreen.ui
++                    Examples.DotAtTheCenterOfTheScreen.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -537,7 +549,7 @@ document =
+=        centeredDot =
+=            let
+=                render model =
+-                    CenteredDot.main
++                    Examples.CenteredDot.main
+=                        |> Element.html
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+@@ -550,9 +562,9 @@ document =
+=        transformationsCircle : Mark.Block (Model -> Element Msg)
+=        transformationsCircle =
+=            let
+-                render : TransformationsCircle.Config -> Model -> Element Msg
++                render : Examples.TransformationsCircle.Config -> Model -> Element Msg
+=                render config model =
+-                    TransformationsCircle.ui config
++                    Examples.TransformationsCircle.ui config
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -561,7 +573,7 @@ document =
+=                        |> BrowserWindow.window []
+=            in
+=            Mark.record4 "TransformationsCircle"
+-                TransformationsCircle.Config
++                Examples.TransformationsCircle.Config
+=                (Mark.field "circle" Mark.bool)
+=                (Mark.field "center" Mark.bool)
+=                (Mark.field "angle" Mark.bool)
+@@ -572,7 +584,7 @@ document =
+=        line =
+=            let
+=                render model =
+-                    Line.ui
++                    Examples.Line.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -585,7 +597,7 @@ document =
+=        gradient =
+=            let
+=                render model =
+-                    Gradient.ui
++                    Examples.Gradient.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -599,7 +611,7 @@ document =
+=            let
+=                render model =
+=                    model.transformations
+-                        |> Transformations.ui
++                        |> Examples.Transformations.ui
+=                        |> Element.el
+=                            [ Element.width Element.fill
+=                            ]
+@@ -613,7 +625,7 @@ document =
+=            let
+=                render model =
+=                    model.nestedTransformations
+-                        |> NestedTransformations.ui
++                        |> Examples.NestedTransformations.ui
+=                        |> Element.el
+=                            [ Element.width Element.fill
+=                            ]
+@@ -627,7 +639,7 @@ document =
+=            let
+=                render model =
+=                    model.cartesianCoordinates
+-                        |> CartesianCoordinates.ui
++                        |> Examples.CartesianCoordinates.ui
+=                        |> Element.el
+=                            [ Element.width Element.fill
+=                            ]
+@@ -641,7 +653,7 @@ document =
+=            let
+=                render model =
+=                    model.polarCoordinates
+-                        |> PolarCoordinates.ui
++                        |> Examples.PolarCoordinates.ui
+=                        |> Element.el
+=                            [ Element.width Element.fill
+=                            ]
+@@ -654,7 +666,7 @@ document =
+=        rosette =
+=            let
+=                render model =
+-                    RosetteTypedTransformations.ui
++                    Examples.RosetteTypedTransformations.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -667,7 +679,7 @@ document =
+=        spiral =
+=            let
+=                render model =
+-                    Spiral.ui
++                    Examples.Spiral.ui
+=                        |> Element.el
+=                            [ Element.height (Element.px 400)
+=                            , Element.width Element.fill
+@@ -681,7 +693,7 @@ document =
+=            let
+=                render model =
+=                    model.tree
+-                        |> Maybe.map Tree.ui
++                        |> Maybe.map Examples.Tree.ui
+=                        |> Maybe.withDefault Element.none
+=                        |> Element.el
+=                            [ Element.height (Element.px 600)
+@@ -697,7 +709,7 @@ document =
+=            let
+=                render model =
+=                    model.viewBox
+-                        |> ViewBox.ui
++                        |> Examples.ViewBox.ui
+=                        |> Element.el
+=                            [ Element.width Element.fill
+=                            ]
+```
+
+``` diff
+index 1b3937d..d72965f 100644
+--- a/src/Transformations.elm
++++ b/src/Transformations.elm
+@@ -1,45 +1,8 @@
+=module Transformations exposing
+-    ( Model
+-    , Msg
+-    , Transformation(..)
++    ( Transformation(..)
+=    , apply
+-    , init
+-    , main
+-    , ui
+-    , update
+=    )
+=
+-import Array exposing (Array)
+-import Browser
+-import Browser.Events
+-import CartesianPlane
+-import Dict exposing (Dict)
+-import Element exposing (Element)
+-import Element.Background as Background
+-import Element.Border as Border
+-import Element.Input as Input
+-import Geometry.Svg
+-import Html exposing (Html)
+-import Json.Decode exposing (Decoder)
+-import LineSegment2d
+-import List.Extra as List
+-import Point2d
+-import Svg exposing (..)
+-import Svg.Attributes exposing (..)
+-
+-
+-main : Program () Model Msg
+-main =
+-    Browser.sandbox
+-        { init = init
+-        , view = view
+-        , update = update
+-        }
+-
+-
+-type alias Model =
+-    Array Transformation
+-
+=
+=type Transformation
+=    = Identity
+@@ -48,269 +11,6 @@ type Transformation
+=    | Rotate Float
+=
+=
+-type Msg
+-    = AddTransformation Transformation
+-    | DeleteTransformation Int
+-    | SetTransformation Int Transformation
+-
+-
+-init : Model
+-init =
+-    Array.fromList
+-        [ Translate 0 0
+-        , Rotate 0
+-        , Scale 1 1
+-        ]
+-
+-
+-view : Model -> Html Msg
+-view model =
+-    Element.layout
+-        [ Element.height Element.fill
+-        , Element.width Element.fill
+-        ]
+-        (ui model)
+-
+-
+-ui : Model -> Element Msg
+-ui model =
+-    let
+-        transformations =
+-            Array.toList model
+-
+-        wrapper element =
+-            Element.column
+-                [ Element.width (Element.maximum 600 Element.fill)
+-                , Element.centerX
+-                , Element.spacing 20
+-                ]
+-                [ Element.el
+-                    [ Element.width Element.fill
+-                    ]
+-                    (Element.html element)
+-                , Element.el
+-                    [ Background.color (Element.rgb 1 0.8 0.8)
+-                    , Element.padding 10
+-                    , Element.width Element.fill
+-                    ]
+-                    (transformationsUI transformations)
+-                ]
+-
+-        shape =
+-            g [ transform (apply transformations) ]
+-                [ line
+-                    [ x1 "0"
+-                    , x2 "100"
+-                    , y1 "0"
+-                    , y2 "0"
+-                    , stroke "red"
+-                    , strokeWidth "1"
+-                    ]
+-                    []
+-                , circle [ cx "0", cy "0", r "2", fill "red" ] []
+-                , grid
+-                    [ stroke "pink"
+-                    , fill "pink"
+-                    , strokeWidth "0.3"
+-                    ]
+-                    10
+-                    30
+-
+-                -- , rect [ x "", y "-2", width "1", height "4" ] []
+-                ]
+-    in
+-    shape
+-        |> List.singleton
+-        |> CartesianPlane.graph 300
+-        |> wrapper
+-
+-
+-transformationsUI : List Transformation -> Element Msg
+-transformationsUI transformations =
+-    let
+-        addButtons =
+-            [ Element.text "Add transformation: "
+-            , Input.button []
+-                { onPress = Just (AddTransformation (Translate 0 0))
+-                , label = Element.text "Translate"
+-                }
+-            , Input.button []
+-                { onPress = Just (AddTransformation (Scale 1 1))
+-                , label = Element.text "Scale"
+-                }
+-            , Input.button []
+-                { onPress = Just (AddTransformation (Rotate 0))
+-                , label = Element.text "Rotate"
+-                }
+-            ]
+-
+-        currentTrasformations =
+-            transformations
+-                |> List.indexedMap transformationUI
+-    in
+-    Element.column
+-        [ Element.width Element.fill
+-        , Element.spacing 10
+-        ]
+-        [ Element.row
+-            [ Element.width Element.fill
+-            , Element.spacing 10
+-            ]
+-            addButtons
+-        , Element.column
+-            [ Element.width Element.fill
+-            , Element.spacing 10
+-            ]
+-            currentTrasformations
+-        ]
+-
+-
+-transformationUI : Int -> Transformation -> Element Msg
+-transformationUI index transformation =
+-    let
+-        sliderBackground =
+-            Element.el
+-                [ Element.width Element.fill
+-                , Element.height (Element.px 2)
+-                , Element.centerY
+-                , Background.color <| Element.rgb 0.7 0.7 0.7
+-                , Border.rounded 2
+-                ]
+-                Element.none
+-
+-        controls =
+-            case transformation of
+-                Identity ->
+-                    [ Element.text "Identity" ]
+-
+-                Scale horizontal vertical ->
+-                    [ Input.slider
+-                        [ Element.behindContent sliderBackground
+-                        ]
+-                        { onChange =
+-                            \x ->
+-                                SetTransformation index (Scale x vertical)
+-                        , label = Input.labelLeft [] (Element.text "horizontal")
+-                        , min = 0
+-                        , max = 10
+-                        , value = horizontal
+-                        , thumb = Input.defaultThumb
+-                        , step = Just 0.1
+-                        }
+-                    , Input.slider
+-                        [ Element.behindContent sliderBackground
+-                        ]
+-                        { onChange =
+-                            \y ->
+-                                SetTransformation index (Scale horizontal y)
+-                        , label = Input.labelLeft [] (Element.text "vertical")
+-                        , min = 0
+-                        , max = 10
+-                        , value = vertical
+-                        , thumb = Input.defaultThumb
+-                        , step = Just 0.1
+-                        }
+-                    ]
+-
+-                Translate x y ->
+-                    [ Input.slider
+-                        [ Element.behindContent sliderBackground
+-                        ]
+-                        { onChange =
+-                            \value ->
+-                                SetTransformation index (Translate value y)
+-                        , label = Input.labelLeft [] (Element.text "x")
+-                        , min = -100
+-                        , max = 100
+-                        , value = x
+-                        , thumb = Input.defaultThumb
+-                        , step = Just 1
+-                        }
+-                    , Input.slider
+-                        [ Element.behindContent sliderBackground
+-                        ]
+-                        { onChange =
+-                            \value ->
+-                                SetTransformation index (Translate x value)
+-                        , label = Input.labelLeft [] (Element.text "y")
+-                        , min = -100
+-                        , max = 100
+-                        , value = y
+-                        , thumb = Input.defaultThumb
+-                        , step = Just 1
+-                        }
+-                    ]
+-
+-                Rotate angle ->
+-                    [ Input.slider
+-                        [ Element.behindContent sliderBackground
+-                        ]
+-                        { onChange =
+-                            \value ->
+-                                SetTransformation index (Rotate value)
+-                        , label = Input.labelLeft [] (Element.text "angle")
+-                        , min = -360
+-                        , max = 360
+-                        , value = angle
+-                        , thumb = Input.defaultThumb
+-                        , step = Just 1
+-                        }
+-                    ]
+-    in
+-    Element.column
+-        [ Element.width Element.fill
+-        , Border.color (Element.rgb 0.9 0.9 0.9)
+-        , Border.width 3
+-        , Element.padding 5
+-        , Element.spacing 20
+-        ]
+-        [ Element.row [ Element.width Element.fill ]
+-            [ transformation
+-                |> List.singleton
+-                |> apply
+-                |> Element.text
+-                |> Element.el [ Element.width Element.fill ]
+-            , Input.button []
+-                { onPress = Just (DeleteTransformation index)
+-                , label = Element.text "X"
+-                }
+-            ]
+-        , Element.column
+-            [ Element.width Element.fill
+-            , Element.spacing 20
+-            ]
+-            controls
+-        ]
+-
+-
+-update : Msg -> Model -> Model
+-update msg model =
+-    case msg of
+-        AddTransformation transformation ->
+-            Array.push transformation model
+-
+-        DeleteTransformation index ->
+-            let
+-                end =
+-                    Array.length model
+-
+-                front =
+-                    Array.slice 0 index model
+-
+-                back =
+-                    Array.slice (index + 1) end model
+-            in
+-            Array.append front back
+-
+-        SetTransformation index transformation ->
+-            Array.set index transformation model
+-
+-
+-subscriptions : Model -> Sub Msg
+-subscriptions model =
+-    Sub.none
+-
+-
+=apply : List Transformation -> String
+=apply transformations =
+=    let
+@@ -342,43 +42,3 @@ apply transformations =
+=    transformations
+=        |> List.map toString
+=        |> String.join " "
+-
+-
+-grid : List (Svg.Attribute msg) -> Float -> Float -> Svg msg
+-grid attributes unit size =
+-    let
+-        positiveValues =
+-            size
+-                / 2
+-                |> floor
+-                |> List.range 1
+-                |> List.map toFloat
+-                |> List.map ((*) unit)
+-
+-        negativeValues =
+-            positiveValues
+-                |> List.map negate
+-
+-        max =
+-            unit * size / 2
+-
+-        min =
+-            negate max
+-    in
+-    ((positiveValues ++ negativeValues)
+-        |> List.map
+-            (\value ->
+-                [ ( Point2d.fromCoordinates ( value, min )
+-                  , Point2d.fromCoordinates ( value, max )
+-                  )
+-                , ( Point2d.fromCoordinates ( min, value )
+-                  , Point2d.fromCoordinates ( max, value )
+-                  )
+-                ]
+-            )
+-        |> List.concat
+-        |> List.map LineSegment2d.fromEndpoints
+-        |> List.map (Geometry.Svg.lineSegment2d attributes)
+-    )
+-        |> (::) (CartesianPlane.axes attributes (size * unit))
+-        |> g []
+```
\ No newline at end of file
new file mode 100644
index 0000000..b1bf6d0
--- /dev/null
+++ b/content/devlog/2018-12-19-elm-tree-workshop.md
@@ -0,0 +1,1604 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 8
+
+
+# More progress on Day 1
+
+
+
+``` diff
+index 8359570..beac6da 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -6,3 +6,4 @@
+=/public/
+=/built/
+=/node_modules/
++/src/Playground.elm
+```
+
+``` diff
+index 83cc9bf..9cf6974 100644
+--- a/content/day-1.txt
++++ b/content/day-1.txt
+@@ -85,7 +85,11 @@ Note that the same address was printed by the Elm reactor.
+=
+=We want to display a dot at the center of the screen, like this:
+=
+-| DotAtTheCenterOfTheScreen
++| DotWithViewBox
++    background=none
++    fill=black
++    viewBox=-300 -300 600 600
++
+=
+=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
+=
+@@ -128,9 +132,8 @@ Its all about drawing shapes on the screen. Let's install an Elm Package to help
+=Now that we have {Code|elm//svg} installed, we can write the code that will draw our dot:
+=
+=| Code
+-    module Simplest exposing (main)
++    module Main exposing (main)
+=
+-    import Element
+=    import Svg
+=    import Svg.Attributes
+=
+@@ -138,9 +141,7 @@ Now that we have {Code|elm//svg} installed, we can write the code that will draw
+=    main =
+=        Svg.svg []
+=            [ Svg.circle
+-                [ Svg.Attributes.r "10"
+-                ]
+-                []
++                [ Svg.Attributes.r "10" ] []
+=            ]
+=
+=Start the reactor again:
+@@ -153,7 +154,7 @@ Start the reactor again:
+=
+=Reload the browser. You should see something like this:
+=
+-| Simplest
++| Dot
+=    background=none
+=    fill=black
+=
+@@ -163,15 +164,22 @@ There are two reasons. First, because the center of the dot is at a point called
+=
+=Second, it's because the SVG space doesn't fill the browser window. Don't believe me? We can see that the space doesn't fill the window by changing the background color of the SVG element.
+=
+-| Note
+-    Code sample for simplest with pink background should be here.
++| Code
++    module Main exposing (main)
+=
+-When you reload the browser, you'll see this:
++    import Svg
++    import Svg.Attributes
+=
+-| Note
+-    Example of simplest with pink background should be here.
+=
+-| Simplest
++    main =
++        Svg.svg [ Svg.Attributes.style "background: pink" ]
++            [ Svg.circle
++                [ Svg.Attributes.r "10" ] []
++            ]
++
++When you reload the browser, you'll see this:
++
++| Dot
+=    background=pink
+=    fill=black
+=
+@@ -185,11 +193,42 @@ Press {Code|CTRL + C} to stop the elm-reactor, type {Code|elm install mdgriffith
+=
+=Then in {Code|src//Main.elm} add the necessary import {Code|import Element} and change {Code|Main} to look like this:
+=
+-Main = * the code of Element.layout should be here*
++| Code
++    module Main exposing (main)
+=
+-Take a wild guess: what do you think {Code|Element.height Element.fill} and {Code|Element.height Element.width} do?
++    import Element
++    import Svg
++    import Svg.Attributes
+=
+-Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen. It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
++
++    main =
++        Element.layout
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            ]
++            (Element.html
++                (Svg.svg
++                    [ Svg.Attributes.height "100%"
++                    , Svg.Attributes.width "100%"
++                    , Svg.Attributes.style "background: pink"
++                    ]
++                    [ Svg.circle
++                        [ Svg.Attributes.r "10" ] []
++                    ]
++                )
++            )
++
++Take a wild guess: what do you think {Code|Element.height Element.fill}, {Code|Svg.Attributes.height "100%"}, {Code|Element.width Element.fill} and {Code|Svg.Attributes.width "100%"} do?
++
++Reload the browser to confirm your hypothesis. Voila! The SVG space now fills the screen.
++
++| DotInElement
++    background=pink
++    fill=black
++    cx=0
++    cy=0
++
++It should now be possible to place our dot in the center of the screen. But, how exactly will we achieve it?
+=
+=Let's return to the idea we introduced earlier, that the center of the dot is at a point called the {Code|origin} or {Code|(0, 0)}. What eactly does this mean?
+=
+@@ -203,7 +242,40 @@ Knowing that the center of the dot is at the {Code|origin} or point {Code|(0,0)}
+=
+=To change the position of the dot, we can use the {Code|cx} and {Code|cy} attributes of the {Code|circle} element.
+=
+-* simplest with cx and cy should be here *
++| Code
++    module Main exposing (main)
++
++    import Element
++    import Svg
++    import Svg.Attributes
++
++
++    main =
++        Element.layout
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            ]
++            (Element.html
++                (Svg.svg
++                    [ Svg.Attributes.height "100%"
++                    , Svg.Attributes.width "100%"
++                    , Svg.Attributes.style "background: pink"
++                    ]
++                    [ Svg.circle
++                        [ Svg.Attributes.r "10"
++                        , Svg.Attributes.cx "100"
++                        , Svg.Attributes.cy "50"
++                        ]
++                        []
++                    ]
++                )
++            )
++
++| DotInElement
++    background=pink
++    fill=black
++    cx=100
++    cy=50
+=
+=Now, if we want our dot to be exactly in the center of the screen, we'll have to set {Code|cx} and {Code|cy} to values equal to exactly half of the {Code|width} and {Code|height} of the SVG space.
+=
+@@ -213,7 +285,7 @@ We can calculate the position of the circle as:
+=    cx = width/2
+=    cy = height/2
+=
+-There is a problem though! We don't actually know the width and height of the SVG space. All we know is that its {Code|width} and {Code|height} will {Code|fill} the viewport, whatever its size.
++There is a problem though! We don't actually know the width and height of the SVG space. All we know is that out {Code|layout's} {Code|width} and {Code|height} will {Code|fill} the viewport, and the {Code|svg} space's {Code|width} and {Code|height} are {Code|100%} of the {Code|layout}. We don't know the exact size of either (because the {Code|layout} will scale to the browser window).
+=☹️
+=
+=Fortunately, we don't need to know the width and height of the SVG space!
+@@ -226,12 +298,37 @@ A videwbox is like a window into an SVG space. It allows us to change the point
+=
+=| ViewBox
+=
+-In this example, we can set the width and height of the viewbox to an arbitrary value. As we see in the interactive example, the width and height of the viewbox will effect how big the dot appears, but that's not so important to us at the moment. Let's set them both to 1000.
++In this example, we can set the width and height of the viewbox to an arbitrary value. As we see in the interactive example, the width and height of the viewbox will effect how big the dot appears, but that's not so important to us at the moment. Let's set them both to 600.
++
++The top and left values of the viewbox are a bit trickier. We saw ealier that if we knew the width and height of our SVG scene, we could have set the cx and cy values of the dot to half the width and height of the scene to center the dot. We will have to use similar reasoning here. If we set the top and left values of the viewbox to 0, the dot will still still appear in the top left corner of the scene. If we want to see the dot moved to the right and downward within the scene, we will need the top and left values of the viewbox to be negative. If we want the origin of the SVG space to appear directly in the center of the scene, we will need to set the top and left values of the viewbox to {Code|- height//2} and {Code|- width//2}. In this case, they will both have the value 300.
++
++| Code
++    module Main exposing (main)
++
++    import Element
++    import Svg
++    import Svg.Attributes
++
+=
+-The top and left values of the viewbox are a bit trickier. We saw ealier that if we knew the width and height of our SVG scene, we could have set the cx and cy values of the dot to half the width and height of the scene to center the dot. We will have to use similar reasoning here. If we set the top and left values of the viewbox to 0, the dot will still still appear in the top left corner of the scene. If we want to see the dot moved to the right and downward within the scene, we will need the top and left values of the viewbox to be negative. If we want the origin of the SVG space to appear directly in the center of the scene, we will need to set the top and left values of the viewbox to {Code|- height//2} and {Code|- width//2}. In this case, they will both have the value 500.
++    main =
++        Element.layout
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            ]
++            (Element.html
++                (Svg.svg
++                    [ Svg.Attributes.viewBox "-300 -300 600 600" ]
++                    [ Svg.circle [ Svg.Attributes.r "10" ] []
++                    ]
++                )
++            )
+=
+=Refresh the broswer. We should now see the dot in the center of the screen.
+=
++| DotWithViewBox
++    background=none
++    fill=black
++    viewBox=-300 -300 600 600
+=
+=Give a color to the dot
+=
+```
+
+``` diff
+similarity index 91%
+rename from src/Simplest.elm
+rename to src/Dot.elm
+index 2e65fa4..0f6eec4 100644
+--- a/src/Simplest.elm
++++ b/src/Dot.elm
+@@ -1,4 +1,4 @@
+-module Simplest exposing (Config, defaults, ui)
++module Dot exposing (Config, defaults, ui)
+=
+=import Html exposing (Html)
+=import Svg
+```
+
+``` diff
+deleted file mode 100644
+index 83f0d1d..0000000
+--- a/src/DotAtTheCenterOfTheScreen.elm
++++ /dev/null
+@@ -1,27 +0,0 @@
+-module DotAtTheCenterOfTheScreen exposing (main, ui)
+-
+-import Element
+-import Svg
+-import Svg.Attributes
+-
+-
+-main =
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-        ui
+-
+-
+-ui =
+-    Svg.svg
+-        [ Svg.Attributes.viewBox "-100 -100 200 200"
+-        ]
+-        [ Svg.circle
+-            [ Svg.Attributes.r "10"
+-            , Svg.Attributes.cx "0"
+-            , Svg.Attributes.cy "0"
+-            ]
+-            []
+-        ]
+-        |> Element.html
+```
+
+``` diff
+new file mode 100644
+index 0000000..a3e3397
+--- /dev/null
++++ b/src/DotInElement.elm
+@@ -0,0 +1,44 @@
++module DotInElement exposing (Config, main, ui)
++
++import Element exposing (Element)
++import Svg
++import Svg.Attributes
++
++
++type alias Config =
++    { background : String
++    , fill : String
++    , cx : String
++    , cy : String
++    }
++
++
++defaults : Config
++defaults =
++    Config "pink" "black" "0" "0"
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui defaults)
++
++
++ui : Config -> Element msg
++ui { background, fill, cx, cy } =
++    Svg.svg
++        [ Svg.Attributes.height "100%"
++        , Svg.Attributes.width "100%"
++        , Svg.Attributes.style <| "background: " ++ background
++        ]
++        [ Svg.circle
++            [ Svg.Attributes.r "10"
++            , Svg.Attributes.fill fill
++            , Svg.Attributes.cx cx
++            , Svg.Attributes.cy cy
++            ]
++            []
++        ]
++        |> Element.html
+```
+
+``` diff
+similarity index 66%
+rename from src/CenteredDot.elm
+rename to src/DotWithViewBox.elm
+index 59fb692..d26753c 100644
+--- a/src/CenteredDot.elm
++++ b/src/DotWithViewBox.elm
+@@ -1,4 +1,9 @@
+-module CenteredDot exposing (main)
++module DotWithViewBox exposing (Config, main, ui)
++
++import Element
++import Svg
++import Svg.Attributes
++
+=
+={-| This program demonstrates how to center an element within an SVG viewport. The idea is to make sure that (0, 0) point (so called origin) should be in the middle of the ViewBox. We can achive that, by making the top-left corner of the ViewBox same distance from the (0, 0) as the bottom right.
+=
+@@ -18,23 +23,35 @@ In fact it doesnt matter how wide and high the viewBox is. As long as the top wi
+=That's the trick to position an object (like a dot) exactly in the center of the SVG viewport
+=
+=-}
++type alias Config =
++    { background : String
++    , fill : String
++    , viewBox : String
++    }
+=
+-import Element
+-import Svg
+-import Svg.Attributes
++
++defaults : Config
++defaults =
++    Config "white" "black" "-500 -500 1000 1000"
+=
+=
+=main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui defaults)
++
++
++ui { background, fill, viewBox } =
+=    Svg.svg
+-        [ Svg.Attributes.style "background: pink"
+-        , Svg.Attributes.width "300"
+-        , Svg.Attributes.height "150"
+-        , Svg.Attributes.viewBox "-100 -100 200 200"
++        [ Svg.Attributes.viewBox viewBox
++        , Svg.Attributes.style <| "background: " ++ background
+=        ]
+=        [ Svg.circle
+=            [ Svg.Attributes.r "10"
+-            , Svg.Attributes.cx "0"
+-            , Svg.Attributes.cy "0"
++            , Svg.Attributes.fill fill
+=            ]
+=            []
+=        ]
++        |> Element.html
+```
+
+``` diff
+deleted file mode 100644
+index 56f88d6..0000000
+--- a/src/FillTheScreen.elm
++++ /dev/null
+@@ -1,28 +0,0 @@
+-module FillTheScreen exposing (main, ui)
+-
+-import Element
+-import Svg
+-import Svg.Attributes
+-
+-
+-main =
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-        ui
+-
+-
+-ui =
+-    Svg.svg
+-        [ Svg.Attributes.style "background: pink"
+-        , Svg.Attributes.viewBox "-100 -100 200 200"
+-        ]
+-        [ Svg.circle
+-            [ Svg.Attributes.r "10"
+-            , Svg.Attributes.cx "0"
+-            , Svg.Attributes.cy "0"
+-            ]
+-            []
+-        ]
+-        |> Element.html
+```
+
+``` diff
+index 019ba58..87b6396 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -11,10 +11,11 @@ import Browser
+=import Browser.Navigation as Navigation
+=import BrowserWindow
+=import CartesianCoordinates
+-import CenteredDot
+=import Counter
+=import Dict
+-import DotAtTheCenterOfTheScreen
++import Dot
++import DotInElement
++import DotWithViewBox
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+@@ -22,7 +23,6 @@ import Element.Events
+=import Element.Font as Font
+=import Element.Input as Input
+=import FeatherIcons exposing (icons)
+-import FillTheScreen
+=import Gradient
+=import Html exposing (Html)
+=import Html.Attributes
+@@ -38,7 +38,6 @@ import PolarCoordinates
+=import Result.Extra as Result
+=import RosetteTypedTransformations
+=import Routes exposing (Route)
+-import Simplest
+=import Spiral
+=import Transformations
+=import Tree
+@@ -447,10 +446,9 @@ document =
+=
+=                    -- Embeded programs
+=                    , counter
+-                    , simplest
+-                    , fillTheScreen
+-                    , dotAtTheCenterOfTheScreen
+-                    , centeredDot
++                    , dot
++                    , dotInElement
++                    , dotWithViewBox
+=                    , line
+=                    , gradient
+=                    , transformations
+@@ -484,66 +482,54 @@ document =
+=            in
+=            Mark.stub "Counter" render
+=
+-        simplest : Mark.Block (Model -> Element Msg)
+-        simplest =
++        dot : Mark.Block (Model -> Element Msg)
++        dot =
+=            let
+-                render : Simplest.Config -> Model -> Element Msg
++                render : Dot.Config -> Model -> Element Msg
+=                render config model =
+-                    Simplest.ui config
++                    Dot.ui config
+=                        |> Element.html
+-                        |> Element.el []
+=                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
++                            [ Element.width (Element.px 300)
+=                            , Element.padding 5
+=                            ]
+-                        |> BrowserWindow.window []
++                        |> BrowserWindow.window [ Element.height (Element.px 300) ]
+=            in
+-            Mark.record2 "Simplest"
+-                Simplest.Config
++            Mark.record2 "Dot"
++                Dot.Config
+=                (Mark.field "background" Mark.string)
+=                (Mark.field "fill" Mark.string)
+=                |> Mark.map render
+=
+-        fillTheScreen : Mark.Block (Model -> Element Msg)
+-        fillTheScreen =
+-            let
+-                render model =
+-                    FillTheScreen.ui
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            ]
+-                        |> BrowserWindow.window []
+-            in
+-            Mark.stub "FillTheScreen" render
+-
+-        dotAtTheCenterOfTheScreen : Mark.Block (Model -> Element Msg)
+-        dotAtTheCenterOfTheScreen =
++        dotInElement : Mark.Block (Model -> Element Msg)
++        dotInElement =
+=            let
+-                render model =
+-                    DotAtTheCenterOfTheScreen.ui
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            ]
+-                        |> BrowserWindow.window []
++                render config model =
++                    DotInElement.ui config
++                        |> BrowserWindow.window [ Element.height (Element.px 300) ]
+=            in
+-            Mark.stub "DotAtTheCenterOfTheScreen" render
++            Mark.record4 "DotInElement"
++                DotInElement.Config
++                (Mark.field "background" Mark.string)
++                (Mark.field "fill" Mark.string)
++                (Mark.field "cx" Mark.string)
++                (Mark.field "cy" Mark.string)
++                |> Mark.map render
+=
+-        centeredDot : Mark.Block (Model -> Element Msg)
+-        centeredDot =
++        dotWithViewBox : Mark.Block (Model -> Element Msg)
++        dotWithViewBox =
+=            let
+-                render model =
+-                    CenteredDot.main
+-                        |> Element.html
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            ]
++                render config model =
++                    DotWithViewBox.ui config
++                        |> Element.el [ Element.height (Element.px 300) ]
+=                        |> BrowserWindow.window []
+=            in
+-            Mark.stub "CenteredDot" render
++            Mark.record3 "DotWithViewBox"
++                DotWithViewBox.Config
++                (Mark.field "background" Mark.string)
++                (Mark.field "fill" Mark.string)
++                (Mark.field "viewBox" Mark.string)
++                |> Mark.map render
+=
+=        line : Mark.Block (Model -> Element Msg)
+=        line =
+```
+
+# Merge branch 'multiple-pages' into review-day-one
+
+
+
+
+
+# Fix bad imports with merge
+
+
+
+``` diff
+index 0f6eec4..5581546 100644
+--- a/src/Examples/Dot.elm
++++ b/src/Examples/Dot.elm
+@@ -1,4 +1,4 @@
+-module Dot exposing (Config, defaults, ui)
++module Examples.Dot exposing (Config, defaults, ui)
+=
+=import Html exposing (Html)
+=import Svg
+```
+
+``` diff
+index a3e3397..ee127b3 100644
+--- a/src/Examples/DotInElement.elm
++++ b/src/Examples/DotInElement.elm
+@@ -1,4 +1,4 @@
+-module DotInElement exposing (Config, main, ui)
++module Examples.DotInElement exposing (Config, main, ui)
+=
+=import Element exposing (Element)
+=import Svg
+```
+
+``` diff
+index d26753c..6f91dc6 100644
+--- a/src/Examples/DotWithViewBox.elm
++++ b/src/Examples/DotWithViewBox.elm
+@@ -1,4 +1,4 @@
+-module DotWithViewBox exposing (Config, main, ui)
++module Examples.DotWithViewBox exposing (Config, main, ui)
+=
+=import Element
+=import Svg
+```
+
+``` diff
+index 6451e35..56ef197 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -10,12 +10,7 @@ import Basics.Extra exposing (curry)
+=import Browser
+=import Browser.Navigation as Navigation
+=import BrowserWindow
+-import CartesianCoordinates
+-import Counter
+=import Dict
+-import Dot
+-import DotInElement
+-import DotWithViewBox
+=import Element exposing (Element)
+=import Element.Background as Background
+=import Element.Border as Border
+@@ -23,23 +18,21 @@ import Element.Events
+=import Element.Font as Font
+=import Element.Input as Input
+=import Examples.CartesianCoordinates
+-import Examples.CenteredDot
+=import Examples.Counter
+-import Examples.DotAtTheCenterOfTheScreen
+-import Examples.FillTheScreen
++import Examples.Dot as Dot
++import Examples.DotInElement as DotInElement
++import Examples.DotWithViewBox as DotWithViewBox
+=import Examples.Gradient
+=import Examples.Line
+=import Examples.NestedTransformations
+=import Examples.PolarCoordinates
+=import Examples.RosetteTypedTransformations
+-import Examples.Simplest
+=import Examples.Spiral
+=import Examples.Transformations
+=import Examples.TransformationsCircle
+=import Examples.Tree
+=import Examples.ViewBox
+=import FeatherIcons exposing (icons)
+-import Gradient
+=import Html exposing (Html)
+=import Html.Attributes
+=import Http
+@@ -50,9 +43,7 @@ import Parser
+=import Parser.Advanced
+=import Result.Extra as Result
+=import Routes exposing (Route)
+-import Spiral
+=import Transformations
+-import Tree
+=import Url exposing (Url)
+=
+=
+```
+
+# Finish day 1
+
+Create multiple dots example
+
+``` diff
+index 9cf6974..b9a8560 100644
+--- a/content/day-1.txt
++++ b/content/day-1.txt
+@@ -318,8 +318,7 @@ The top and left values of the viewbox are a bit trickier. We saw ealier that if
+=            (Element.html
+=                (Svg.svg
+=                    [ Svg.Attributes.viewBox "-300 -300 600 600" ]
+-                    [ Svg.circle [ Svg.Attributes.r "10" ] []
+-                    ]
++                    [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+=                )
+=            )
+=
+@@ -330,29 +329,215 @@ Refresh the broswer. We should now see the dot in the center of the screen.
+=    fill=black
+=    viewBox=-300 -300 600 600
+=
+-Give a color to the dot
+=
++Now that we've centered our dot, we can customize it by giving it a color! Let's take a deeper look at how we give attributes to an SVG element.
++
++We've already seen a few example of SVG attributes. The radius of our dot ({Code|circle}) is an attribute.
++
++| Code
++     Svg.circle [ Svg.Attributes.r "10" ] []
+=
+-| List
+-    - Explain SVG attributes ( svg elements take a list of attributes)
+-    - Everyone can pick a color
++Try giving it a new value to see your dot grow or shrink.
+=
++Our viewBox is also an attribute. It's an attribute of the {Code|svg} element.
+=
+-Multiple dots
++| Code
++    Svg.svg
++        [ Svg.Attributes.viewBox "-300 -300 600 600" ]
++        [ Svg.circle [ Svg.Attributes.r "10" ] [] ]
+=
++Again, try giving it new values. As we saw earlier, the first two values (which represent the left and top of the viewBox) must be equal to the negative of the half of the last two values (which correspond to width and height). Try other combinations of values to adjust the placement of the dot. If the behavior of the viewBox is difficult to understand, refer again to the interactive tutorial above.
+=
+-| List
+-    -> Introduce a problem: We want to have two dots on the screen
+-    -> Explain a list, and show there is already a list there (with one item)
++To color our dot, we'll have to give it a second attribute. Many SVG elements take a {Code|List} of attributes. A {Code|List} is a very important concept in software development. {Code|Lists} allow us to group an arbitary number of similar values. In this case, we'll be using a {Code|List} to group {Code|Attribute a} values. Whenever you see {Code|[...]} in an Elm source code file, you are dealing with a list.
+=
++Before we give a color to our dot, try to identify all the lists in the code we're writen already. There are 5. Hint: It is possible for a list to have only 1 or 0 items ({Code|[]}).
+=
+-| Header
+-    Do you see any other lists?
++| Code
++    Svg.circle [ Svg.Attributes.r "10" ] []
++
++When we take a look at the code responsible for creating our dot, we see that it is followed by two lists. We'll only need to worry about the first one at the moment. As we've discussed, the first list already contains the {Code|r} attribute of the circle, representing the radius. We'll need to add a second attribute to the circle. We use a comma ({Code|,}) to seperate elements in a list.
++
++| Code
++    Svg.circle [ Svg.Attributes.r "10", ... ] []
++
++To color our circle, we'll use the {Code|fill} attribute.
++
++| Code
++    Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++
++You should see this result:
++
++| DotWithViewBox
++    background=none
++    fill=blue
++    viewBox=-300 -300 600 600
++
++
++Let's use our new understanding of lists to do something a bit more interesting. Let's draw multiple dots!
++
++We can start by taking a look at our {Code|main} value:
++
++| Code
++    main =
++        Element.layout
++            [ Element.width Element.fill
++            , Element.height Element.fill
++            ]
++            (Element.html
++                (Svg.svg
++                    [ Svg.Attributes.viewBox "-500 -500 1000 1000"
++                    ]
++                    [ Svg.circle
++                        [ Svg.Attributes.r "10"
++                        , Svg.Attributes.fill "blue"
++                        ]
++                        []
++                    ]
++                )
++            )
++
++Where is our list of circles? To find it, first find the circle.
++
++| Code
++    Svg.circle
++      [ Svg.Attributes.r "10"
++      , Svg.Attributes.fill "blue"
++      ]
++      []
++
++Next, identify the first open square bracket {Code|[} before the circle.
++
++| Code
++    [ Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++    ...
++
++This is where the list begins.
++
++Finally, identify the matching closing brakcet {Code|]}. This is a bit tricky because there are two lists within this list. The first is the list of attributes to the circle we saw earlier. The second is an empty list. We'll see exactly what they do in a moment. Ignore both of these for now. The matching closing bracket should be on the same indentation level as the opening bracket. Your text editor should be able to help you here. When you move your cursor before the opening bracket, the closing bracket should be highlighted.
++
++| Code
++    [ Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++    ]
++
++Right now, our list of circles has one value. We want to give it a second value. The second value will look identical to the first. What is the first value?
++
++| Code
++    Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++
++It might not seem obvious at first that this is all one value. But it is. {Code|Svg.circle} takes two lists as arguments. We'll see what an argument is later. For now, we will need to understand the difference between an attribute and a child.
++
++Imagine a box of colored easter eggs. One egg is painted yellow, another red, another blue. Moreover, they don't all come from the same bird. One is a small robin's egg. Another is a medium chicken egg. Another is a huge ostrich egg.
++
++Some of the elements in our image are 'things'. A box is a thing. Each egg is also a thing. Some of the elements in our image are not things. Blueness is not a thing. Medium-sizeness is not a thing. You can see blueness. You can see medium-sizeness. But they are not things. They are attributes of a thing. A thing can be blue. It can be medium-sized.
++
++It is a convention in many programming languages, including Elm and SVG, to distinguish between the attributes and children of an element. If we wanted to represent our box in SVG, we would give our box several attributes. It would have a color (maybe brown). It would have a width, length, depth. We would also give it several children. Each of the eggs within the box would be a child of the box. A box with 6 eggs would have 6 children. Each of these children (the eggs) would have different attributes: blue, red, medium-sized, etc. The children of an element are the 'things' it contains. The attributes of a thing are the elements that describe it.
++
++The first list following {Code|Svg.circle} is:
++
++| Code
++    [ Svg.Attributes.r "10"
++    , Svg.Attributes.fill "blue"
++    ]
++
++This is a list of attributes. A radius is not a 'thing'. A fill color is not a 'thing'. They are elements that describe a thing. They describe our circle.
++
++The second list is:
++
++| Code
++    []
++
++It is an empty list. Nevertheless, it is a list of children. Our circles don't have any children.
++
++Is the circle itself an attribute or a thing?
++
++From Elm's perspective, this entire code block is a circle:
++
++| Code
++    Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++
++This is not a circle:
++
++| Code
++    Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++
++Nor is this:
++
++| Code
++    Svg.circle
++
++To create a list with two circles, we'll want to duplicate the properly formed circle value. You can give the second dot a different color from the first.
++
++| Code
++    [ Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++    , Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.fill "red"
++        ]
++        []
++    ]
++
++Make add the second circle to the list of circles in your source code and refresh the page. What do you see?
++
++| TwoDots
++    background=none
++    viewBox=-300 -300 600 600
++    firstFill=blue
++    secondFill=red
++    firstCX=0
++    secondCX=0
++
++Only one dot! We see the second one, but what happened to the first dot? It's under the second... Both are at the origin (0, 0), so they are both in the same spot. If we want to see both dots, we will have to change their positions. To do this we'll have to use their {Code|cx} or {Code|cy} values.
++
++| Code
++    [ Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.cx "-10"
++        , Svg.Attributes.fill "blue"
++        ]
++        []
++    , Svg.circle
++        [ Svg.Attributes.r "10"
++        , Svg.Attributes.cx "10"
++        , Svg.Attributes.fill "red"
++        ]
++        []
++    ]
++
++| TwoDots
++    background=none
++    viewBox=-300 -300 600 600
++    firstFill=blue
++    secondFill=red
++    firstCX=-100
++    secondCX=100
+=
++As an exercise, try adding an {Code|Svg.Attribute.cy} attribute to the circles to adjust their vertical position.
+=
+-| List
+-    -> Element takes a list of children
+-    -> Mention , how can we have a list with one or zero elements? Shopping list with one item. Empty spoon drawer( list of spoons)
+-    -> Give your new dot a color and different coordinates
+-    -> Show the code for 2 dots
+-    -> Exercise: make 3 more dots (5 total)
++For a more advanced exercise, try adding a new circle. Why not draw 5 circles on the screen? Give them each a unique color and position.
+```
+
+``` diff
+new file mode 100644
+index 0000000..b0625da
+--- /dev/null
++++ b/src/Examples/MultipleDots.elm
+@@ -0,0 +1,41 @@
++module Examples.MultipleDots exposing (Config, main, ui)
++
++import Element
++import Svg exposing (Attribute)
++import Svg.Attributes
++
++
++type alias Config msg =
++    { background : String
++    , viewBox : String
++    , dotsAttributes : List (List (Attribute msg))
++    }
++
++
++defaults : Config msg
++defaults =
++    Config "white" "-500 -500 1000 1000" [ [ Svg.Attributes.r "10" ] ]
++
++
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui defaults)
++
++
++ui { background, viewBox, dotsAttributes } =
++    Svg.svg
++        [ Svg.Attributes.viewBox viewBox
++        , Svg.Attributes.style <| "background: " ++ background
++        ]
++        (dotsAttributes
++            |> List.map
++                (\attributes ->
++                    Svg.circle
++                        attributes
++                        []
++                )
++        )
++        |> Element.html
+```
+
+``` diff
+index 56ef197..5f855b9 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -24,6 +24,7 @@ import Examples.DotInElement as DotInElement
+=import Examples.DotWithViewBox as DotWithViewBox
+=import Examples.Gradient
+=import Examples.Line
++import Examples.MultipleDots as MultipleDots
+=import Examples.NestedTransformations
+=import Examples.PolarCoordinates
+=import Examples.RosetteTypedTransformations
+@@ -43,6 +44,7 @@ import Parser
+=import Parser.Advanced
+=import Result.Extra as Result
+=import Routes exposing (Route)
++import Svg.Attributes
+=import Transformations
+=import Url exposing (Url)
+=
+@@ -463,6 +465,7 @@ document =
+=                    , dot
+=                    , dotInElement
+=                    , dotWithViewBox
++                    , twoDots
+=                    , transformationsCircle
+=                    , line
+=                    , gradient
+@@ -546,6 +549,36 @@ document =
+=                (Mark.field "viewBox" Mark.string)
+=                |> Mark.map render
+=
++        twoDots : Mark.Block (Model -> Element Msg)
++        twoDots =
++            let
++                render config model =
++                    MultipleDots.ui config
++                        |> Element.el [ Element.height (Element.px 300) ]
++                        |> BrowserWindow.window []
++            in
++            Mark.record6 "TwoDots"
++                (\background viewBoxConf firstFill secondFill firstCX secondCX ->
++                    MultipleDots.Config background
++                        viewBoxConf
++                        [ [ Svg.Attributes.r "10"
++                          , Svg.Attributes.fill firstFill
++                          , Svg.Attributes.cx firstCX
++                          ]
++                        , [ Svg.Attributes.r "10"
++                          , Svg.Attributes.fill secondFill
++                          , Svg.Attributes.cx secondCX
++                          ]
++                        ]
++                )
++                (Mark.field "background" Mark.string)
++                (Mark.field "viewBox" Mark.string)
++                (Mark.field "firstFill" Mark.string)
++                (Mark.field "secondFill" Mark.string)
++                (Mark.field "firstCX" Mark.string)
++                (Mark.field "secondCX" Mark.string)
++                |> Mark.map render
++
+=        transformationsCircle : Mark.Block (Model -> Element Msg)
+=        transformationsCircle =
+=            let
+```
+
+# Remove duplicated ViewBox module
+
+
+
+``` diff
+deleted file mode 100644
+index b3f0a44..0000000
+--- a/src/ViewBox.elm
++++ /dev/null
+@@ -1,276 +0,0 @@
+-module ViewBox exposing
+-    ( Model
+-    , Msg
+-    , init
+-    , main
+-    , ui
+-    , update
+-    , view
+-    )
+-
+-import Browser
+-import BrowserWindow
+-import CartesianPlane exposing (graph)
+-import Element
+-import Element.Background as Background
+-import Element.Border as Border
+-import Element.Input as Input
+-import Html exposing (Html)
+-import Svg exposing (..)
+-import Svg.Attributes exposing (..)
+-
+-
+-main =
+-    Browser.sandbox
+-        { init = init
+-        , view = view
+-        , update = update
+-        }
+-
+-
+-type alias Model =
+-    { left : Float
+-    , top : Float
+-    , width : Float
+-    , height : Float
+-    , preserveAspectRatio : Bool
+-    }
+-
+-
+-type Msg
+-    = Move Float Float
+-    | Resize Float Float
+-    | PreserveAspectRatio Bool
+-
+-
+-init : Model
+-init =
+-    { left = 0
+-    , top = 0
+-    , width = 400
+-    , height = 400
+-    , preserveAspectRatio = False
+-    }
+-
+-
+-view : Model -> Html.Html Msg
+-view model =
+-    let
+-        wrapper element =
+-            Element.el
+-                [ Element.width (Element.maximum 1200 Element.fill)
+-                , Element.height Element.fill
+-                , Element.centerX
+-                ]
+-                element
+-    in
+-    ui model
+-        |> wrapper
+-        |> Element.layout
+-            [ Element.height Element.fill
+-            , Element.width Element.fill
+-            ]
+-
+-
+-update : Msg -> Model -> Model
+-update msg model =
+-    case msg of
+-        Move left top ->
+-            { model
+-                | left = left
+-                , top = top
+-            }
+-
+-        Resize width height ->
+-            { model
+-                | width = width
+-                , height = height
+-            }
+-
+-        PreserveAspectRatio value ->
+-            { model | preserveAspectRatio = value }
+-
+-
+-ui model =
+-    let
+-        viewbox =
+-            svg
+-                [ viewBox "-500 -500 1000 1000"
+-                ]
+-                [ g [] world
+-                , rect
+-                    [ x (String.fromFloat model.left)
+-                    , y (String.fromFloat model.top)
+-                    , width (String.fromFloat model.width)
+-                    , height (String.fromFloat model.height)
+-                    , opacity "0.3"
+-                    , stroke "white"
+-                    , fill "pink"
+-                    ]
+-                    []
+-                ]
+-                |> Element.html
+-                |> Element.el
+-                    [ Element.centerX
+-                    , Element.centerY
+-                    , Element.width Element.fill
+-                    , Element.height Element.fill
+-                    ]
+-
+-        viewport =
+-            svg
+-                [ [ model.left, model.top, model.width, model.height ]
+-                    |> List.map String.fromFloat
+-                    |> String.join " "
+-                    |> viewBox
+-                , preserveAspectRatio <|
+-                    case model.preserveAspectRatio of
+-                        True ->
+-                            "xMidYMid meet"
+-
+-                        False ->
+-                            "none"
+-                , Svg.Attributes.style "width: 100%; height: 100%; flex-grow: 1; flex-basis: 0"
+-                ]
+-                [ g [] world
+-                , rect
+-                    [ x (String.fromFloat model.left)
+-                    , y (String.fromFloat model.top)
+-                    , width (String.fromFloat model.width)
+-                    , height (String.fromFloat model.height)
+-                    , opacity "0.3"
+-                    , stroke "white"
+-                    , fill "pink"
+-                    ]
+-                    []
+-                ]
+-                |> Element.html
+-                |> Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height Element.fill
+-                    ]
+-                |> BrowserWindow.window
+-                    [ Element.centerX
+-                    , Element.centerY
+-                    , Element.width Element.fill
+-                    , Element.height Element.fill
+-                    ]
+-    in
+-    Element.column
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        , Element.spacing 30
+-        , Element.padding 30
+-        ]
+-        [ Element.row
+-            [ Element.width Element.fill
+-            , Element.height Element.fill
+-            , Element.centerX
+-            , Element.spacing 30
+-            ]
+-            [ viewbox
+-            , viewport
+-            ]
+-        , Input.slider
+-            [ Element.behindContent
+-                (Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height (Element.px 2)
+-                    , Element.centerY
+-                    , Background.color <| Element.rgb 0.7 0.7 0.7
+-                    , Border.rounded 2
+-                    ]
+-                    Element.none
+-                )
+-            ]
+-            { onChange = \left -> Move left model.top
+-            , label =
+-                Input.labelBelow [ Element.centerX ] <|
+-                    Element.text ("left: " ++ String.fromFloat model.left)
+-            , min = -500
+-            , max = 500
+-            , value = model.left
+-            , thumb = Input.defaultThumb
+-            , step = Just 1
+-            }
+-        , Input.slider
+-            [ Element.behindContent
+-                (Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height (Element.px 2)
+-                    , Element.centerY
+-                    , Background.color <| Element.rgb 0.7 0.7 0.7
+-                    , Border.rounded 2
+-                    ]
+-                    Element.none
+-                )
+-            ]
+-            { onChange = \top -> Move model.left top
+-            , label =
+-                Input.labelBelow [ Element.centerX ] <|
+-                    Element.text ("top: " ++ String.fromFloat model.top)
+-            , min = -500
+-            , max = 500
+-            , value = model.top
+-            , thumb = Input.defaultThumb
+-            , step = Just 1
+-            }
+-        , Input.slider
+-            [ Element.behindContent
+-                (Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height (Element.px 2)
+-                    , Element.centerY
+-                    , Background.color <| Element.rgb 0.7 0.7 0.7
+-                    , Border.rounded 2
+-                    ]
+-                    Element.none
+-                )
+-            ]
+-            { onChange = \width -> Resize width model.height
+-            , label =
+-                Input.labelBelow [ Element.centerX ] <|
+-                    Element.text ("width: " ++ String.fromFloat model.width)
+-            , min = 1
+-            , max = 500
+-            , value = model.width
+-            , thumb = Input.defaultThumb
+-            , step = Just 1
+-            }
+-        , Input.slider
+-            [ Element.behindContent
+-                (Element.el
+-                    [ Element.width Element.fill
+-                    , Element.height (Element.px 2)
+-                    , Element.centerY
+-                    , Background.color <| Element.rgb 0.7 0.7 0.7
+-                    , Border.rounded 2
+-                    ]
+-                    Element.none
+-                )
+-            ]
+-            { onChange = \height -> Resize model.width height
+-            , label =
+-                Input.labelBelow [ Element.centerX ] <|
+-                    Element.text ("height: " ++ String.fromFloat model.height)
+-            , min = 1
+-            , max = 500
+-            , value = model.height
+-            , thumb = Input.defaultThumb
+-            , step = Just 1
+-            }
+-        , Input.checkbox []
+-            { checked = model.preserveAspectRatio
+-            , icon = Input.defaultCheckbox
+-            , label = Input.labelRight [] (Element.text "Preserve aspect ratio")
+-            , onChange = PreserveAspectRatio
+-            }
+-        ]
+-
+-
+-world : List (Svg Msg)
+-world =
+-    [ circle [ cx "400", cy "300", r "250", fill "purple" ] []
+-    , circle [ cx "200", cy "350", r "50", fill "yellow" ] []
+-    , circle [ cx "0", cy "0", r "20", fill "red" ] []
+-    ]
+```
+
+# Separate Window block from example blocks
+
+Now each example can be nested in a Window, but doesn't have to be.
+
+``` diff
+index 83cc9bf..bda2078 100644
+--- a/content/day-1.txt
++++ b/content/day-1.txt
+@@ -85,7 +85,8 @@ Note that the same address was printed by the Elm reactor.
+=
+=We want to display a dot at the center of the screen, like this:
+=
+-| DotAtTheCenterOfTheScreen
++| Window
++    | DotAtTheCenterOfTheScreen
+=
+=Below is the complete code to draw a dot like this, but don't type it in your editor yet! We are going to recreate it together, step by step.
+=
+@@ -153,9 +154,10 @@ Start the reactor again:
+=
+=Reload the browser. You should see something like this:
+=
+-| Simplest
+-    background=none
+-    fill=black
++| Window
++    | Simplest
++        background=none
++        fill=black
+=
+=Something isn't quite right here. Why is the dot in the corner?
+=
+@@ -171,9 +173,10 @@ When you reload the browser, you'll see this:
+=| Note
+=    Example of simplest with pink background should be here.
+=
+-| Simplest
+-    background=pink
+-    fill=black
++| Window
++    | Simplest
++        background=pink
++        fill=black
+=
+=Now we can clearly see that our SVG element (which is pink) does not fill the screen.
+=
+```
+
+``` diff
+index 99d1276..e6bd250 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -475,6 +475,28 @@ document =
+=                    , spiral
+=                    , tree
+=                    , viewBox
++
++                    -- Window block can contain any embeded program
++                    , Mark.Custom.window
++                        (Mark.oneOf
++                            [ counter
++                            , simplest
++                            , fillTheScreen
++                            , dotAtTheCenterOfTheScreen
++                            , centeredDot
++                            , circle
++                            , line
++                            , gradient
++                            , transformations
++                            , nestedTransformations
++                            , cartesianCoordinates
++                            , polarCoordinates
++                            , rosette
++                            , spiral
++                            , tree
++                            , viewBox
++                            ]
++                        )
+=                    ]
+=                )
+=
+@@ -489,11 +511,6 @@ document =
+=                            [ Element.centerX
+=                            , Element.centerY
+=                            ]
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            ]
+-                        |> BrowserWindow.window []
+=                        |> Element.map CounterMsg
+=            in
+=            Mark.stub "Counter" render
+@@ -505,13 +522,12 @@ document =
+=                render config model =
+=                    Examples.Simplest.ui config
+=                        |> Element.html
+-                        |> Element.el []
+=                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            , Element.padding 5
++                            [ Element.width (Element.px 300)
++                            , Element.height (Element.px 150)
+=                            ]
+-                        |> BrowserWindow.window []
++                        |> Element.el
++                            [ Element.padding 5 ]
+=            in
+=            Mark.record2 "Simplest"
+=                Examples.Simplest.Config
+@@ -537,11 +553,6 @@ document =
+=            let
+=                render model =
+=                    Examples.DotAtTheCenterOfTheScreen.ui
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            ]
+-                        |> BrowserWindow.window []
+=            in
+=            Mark.stub "DotAtTheCenterOfTheScreen" render
+=
+@@ -565,12 +576,6 @@ document =
+=                render : Examples.TransformationsCircle.Config -> Model -> Element Msg
+=                render config model =
+=                    Examples.TransformationsCircle.ui config
+-                        |> Element.el
+-                            [ Element.height (Element.px 400)
+-                            , Element.width Element.fill
+-                            , Element.padding 5
+-                            ]
+-                        |> BrowserWindow.window []
+=            in
+=            Mark.record4 "TransformationsCircle"
+=                Examples.TransformationsCircle.Config
+@@ -640,10 +645,6 @@ document =
+=                render model =
+=                    model.cartesianCoordinates
+=                        |> Examples.CartesianCoordinates.ui
+-                        |> Element.el
+-                            [ Element.width Element.fill
+-                            ]
+-                        |> BrowserWindow.window []
+=                        |> Element.map CartesianCoordinatesMsg
+=            in
+=            Mark.stub "CartesianCoordinates" render
+```
+
+``` diff
+index 3e50354..ca76c37 100644
+--- a/src/Mark/Custom.elm
++++ b/src/Mark/Custom.elm
+@@ -1,5 +1,21 @@
+-module Mark.Custom exposing (code, colors, css, emphasize, header, icon, image, list, monospace, note, paragraph, text, title)
+-
++module Mark.Custom exposing
++    ( code
++    , colors
++    , css
++    , emphasize
++    , header
++    , icon
++    , image
++    , list
++    , monospace
++    , note
++    , paragraph
++    , text
++    , title
++    , window
++    )
++
++import BrowserWindow
+=import Dict
+=import Element exposing (Element)
+=import Element.Background as Background
+@@ -107,6 +123,24 @@ code =
+=        Mark.multiline
+=
+=
++window :
++    Mark.Block (a -> Element msg)
++    -> Mark.Block (a -> Element msg)
++window block =
++    let
++        render child model =
++            child model
++                |> Element.el
++                    [ Element.height (Element.px 400)
++                    , Element.width Element.fill
++                    ]
++                |> BrowserWindow.window []
++    in
++    Mark.block "Window"
++        render
++        block
++
++
+=note : Mark.Block (model -> Element msg)
+=note =
+=    let
+```
+
+# Make develop script print the modules it compiles to HTML
+
+Otherwise it just prints a lot of "Success" which looks dumb.
+
+``` diff
+index 3b0e90e..bf51dda 100755
+--- a/scripts/develop
++++ b/scripts/develop
+@@ -5,6 +5,7 @@ set -euo pipefail
+=# Build examples
+=for example in src/Examples/*.elm
+=do
++  echo "Compiling ${example} to public/${example}.html"
+=  npx elm make --output "public/${example}.html" "${example}"
+=done
+=
+```
+
+# Remove calls to Debug.log from Main
+
+
+
+``` diff
+index e6bd250..072cef8 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -275,11 +275,10 @@ view model =
+=
+=update : Msg -> Model -> ( Model, Cmd Msg )
+=update msg model =
+-    case Debug.log "update" msg of
++    case msg of
+=        UrlRequested (Browser.Internal url) ->
+=            ( model
+=            , url
+-                |> Debug.log "New URL"
+=                |> Url.toString
+=                |> Navigation.pushUrl model.key
+=            )
+@@ -376,7 +375,7 @@ subscriptions model =
+=
+=loadContent : Route -> Cmd Msg
+=loadContent route =
+-    case Debug.log "Route" route of
++    case route of
+=        Routes.Home ->
+=            Http.get
+=                { url = "/content/index.txt"
+```
\ No newline at end of file
new file mode 100644
index 0000000..210583f
--- /dev/null
+++ b/content/devlog/2018-12-20-elm-tree-workshop.md
@@ -0,0 +1,4395 @@
+---
+title: Elm Tree Workshop
+extra:
+  projects:
+  - Elm Tree Workshop
+
+---
+
+Commits: 4
+
+
+# Rename TransformationsCircle to Circle, make it parametrized, edit day-2
+
+
+
+``` diff
+index 32d4a6c..6d2aed2 100644
+--- a/content/day-2.txt
++++ b/content/day-2.txt
+@@ -15,36 +15,135 @@
+=| Header
+=    The Problem
+=
+-We want to place the dots in a circle, like that:
++We want to place the dots in a circle, like this:
+=
+-| TransformationsCircle
+-    dots = True
+-    circle = False
+-    angle = False
+-    center = False
++| Window
++    | Circle
++        dots = 5
++        circle = 0 1
++        angles = 0 1
++        center = none
++        radius = 0 1
++        scatter = False
+=
+-As you can see, we will make the dot's have different colors, so it's more fun!
++We will also make the dots have different colors... so it's more fun!
+=
+-How we will get there?
++| Header
++    What does it mean to be placed on a circle?
++
++We all have some intuition about a circle. You can tell if you see one and if you have a steady hand, you can probably draw one with a pencil. It's a thing that looks like this:
++
++| Circle
++    dots = 0
++    circle = 1 0
++    angles = 0 1
++    center = none
++    radius = 0 1
++    scatter = False
+=
+-We will need more than one dot. Let's make it 5. Then let's think what does it mean to be placed on a circle.
+=
+-First of all we have to realize that a circle has a center. It's a point that lays exactly in the middle:
++But what is it that makes a circle what it is? First of all we have to realize that a circle has a center. It's a point that lays exactly in the middle:
+=
++| Circle
++    dots = 0
++    circle = 1 0
++    angles = 0 1
++    center = True
++    radius = 0 1
++    scatter = False
+=
+-Then we can say that several things lay on a circle if the distance between each of them and the center is the same.
+=
+-In other words, you can put a thing in the center and then move it in any direction, let's say 1 meter. If you do the same to several things, you will make them lay on a circle.
++We can say that four or more things lay on a circle, if the distance to the center is the same for each of them, like this:
+=
+-Of course the direction must be different each time. Otherwise the things would just stack one on top of another! We wouldn't call it a circle, right?
++| Circle
++    dots = 5
++    circle =  3 3
++    angles = 0 1
++    center = True
++    radius = 3 3
++    scatter = False
+=
+-We will change the cartesian coordinates of the dots, nothing else! But figuring out the right coordinates is a bit tricky
++Why more than three? Well, think about it in terms of a fair challenge. If it's one, then it can lay on any circle that crosses the place. There is very many circles like that (in fact infinitely many). The same for two. Not every circle will do, but still you could draw many different circles that they will lay on. For three things there will always be exactly one circle that they lay on (except if they lay in one line, unless you claim that straight line is an infinite circle). So three is still not much of a challenge. You could place them however you like, and claim that they lay on a circle.
+=
+-How can we figure out the correct cartesian coordinates?
++So only when it's four or more things, we can seriously ask whether they lay on a circle or not.
+=
+=| Note
+-    Story about the flowerpot and the table: two ways to measure relative position, one is distance from an x and y axis (cartesian coordinates), the other is angle and distance relative to origin (polar coordinates)
++    TODO: Consider if the above adds more value or confusion.
++
++    TODO: An example showing multiple circles crossing one and two points
++
++    TODO: An example showing that for three different points there will always be a circle that crosses them, see {Link|circumcircle|url=https://package.elm-lang.org/packages/ianmackenzie/elm-geometry/latest/Triangle2d#circumcircle}
++
++
++Because we are all about challenges, let's make five dots.
++
++So, once again - we can say that several things lay on a circle if the distance between each of them and the center of the circle is the same. We call this distance /a *radius* of a circle/.
++
++| Note
++    Matematician would say:
++
++    /Four or more points belong to a circle when there is a point (called center) that is equally distant from each of the points./
++
++    That is more precise, but perhaps a little more difficult to visualize.
++
++Ok, so this describes what it means to lay on a circle. But what are some efficient methods of putting things there? How about this.
++
++You choose a center (anywhere, say {Code|(0, 0)}) and radius (any length you like, say 80).
++
++Take a ruler and place it's one end (the one showing the value 0) at the center point. Then move the thing you want to place along the ruler the length you chose. Leave it there.
++
++Next slightly rotate the ruler around the center (keep point {Code|0} of the ruler at point {Code|(0, 0)}). Repeat the steps above with another thing. Move it along the slider the same length as previously (radius). Because this time the ruler points in different direction, the thing will end up in a different place.
++
++| Note
++    TODO: An interactive example with ruler. Basically a modified Translations.
++
++In other words, you can put a thing in the center and then move it in any direction, let's say 1 meter. If you do the same to several things in several directions (but same length), you will make them lay on a circle. The direction must be different each time. Otherwise the things would just stack one on top of another! We wouldn't call it a circle, right?
++
++Now, notice that the dots displayed by the program we are creating are not laying just anywhere on the circle. They are evenly distributed:
++
++| Circle
++    dots = 5
++    circle = 0 1
++    angles = 0 1
++    center = none
++    radius = 0 1
++    scatter = False
++
++It means that not only the distance between each dot and the center is the same. Also each dots lay the same distance away from it's two nearest neighbors.
++
++Just compare it with the following picture:
++
++| Circle
++    dots = 5
++    circle = 0 1
++    angles = 0 1
++    center = none
++    radius = 0 1
++    scatter = True
++
++These are also 5 dots laying on a circle. Look:
++
++| Circle
++    dots = 5
++    circle = 3
++    angles = 0 1
++    center = none
++    radius = 0 1
++    scatter = True
++
++| Note
++    IDEA: An interactive program (game of sorts) that shows a ruler and dots. User can drag the ruler around and rotate it. Also dots can be dragged around. Once the dots are placed on a circle, the program rewards the user with a nice animation and message. It would be super awesome to make it touch capable {Icon|name=award}
++
++    Then second variety. This time there is also a compass tool. User has to place the dots on a circle so that they are evenly distributed.
++
++    In the mean time we have two non-interactive examples with evenly and randomly distributed dots.
++
++
++So they lay on a circle, but there is a striking difference. They are all scattered! This means that there is something missing in our solution. We need to take care of one more aspect when we place our dots.
++
++Since the center and radius is the same for each dot, the only thing we can play with is the angle that we rotate the ruler each time.
+=
++TODO: ___✁___ edit from here
+=
+=What does this have to do with a circle? Do you know the expression 'I did a 180'. Where would you be looking if you did two 180s (a '360'). We see that circles and angles are closely related!
+=
+```
+
+``` diff
+new file mode 100644
+index 0000000..7d4ced3
+--- /dev/null
++++ b/src/Examples/Circle.elm
+@@ -0,0 +1,189 @@
++module Examples.Circle exposing (Config, defaults, main, ui)
++
++import Arc2d
++import Array
++import Element exposing (Element)
++import Geometry.Svg
++import Html exposing (Html)
++import List.Extra as List
++import Maybe.Extra as Maybe
++import Point2d
++import Svg exposing (Svg)
++import Svg.Attributes
++import Transformations exposing (Transformation(..))
++
++
++type alias Config =
++    { dots : Int -- Number of dots
++    , circle : String -- Dash array
++    , center : String -- Color
++    , radius : String -- Dash array
++    , angles : String -- Dash array
++    , scatter : Bool -- Are doot evenly distributed or scattered
++    }
++
++
++main : Html.Html msg
++main =
++    Element.layout
++        [ Element.width Element.fill
++        , Element.height Element.fill
++        ]
++        (ui defaults)
++
++
++palette : List String
++palette =
++    [ "skyblue"
++    , "orange"
++    , "red"
++    , "lime"
++    , "maroon"
++    ]
++
++
++ui : Config -> Element msg
++ui config =
++    let
++        shapes =
++            [ dots
++            , center
++            , circle
++            , compass
++            , radi
++            ]
++
++        compass : Svg msg
++        compass =
++            0
++                :: angles
++                |> pairs
++                |> Debug.log "pairs"
++                |> List.indexedMap arc
++                |> Svg.g []
++
++        pairs : List a -> List ( a, a )
++        pairs list =
++            case list of
++                [] ->
++                    []
++
++                [ one ] ->
++                    []
++
++                one :: two :: rest ->
++                    ( one, two ) :: pairs (two :: rest)
++
++        arc : Int -> ( Float, Float ) -> Svg msg
++        arc index ( start, end ) =
++            Arc2d.with
++                { centerPoint = Point2d.origin
++                , radius = 30
++                , startAngle = degrees start
++                , sweptAngle = degrees (end - start)
++                }
++                |> Geometry.Svg.arc2d
++                    [ Svg.Attributes.stroke (color index)
++                    , Svg.Attributes.strokeDasharray config.angles
++                    , Svg.Attributes.fill "none"
++                    ]
++
++        dots =
++            angles
++                |> List.indexedMap
++                    (\index angle ->
++                        dot angle (color index)
++                    )
++                |> Svg.g []
++
++        center =
++            Svg.circle
++                [ Svg.Attributes.r "2"
++                , Svg.Attributes.fill config.center
++                ]
++                []
++
++        circle =
++            Svg.circle
++                [ Svg.Attributes.r "80"
++                , Svg.Attributes.stroke "silver"
++                , Svg.Attributes.strokeWidth "1"
++                , Svg.Attributes.strokeDasharray config.circle
++                , Svg.Attributes.fill "none"
++                ]
++                []
++
++        radi =
++            angles
++                |> Debug.log "angles"
++                |> List.map radius
++                |> Svg.g []
++
++        angles : List Float
++        angles =
++            if config.scatter then
++                -- TODO: Generate a list of numbers [ 0, 5, 10, 15, ... 355 ] and use Random.Extra.List.shuffle and List.take config.dots to take a random sample.
++                [ 5, 35, 85, 100, 260, 340, 300, 95, 180, 220 ]
++                    |> List.take config.dots
++
++            else
++                config.dots
++                    |> List.range 1
++                    |> List.map
++                        (\index ->
++                            360 * toFloat index / toFloat config.dots
++                        )
++
++        dot : Float -> String -> Svg msg
++        dot angle fill =
++            Svg.circle
++                [ Svg.Attributes.r "10"
++                , Svg.Attributes.cx "0"
++                , Svg.Attributes.cy "0"
++                , Svg.Attributes.fill fill
++                , Svg.Attributes.transform <|
++                    Transformations.apply
++                        [ Rotate angle
++                        , Translate 80 0
++                        ]
++                ]
++                []
++
++        color : Int -> String
++        color index =
++            palette
++                |> Array.fromList
++                |> Array.get (modBy (List.length palette) index)
++                |> Maybe.withDefault "black"
++
++        radius angle =
++            Svg.line
++                [ Svg.Attributes.x1 "0"
++                , Svg.Attributes.y1 "0"
++                , Svg.Attributes.x2 "80"
++                , Svg.Attributes.y2 "0"
++                , Svg.Attributes.stroke "pink"
++                , Svg.Attributes.strokeDasharray config.radius
++                , Svg.Attributes.transform <|
++                    Transformations.apply
++                        [ Rotate angle
++                        ]
++                ]
++                []
++    in
++    shapes
++        |> Svg.svg
++            [ Svg.Attributes.viewBox "-100 -100 200 200"
++            ]
++        |> Element.html
++
++
++defaults : Config
++defaults =
++    { dots = 5
++    , circle = "0 1"
++    , center = "none"
++    , radius = "0 1"
++    , angles = "0 1"
++    , scatter = False
++    }
+```
+
+``` diff
+deleted file mode 100644
+index a3a507f..0000000
+--- a/src/Examples/TransformationsCircle.elm
++++ /dev/null
+@@ -1,135 +0,0 @@
+-module Examples.TransformationsCircle exposing (Config, defaults, main, ui)
+-
+-import Element exposing (Element)
+-import Html exposing (Html)
+-import Maybe.Extra as Maybe
+-import Svg exposing (Svg)
+-import Svg.Attributes
+-import Transformations exposing (Transformation(..))
+-
+-
+-main : Html.Html msg
+-main =
+-    Element.layout
+-        [ Element.width Element.fill
+-        , Element.height Element.fill
+-        ]
+-        (ui defaults)
+-
+-
+-ui : Config -> Element msg
+-ui config =
+-    let
+-        present : Bool -> Svg msg -> Maybe (Svg msg)
+-        present flag shape =
+-            if flag then
+-                Just shape
+-
+-            else
+-                Nothing
+-
+-        shapes =
+-            dots
+-                ++ Maybe.values
+-                    [ present config.circle circle
+-                    ]
+-
+-        dots =
+-            if config.dots then
+-                [ Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "0"
+-                    , Svg.Attributes.cy "0"
+-                    , Svg.Attributes.fill "skyblue"
+-                    , Svg.Attributes.transform <|
+-                        Transformations.apply
+-                            [ Rotate 0
+-                            , Translate 80 0
+-                            ]
+-                    ]
+-                    []
+-                , Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "0"
+-                    , Svg.Attributes.cy "0"
+-                    , Svg.Attributes.fill "pink"
+-                    , Svg.Attributes.transform <|
+-                        Transformations.apply
+-                            [ Rotate 72
+-                            , Translate 80 0
+-                            ]
+-                    ]
+-                    []
+-                , Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "0"
+-                    , Svg.Attributes.cy "0"
+-                    , Svg.Attributes.fill "yellow"
+-                    , Svg.Attributes.transform <|
+-                        Transformations.apply
+-                            [ Rotate 144
+-                            , Translate 80 0
+-                            ]
+-                    ]
+-                    []
+-                , Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "0"
+-                    , Svg.Attributes.cy "0"
+-                    , Svg.Attributes.fill "lime"
+-                    , Svg.Attributes.transform <|
+-                        Transformations.apply
+-                            [ Rotate 216
+-                            , Translate 80 0
+-                            ]
+-                    ]
+-                    []
+-                , Svg.circle
+-                    [ Svg.Attributes.r "10"
+-                    , Svg.Attributes.cx "0"
+-                    , Svg.Attributes.cy "0"
+-                    , Svg.Attributes.fill "maroon"
+-                    , Svg.Attributes.transform <|
+-                        Transformations.apply
+-                            [ Rotate 288
+-                            , Translate 80 0
+-                            ]
+-                    ]
+-                    []
+-                ]
+-
+-            else
+-                []
+-
+-        circle =
+-            Svg.circle
+-                [ Svg.Attributes.r "80"
+-                , Svg.Attributes.stroke "silver"
+-                , Svg.Attributes.strokeWidth "1"
+-                , Svg.Attributes.strokeDasharray "5 5"
+-                , Svg.Attributes.fill "none"
+-                ]
+-                []
+-    in
+-    shapes
+-        |> Svg.svg
+-            [ Svg.Attributes.viewBox "-100 -100 200 200"
+-            ]
+-        |> Element.html
+-
+-
+-defaults : Config
+-defaults =
+-    { circle = False
+-    , center = False
+-    , angle = False
+-    , dots = False
+-    }
+-
+-
+-type alias Config =
+-    { circle : Bool
+-    , center : Bool
+-    , angle : Bool
+-    , dots : Bool
+-    }
+```
+
+``` diff
+index 072cef8..d8624ad 100644
+--- a/src/Main.elm
++++ b/src/Main.elm
+@@ -19,6 +19,7 @@ import Element.Font as Font
+=import Element.Input as Input
+=import Examples.CartesianCoordinates
+=import Examples.CenteredDot
++import Examples.Circle
+=import Examples.Counter
+=import Examples.DotAtTheCenterOfTheScreen
+=import Examples.FillTheScreen
+@@ -30,7 +31,6 @@ import Examples.RosetteTypedTransformations
+=import Examples.Simplest
+=import Examples.Spiral
+=import Examples.Transformations
+-import Examples.TransformationsCircle
+=import Examples.Tree
+=import Examples.ViewBox
+=import FeatherIcons exposing (icons)
+@@ -463,7 +463,7 @@ document =
+=                    , fillTheScreen
+=                    , dotAtTheCenterOfTheScreen
+=                    , centeredDot
+-                    , transformationsCircle
++                    , circle
+=                    , line
+=                    , gradient
+=                    , transformations
+@@ -569,19 +569,21 @@ document =
+=            in
+=            Mark.stub "CenteredDot" render
+=
+-        transformationsCircle : Mark.Block (Model -> Element Msg)
+-        transformationsCircle =
++        circle : Mark.Block (Model -> Element Msg)
++        circle =
+=            let
+-                render : Examples.TransformationsCircle.Config -> Model -> Element Msg
++                render : Examples.Circle.Config -> Model -> Element Msg
+=                render config model =
+-                    Examples.TransformationsCircle.ui config
++                    Examples.Circle.ui config
+=            in
+-            Mark.record4 "TransformationsCircle"
+-                Examples.TransformationsCircle.Config
+-                (Mark.field "circle" Mark.bool)
+-                (Mark.field "center" Mark.bool)
+-                (Mark.field "angle" Mark.bool)
+-                (Mark.field "dots" Mark.bool)
++            Mark.record6 "Circle"
++                Examples.Circle.Config
++                (Mark.field "dots" Mark.int)
++                (Mark.field "circle" Mark.string)
++                (Mark.field "center" Mark.string)
++                (Mark.field "radius" Mark.string)
++                (Mark.field "angles" Mark.string)
++                (Mark.field "scatter" Mark.bool)
+=                |> Mark.map render
+=
+=        line : Mark.Block (Model -> Element Msg)
+```
+
+# Merge branch 'multiple-pages' into review-day-one
+
+
+
+
+
+# Finish first draft of day 2
+
+
+
+``` diff
+new file mode 100644
+index 0000000..1c0256e
+--- /dev/null
++++ b/assets/protractor.svg
+@@ -0,0 +1,3406 @@
++<?xml version="1.0" encoding="UTF-8" standalone="no"?>
++<svg
++   xmlns:dc="http://purl.org/dc/elements/1.1/"
++   xmlns:cc="http://web.resource.org/cc/"
++   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++   xmlns:svg="http://www.w3.org/2000/svg"
++   xmlns="http://www.w3.org/2000/svg"
++   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
++   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
++   version="1.0"
++   id="svg2"
++   sodipodi:version="0.32"
++   inkscape:version="0.45.1"
++   width="531.49603"
++   height="531.49603"
++   sodipodi:docname="rapporteur.svg"
++   sodipodi:docbase="/tmp"
++   inkscape:output_extension="org.inkscape.output.svg.inkscape">
++  <metadata
++     id="metadata733">
++    <rdf:RDF>
++      <cc:Work
++         rdf:about="">
++        <dc:format>image/svg+xml</dc:format>
++        <dc:type
++           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
++      </cc:Work>
++    </rdf:RDF>
++  </metadata>
++  <defs
++     id="defs731" />
++  <sodipodi:namedview
++     inkscape:window-height="581"
++     inkscape:window-width="744"
++     inkscape:pageshadow="2"
++     inkscape:pageopacity="0.0"
++     guidetolerance="10.0"
++     gridtolerance="10.0"
++     objecttolerance="10.0"
++     borderopacity="1.0"
++     bordercolor="#666666"
++     pagecolor="#ffffff"
++     id="base"
++     width="150mm"
++     height="150mm"
++     units="mm"
++     inkscape:document-units="mm"
++     inkscape:zoom="1.4367677"
++     inkscape:cx="183.55229"
++     inkscape:cy="130.82769"
++     inkscape:window-x="46"
++     inkscape:window-y="46"
++     inkscape:current-layer="layer1" />
++  <title
++     id="title4">Protractor</title>
++  <g
++     transform="translate(270.98819,265.42013)"
++     id="g6">
++    <circle
++       cx="0"
++       cy="0"
++       r="250"
++       id="circle8"
++       sodipodi:cx="0"
++       sodipodi:cy="0"
++       sodipodi:rx="250"
++       sodipodi:ry="250"
++       style="fill:#d3d3d3;fill-opacity:0.3;stroke:#000000;stroke-width:3" />
++<!-- Units of 1 -->    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9998477,1.7452406e-2,-1.7452406e-2,0.9998477,0,0)"
++       id="line10"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9993908,3.4899497e-2,-3.4899497e-2,0.9993908,0,0)"
++       id="line12"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9986295,5.2335956e-2,-5.2335956e-2,0.9986295,0,0)"
++       id="line14"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9975641,6.9756474e-2,-6.9756474e-2,0.9975641,0,0)"
++       id="line16"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9945219,0.1045285,-0.1045285,0.9945219,0,0)"
++       id="line18"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9925462,0.1218693,-0.1218693,0.9925462,0,0)"
++       id="line20"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9902681,0.1391731,-0.1391731,0.9902681,0,0)"
++       id="line22"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9876883,0.1564345,-0.1564345,0.9876883,0,0)"
++       id="line24"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9816272,0.190809,-0.190809,0.9816272,0,0)"
++       id="line26"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9781476,0.2079117,-0.2079117,0.9781476,0,0)"
++       id="line28"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9743701,0.2249511,-0.2249511,0.9743701,0,0)"
++       id="line30"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9702957,0.2419219,-0.2419219,0.9702957,0,0)"
++       id="line32"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9612617,0.2756374,-0.2756374,0.9612617,0,0)"
++       id="line34"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9563048,0.2923717,-0.2923717,0.9563048,0,0)"
++       id="line36"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9510565,0.309017,-0.309017,0.9510565,0,0)"
++       id="line38"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9455186,0.3255682,-0.3255682,0.9455186,0,0)"
++       id="line40"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9335804,0.3583679,-0.3583679,0.9335804,0,0)"
++       id="line42"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9271839,0.3746066,-0.3746066,0.9271839,0,0)"
++       id="line44"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9205049,0.3907311,-0.3907311,0.9205049,0,0)"
++       id="line46"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.9135455,0.4067366,-0.4067366,0.9135455,0,0)"
++       id="line48"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.898794,0.4383711,-0.4383711,0.898794,0,0)"
++       id="line50"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8910065,0.4539905,-0.4539905,0.8910065,0,0)"
++       id="line52"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8829476,0.4694716,-0.4694716,0.8829476,0,0)"
++       id="line54"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8746197,0.4848096,-0.4848096,0.8746197,0,0)"
++       id="line56"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8571673,0.5150381,-0.5150381,0.8571673,0,0)"
++       id="line58"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8480481,0.5299193,-0.5299193,0.8480481,0,0)"
++       id="line60"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8386706,0.544639,-0.544639,0.8386706,0,0)"
++       id="line62"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.8290376,0.5591929,-0.5591929,0.8290376,0,0)"
++       id="line64"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.809017,0.5877853,-0.5877853,0.809017,0,0)"
++       id="line66"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7986355,0.601815,-0.601815,0.7986355,0,0)"
++       id="line68"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7880108,0.6156615,-0.6156615,0.7880108,0,0)"
++       id="line70"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.777146,0.6293204,-0.6293204,0.777146,0,0)"
++       id="line72"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7547096,0.656059,-0.656059,0.7547096,0,0)"
++       id="line74"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7431448,0.6691306,-0.6691306,0.7431448,0,0)"
++       id="line76"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7313537,0.6819984,-0.6819984,0.7313537,0,0)"
++       id="line78"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.7193398,0.6946584,-0.6946584,0.7193398,0,0)"
++       id="line80"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.6946584,0.7193398,-0.7193398,0.6946584,0,0)"
++       id="line82"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.6819984,0.7313537,-0.7313537,0.6819984,0,0)"
++       id="line84"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.6691306,0.7431448,-0.7431448,0.6691306,0,0)"
++       id="line86"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.656059,0.7547096,-0.7547096,0.656059,0,0)"
++       id="line88"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.6293204,0.777146,-0.777146,0.6293204,0,0)"
++       id="line90"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.6156615,0.7880108,-0.7880108,0.6156615,0,0)"
++       id="line92"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.601815,0.7986355,-0.7986355,0.601815,0,0)"
++       id="line94"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.5877853,0.809017,-0.809017,0.5877853,0,0)"
++       id="line96"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.5591929,0.8290376,-0.8290376,0.5591929,0,0)"
++       id="line98"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.544639,0.8386706,-0.8386706,0.544639,0,0)"
++       id="line100"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.5299193,0.8480481,-0.8480481,0.5299193,0,0)"
++       id="line102"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
++       x2="0"
++       y2="250"
++       transform="matrix(0.5150381,0.8571673,-0.8571673,0.5150381,0,0)"
++       id="line104"
++       style="stroke:#808080" />
++    <line
++       x1="0"
++       y1="240"
+