diff --git a/Project 1/.gitignore b/Project 1/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5bfb48ed0b155fac605c25bfac13ca2115525da1
--- /dev/null
+++ b/Project 1/.gitignore	
@@ -0,0 +1,20 @@
+contracts/
+.classpath
+.project
+.history/
+.idea
+.jqwik-database
+.lib/
+.worksheet
+.settings/
+*.iml
+*.ipr
+*.iws
+*.log
+project/boot/
+project/plugins/project/
+project/project/
+project/*-shim.sbt
+project/target/
+target/
+openjfx/
diff --git a/Project 1/.gitlab-ci.yml b/Project 1/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898
--- /dev/null
+++ b/Project 1/.gitlab-ci.yml	
@@ -0,0 +1,13 @@
+image: maven:latest
+
+stages:
+  - build
+  - test
+
+build:
+  script:
+    - mvn compile
+
+test:
+  script:
+    - mvn test
\ No newline at end of file
diff --git a/Project 1/README.md b/Project 1/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52
--- /dev/null
+++ b/Project 1/README.md	
@@ -0,0 +1,39 @@
+# Project description
+
+Simple template for projects that make use of JavaFX and FXML (Scene Builder).
+Requires Java 11 or later. Compatible with
+Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically
+integrates with Gitlab CI.
+
+## Installation
+
+Maven:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ mvn compile exec:java
+```
+
+SBT:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ sbt compile run
+```
+
+## Further instructions
+
+  * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform
+  * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc
+  * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc
+  
+External sources:
+
+  * JavaFX: https://openjfx.io/javadoc/11/
+  * Scene Builder: https://docs.gluonhq.com/scenebuilder/
diff --git a/Project 1/build.sbt b/Project 1/build.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648
--- /dev/null
+++ b/Project 1/build.sbt	
@@ -0,0 +1,236 @@
+// Project template
+
+// Supported operating systems: Windows, Mac, Linux
+// Supported JDKs: 8, 10+
+
+// Project name
+name := "template-javafx"
+
+// organization name
+organization := "fi.utu.tech"
+
+version := "1.0"
+
+// project description
+description := "JavaFX project template"
+
+// main class
+Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main")
+
+// force the java version by typing it here (remove the comment)
+val force_javaVersion = None // Some(13)
+
+// force the javafx version by typing it here (remove the comment)
+val force_javaFxVersion = None // Some(13)
+
+val useJavaFX = true
+
+val useScalaOrScalaFX = true
+
+// END_OF_SIMPLE_CONFIGURATION
+// you can copy the rest for each new project
+// --- --- ---
+
+def fail(msg: String) = {
+  println("Error :-/")
+  println
+  println(msg)
+  System.exit(1)
+  null
+}
+
+val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt
+
+val javaVersionNum = force_javaVersion.getOrElse(detectedJDK)
+
+val javaVersionString = javaVersionNum match {
+  case 7 => "1.7"
+  case 8 => "1.8"
+  case x if x > 8 => x.toString
+}
+
+val lts = 11
+val dev = 13
+
+val supported = javaVersionNum match {
+  case x if x < 8              => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS")
+  case 9                       => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS")
+  case x if x < lts            => println("Consider upgrading to Java " + lts + " LTS"); true
+  case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true
+  case x if x > dev            => println("Unsupported early access version. Consider switching back to Java " + dev); true
+  case _                       => true
+}
+
+javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation")
+javacOptions in doc := Seq("-source", javaVersionString) 
+
+enablePlugins(JShellPlugin)
+
+compileOrder := CompileOrder.JavaThenScala
+
+// Enables publishing to maven repo
+publishMavenStyle := true
+
+// Do not append Scala versions to the generated artifacts
+crossPaths := false
+
+// This forbids including Scala related libraries into the dependency
+autoScalaLibrary := false
+
+assemblyMergeStrategy in assembly := {
+  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
+  case _ => MergeStrategy.first
+}
+
+// contains libraries provided by utu/ft dep
+resolvers += "ftdev" at "https://ftdev.utu.fi/maven2"
+
+fork in Global := true
+
+val javaVersion = taskKey[Unit]("Prints the Java version.")
+
+javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) }
+
+publishTo := Some(Resolver.file("file", new File("/tmp/repository")))
+
+val oomkit = "fi.utu.tech" % "oomkit" % "1.15"
+
+libraryDependencies ++= Seq()
+
+////
+//// JQWIK / JUNIT configuration
+////
+
+resolvers in ThisBuild += Resolver.jcenterRepo
+
+val junit_version = "5.5.2"
+
+// library dependencies. (orginization name) % (project name) % (version)
+libraryDependencies ++= Seq(
+  "net.aichler"        % "jupiter-interface"              % JupiterKeys.jupiterVersion.value % Test,
+  "org.junit.platform" % "junit-platform-commons"         % ("1"+junit_version.tail) % Test,
+  "org.junit.platform" % "junit-platform-runner"          % ("1"+junit_version.tail) % Test,
+  "org.junit.jupiter"  % "junit-jupiter-engine"           % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-api"              % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-migrationsupport" % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-params"           % junit_version % Test,
+  "net.jqwik"          % "jqwik"                          % "1.2.0" % Test,
+  "org.scalatest"      %% "scalatest"                     % "3.0.8" % Test,
+)
+
+testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c")
+
+////
+//// JAVAFX configuration
+////
+
+val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match {
+  case 7 => (7, "7", "8.0.181-R13")
+  case 8 => (8, "8", "8.0.181-R13")
+  case 10 => (11, "11.0.2", "11-R16")
+  case x if x>10 => (13, "13", "12.0.2-R18")
+  case _ => fail("Unsupported Java version for JavaFX")
+}
+
+// JAVA_HOME location
+val javaHomeDir = {
+  val path = try {
+    if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME")
+  } catch {
+    case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM
+  }
+
+  val f = file(path)
+  if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.")
+  f
+}
+
+val osName: SettingKey[String] = SettingKey[String]("osName")
+
+osName := (System.getProperty("os.name") match {
+  case n if n.startsWith("Linux")   => "linux"
+  case n if n.startsWith("Mac")     => "mac"
+  case n if n.startsWith("Windows") => "win"
+  case _ => throw new Exception("Unknown platform!")
+})
+
+def legacyJavaFX() = {
+  val searchDirs = Seq(
+    "/jre/lib/jfxrt.jar",     // OpenJDK 7
+    "/jre/lib/ext/jfxrt.jar", // OpenJDK 8
+    "/lib/ext/jfxrt.jar"      // Windows & Oracle Java 8
+  )
+
+  if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.")
+
+  val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() }
+
+  javaFxJAR.getOrElse {
+    fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.")
+  }
+}
+
+val jfx_sdk_version = javafx_versions._2
+val jfx_scalafx_version = javafx_versions._3
+
+val javaFxPath = Def.taskKey[File]("OpenJFX fetcher")
+javaFxPath := {
+  val javaFxHome =
+    try {
+      val envHome = file(scala.sys.env("JAVAFX_HOME"))
+      if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME")
+      println("Using OpenJFX from " + envHome)
+      envHome
+    }
+    catch { case _: Throwable =>
+        println("Using local OpenJFX")
+        baseDirectory.value / "openjfx"
+    }
+
+  if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath)
+
+  val jfx_os = osName.value match {
+    case "linux" => "linux"
+    case "mac"   => "osx"
+    case "win"   => "windows"
+  }
+
+  val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip"
+
+  try {
+    val testDir = javaFxHome / "all.ok"
+    if (!testDir.exists()) {
+      println("Fetching OpenJFX from "+sdkURL+"..")
+      IO.unzipURL(new URL(sdkURL), javaFxHome)
+      java.nio.file.Files.createDirectory(testDir.toPath)
+      println("Fetching OpenJFX done.")
+    } else {
+      println("Using OpenJFX from "+javaFxHome)
+    }
+
+    javaFxHome
+  }
+  catch {
+    case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage)
+  }
+}
+
+val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web")
+
+
+if (!useJavaFX) Seq() else javafx_versions._1 match {
+  case 7 =>
+    // TODO libraryDependencies
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case 8 =>
+    (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case _ =>
+    Seq(
+      javaOptions in run ++= Seq(
+        "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString,
+        "--add-modules=" + jfxModules.map("javafx."+_).mkString(","))
+    ) ++
+      (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++
+      jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value)
+}
diff --git a/Project 1/pom.xml b/Project 1/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..40e1bb8e9bf40fbf9a984a7b5bb3b2ff7177fd54
--- /dev/null
+++ b/Project 1/pom.xml	
@@ -0,0 +1,452 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!--
+     
+      Welcome!
+      This pom.xml is *THE* file that defines your Maven style Java project.
+      
+      Eclipse, IDEA and other development environments with Maven support
+      or plugins can *import* this project by reading this file.
+      
+      It usually contains tons of barely readable configuration. Luckily
+      this basic pom.xml is somewhat readable. All necessary configuration
+      for customizing your project if located here on top before the
+      'END OF SIMPLE CONFIGURATION' line below.
+      
+      Some basics:
+      
+        - Maven is a build system for Java/JVM
+        - the Maven projects define an artifact that has a three part id:
+          groupId - artifactId - version
+          
+        - For example, this project is called:
+          fi.utu.tech - oomkit - 1.15
+          
+        - The group id is usually a web domain in reverse order.
+          
+        - You can use these id parts to search for projects at
+          https://search.maven.org/
+          
+        - If your version is not final and you have plans to modify the
+          source code at some point, please use a version id that ends with
+          -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar
+          and refuse to overwrite old cached versions with new ones unless
+          you remember this convention.
+          
+        - This file supports
+          compiling the project with      'mvn compile' (see target/)
+          cleaning the class files with   'mvn clean'
+          executing the main routine with 'mvn exec:java'
+          executing the unit tests with   'mvn test'
+          packaging the application with  'mvn package' (see target/)
+          
+        - the Maven project structure is as follows:
+          https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+          
+    -->
+    
+    <!-- ==== START OF SIMPLE CONFIGURATION ==== -->
+    
+    <!-- the three parts of the artifact name -->
+    <groupId>fi.utu.tech</groupId>
+    <artifactId>template-javafx</artifactId>
+    <version>1.0</version>
+    
+    <!-- additional information about the project -->
+    <name>JavaFX project template</name>
+    <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url>
+    
+    <packaging>jar</packaging>
+
+    <!-- HINT: More configuration here! -->
+    <properties>
+        <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class -->
+        <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass>
+        
+        
+        <!-- Don't touch these unless you know what you're doing!
+        
+             For example, the source encoding should always be utf-8.
+             You're probably doing something stupid if you think it
+             should be a 8-bit code page in 2019. -->
+        <jdk.version>11</jdk.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <jqwik.version>1.2.0</jqwik.version>
+        <junit.version>5.5.2</junit.version>
+        <junitplatform.version>1.5.2</junitplatform.version>
+        <javafx.version>13</javafx.version>
+    </properties>
+
+    <!-- ==== END OF SIMPLE CONFIGURATION ==== -->
+    
+    <repositories>
+        <repository>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <id>central</id>
+            <name>Central Repository</name>
+            <url>https://repo.maven.apache.org/maven2</url>
+        </repository>
+        <repository>
+            <id>jcenter</id>
+            <name>jcenter</name>
+            <url>https://jcenter.bintray.com/</url>
+        </repository>
+        <!-- UTU repository -->
+        <repository>
+            <id>ftdev</id>
+            <name>ftdev</name>
+            <url>https://ftdev.utu.fi/maven2</url>
+        </repository>
+    </repositories>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-commons</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.jqwik</groupId>
+            <artifactId>jqwik</artifactId>
+            <version>${jqwik.version}</version>
+            <scope>test</scope>
+        </dependency><!--
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-migrationsupport</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-runner</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency> -->
+        
+        <!-- JavaFX (remove if not needed to speed up dep downloads)-->
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-base</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-controls</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-fxml</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-graphics</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-media</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-swing</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-web</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is.
+                 See: https://stackoverflow.com/a/57691362 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>module-info.class</exclude>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>${project.mainclass}</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Run this app with exec:java -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>1.6.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                    <arguments>
+                        <argument>arg1</argument>
+                        <argument>arg2</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+
+            <!-- Make the packaged jar executable -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.2</version>
+                <configuration>
+                    <!-- DO NOT include log4j.properties file in your Jar -->
+                    <excludes>
+                        <exclude>**/log4j.properties</exclude>
+                    </excludes>
+                    <archive>
+                        <manifest>
+                            <!-- Jar file entry point -->
+                            <mainClass>${project.mainclass}</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- download source code in Eclipse, best practice -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.10</version>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                    <downloadJavadocs>false</downloadJavadocs>
+                </configuration>
+            </plugin>
+
+            <!-- JDK source/target versions -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${jdk.version}</source>
+                    <target>${jdk.version}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.1.1</version>
+                <executions>
+                    <execution>
+                    <id>attach-javadocs</id>
+                    <goals>
+                        <goal>jar</goal>
+                    </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <tags>
+                        <tag>
+                            <name>toDo</name>
+                            <placement>a</placement>
+                            <head>To&nbsp;do:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariant</name>
+                            <placement>t</placement>
+                            <head>Class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantProtected</name>
+                            <placement>t</placement>
+                            <head>Protected&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantPrivate</name>
+                            <placement>t</placement>
+                            <head>Private&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>abstractionFunction</name>
+                            <placement>t</placement>
+                            <head>Abstraction&nbsp;function:</head>
+                        </tag>
+                        <tag>
+                            <name>pre</name>
+                            <placement>cm</placement>
+                            <head>Precondition:</head>
+                        </tag>
+                        <tag>
+                            <name>post</name>
+                            <placement>cm</placement>
+                            <head>Postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postProtected</name>
+                            <placement>cm</placement>
+                            <head>Protected&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postPrivate</name>
+                            <placement>cm</placement>
+                            <head>Private&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>time</name>
+                            <placement>cmf</placement>
+                            <head>Time&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>space</name>
+                            <placement>cmf</placement>
+                            <head>Space&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>correspondence</name>
+                            <placement>a</placement>
+                            <head>Correspondence:</head>
+                        </tag>
+                        <tag>
+                            <name>download</name>
+                            <placement>a</placement>
+                            <head>Download:</head>
+                        </tag>
+                    </tags>
+                    <show>protected</show>
+                    <failOnError>false</failOnError>
+                    <sourceFileExcludes>
+                        <sourceFileExclude>**/module-info.java</sourceFileExclude>
+                    </sourceFileExcludes>
+                </configuration>
+            </plugin>
+
+            <!-- JUnit & JQwik test integration -->
+
+            <!-- junit-platform-maven-plugin: supports modular tests
+                 maven-surefire-plugin: non-modular tests
+
+                 Modular testing works via command line mvn, but is
+                 still broken in Eclipse due to this
+                 https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667
+              -->
+            <plugin>
+                <groupId>de.sormuras.junit</groupId>
+                <artifactId>junit-platform-maven-plugin</artifactId>
+                <version>1.0.0-M5</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <executor>JAVA</executor>
+                </configuration>
+            </plugin>
+
+            <!--
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin> -->
+
+            <!-- javafx:jlink:
+                 https://github.com/openjfx/javafx-maven-plugin
+                 Use 'mvn package' instead if you don't need jlink.
+
+            <plugin>
+                <groupId>org.openjfx</groupId>
+                <artifactId>javafx-maven-plugin</artifactId>
+                <version>0.0.3</version>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                </configuration>
+            </plugin> -->
+
+        </plugins>
+        <extensions>
+            <!-- Enables the use of SSH for deployments -->
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>3.3.3</version>
+            </extension>
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh</artifactId>
+                <version>3.3.3</version>
+            </extension>
+        </extensions>
+    </build>
+    <distributionManagement>
+        <repository>
+            <id>ftdev</id>
+            <name>UTU tech ftdev repository</name>
+            <url>scp://localhost:2222/var/www/maven2</url>
+        </repository>
+    </distributionManagement>
+</project>
diff --git a/Project 1/project/build.properties b/Project 1/project/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d
--- /dev/null
+++ b/Project 1/project/build.properties	
@@ -0,0 +1 @@
+sbt.version=1.3.3
diff --git a/Project 1/project/plugins.sbt b/Project 1/project/plugins.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638
--- /dev/null
+++ b/Project 1/project/plugins.sbt	
@@ -0,0 +1,7 @@
+resolvers += Resolver.jcenterRepo
+
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
+addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3")
+addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2")
+//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8")
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0")
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java	
@@ -0,0 +1,13 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.scene.control.Alert;
+
+public class Dialogs {
+    public static void warning(String title, String header, String content) {
+        Alert alert = new Alert(Alert.AlertType.WARNING);
+        alert.setTitle(title);
+        alert.setHeaderText(header);
+        alert.setContentText(content);
+        alert.showAndWait();
+    }
+}
\ No newline at end of file
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java
new file mode 100644
index 0000000000000000000000000000000000000000..7777d6696e7c286beead79605114a62997be9867
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java	
@@ -0,0 +1,63 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Platform;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+
+import java.util.function.Supplier;
+
+public class FXMLController {
+    private IntegerProperty clicks = new SimpleIntegerProperty(0);
+
+    private Supplier<Parent> supplier;
+
+    @FXML
+    private Label clicksLabel;
+
+    protected void updateClicks() {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.textProperty().bind(clicks.asString().concat(" clicks."));
+
+        clicks.setValue(clicks.getValue() + 1);
+    }
+
+    protected void setLabel(String text) {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.setText(text);
+    }
+
+    @FXML
+    protected void handleDialogButton(ActionEvent event) {
+        updateClicks();
+        Dialogs.warning("Example dialog", "Content header", "Content");
+    }
+
+    @FXML
+    protected void handleExitButton(ActionEvent event) {
+        System.out.println("Closing app.");
+        Platform.exit();
+    }
+
+    @FXML
+    protected void handleWindowButton(ActionEvent event) {
+        updateClicks();
+        Scene other = new Scene(supplier.get());
+        Stage otherStage = new Stage();
+        otherStage.setScene(other);
+        otherStage.show();
+    }
+
+    protected void setWindowFactory(Supplier<Parent> supplier) {
+        this.supplier = supplier;
+    }
+
+    public void initialize() {
+        System.out.println("2-A");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java
new file mode 100644
index 0000000000000000000000000000000000000000..11ce009829cb4914c8324600b306da95237c7c07
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/FXMLController2.java	
@@ -0,0 +1,83 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Platform;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+
+import java.util.function.Supplier;
+
+public class FXMLController2 {
+    private IntegerProperty clicks = new SimpleIntegerProperty(0);
+
+    private Supplier<Parent> supplier;
+    private Parent parent;
+    private int ikkunoita_avattu = 0;
+    private Scene other;
+    private Stage otherStage;
+
+
+    private Label clicksLabel;
+
+    protected void updateClicks() {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.textProperty().bind(clicks.asString().concat(" clicks."));
+
+        clicks.setValue(clicks.getValue() + 1);
+    }
+
+    protected void setLabel(Label l) {clicksLabel = l;}
+    
+    
+    protected void setLabel(String text) {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.setText(text);
+    }
+
+    protected void handleDialogButton(ActionEvent event) {
+        updateClicks();
+        Dialogs.warning("Example dialog", "Content header", "Content");
+    }
+
+
+    protected void handleExitButton(ActionEvent event) {
+        System.out.println("Closing app.");
+        Platform.exit();
+    }
+
+    protected void handleWindowButton(ActionEvent event) {
+    	if(ikkunoita_avattu < 1) {
+    		setLabel("Welcome!");
+    		updateClicks();
+        	ResourceLoader<Parent, FXMLController2> loader = new ResourceLoader<>("otherwindow.fxml");
+    		other = new Scene(loader.root);
+    		otherStage = new Stage();
+    		otherStage.setScene(other);
+    		otherStage.show();
+    		System.out.println("Uusi ikkuna avattu");
+    		ikkunoita_avattu += 1;
+    	}
+    	else if(otherStage.isShowing()){
+    		otherStage.hide();
+    	}
+    	else if (!otherStage.isShowing() || !otherStage.isFocused()) {
+    		otherStage.show();
+    		otherStage.toFront();
+    	}
+    }
+
+    protected void setWindowFactory(Supplier<Parent> supplier) {
+        this.supplier = supplier;
+    }
+    
+    protected void setParent(Parent p) {parent = p;System.out.println(parent + " parent asetettu");}
+
+    public void initialize() {
+        System.out.println("2-A");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3f36884092e60cce6b4089131d57c7be407ddaf
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Main.java	
@@ -0,0 +1,29 @@
+package fi.utu.tech.gui.javafx;
+
+public class Main {
+    /**
+     * The main() method is ignored in correctly deployed JavaFX application.
+     * main() serves only as fallback in case the application can not be
+     * launched through deployment artifacts, e.g., in IDEs with limited FX
+     * support. NetBeans ignores main().
+     *
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        System.out.println("Stage A");
+        if (args.length == 1 && args[0].equals("--test")) return;
+
+        int exercise = 1;
+        if (args.length == 1) exercise = Integer.parseInt(args[0]);
+        switch(exercise) {
+            case 1:
+            case 2:
+            case 3:
+            case 4: MainApp.launch(MainApp4.class, args); break;
+            case 5: MainApp.launch(MainApp5.class, args); break;
+            case 10: MainApp.launch(MainApp10.class, args); break;
+            default: MainApp.launch(MainApp.class, args); break;
+        }
+    }
+
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dc8ba36d119ed7103cff242ce1cb5da8d0e99a1
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new OtherWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java
new file mode 100644
index 0000000000000000000000000000000000000000..3875e03e085c1d98c77a35c01b8b90c2a89a4490
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp10.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp10 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new OtherWindow10();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java
new file mode 100644
index 0000000000000000000000000000000000000000..3833d30dd5740f188e84d8cad62179d88490eaaa
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp4 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new OtherWindow4();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java
new file mode 100644
index 0000000000000000000000000000000000000000..921f7d7a79c5ed5855d97d728ee84d85d34a43ad
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java	
@@ -0,0 +1,62 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp5 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new Teht_5_ikkuna();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        
+        //Teht_5_ikkuna loader = new Teht_5_ikkuna();
+        
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f644487c5d3f683f77bde383e689e2d4c5d83db
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java	
@@ -0,0 +1,47 @@
+package fi.utu.tech.gui.javafx;
+
+import java.io.IOException;
+
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.layout.StackPane; 
+
+
+public class OtherWindow extends BorderPane {
+    public OtherWindow() {
+        Circle c = new Circle(200, Color.NAVY);
+        setLeft(new ImageView(ResourceLoader.image("hmm.png")));
+        
+        Label terve = new Label("Terve");
+        setAlignment(terve,Pos.CENTER);
+        setTop(terve);
+        
+        Label maailma = new Label("Maailma");
+        
+        StackPane stackPane = new StackPane();
+        stackPane.getChildren().addAll(c, maailma);
+        setCenter(stackPane);
+        
+        TextField textField1 = new TextField();
+        TextField textField2 = new TextField();
+        TextField textField3 = new TextField();
+        HBox hBox = new HBox(textField1,textField2,textField3);
+        setBottom(hBox);
+        
+        
+        
+        
+        
+        
+        
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java
new file mode 100644
index 0000000000000000000000000000000000000000..158757f90a822bd89a15f169554bb59c9150af51
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow10.java	
@@ -0,0 +1,48 @@
+package fi.utu.tech.gui.javafx;
+
+import java.io.IOException;
+
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.layout.StackPane; 
+
+
+public class OtherWindow10 extends BorderPane {
+    public OtherWindow10() {
+        Circle c = new Circle(200, Color.NAVY);
+        setLeft(new ImageView(ResourceLoader.image("hmm.png")));
+        
+        Label terve = new Label("Terve");
+        setAlignment(terve,Pos.CENTER);
+        setTop(terve);
+        
+        Label maailma = new Label("Maailma");
+        
+        StackPane stackPane = new StackPane();
+        stackPane.getChildren().addAll(c, maailma);
+        setCenter(stackPane);
+        
+        TextField textField1 = new TextField();
+        TextField textField2 = new TextField();
+        TextField textField3 = new TextField();
+        HBox hBox = new HBox(textField1,textField2,textField3);
+        setBottom(hBox);
+        
+        
+        textField1.textProperty().bindBidirectional(textField2.textProperty());
+        textField2.textProperty().bindBidirectional(textField3.textProperty());
+        
+        
+        
+        
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java
new file mode 100644
index 0000000000000000000000000000000000000000..58a71d1e9da4eef5a2a9b952cb4c35b897103945
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/OtherWindow4.java	
@@ -0,0 +1,50 @@
+package fi.utu.tech.gui.javafx;
+
+import java.io.IOException;
+
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.layout.StackPane; 
+
+
+public class OtherWindow4 extends BorderPane {
+    public OtherWindow4() {
+    	
+    	getStylesheets().add(getClass().getResource("styles2.css").toExternalForm());
+    	
+        Circle c = new Circle(200, Color.NAVY);
+        setLeft(new ImageView(ResourceLoader.image("hmm.png")));
+        
+        Label terve = new Label("Terve");
+        setAlignment(terve,Pos.CENTER);
+        setTop(terve);
+        
+        Label maailma = new Label("Maailma");
+        
+        StackPane stackPane = new StackPane();
+        stackPane.getChildren().addAll(c, maailma);
+        setCenter(stackPane);
+        
+        TextField textField1 = new TextField();
+        TextField textField2 = new TextField();
+        TextField textField3 = new TextField();
+        HBox hBox = new HBox(textField1,textField2,textField3);
+        setBottom(hBox);
+        
+        
+        
+        
+        
+        
+        
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..f917bd339f01757b5c81f9a4252f1adf2d3a9da4
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java	
@@ -0,0 +1,42 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+
+public class ResourceLoader<N extends Parent, C> {
+    protected final N root;
+    protected final C controller;
+
+    public ResourceLoader(String contentPath) {
+        N root_ = null;
+        C controller_ = null;
+        try {
+            // determines where to look for the resources (the root path)
+            Class resourceRootClass = getClass();
+
+            FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath));
+            root_ = loader.load();
+            controller_ = loader.getController();
+            System.out.println("DEBUG: " + contentPath + " loaded.");
+        } catch (Exception e) {
+            Dialogs.warning(
+                    "Internal error",
+                    "Could not open FXML file: " + contentPath,
+                    "Reason: " + e.getMessage()
+            );
+            System.exit(1);
+        }
+        root = root_;
+        controller = controller_;
+    }
+
+    // finds images both outside and inside jars
+    public static String image(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+    
+    // finds stylesheets both outside and inside jars
+    public static String stylesheet(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+}
diff --git a/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java
new file mode 100644
index 0000000000000000000000000000000000000000..30402f333f5e15549cb69f223fc68636d774ced3
--- /dev/null
+++ b/Project 1/src/main/java/fi/utu/tech/gui/javafx/Teht_5_ikkuna.java	
@@ -0,0 +1,80 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.VBox;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.text.Font;
+
+public class Teht_5_ikkuna extends BorderPane{
+	
+	Label label2;
+
+	public Teht_5_ikkuna(){
+		
+		setLeft(new ImageView(ResourceLoader.image("hmm.png")));
+		
+		FXMLController2 ctrl = new FXMLController2();
+		BorderPane bp = new BorderPane();
+		setAlignment(bp, Pos.CENTER);
+		bp.setPadding(new Insets(16, 16, 16, 16));
+		//setPrefHeight(bp);
+		//setMargin(bp, new Insets(16.0, 16.0, 16.0, 16.0));
+		bp.setTop(new Label("What should we do?"));
+		Button button1 = new Button("Open a new window");
+		Button button2 = new Button("Display Dialog");
+		Button button3 = new Button("Exit");
+		Label label1 = new Label("Loading...");
+		
+		ctrl.setLabel(label1);
+		label2 = new Label("Welcome!");
+		
+		EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() { 
+            public void handle(ActionEvent e) 
+            { 
+                ctrl.handleWindowButton(e);
+           } 
+        }; 
+        EventHandler<ActionEvent> event2 = new EventHandler<ActionEvent>() { 
+            public void handle(ActionEvent e) 
+            { 
+                ctrl.handleDialogButton(e);
+           } 
+        }; 
+        EventHandler<ActionEvent> event3 = new EventHandler<ActionEvent>() { 
+            public void handle(ActionEvent e) 
+            { 
+                ctrl.handleExitButton(e);
+           } 
+        }; 
+        button1.setOnAction(event);
+        button2.setOnAction(event2);
+        button3.setOnAction(event3);
+        
+        button1.setPadding(new Insets(5, 110, 5, 110));
+        button2.setPadding(new Insets(5, 122, 5, 122));
+        button3.setPadding(new Insets(5, 154, 5, 154));
+        
+		VBox vbox = new VBox(button1, button2, button3, label1);
+		vbox.setAlignment(Pos.CENTER);
+		vbox.setPadding(new Insets(16, 16, 16, 16));
+		
+		Label otsikko = new Label("What should we do?");
+		bp.setTop(otsikko);
+		bp.setAlignment(otsikko, Pos.CENTER);
+		bp.setMargin(otsikko,new Insets(15, 0, 15, 0));
+		otsikko.setFont(new Font(28));
+		
+		bp.setCenter(vbox);
+		setAlignment(bp ,Pos.CENTER);
+		bp.setMargin(vbox,new Insets(5, 40, 5, 40));
+	
+		setCenter(bp);
+	}
+	
+}
\ No newline at end of file
diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png
new file mode 100644
index 0000000000000000000000000000000000000000..25e8eb650f6752e716570cfb3c590bf09dff41ee
Binary files /dev/null and b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ
diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..cfaf34b85a6c38bde7658f287deff1f27f9aa521
--- /dev/null
+++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/otherwindow.fxml	
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.image.Image?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.shape.Circle?>
+
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="550.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController2">
+   <left>
+      <ImageView fitHeight="379.0" fitWidth="163.0" pickOnBounds="true" preserveRatio="true">
+         <image>
+            <Image url="@hmm.png" />
+         </image>
+         <BorderPane.margin>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </BorderPane.margin>
+      </ImageView>
+   </left>
+   <top>
+      <Label text="Terve" BorderPane.alignment="CENTER" />
+   </top>
+   <center>
+      <StackPane prefHeight="358.0" prefWidth="388.0" BorderPane.alignment="CENTER">
+         <children>
+            <Circle fill="#0e166f" radius="178.0" smooth="false" stroke="BLACK" strokeType="INSIDE" />
+            <Label text="Maailma" />
+         </children>
+      </StackPane>
+   </center>
+   <bottom>
+      <HBox alignment="BOTTOM_RIGHT" prefHeight="14.0" prefWidth="600.0" BorderPane.alignment="CENTER">
+         <children>
+            <TextField prefHeight="25.0" prefWidth="103.0" />
+            <TextField prefHeight="25.0" prefWidth="108.0" />
+            <TextField prefHeight="25.0" prefWidth="113.0" />
+         </children>
+      </HBox>
+   </bottom>
+</BorderPane>
diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..434e233d182a2a3205228268795bbedbb6104185
--- /dev/null
+++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml	
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.image.Image?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController">
+   <left>
+      <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
+         <image>
+            <Image url="@hmm.png" />
+         </image>
+         <BorderPane.margin>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </BorderPane.margin>
+      </ImageView>
+   </left>
+   <center>
+      <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+         <padding>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </padding>
+         <center>
+            <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER">
+               <padding>
+                  <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+               </padding>
+               <children>
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleWindowButton" text="Open a new window" />
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleDialogButton" text="Display a dialog" />
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleExitButton" text="Exit" />
+                  <Label fx:id="clicksLabel" text="Loading..." />
+               </children></VBox>
+         </center>
+         <top>
+            <Label text="What should we do?" BorderPane.alignment="CENTER">
+               <font>
+                  <Font size="26.0" />
+               </font></Label>
+         </top>
+      </BorderPane>
+   </center>
+</BorderPane>
diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..3ab643a3ea2d9be4b2f210cd57132b6fc8569a82
--- /dev/null
+++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles.css	
@@ -0,0 +1,3 @@
+.button {
+    -fx-font-weight: bold;
+}
diff --git a/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css
new file mode 100644
index 0000000000000000000000000000000000000000..6444aafc9f6d8834244b305e5defcf52c1acb37c
--- /dev/null
+++ b/Project 1/src/main/resources/fi/utu/tech/gui/javafx/styles2.css	
@@ -0,0 +1,7 @@
+.root {
+	-fx-font-weight: bold;
+	-fx-background-color: black;
+}
+.root .label {
+	-fx-text-fill: white;
+}
diff --git a/Project 2/.gitignore b/Project 2/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2b186553c37a0083b6357ae6d311b05e880e4052
--- /dev/null
+++ b/Project 2/.gitignore	
@@ -0,0 +1,21 @@
+bin/
+contracts/
+.classpath
+.project
+.history/
+.idea
+.jqwik-database
+.lib/
+.worksheet
+.settings/
+*.iml
+*.ipr
+*.iws
+*.log
+project/boot/
+project/plugins/project/
+project/project/
+project/*-shim.sbt
+project/target/
+target/
+openjfx/
diff --git a/Project 2/.gitlab-ci.yml b/Project 2/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898
--- /dev/null
+++ b/Project 2/.gitlab-ci.yml	
@@ -0,0 +1,13 @@
+image: maven:latest
+
+stages:
+  - build
+  - test
+
+build:
+  script:
+    - mvn compile
+
+test:
+  script:
+    - mvn test
\ No newline at end of file
diff --git a/Project 2/README.md b/Project 2/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52
--- /dev/null
+++ b/Project 2/README.md	
@@ -0,0 +1,39 @@
+# Project description
+
+Simple template for projects that make use of JavaFX and FXML (Scene Builder).
+Requires Java 11 or later. Compatible with
+Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically
+integrates with Gitlab CI.
+
+## Installation
+
+Maven:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ mvn compile exec:java
+```
+
+SBT:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ sbt compile run
+```
+
+## Further instructions
+
+  * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform
+  * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc
+  * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc
+  
+External sources:
+
+  * JavaFX: https://openjfx.io/javadoc/11/
+  * Scene Builder: https://docs.gluonhq.com/scenebuilder/
diff --git a/Project 2/build.sbt b/Project 2/build.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648
--- /dev/null
+++ b/Project 2/build.sbt	
@@ -0,0 +1,236 @@
+// Project template
+
+// Supported operating systems: Windows, Mac, Linux
+// Supported JDKs: 8, 10+
+
+// Project name
+name := "template-javafx"
+
+// organization name
+organization := "fi.utu.tech"
+
+version := "1.0"
+
+// project description
+description := "JavaFX project template"
+
+// main class
+Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main")
+
+// force the java version by typing it here (remove the comment)
+val force_javaVersion = None // Some(13)
+
+// force the javafx version by typing it here (remove the comment)
+val force_javaFxVersion = None // Some(13)
+
+val useJavaFX = true
+
+val useScalaOrScalaFX = true
+
+// END_OF_SIMPLE_CONFIGURATION
+// you can copy the rest for each new project
+// --- --- ---
+
+def fail(msg: String) = {
+  println("Error :-/")
+  println
+  println(msg)
+  System.exit(1)
+  null
+}
+
+val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt
+
+val javaVersionNum = force_javaVersion.getOrElse(detectedJDK)
+
+val javaVersionString = javaVersionNum match {
+  case 7 => "1.7"
+  case 8 => "1.8"
+  case x if x > 8 => x.toString
+}
+
+val lts = 11
+val dev = 13
+
+val supported = javaVersionNum match {
+  case x if x < 8              => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS")
+  case 9                       => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS")
+  case x if x < lts            => println("Consider upgrading to Java " + lts + " LTS"); true
+  case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true
+  case x if x > dev            => println("Unsupported early access version. Consider switching back to Java " + dev); true
+  case _                       => true
+}
+
+javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation")
+javacOptions in doc := Seq("-source", javaVersionString) 
+
+enablePlugins(JShellPlugin)
+
+compileOrder := CompileOrder.JavaThenScala
+
+// Enables publishing to maven repo
+publishMavenStyle := true
+
+// Do not append Scala versions to the generated artifacts
+crossPaths := false
+
+// This forbids including Scala related libraries into the dependency
+autoScalaLibrary := false
+
+assemblyMergeStrategy in assembly := {
+  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
+  case _ => MergeStrategy.first
+}
+
+// contains libraries provided by utu/ft dep
+resolvers += "ftdev" at "https://ftdev.utu.fi/maven2"
+
+fork in Global := true
+
+val javaVersion = taskKey[Unit]("Prints the Java version.")
+
+javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) }
+
+publishTo := Some(Resolver.file("file", new File("/tmp/repository")))
+
+val oomkit = "fi.utu.tech" % "oomkit" % "1.15"
+
+libraryDependencies ++= Seq()
+
+////
+//// JQWIK / JUNIT configuration
+////
+
+resolvers in ThisBuild += Resolver.jcenterRepo
+
+val junit_version = "5.5.2"
+
+// library dependencies. (orginization name) % (project name) % (version)
+libraryDependencies ++= Seq(
+  "net.aichler"        % "jupiter-interface"              % JupiterKeys.jupiterVersion.value % Test,
+  "org.junit.platform" % "junit-platform-commons"         % ("1"+junit_version.tail) % Test,
+  "org.junit.platform" % "junit-platform-runner"          % ("1"+junit_version.tail) % Test,
+  "org.junit.jupiter"  % "junit-jupiter-engine"           % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-api"              % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-migrationsupport" % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-params"           % junit_version % Test,
+  "net.jqwik"          % "jqwik"                          % "1.2.0" % Test,
+  "org.scalatest"      %% "scalatest"                     % "3.0.8" % Test,
+)
+
+testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c")
+
+////
+//// JAVAFX configuration
+////
+
+val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match {
+  case 7 => (7, "7", "8.0.181-R13")
+  case 8 => (8, "8", "8.0.181-R13")
+  case 10 => (11, "11.0.2", "11-R16")
+  case x if x>10 => (13, "13", "12.0.2-R18")
+  case _ => fail("Unsupported Java version for JavaFX")
+}
+
+// JAVA_HOME location
+val javaHomeDir = {
+  val path = try {
+    if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME")
+  } catch {
+    case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM
+  }
+
+  val f = file(path)
+  if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.")
+  f
+}
+
+val osName: SettingKey[String] = SettingKey[String]("osName")
+
+osName := (System.getProperty("os.name") match {
+  case n if n.startsWith("Linux")   => "linux"
+  case n if n.startsWith("Mac")     => "mac"
+  case n if n.startsWith("Windows") => "win"
+  case _ => throw new Exception("Unknown platform!")
+})
+
+def legacyJavaFX() = {
+  val searchDirs = Seq(
+    "/jre/lib/jfxrt.jar",     // OpenJDK 7
+    "/jre/lib/ext/jfxrt.jar", // OpenJDK 8
+    "/lib/ext/jfxrt.jar"      // Windows & Oracle Java 8
+  )
+
+  if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.")
+
+  val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() }
+
+  javaFxJAR.getOrElse {
+    fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.")
+  }
+}
+
+val jfx_sdk_version = javafx_versions._2
+val jfx_scalafx_version = javafx_versions._3
+
+val javaFxPath = Def.taskKey[File]("OpenJFX fetcher")
+javaFxPath := {
+  val javaFxHome =
+    try {
+      val envHome = file(scala.sys.env("JAVAFX_HOME"))
+      if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME")
+      println("Using OpenJFX from " + envHome)
+      envHome
+    }
+    catch { case _: Throwable =>
+        println("Using local OpenJFX")
+        baseDirectory.value / "openjfx"
+    }
+
+  if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath)
+
+  val jfx_os = osName.value match {
+    case "linux" => "linux"
+    case "mac"   => "osx"
+    case "win"   => "windows"
+  }
+
+  val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip"
+
+  try {
+    val testDir = javaFxHome / "all.ok"
+    if (!testDir.exists()) {
+      println("Fetching OpenJFX from "+sdkURL+"..")
+      IO.unzipURL(new URL(sdkURL), javaFxHome)
+      java.nio.file.Files.createDirectory(testDir.toPath)
+      println("Fetching OpenJFX done.")
+    } else {
+      println("Using OpenJFX from "+javaFxHome)
+    }
+
+    javaFxHome
+  }
+  catch {
+    case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage)
+  }
+}
+
+val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web")
+
+
+if (!useJavaFX) Seq() else javafx_versions._1 match {
+  case 7 =>
+    // TODO libraryDependencies
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case 8 =>
+    (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case _ =>
+    Seq(
+      javaOptions in run ++= Seq(
+        "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString,
+        "--add-modules=" + jfxModules.map("javafx."+_).mkString(","))
+    ) ++
+      (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++
+      jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value)
+}
diff --git a/Project 2/pom.xml b/Project 2/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..40e1bb8e9bf40fbf9a984a7b5bb3b2ff7177fd54
--- /dev/null
+++ b/Project 2/pom.xml	
@@ -0,0 +1,452 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!--
+     
+      Welcome!
+      This pom.xml is *THE* file that defines your Maven style Java project.
+      
+      Eclipse, IDEA and other development environments with Maven support
+      or plugins can *import* this project by reading this file.
+      
+      It usually contains tons of barely readable configuration. Luckily
+      this basic pom.xml is somewhat readable. All necessary configuration
+      for customizing your project if located here on top before the
+      'END OF SIMPLE CONFIGURATION' line below.
+      
+      Some basics:
+      
+        - Maven is a build system for Java/JVM
+        - the Maven projects define an artifact that has a three part id:
+          groupId - artifactId - version
+          
+        - For example, this project is called:
+          fi.utu.tech - oomkit - 1.15
+          
+        - The group id is usually a web domain in reverse order.
+          
+        - You can use these id parts to search for projects at
+          https://search.maven.org/
+          
+        - If your version is not final and you have plans to modify the
+          source code at some point, please use a version id that ends with
+          -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar
+          and refuse to overwrite old cached versions with new ones unless
+          you remember this convention.
+          
+        - This file supports
+          compiling the project with      'mvn compile' (see target/)
+          cleaning the class files with   'mvn clean'
+          executing the main routine with 'mvn exec:java'
+          executing the unit tests with   'mvn test'
+          packaging the application with  'mvn package' (see target/)
+          
+        - the Maven project structure is as follows:
+          https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+          
+    -->
+    
+    <!-- ==== START OF SIMPLE CONFIGURATION ==== -->
+    
+    <!-- the three parts of the artifact name -->
+    <groupId>fi.utu.tech</groupId>
+    <artifactId>template-javafx</artifactId>
+    <version>1.0</version>
+    
+    <!-- additional information about the project -->
+    <name>JavaFX project template</name>
+    <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url>
+    
+    <packaging>jar</packaging>
+
+    <!-- HINT: More configuration here! -->
+    <properties>
+        <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class -->
+        <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass>
+        
+        
+        <!-- Don't touch these unless you know what you're doing!
+        
+             For example, the source encoding should always be utf-8.
+             You're probably doing something stupid if you think it
+             should be a 8-bit code page in 2019. -->
+        <jdk.version>11</jdk.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <jqwik.version>1.2.0</jqwik.version>
+        <junit.version>5.5.2</junit.version>
+        <junitplatform.version>1.5.2</junitplatform.version>
+        <javafx.version>13</javafx.version>
+    </properties>
+
+    <!-- ==== END OF SIMPLE CONFIGURATION ==== -->
+    
+    <repositories>
+        <repository>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <id>central</id>
+            <name>Central Repository</name>
+            <url>https://repo.maven.apache.org/maven2</url>
+        </repository>
+        <repository>
+            <id>jcenter</id>
+            <name>jcenter</name>
+            <url>https://jcenter.bintray.com/</url>
+        </repository>
+        <!-- UTU repository -->
+        <repository>
+            <id>ftdev</id>
+            <name>ftdev</name>
+            <url>https://ftdev.utu.fi/maven2</url>
+        </repository>
+    </repositories>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-commons</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.jqwik</groupId>
+            <artifactId>jqwik</artifactId>
+            <version>${jqwik.version}</version>
+            <scope>test</scope>
+        </dependency><!--
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-migrationsupport</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-runner</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency> -->
+        
+        <!-- JavaFX (remove if not needed to speed up dep downloads)-->
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-base</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-controls</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-fxml</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-graphics</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-media</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-swing</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-web</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is.
+                 See: https://stackoverflow.com/a/57691362 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>module-info.class</exclude>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>${project.mainclass}</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Run this app with exec:java -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>1.6.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                    <arguments>
+                        <argument>arg1</argument>
+                        <argument>arg2</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+
+            <!-- Make the packaged jar executable -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.2</version>
+                <configuration>
+                    <!-- DO NOT include log4j.properties file in your Jar -->
+                    <excludes>
+                        <exclude>**/log4j.properties</exclude>
+                    </excludes>
+                    <archive>
+                        <manifest>
+                            <!-- Jar file entry point -->
+                            <mainClass>${project.mainclass}</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- download source code in Eclipse, best practice -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.10</version>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                    <downloadJavadocs>false</downloadJavadocs>
+                </configuration>
+            </plugin>
+
+            <!-- JDK source/target versions -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${jdk.version}</source>
+                    <target>${jdk.version}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.1.1</version>
+                <executions>
+                    <execution>
+                    <id>attach-javadocs</id>
+                    <goals>
+                        <goal>jar</goal>
+                    </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <tags>
+                        <tag>
+                            <name>toDo</name>
+                            <placement>a</placement>
+                            <head>To&nbsp;do:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariant</name>
+                            <placement>t</placement>
+                            <head>Class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantProtected</name>
+                            <placement>t</placement>
+                            <head>Protected&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantPrivate</name>
+                            <placement>t</placement>
+                            <head>Private&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>abstractionFunction</name>
+                            <placement>t</placement>
+                            <head>Abstraction&nbsp;function:</head>
+                        </tag>
+                        <tag>
+                            <name>pre</name>
+                            <placement>cm</placement>
+                            <head>Precondition:</head>
+                        </tag>
+                        <tag>
+                            <name>post</name>
+                            <placement>cm</placement>
+                            <head>Postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postProtected</name>
+                            <placement>cm</placement>
+                            <head>Protected&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postPrivate</name>
+                            <placement>cm</placement>
+                            <head>Private&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>time</name>
+                            <placement>cmf</placement>
+                            <head>Time&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>space</name>
+                            <placement>cmf</placement>
+                            <head>Space&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>correspondence</name>
+                            <placement>a</placement>
+                            <head>Correspondence:</head>
+                        </tag>
+                        <tag>
+                            <name>download</name>
+                            <placement>a</placement>
+                            <head>Download:</head>
+                        </tag>
+                    </tags>
+                    <show>protected</show>
+                    <failOnError>false</failOnError>
+                    <sourceFileExcludes>
+                        <sourceFileExclude>**/module-info.java</sourceFileExclude>
+                    </sourceFileExcludes>
+                </configuration>
+            </plugin>
+
+            <!-- JUnit & JQwik test integration -->
+
+            <!-- junit-platform-maven-plugin: supports modular tests
+                 maven-surefire-plugin: non-modular tests
+
+                 Modular testing works via command line mvn, but is
+                 still broken in Eclipse due to this
+                 https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667
+              -->
+            <plugin>
+                <groupId>de.sormuras.junit</groupId>
+                <artifactId>junit-platform-maven-plugin</artifactId>
+                <version>1.0.0-M5</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <executor>JAVA</executor>
+                </configuration>
+            </plugin>
+
+            <!--
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin> -->
+
+            <!-- javafx:jlink:
+                 https://github.com/openjfx/javafx-maven-plugin
+                 Use 'mvn package' instead if you don't need jlink.
+
+            <plugin>
+                <groupId>org.openjfx</groupId>
+                <artifactId>javafx-maven-plugin</artifactId>
+                <version>0.0.3</version>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                </configuration>
+            </plugin> -->
+
+        </plugins>
+        <extensions>
+            <!-- Enables the use of SSH for deployments -->
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>3.3.3</version>
+            </extension>
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh</artifactId>
+                <version>3.3.3</version>
+            </extension>
+        </extensions>
+    </build>
+    <distributionManagement>
+        <repository>
+            <id>ftdev</id>
+            <name>UTU tech ftdev repository</name>
+            <url>scp://localhost:2222/var/www/maven2</url>
+        </repository>
+    </distributionManagement>
+</project>
diff --git a/Project 2/project/build.properties b/Project 2/project/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d
--- /dev/null
+++ b/Project 2/project/build.properties	
@@ -0,0 +1 @@
+sbt.version=1.3.3
diff --git a/Project 2/project/plugins.sbt b/Project 2/project/plugins.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638
--- /dev/null
+++ b/Project 2/project/plugins.sbt	
@@ -0,0 +1,7 @@
+resolvers += Resolver.jcenterRepo
+
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
+addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3")
+addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2")
+//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8")
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0")
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6c72d39efd3065afb24c7b1e26f78a10a3e5eab
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/BMI.java	
@@ -0,0 +1,46 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.event.EventHandler;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+
+public class BMI extends BorderPane{
+	
+	public BMI() {
+		Label label1 = new Label("Pituus: ");
+		Label label2 = new Label("Paino: ");
+		Label tulos = new Label("Virhellinen syöttö");
+		Label BMI = new Label("BMI: ");
+		TextField tf1 = new TextField();
+		TextField tf2 = new TextField();
+		
+		VBox vb1 = new VBox(label1, tf1);
+		VBox vb2 = new VBox(label2, tf2);
+		
+		HBox hb = new HBox(vb1, vb2);
+		HBox hb2 = new HBox(BMI, tulos);
+		
+		EventHandler<KeyEvent> BMIKaava = e -> {
+			try {
+				float pituus = Float.valueOf(tf1.getText());
+				float paino = Float.valueOf(tf2.getText());
+				tulos.setText(String.valueOf((paino/(pituus*pituus))));
+			} catch(Exception a) {
+				tulos.setText("Virheellinen syöttö");
+			}
+		};
+		
+		
+		tf1.setOnKeyTyped(BMIKaava);
+		tf2.setOnKeyTyped(BMIKaava);
+
+	
+		setCenter(hb);
+		setBottom(hb2);
+	
+	}
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..c01c54dbe9a2c858dde59959dd454dfad746621a
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/CanvasWindow.java	
@@ -0,0 +1,84 @@
+package fi.utu.tech.gui.javafx;
+
+import java.util.Random;
+
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.paint.Color;
+
+public class CanvasWindow extends BorderPane{
+
+	public CanvasWindow() {
+		
+		Canvas canvas = new Canvas(500, 500);
+		final GraphicsContext graphicsContext = canvas.getGraphicsContext2D();		
+		initDraw(graphicsContext);
+		Random r = new Random();
+		
+		
+		canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>(){
+				
+		@Override
+		public void handle(MouseEvent event) {
+			if (event.getButton() == MouseButton.PRIMARY) {
+				graphicsContext.beginPath();
+				graphicsContext.moveTo(event.getX(), event.getY());
+				graphicsContext.stroke();
+			} else if (event.getButton() == MouseButton.SECONDARY) {
+            	
+            }
+		}
+		});
+		
+		canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>(){
+ 
+        @Override
+        public void handle(MouseEvent event) {
+        	if (event.getButton() == MouseButton.PRIMARY) {
+        		graphicsContext.lineTo(event.getX(), event.getY());
+        		graphicsContext.stroke();
+            } else if (event.getButton() == MouseButton.SECONDARY) {
+            	graphicsContext.fillRect(event.getX()+ (r.nextInt(20)-10), event.getY()+ (r.nextInt(20)-10), 2, 2);
+            	graphicsContext.stroke();
+            }
+        }
+        });
+		
+		canvas.addEventHandler(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>(){
+ 
+            @Override
+            public void handle(MouseEvent event) {
+ 
+            }
+        });
+		
+		setCenter(canvas);
+	}
+	
+	private void initDraw(GraphicsContext gc){
+        double canvasWidth = gc.getCanvas().getWidth();
+        double canvasHeight = gc.getCanvas().getHeight();
+         
+        gc.setFill(Color.BLACK);
+        gc.setStroke(Color.BLACK);
+        gc.setLineWidth(5);
+ 
+        gc.fill();
+        gc.strokeRect(
+                0,              //x of the upper left corner
+                0,              //y of the upper left corner
+                canvasWidth,    //width of the rectangle
+                canvasHeight);  //height of the rectangle
+         
+        gc.setFill(Color.BLACK);
+        gc.setStroke(Color.BLACK);
+        gc.setLineWidth(1);
+         
+    }
+	
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java	
@@ -0,0 +1,13 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.scene.control.Alert;
+
+public class Dialogs {
+    public static void warning(String title, String header, String content) {
+        Alert alert = new Alert(Alert.AlertType.WARNING);
+        alert.setTitle(title);
+        alert.setHeaderText(header);
+        alert.setContentText(content);
+        alert.showAndWait();
+    }
+}
\ No newline at end of file
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java
new file mode 100644
index 0000000000000000000000000000000000000000..7777d6696e7c286beead79605114a62997be9867
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/FXMLController.java	
@@ -0,0 +1,63 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Platform;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+
+import java.util.function.Supplier;
+
+public class FXMLController {
+    private IntegerProperty clicks = new SimpleIntegerProperty(0);
+
+    private Supplier<Parent> supplier;
+
+    @FXML
+    private Label clicksLabel;
+
+    protected void updateClicks() {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.textProperty().bind(clicks.asString().concat(" clicks."));
+
+        clicks.setValue(clicks.getValue() + 1);
+    }
+
+    protected void setLabel(String text) {
+        if (!clicksLabel.textProperty().isBound())
+            clicksLabel.setText(text);
+    }
+
+    @FXML
+    protected void handleDialogButton(ActionEvent event) {
+        updateClicks();
+        Dialogs.warning("Example dialog", "Content header", "Content");
+    }
+
+    @FXML
+    protected void handleExitButton(ActionEvent event) {
+        System.out.println("Closing app.");
+        Platform.exit();
+    }
+
+    @FXML
+    protected void handleWindowButton(ActionEvent event) {
+        updateClicks();
+        Scene other = new Scene(supplier.get());
+        Stage otherStage = new Stage();
+        otherStage.setScene(other);
+        otherStage.show();
+    }
+
+    protected void setWindowFactory(Supplier<Parent> supplier) {
+        this.supplier = supplier;
+    }
+
+    public void initialize() {
+        System.out.println("2-A");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..8039f74d6f63866f01415316911afc470cb27731
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/Main.java	
@@ -0,0 +1,29 @@
+package fi.utu.tech.gui.javafx;
+
+public class Main {
+    /**
+     * The main() method is ignored in correctly deployed JavaFX application.
+     * main() serves only as fallback in case the application can not be
+     * launched through deployment artifacts, e.g., in IDEs with limited FX
+     * support. NetBeans ignores main().
+     *
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        System.out.println("Stage A");
+        if (args.length == 1 && args[0].equals("--test")) return;
+        
+        int exercise = 1;
+        if (args.length == 1) exercise = Integer.parseInt(args[0]);
+        switch(exercise) {
+            case 1: MainApp1.launch(MainApp1.class, args); break;
+            case 2: MainApp2.launch(MainApp2.class, args); break;
+            case 3: MainApp3.launch(MainApp3.class, args); break;
+            case 4: MainApp4.launch(MainApp4.class, args); break;
+            case 5: MainApp5.launch(MainApp5.class, args); break;
+            case 6: MainApp6.launch(MainApp6.class, args); break;
+            default: MainApp.launch(MainApp.class, args); break;
+        }
+    }
+
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java
new file mode 100644
index 0000000000000000000000000000000000000000..6dc8ba36d119ed7103cff242ce1cb5da8d0e99a1
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new OtherWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b8c8b3ba81acac1c4b6161d0f51a2f0dc730aa0
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp1.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp1 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new PasswordWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java
new file mode 100644
index 0000000000000000000000000000000000000000..f86a01d070d49e8c9d9cd3b4e29373e06b2e3a9b
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp2.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp2 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new BMI();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java
new file mode 100644
index 0000000000000000000000000000000000000000..58ea02f48431f1486e8c193e58aa18979f00db51
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp3.java	
@@ -0,0 +1,76 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.animation.Animation;
+import javafx.animation.Interpolator;
+import javafx.animation.RotateTransition;
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+public class MainApp3 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new OtherWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+        
+        RotateTransition rt = new RotateTransition(Duration.millis(5000), loader.root);
+        
+        rt.setCycleCount(Animation.INDEFINITE);
+        rt.setFromAngle(0);
+        rt.setToAngle(360);
+        rt.setInterpolator(Interpolator.LINEAR);
+        rt.play();
+        
+        loader.root.setOnMouseClicked(mouseEvent -> {
+        	rt.setRate(-1*rt.getRate());
+        });
+        
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java
new file mode 100644
index 0000000000000000000000000000000000000000..253aaaef03b38950ef53687da04bc30c35f9f1b6
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp4.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp4 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new WebSelain();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java
new file mode 100644
index 0000000000000000000000000000000000000000..73acf69c905707e3e37664e330cd2af2a9dccb81
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp5.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp5 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new StackPaneWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java
new file mode 100644
index 0000000000000000000000000000000000000000..fefcf2d57a73eb6fb3448ea8e80d819bbf3d7b55
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/MainApp6.java	
@@ -0,0 +1,59 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp6 extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+        System.out.println("Stage B");
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("Stage E");
+    }
+
+    protected Parent createWindow() {
+        return new CanvasWindow();
+    }
+    
+    protected String createStyle() {
+        return ResourceLoader.stylesheet("styles.css");
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        System.out.println("Stage C");
+        ResourceLoader<Parent, FXMLController> loader = new ResourceLoader<>("scene.fxml");
+
+        loader.controller.setLabel("Welcome!");
+
+        // configure the menu to create windows with createWindow
+        loader.controller.setWindowFactory(this::createWindow);
+
+        Scene scene = new Scene(loader.root);
+        scene.getStylesheets().add(createStyle());
+        
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+        System.out.println("Stage D");
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..5328b153fd5aab1989df1f54049401893272a439
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/OtherWindow.java	
@@ -0,0 +1,14 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+
+public class OtherWindow extends BorderPane {
+    public OtherWindow() {
+        Circle c = new Circle(200, Color.NAVY);
+        setCenter(c);
+        setLeft(new ImageView(ResourceLoader.image("hmm.png")));
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c799984583388219eab1f153b6ee4cd02c5c7ef
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/PasswordWindow.java	
@@ -0,0 +1,91 @@
+package fi.utu.tech.gui.javafx;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Formatter;
+
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+
+public class PasswordWindow extends BorderPane {
+
+	public PasswordWindow() {
+		PasswordField pf = new PasswordField();
+		Label label = new Label("Aseta salasana");
+		Button b1 = new Button("Tyhjennä");
+		Button b2 = new Button("Hyväksy");
+		HBox hb = new HBox(b1, b2);
+		Alert alert = new Alert(Alert.AlertType.NONE);
+
+		
+		EventHandler<KeyEvent> salasanaPituus = e -> {
+			if(pf.getLength() < 8) {
+				label.setText("Salasana liian lyhyt");
+			}else {
+				label.setText("Salasana on riittävän pitkä");
+			}
+		};
+		
+		
+		EventHandler<ActionEvent> tyhjennys = e -> {
+			pf.clear();
+			label.setText("Aseta salasana");
+		};
+		
+		EventHandler<ActionEvent> hyvaksy = e -> {
+			Dialogs.warning("SHA1", "Salasana SHA1 muodossa", encryptPassword(pf.getText()) );
+		};
+		
+		pf.setOnKeyTyped(salasanaPituus);
+		b1.setOnAction(tyhjennys);
+		b2.setOnAction(hyvaksy);
+		
+		setTop(label);
+		setCenter(pf);
+		setBottom(hb);
+		
+	}
+	
+	
+	private static String encryptPassword(String password)
+	{
+	    String sha1 = "";
+	    try
+	    {
+	        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
+	        crypt.reset();
+	        crypt.update(password.getBytes("UTF-8"));
+	        sha1 = byteToHex(crypt.digest());
+	    }
+	    catch(NoSuchAlgorithmException e)
+	    {
+	        e.printStackTrace();
+	    }
+	    catch(UnsupportedEncodingException e)
+	    {
+	        e.printStackTrace();
+	    }
+	    return sha1;
+	}
+
+	private static String byteToHex(final byte[] hash)
+	{
+	    Formatter formatter = new Formatter();
+	    for (byte b : hash)
+	    {
+	        formatter.format("%02x", b);
+	    }
+	    String result = formatter.toString();
+	    formatter.close();
+	    return result;
+	}
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..f917bd339f01757b5c81f9a4252f1adf2d3a9da4
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java	
@@ -0,0 +1,42 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+
+public class ResourceLoader<N extends Parent, C> {
+    protected final N root;
+    protected final C controller;
+
+    public ResourceLoader(String contentPath) {
+        N root_ = null;
+        C controller_ = null;
+        try {
+            // determines where to look for the resources (the root path)
+            Class resourceRootClass = getClass();
+
+            FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath));
+            root_ = loader.load();
+            controller_ = loader.getController();
+            System.out.println("DEBUG: " + contentPath + " loaded.");
+        } catch (Exception e) {
+            Dialogs.warning(
+                    "Internal error",
+                    "Could not open FXML file: " + contentPath,
+                    "Reason: " + e.getMessage()
+            );
+            System.exit(1);
+        }
+        root = root_;
+        controller = controller_;
+    }
+
+    // finds images both outside and inside jars
+    public static String image(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+    
+    // finds stylesheets both outside and inside jars
+    public static String stylesheet(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0cadac53d210ed521d80414ec8830a84f8af137
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/StackPaneWindow.java	
@@ -0,0 +1,41 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.event.EventHandler;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.StackPane;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Rectangle;
+
+public class StackPaneWindow extends BorderPane {
+
+	public StackPaneWindow() {
+		
+		Rectangle r = new Rectangle(300,300, Paint.valueOf("#ff0000"));
+		
+		StackPane sp1 = new StackPane(r);
+		StackPane sp2 = new StackPane(sp1);
+		
+		EventHandler<MouseEvent> rKlikkaus = e -> {
+			System.out.println(r.toString());
+			e.consume();
+		};
+		
+		EventHandler<MouseEvent> sp1Klikkaus = e -> {
+			System.out.println(sp1.toString());
+		};
+		
+		EventHandler<MouseEvent> sp2Klikkaus = e -> {
+			System.out.println(sp2.toString());
+		};
+		
+		r.setOnMouseClicked(rKlikkaus);
+		sp1.setOnMouseClicked(sp1Klikkaus);
+		sp2.setOnMouseClicked(sp2Klikkaus);
+		
+		setCenter(sp2);
+		
+	}
+	
+}
diff --git a/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java b/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae0bb790d003f68de3ccb62b9b8a71a14b1e4792
--- /dev/null
+++ b/Project 2/src/main/java/fi/utu/tech/gui/javafx/WebSelain.java	
@@ -0,0 +1,40 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.event.EventHandler;
+import javafx.scene.Scene;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Color;
+import javafx.scene.web.WebEngine;
+import javafx.scene.web.WebView;
+
+public class WebSelain extends BorderPane {
+
+	public WebSelain() {
+		
+		TextField osoiteRivi = new TextField();
+		
+		WebView webView = new WebView();
+		setCenter(webView);
+		
+		setTop(osoiteRivi);
+		
+		osoiteRivi.setOnKeyPressed(new EventHandler<KeyEvent>(){
+	        @Override
+	        public void handle(KeyEvent ke)
+	        {
+	            if (ke.getCode().equals(KeyCode.ENTER))
+	            {
+	            	System.out.println("moi");
+	            	
+	            	webView.getEngine().load(osoiteRivi.getText());
+	                
+	            }
+	        }
+	    });
+		
+	}
+}
diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png
new file mode 100644
index 0000000000000000000000000000000000000000..25e8eb650f6752e716570cfb3c590bf09dff41ee
Binary files /dev/null and b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ
diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..434e233d182a2a3205228268795bbedbb6104185
--- /dev/null
+++ b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/scene.fxml	
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.image.Image?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.FXMLController">
+   <left>
+      <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
+         <image>
+            <Image url="@hmm.png" />
+         </image>
+         <BorderPane.margin>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </BorderPane.margin>
+      </ImageView>
+   </left>
+   <center>
+      <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+         <padding>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </padding>
+         <center>
+            <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER">
+               <padding>
+                  <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+               </padding>
+               <children>
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleWindowButton" text="Open a new window" />
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleDialogButton" text="Display a dialog" />
+                  <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#handleExitButton" text="Exit" />
+                  <Label fx:id="clicksLabel" text="Loading..." />
+               </children></VBox>
+         </center>
+         <top>
+            <Label text="What should we do?" BorderPane.alignment="CENTER">
+               <font>
+                  <Font size="26.0" />
+               </font></Label>
+         </top>
+      </BorderPane>
+   </center>
+</BorderPane>
diff --git a/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..3ab643a3ea2d9be4b2f210cd57132b6fc8569a82
--- /dev/null
+++ b/Project 2/src/main/resources/fi/utu/tech/gui/javafx/styles.css	
@@ -0,0 +1,3 @@
+.button {
+    -fx-font-weight: bold;
+}
diff --git a/Project 3/.gitignore b/Project 3/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..5bfb48ed0b155fac605c25bfac13ca2115525da1
--- /dev/null
+++ b/Project 3/.gitignore	
@@ -0,0 +1,20 @@
+contracts/
+.classpath
+.project
+.history/
+.idea
+.jqwik-database
+.lib/
+.worksheet
+.settings/
+*.iml
+*.ipr
+*.iws
+*.log
+project/boot/
+project/plugins/project/
+project/project/
+project/*-shim.sbt
+project/target/
+target/
+openjfx/
diff --git a/Project 3/.gitlab-ci.yml b/Project 3/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4b3553b42ff83c2ef7e7ea37ffa030aaaf554898
--- /dev/null
+++ b/Project 3/.gitlab-ci.yml	
@@ -0,0 +1,13 @@
+image: maven:latest
+
+stages:
+  - build
+  - test
+
+build:
+  script:
+    - mvn compile
+
+test:
+  script:
+    - mvn test
\ No newline at end of file
diff --git a/Project 3/README.md b/Project 3/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2907fd126e89f04e9141a21287b3d0e75094be52
--- /dev/null
+++ b/Project 3/README.md	
@@ -0,0 +1,39 @@
+# Project description
+
+Simple template for projects that make use of JavaFX and FXML (Scene Builder).
+Requires Java 11 or later. Compatible with
+Eclipse and IntelliJ IDEA. Minor issues with Netbeans. Automatically
+integrates with Gitlab CI.
+
+## Installation
+
+Maven:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ mvn compile exec:java
+```
+
+SBT:
+
+```bash
+$ git clone https://gitlab.utu.fi/tech/education/gui/template-javafx
+
+$ cd template-javafx
+
+$ sbt compile run
+```
+
+## Further instructions
+
+  * Java platform: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/jvm-platform
+  * Maven: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/maven-misc
+  * SBT: https://gitlab.utu.fi/soft/ftdev/wikis/tutorials/sbt-misc
+  
+External sources:
+
+  * JavaFX: https://openjfx.io/javadoc/11/
+  * Scene Builder: https://docs.gluonhq.com/scenebuilder/
diff --git a/Project 3/build.sbt b/Project 3/build.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..e703e9051c964d2e35d6f297a81b241437860648
--- /dev/null
+++ b/Project 3/build.sbt	
@@ -0,0 +1,236 @@
+// Project template
+
+// Supported operating systems: Windows, Mac, Linux
+// Supported JDKs: 8, 10+
+
+// Project name
+name := "template-javafx"
+
+// organization name
+organization := "fi.utu.tech"
+
+version := "1.0"
+
+// project description
+description := "JavaFX project template"
+
+// main class
+Compile/mainClass := Some("fi.utu.tech.gui.javafx.Main")
+
+// force the java version by typing it here (remove the comment)
+val force_javaVersion = None // Some(13)
+
+// force the javafx version by typing it here (remove the comment)
+val force_javaFxVersion = None // Some(13)
+
+val useJavaFX = true
+
+val useScalaOrScalaFX = true
+
+// END_OF_SIMPLE_CONFIGURATION
+// you can copy the rest for each new project
+// --- --- ---
+
+def fail(msg: String) = {
+  println("Error :-/")
+  println
+  println(msg)
+  System.exit(1)
+  null
+}
+
+val detectedJDK = System.getProperty("java.version").replace("-ea","").split('.').dropWhile(_.toInt<8).head.toInt
+
+val javaVersionNum = force_javaVersion.getOrElse(detectedJDK)
+
+val javaVersionString = javaVersionNum match {
+  case 7 => "1.7"
+  case 8 => "1.8"
+  case x if x > 8 => x.toString
+}
+
+val lts = 11
+val dev = 13
+
+val supported = javaVersionNum match {
+  case x if x < 8              => fail("Your Java installation is obsolete. Please upgrade to Java " + lts + "LTS")
+  case 9                       => fail("Your Java installation is unsupported and has known issues. Please upgrade to Java " + lts + "LTS")
+  case x if x < lts            => println("Consider upgrading to Java " + lts + " LTS"); true
+  case x if x > lts && x < dev => println("Consider upgrading to Java " + dev); true
+  case x if x > dev            => println("Unsupported early access version. Consider switching back to Java " + dev); true
+  case _                       => true
+}
+
+javacOptions ++= Seq("-source", javaVersionString, "-target", javaVersionString, "-encoding", "utf8", "-Xlint:unchecked", "-Xlint:deprecation")
+javacOptions in doc := Seq("-source", javaVersionString) 
+
+enablePlugins(JShellPlugin)
+
+compileOrder := CompileOrder.JavaThenScala
+
+// Enables publishing to maven repo
+publishMavenStyle := true
+
+// Do not append Scala versions to the generated artifacts
+crossPaths := false
+
+// This forbids including Scala related libraries into the dependency
+autoScalaLibrary := false
+
+assemblyMergeStrategy in assembly := {
+  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
+  case _ => MergeStrategy.first
+}
+
+// contains libraries provided by utu/ft dep
+resolvers += "ftdev" at "https://ftdev.utu.fi/maven2"
+
+fork in Global := true
+
+val javaVersion = taskKey[Unit]("Prints the Java version.")
+
+javaVersion := { println("SBT uses Java SDK located at "+System.getProperty("java.home")) }
+
+publishTo := Some(Resolver.file("file", new File("/tmp/repository")))
+
+val oomkit = "fi.utu.tech" % "oomkit" % "1.15"
+
+libraryDependencies ++= Seq()
+
+////
+//// JQWIK / JUNIT configuration
+////
+
+resolvers in ThisBuild += Resolver.jcenterRepo
+
+val junit_version = "5.5.2"
+
+// library dependencies. (orginization name) % (project name) % (version)
+libraryDependencies ++= Seq(
+  "net.aichler"        % "jupiter-interface"              % JupiterKeys.jupiterVersion.value % Test,
+  "org.junit.platform" % "junit-platform-commons"         % ("1"+junit_version.tail) % Test,
+  "org.junit.platform" % "junit-platform-runner"          % ("1"+junit_version.tail) % Test,
+  "org.junit.jupiter"  % "junit-jupiter-engine"           % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-api"              % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-migrationsupport" % junit_version % Test,
+  "org.junit.jupiter"  % "junit-jupiter-params"           % junit_version % Test,
+  "net.jqwik"          % "jqwik"                          % "1.2.0" % Test,
+  "org.scalatest"      %% "scalatest"                     % "3.0.8" % Test,
+)
+
+testOptions += Tests.Argument(TestFrameworks.JUnit, "-q", "-c")
+
+////
+//// JAVAFX configuration
+////
+
+val javafx_versions = if (!useJavaFX) (0,"-","-") else (force_javaFxVersion getOrElse javaVersionNum) match {
+  case 7 => (7, "7", "8.0.181-R13")
+  case 8 => (8, "8", "8.0.181-R13")
+  case 10 => (11, "11.0.2", "11-R16")
+  case x if x>10 => (13, "13", "12.0.2-R18")
+  case _ => fail("Unsupported Java version for JavaFX")
+}
+
+// JAVA_HOME location
+val javaHomeDir = {
+  val path = try {
+    if (scala.sys.env("JAVA_HOME").trim.isEmpty) throw new Exception("Empty JAVA_HOME") else scala.sys.env("JAVA_HOME")
+  } catch {
+    case _: Throwable => System.getProperty("java.home") // not set -> ask from current JVM
+  }
+
+  val f = file(path)
+  if (!f.exists()) fail("The environment variable JAVA_HOME points to a non-existent directory!\nSolution: Edit your system settings (Windows control panel / *nix .bashrc) and fix the JAVA_HOME location.")
+  f
+}
+
+val osName: SettingKey[String] = SettingKey[String]("osName")
+
+osName := (System.getProperty("os.name") match {
+  case n if n.startsWith("Linux")   => "linux"
+  case n if n.startsWith("Mac")     => "mac"
+  case n if n.startsWith("Windows") => "win"
+  case _ => throw new Exception("Unknown platform!")
+})
+
+def legacyJavaFX() = {
+  val searchDirs = Seq(
+    "/jre/lib/jfxrt.jar",     // OpenJDK 7
+    "/jre/lib/ext/jfxrt.jar", // OpenJDK 8
+    "/lib/ext/jfxrt.jar"      // Windows & Oracle Java 8
+  )
+
+  if (detectedJDK > 8) fail(s"Trying to use legacy non-modular JavaFX with a modern JDK [$detectedJDK].\nSolution: Check the line 'val force_javaFxVersion =' in build.sbt.")
+
+  val javaFxJAR = searchDirs.map{ searchDir => file(javaHomeDir + searchDir) }.find{ _.exists() }
+
+  javaFxJAR.getOrElse {
+    fail(s"Java FX runtime not installed in [${javaHomeDir.toString}]!\nSolution: Install JavaFX or consider upgrading your JDK so that JavaFX can be installed automatically.")
+  }
+}
+
+val jfx_sdk_version = javafx_versions._2
+val jfx_scalafx_version = javafx_versions._3
+
+val javaFxPath = Def.taskKey[File]("OpenJFX fetcher")
+javaFxPath := {
+  val javaFxHome =
+    try {
+      val envHome = file(scala.sys.env("JAVAFX_HOME"))
+      if (envHome.toString.trim.isEmpty) throw new Exception("Empty JAVAFX_HOME")
+      println("Using OpenJFX from " + envHome)
+      envHome
+    }
+    catch { case _: Throwable =>
+        println("Using local OpenJFX")
+        baseDirectory.value / "openjfx"
+    }
+
+  if (!javaFxHome.exists()) java.nio.file.Files.createDirectory(javaFxHome.toPath)
+
+  val jfx_os = osName.value match {
+    case "linux" => "linux"
+    case "mac"   => "osx"
+    case "win"   => "windows"
+  }
+
+  val sdkURL = "http://download2.gluonhq.com/openjfx/" + jfx_sdk_version + "/openjfx-" + jfx_sdk_version + "_" + jfx_os + "-x64_bin-sdk.zip"
+
+  try {
+    val testDir = javaFxHome / "all.ok"
+    if (!testDir.exists()) {
+      println("Fetching OpenJFX from "+sdkURL+"..")
+      IO.unzipURL(new URL(sdkURL), javaFxHome)
+      java.nio.file.Files.createDirectory(testDir.toPath)
+      println("Fetching OpenJFX done.")
+    } else {
+      println("Using OpenJFX from "+javaFxHome)
+    }
+
+    javaFxHome
+  }
+  catch {
+    case t: Throwable => fail("Could not load OpenJFX! Reason:" + t.getMessage)
+  }
+}
+
+val jfxModules = Seq("base","controls","fxml","graphics","media","swing","web")
+
+
+if (!useJavaFX) Seq() else javafx_versions._1 match {
+  case 7 =>
+    // TODO libraryDependencies
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case 8 =>
+    (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" %% "scalafx" % jfx_scalafx_version) else Seq()) ++
+    Seq(unmanagedJars in Compile += Attributed.blank(legacyJavaFX()))
+  case _ =>
+    Seq(
+      javaOptions in run ++= Seq(
+        "--module-path", (javaFxPath.value / ("javafx-sdk-" + jfx_sdk_version) / "lib").toString,
+        "--add-modules=" + jfxModules.map("javafx."+_).mkString(","))
+    ) ++
+      (if (useScalaOrScalaFX) Seq(libraryDependencies += "org.scalafx" % "scalafx_2.13" % jfx_scalafx_version) else Seq()) ++
+      jfxModules.map(module => libraryDependencies += "org.openjfx" % ("javafx-"+module) % jfx_sdk_version classifier osName.value)
+}
diff --git a/Project 3/pom.xml b/Project 3/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d5e648244ca21b15feef4d86023c7ce15e3b51c6
--- /dev/null
+++ b/Project 3/pom.xml	
@@ -0,0 +1,452 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!--
+     
+      Welcome!
+      This pom.xml is *THE* file that defines your Maven style Java project.
+      
+      Eclipse, IDEA and other development environments with Maven support
+      or plugins can *import* this project by reading this file.
+      
+      It usually contains tons of barely readable configuration. Luckily
+      this basic pom.xml is somewhat readable. All necessary configuration
+      for customizing your project if located here on top before the
+      'END OF SIMPLE CONFIGURATION' line below.
+      
+      Some basics:
+      
+        - Maven is a build system for Java/JVM
+        - the Maven projects define an artifact that has a three part id:
+          groupId - artifactId - version
+          
+        - For example, this project is called:
+          fi.utu.tech - oomkit - 1.15
+          
+        - The group id is usually a web domain in reverse order.
+          
+        - You can use these id parts to search for projects at
+          https://search.maven.org/
+          
+        - If your version is not final and you have plans to modify the
+          source code at some point, please use a version id that ends with
+          -SNAPSHOT, e.g 1.0-SNAPSHOT. Maven may cache the project jar
+          and refuse to overwrite old cached versions with new ones unless
+          you remember this convention.
+          
+        - This file supports
+          compiling the project with      'mvn compile' (see target/)
+          cleaning the class files with   'mvn clean'
+          executing the main routine with 'mvn exec:java'
+          executing the unit tests with   'mvn test'
+          packaging the application with  'mvn package' (see target/)
+          
+        - the Maven project structure is as follows:
+          https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html
+          
+    -->
+    
+    <!-- ==== START OF SIMPLE CONFIGURATION ==== -->
+    
+    <!-- the three parts of the artifact name -->
+    <groupId>fi.utu.tech</groupId>
+    <artifactId>template-javafx</artifactId>
+    <version>1.0</version>
+    
+    <!-- additional information about the project -->
+    <name>JavaFX project template</name>
+    <url>https://gitlab.utu.fi/tech/education/gui/template-javafx</url>
+    
+    <packaging>jar</packaging>
+
+    <!-- HINT: More configuration here! -->
+    <properties>
+        <!-- Configures this project to use 'fi.utu.tech.AppMain' as its main class -->
+        <project.mainclass>fi.utu.tech.gui.javafx.Main</project.mainclass>
+        
+        
+        <!-- Don't touch these unless you know what you're doing!
+        
+             For example, the source encoding should always be utf-8.
+             You're probably doing something stupid if you think it
+             should be a 8-bit code page in 2019. -->
+        <jdk.version>11</jdk.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <jqwik.version>1.2.0</jqwik.version>
+        <junit.version>5.5.2</junit.version>
+        <junitplatform.version>1.5.2</junitplatform.version>
+        <javafx.version>13.0.2</javafx.version>
+    </properties>
+
+    <!-- ==== END OF SIMPLE CONFIGURATION ==== -->
+    
+    <repositories>
+        <repository>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <id>central</id>
+            <name>Central Repository</name>
+            <url>https://repo.maven.apache.org/maven2</url>
+        </repository>
+        <repository>
+            <id>jcenter</id>
+            <name>jcenter</name>
+            <url>https://jcenter.bintray.com/</url>
+        </repository>
+        <!-- UTU repository -->
+        <repository>
+            <id>ftdev</id>
+            <name>ftdev</name>
+            <url>https://ftdev.utu.fi/maven2</url>
+        </repository>
+    </repositories>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-commons</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.jqwik</groupId>
+            <artifactId>jqwik</artifactId>
+            <version>${jqwik.version}</version>
+            <scope>test</scope>
+        </dependency><!--
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-migrationsupport</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-runner</artifactId>
+            <version>${junitplatform.version}</version>
+            <scope>test</scope>
+        </dependency> -->
+        
+        <!-- JavaFX (remove if not needed to speed up dep downloads)-->
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-base</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-controls</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-fxml</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-graphics</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-media</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-swing</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjfx</groupId>
+            <artifactId>javafx-web</artifactId>
+            <version>${javafx.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Make a 'fat' jar, that is, jar that contains all its dependencies and runs as is.
+                 See: https://stackoverflow.com/a/57691362 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>module-info.class</exclude>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>${project.mainclass}</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Run this app with exec:java -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>1.6.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>java</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                    <arguments>
+                        <argument>arg1</argument>
+                        <argument>arg2</argument>
+                    </arguments>
+                </configuration>
+            </plugin>
+
+            <!-- Make the packaged jar executable -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.2</version>
+                <configuration>
+                    <!-- DO NOT include log4j.properties file in your Jar -->
+                    <excludes>
+                        <exclude>**/log4j.properties</exclude>
+                    </excludes>
+                    <archive>
+                        <manifest>
+                            <!-- Jar file entry point -->
+                            <mainClass>${project.mainclass}</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <!-- download source code in Eclipse, best practice -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.10</version>
+                <configuration>
+                    <downloadSources>true</downloadSources>
+                    <downloadJavadocs>false</downloadJavadocs>
+                </configuration>
+            </plugin>
+
+            <!-- JDK source/target versions -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>${jdk.version}</source>
+                    <target>${jdk.version}</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.1.1</version>
+                <executions>
+                    <execution>
+                    <id>attach-javadocs</id>
+                    <goals>
+                        <goal>jar</goal>
+                    </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <tags>
+                        <tag>
+                            <name>toDo</name>
+                            <placement>a</placement>
+                            <head>To&nbsp;do:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariant</name>
+                            <placement>t</placement>
+                            <head>Class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantProtected</name>
+                            <placement>t</placement>
+                            <head>Protected&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>classInvariantPrivate</name>
+                            <placement>t</placement>
+                            <head>Private&nbsp;class&nbsp;invariant:</head>
+                        </tag>
+                        <tag>
+                            <name>abstractionFunction</name>
+                            <placement>t</placement>
+                            <head>Abstraction&nbsp;function:</head>
+                        </tag>
+                        <tag>
+                            <name>pre</name>
+                            <placement>cm</placement>
+                            <head>Precondition:</head>
+                        </tag>
+                        <tag>
+                            <name>post</name>
+                            <placement>cm</placement>
+                            <head>Postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postProtected</name>
+                            <placement>cm</placement>
+                            <head>Protected&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>postPrivate</name>
+                            <placement>cm</placement>
+                            <head>Private&nbsp;postcondition:</head>
+                        </tag>
+                        <tag>
+                            <name>time</name>
+                            <placement>cmf</placement>
+                            <head>Time&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>space</name>
+                            <placement>cmf</placement>
+                            <head>Space&nbsp;complexity:</head>
+                        </tag>
+                        <tag>
+                            <name>correspondence</name>
+                            <placement>a</placement>
+                            <head>Correspondence:</head>
+                        </tag>
+                        <tag>
+                            <name>download</name>
+                            <placement>a</placement>
+                            <head>Download:</head>
+                        </tag>
+                    </tags>
+                    <show>protected</show>
+                    <failOnError>false</failOnError>
+                    <sourceFileExcludes>
+                        <sourceFileExclude>**/module-info.java</sourceFileExclude>
+                    </sourceFileExcludes>
+                </configuration>
+            </plugin>
+
+            <!-- JUnit & JQwik test integration -->
+
+            <!-- junit-platform-maven-plugin: supports modular tests
+                 maven-surefire-plugin: non-modular tests
+
+                 Modular testing works via command line mvn, but is
+                 still broken in Eclipse due to this
+                 https://bugs.eclipse.org/bugs/show_bug.cgi?id=520667
+              -->
+            <plugin>
+                <groupId>de.sormuras.junit</groupId>
+                <artifactId>junit-platform-maven-plugin</artifactId>
+                <version>1.0.0-M5</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <executor>JAVA</executor>
+                </configuration>
+            </plugin>
+
+            <!--
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>3.0.0-M3</version>
+            </plugin> -->
+
+            <!-- javafx:jlink:
+                 https://github.com/openjfx/javafx-maven-plugin
+                 Use 'mvn package' instead if you don't need jlink.
+
+            <plugin>
+                <groupId>org.openjfx</groupId>
+                <artifactId>javafx-maven-plugin</artifactId>
+                <version>0.0.3</version>
+                <configuration>
+                    <mainClass>${project.mainclass}</mainClass>
+                </configuration>
+            </plugin> -->
+
+        </plugins>
+        <extensions>
+            <!-- Enables the use of SSH for deployments -->
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>3.3.3</version>
+            </extension>
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh</artifactId>
+                <version>3.3.3</version>
+            </extension>
+        </extensions>
+    </build>
+    <distributionManagement>
+        <repository>
+            <id>ftdev</id>
+            <name>UTU tech ftdev repository</name>
+            <url>scp://localhost:2222/var/www/maven2</url>
+        </repository>
+    </distributionManagement>
+</project>
diff --git a/Project 3/project/build.properties b/Project 3/project/build.properties
new file mode 100644
index 0000000000000000000000000000000000000000..6adcdc753fdca1b4b851d8b75426dee24044bf6d
--- /dev/null
+++ b/Project 3/project/build.properties	
@@ -0,0 +1 @@
+sbt.version=1.3.3
diff --git a/Project 3/project/plugins.sbt b/Project 3/project/plugins.sbt
new file mode 100644
index 0000000000000000000000000000000000000000..37ca261463b6a1800a62a0d5a5279fbb22915638
--- /dev/null
+++ b/Project 3/project/plugins.sbt	
@@ -0,0 +1,7 @@
+resolvers += Resolver.jcenterRepo
+
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
+addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.3")
+addSbtPlugin("com.github.xuwei-k" % "sbt-jshell" % "0.1.2")
+//addSbtPlugin("org.xerial.sbt" % "sbt-sql-sqlite" % "0.8")
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.4.0")
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d0180639373080225fee7cfa3de4b85d5f05f3
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Dialogs.java	
@@ -0,0 +1,13 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.scene.control.Alert;
+
+public class Dialogs {
+    public static void warning(String title, String header, String content) {
+        Alert alert = new Alert(Alert.AlertType.WARNING);
+        alert.setTitle(title);
+        alert.setHeaderText(header);
+        alert.setContentText(content);
+        alert.showAndWait();
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae8c3e30808aa3075a967653268563c6aebc9d1e
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/Main.java	
@@ -0,0 +1,31 @@
+package fi.utu.tech.gui.javafx;
+
+import fi.utu.tech.gui.javafx.gimmick.MainApp2;
+import fi.utu.tech.gui.javafx.zipper.MainApp1;
+
+public class Main {
+    /**
+     * The main() method is ignored in correctly deployed JavaFX application.
+     * main() serves only as fallback in case the application can not be
+     * launched through deployment artifacts, e.g., in IDEs with limited FX
+     * support. NetBeans ignores main().
+     *
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        System.out.println("Launching..");
+        if (args.length == 1 && args[0].equals("--test")) return;
+
+        int exercise = 2;
+        if (args.length == 1) exercise = Integer.parseInt(args[0]);
+        switch (exercise) {
+            case 1:
+                MainApp1.launch(MainApp1.class, args);
+                break;
+            default:
+                MainApp2.launch(MainApp2.class, args);
+                break;
+        }
+    }
+
+}
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java
new file mode 100644
index 0000000000000000000000000000000000000000..519b92e02723bce49a0ca7133868c3ca4a792215
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/MainApp.java	
@@ -0,0 +1,40 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+
+public class MainApp extends Application {
+
+    // https://openjfx.io/javadoc/11/javafx.graphics/javafx/application/Application.html
+
+    // The JavaFX runtime does the following, in order, whenever an application is launched:
+
+    // 1. Starts the JavaFX runtime, if not already started (see Platform.startup(Runnable) for more information)
+    // 2. Constructs an instance of the specified Application class
+    // 3. Calls the Application's init() method
+    // 4. Calls the Application's start(javafx.stage.Stage) method
+    // 5. Waits for the application to finish, which happens when either of the following occur:
+    //   a) the application calls Platform.exit()
+    //   b) the last window has been closed and the implicitExit attribute on Platform is true
+    // 6. Calls the Application's stop() method
+
+    @Override
+    public void init() {
+    }
+
+    @Override
+    public void stop() {
+    }
+
+    @Override
+    public void start(Stage stage) {
+        Scene scene = new Scene(new BorderPane(new Rectangle(300, 300)));
+
+        stage.setTitle("JavaFX template");
+        stage.setScene(scene);
+        stage.show();
+    }
+}
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5c2d1dca12f8fbaa9afd81f5ebdfa5e1d0c1a09
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/ResourceLoader.java	
@@ -0,0 +1,42 @@
+package fi.utu.tech.gui.javafx;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+
+public class ResourceLoader<N extends Parent, C> {
+    public final N root;
+    public final C controller;
+
+    public ResourceLoader(String contentPath) {
+        N root_ = null;
+        C controller_ = null;
+        try {
+            // determines where to look for the resources (the root path)
+            Class resourceRootClass = getClass();
+
+            FXMLLoader loader = new FXMLLoader(resourceRootClass.getResource(contentPath));
+            root_ = loader.load();
+            controller_ = loader.getController();
+            System.out.println("DEBUG: " + contentPath + " loaded.");
+        } catch (Exception e) {
+            Dialogs.warning(
+                    "Internal error",
+                    "Could not open FXML file: " + contentPath,
+                    "Reason: " + e.getMessage()
+            );
+            System.exit(1);
+        }
+        root = root_;
+        controller = controller_;
+    }
+
+    // finds images both outside and inside jars
+    public static String image(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+
+    // finds stylesheets both outside and inside jars
+    public static String stylesheet(String fileName) {
+        return ResourceLoader.class.getResource(fileName).toExternalForm();
+    }
+}
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java
new file mode 100644
index 0000000000000000000000000000000000000000..871c432972cd3e4f759610d540eedcfbddd93505
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/GimmickController.java	
@@ -0,0 +1,27 @@
+package fi.utu.tech.gui.javafx.gimmick;
+
+import javafx.application.Platform;
+import javafx.fxml.FXML;
+import javafx.scene.layout.TilePane;
+
+public class GimmickController {
+    @FXML
+    private TilePane tilePane;
+
+    public void initialize() {
+        tilePane.getChildren().clear();
+        
+            new Thread(() -> {
+            	
+            	for (int i = 0; i < 20; i++) {
+                    final int j = i;
+                    try { Thread.sleep(500); }
+                    catch(Exception e) {
+                    }
+                    View c = j % 2 == 0 ? new View1(150, 150) : new View2(150, 150);
+                    Platform.runLater(() -> tilePane.getChildren().add(c));
+                    new Thread(c).start();
+            	}}).start();
+        
+    }
+}
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java
new file mode 100644
index 0000000000000000000000000000000000000000..353ad8da1ca64146f8f0bfdc1e5e25c92dbe6ca6
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/MainApp2.java	
@@ -0,0 +1,20 @@
+package fi.utu.tech.gui.javafx.gimmick;
+
+import fi.utu.tech.gui.javafx.MainApp;
+import fi.utu.tech.gui.javafx.ResourceLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class MainApp2 extends MainApp {
+    @Override
+    public void start(Stage stage) {
+        ResourceLoader<Parent, GimmickController> loader = new ResourceLoader<>("gimmick.fxml");
+
+        Scene scene = new Scene(loader.root);
+
+        stage.setTitle("The Gimmick Show");
+        stage.setScene(scene);
+        stage.show();
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..666104e6edb0012fc800c3818671c35da247f8a6
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View.java	
@@ -0,0 +1,17 @@
+package fi.utu.tech.gui.javafx.gimmick;
+
+import javafx.scene.canvas.Canvas;
+
+abstract class View extends Canvas implements Runnable {
+    protected boolean active = true;
+
+    public View(double width, double height) {
+        super(width, height);
+
+        setOnMouseClicked(e -> {
+            active = false;
+            setVisible(false);
+            setManaged(false);
+        });
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fb6b9e47592231bb38251ec0153fc4cf228d8df
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View1.java	
@@ -0,0 +1,63 @@
+package fi.utu.tech.gui.javafx.gimmick;
+
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.WritableImage;
+
+import java.util.Random;
+import java.util.Timer;
+import java.util.TimerTask;
+
+
+class View1 extends View {
+    protected final int width, height;
+    private final WritableImage buffer;
+    private final int[] data;
+
+    View1(int width, int height) {
+        super(width, height);
+        this.width = width;
+        this.height = height;
+        buffer = new WritableImage(width, height);
+        data = new int[width * height];
+    }
+
+    public void run() {
+        launchTimer();
+        // uncomment to test if it fails (java.lang.InternalError).
+        // it should work if you managed to program this correctly!
+        //launchTimer();
+    }
+
+    void render() {
+        // uncomment to test if it fails
+        // it should work if you managed to program this correctly!
+        //if (!Platform.isFxApplicationThread()) throw new Error("Wrong thread!");
+
+        for (int pixelIdx = 0; pixelIdx < data.length; pixelIdx++) {
+            final int idx = pixelIdx;
+            new Thread(() -> {
+                data[idx] = new Random().nextInt(200) | ((new Random().nextInt(16) * 16) << 24);
+            }).run();
+        }
+
+        buffer.getPixelWriter().setPixels(
+                0, 0,
+                width, height,
+                PixelFormat.getIntArgbPreInstance(), data, 0, width);
+
+        getGraphicsContext2D().clearRect(0.0, 0.0, width, height);
+        getGraphicsContext2D().drawImage(buffer, 0.0, 0.0);
+    }
+
+    void launchTimer() {
+        final Thread t = new Thread(() -> {
+            render();
+        });
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                t.run();
+            }
+        }, 0l, 1000l);
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java
new file mode 100644
index 0000000000000000000000000000000000000000..be806677ae20f6942a7971a76799869d29b080d7
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/gimmick/View2.java	
@@ -0,0 +1,36 @@
+package fi.utu.tech.gui.javafx.gimmick;
+
+import javafx.scene.paint.Color;
+
+import java.util.Random;
+
+class View2 extends View {
+    protected final int width, height;
+
+    View2(int width, int height) {
+        super(width, height);
+        this.width = width;
+        this.height = height;
+    }
+
+    public void run() {
+        while (true) {
+            render();
+            try {
+                Thread.sleep(200 + new Random().nextInt(10) * 100);
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    void render() {
+        // uncomment to test if it fails
+        // it should work if you managed to program this correctly!
+        //if (!Platform.isFxApplicationThread()) throw new Error("Wrong thread!");
+
+        getGraphicsContext2D().setFill(new Color(0.5,0,0,0.5));
+        getGraphicsContext2D().fillRect(0.0, 0.0, width, height);
+        getGraphicsContext2D().setFill(new Color(0,0,1,0.9));
+        getGraphicsContext2D().fillOval(new Random().nextDouble() * width, new Random().nextDouble() * height, 20, 20);
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java
new file mode 100644
index 0000000000000000000000000000000000000000..9f4449e54d23008d8da87a4062789640c25f6384
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/MainApp1.java	
@@ -0,0 +1,27 @@
+package fi.utu.tech.gui.javafx.zipper;
+
+import fi.utu.tech.gui.javafx.MainApp;
+import fi.utu.tech.gui.javafx.ResourceLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.stage.Stage;
+
+public class MainApp1 extends MainApp {
+    @Override
+    public void start(Stage stage) {
+        ResourceLoader<Parent, ZipperController> loader = new ResourceLoader<>("zipper.fxml");
+
+        
+        
+        loader.controller.setLabel("Welcome!");
+        loader.controller.setStage(stage);
+
+        Scene scene = new Scene(loader.root);
+
+        stage.setTitle("Zip Master 2020");
+        stage.setScene(scene);
+        stage.show();
+        
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java
new file mode 100644
index 0000000000000000000000000000000000000000..11ac8a08ab764a7f90024b2e82eec2db03011cb7
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/Zipper.java	
@@ -0,0 +1,134 @@
+package fi.utu.tech.gui.javafx.zipper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.concurrent.Task;
+import javafx.scene.control.Label;
+
+public class Zipper extends Task<Void> {
+	
+	String sourceDir;
+	String destinationFile;
+	StringProperty currentFile = new SimpleStringProperty();
+	StringProperty zipItButtonText = new SimpleStringProperty();
+	FileOutputStream fos;
+	ZipOutputStream zipOut;
+	BooleanProperty currentlyZipping = new SimpleBooleanProperty();
+	//Boolean currentlyZipping;
+	
+	public Zipper (String sourceDir, String destinationFile) {
+		this.sourceDir = sourceDir;
+		this.destinationFile = destinationFile;
+		try {
+		this.fos = new FileOutputStream(destinationFile);
+	    this.zipOut = new ZipOutputStream(fos);
+	    }
+		catch(Exception e) {}
+		
+	}
+	
+	//runlater
+	public Void call() {
+		
+		//if(currentlyZipping) {
+		//	cancelZip();
+		//}
+		//currentlyZipping.set(true);
+		//currentlyZipping = true;
+		//System.out.println("zipper "+ currentlyZipping.get());
+		
+		try{
+			
+		//Platform.runLater(() -> {zipItButtonText.set("Cancel");
+	    //});	
+		
+		zip();
+		
+		//currentlyZipping.set(false);
+		//currentlyZipping = false;
+		
+		}catch(Exception e){}
+		return null;
+	}
+	
+	public void cancelZip() {
+		try {
+			
+			fos.close();
+			
+		} catch(Exception e) {}
+		
+		Platform.runLater(() -> {zipItButtonText.set("Zip It!");
+	    });	
+		
+		Platform.runLater(() -> {currentFile.set("Zippaus peruutettu");
+        });
+		
+	}
+	
+    public void zip() throws IOException {
+         {
+
+        	
+            File fileToZip = new File(sourceDir);
+            
+            //currentFileLabel.setText(fileToZip.getName());
+            
+            
+            
+            //System.out.println(fileToZip.getName());
+            
+            zipFile(fileToZip, fileToZip.getName(), zipOut);
+            
+        }
+    }
+
+    private void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
+    	Platform.runLater(() -> {currentFile.set("Zipataan tiedostoa: " + fileToZip.getName());
+        });
+        if (fileToZip.isHidden()) {
+            return;
+        }
+        if (fileToZip.isDirectory()) {
+            if (fileName.endsWith("/")) {
+                zipOut.putNextEntry(new ZipEntry(fileName));
+                zipOut.closeEntry();
+            } else {
+                zipOut.putNextEntry(new ZipEntry(fileName + "/"));
+                zipOut.closeEntry();
+            }
+            File[] children = fileToZip.listFiles();
+            for (File childFile : children) {
+                zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
+            }
+            
+            Platform.runLater(() -> {currentFile.set("Valamis");
+            });
+            
+            return;
+        }
+        try (FileInputStream fis = new FileInputStream(fileToZip)) {
+            ZipEntry zipEntry = new ZipEntry(fileName);
+            zipOut.putNextEntry(zipEntry);
+            byte[] bytes = new byte[1024];
+            int length;
+            while ((length = fis.read(bytes)) >= 0) {
+                zipOut.write(bytes, 0, length);
+            }
+            
+            Platform.runLater(() -> {currentFile.set("Valamis");
+            });
+            
+        }
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java
new file mode 100644
index 0000000000000000000000000000000000000000..8293271d15314910ff2526e37ec66d0397bc6e3e
--- /dev/null
+++ b/Project 3/src/main/java/fi/utu/tech/gui/javafx/zipper/ZipperController.java	
@@ -0,0 +1,112 @@
+package fi.utu.tech.gui.javafx.zipper;
+
+import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import javafx.stage.DirectoryChooser;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+import java.io.File;
+import java.io.IOException;
+
+public class ZipperController  {
+    private Stage stage;
+
+    public void setStage(Stage stage) {
+        this.stage = stage;
+    }
+
+    @FXML
+    private TextField sourceField;
+
+    @FXML
+    private TextField destField;
+
+    @FXML
+    private Button zipItButton;
+
+    public Label statusLabel;
+    
+    public Zipper zipper;
+    
+    public BooleanProperty zipping = new SimpleBooleanProperty();
+
+    public void setLabel(String text) {
+        if (!statusLabel.textProperty().isBound())
+            statusLabel.setText(text);
+    }
+
+    @FXML
+    void chooseDest(ActionEvent event) {
+        FileChooser chooser = new FileChooser();
+        chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Zip files (.zip)", "*.zip"));
+        chooser.setTitle("Destination zip");
+        File selected = chooser.showSaveDialog(stage);
+        if (selected == null) return;
+        destField.setText(selected.getAbsolutePath());
+        if (!destField.getText().endsWith(".zip"))
+            destField.setText(destField.getText() + ".zip");
+        setButtonState(null);
+    }
+
+    @FXML
+    void chooseSource(ActionEvent event) {
+        DirectoryChooser chooser = new DirectoryChooser();
+        chooser.setTitle("Source folder");
+        File selected = chooser.showDialog(stage);
+        if (selected == null) return;
+        sourceField.setText(selected.getAbsolutePath());
+    }
+
+    @FXML
+    //yksisuuntanen bind statuslabelille, zipper status
+    void zipIt(ActionEvent event) {
+        try {
+        	
+        	
+        	
+        	zipper = new Zipper(sourceField.getText(), destField.getText());
+        	
+            //zipping.bind(zipper.currentlyZipping);
+        	//zipper.currentlyZipping.bind(zipping);
+            
+            //zipping.set(true);
+        	//System.out.println(zipping.get());
+        	
+        	if(zipper.currentlyZipping.get()) {
+        		zipper.cancelZip();
+        		
+        	}else {
+        	
+            new Thread(zipper).start();
+            
+            //setLabel(zipper.currentFile);
+            
+            statusLabel.textProperty().bind(zipper.currentFile);
+            
+            zipItButton.textProperty().bind(zipper.zipItButtonText);
+            
+            
+            
+        	}
+            
+        } catch (Exception e) {
+            setLabel(e.getMessage());
+        }
+    }
+
+    <T> void setButtonState(T t) {
+        zipItButton.setDisable(!destField.getText().endsWith(".zip"));
+    }
+
+    public void initialize() {
+        setButtonState(null);
+        destField.setOnKeyTyped(this::setButtonState);
+    }
+}
\ No newline at end of file
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png
new file mode 100644
index 0000000000000000000000000000000000000000..e94c775d601e2ae42159af0b4f86b7394ff3c47b
Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/alien.png differ
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..f42f9b6f9d3cadc20095990b5feae828446b10f3
--- /dev/null
+++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/gimmick.fxml	
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ScrollPane?>
+<?import javafx.scene.image.Image?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.StackPane?>
+<?import javafx.scene.layout.TilePane?>
+<?import javafx.scene.shape.Rectangle?>
+<?import javafx.scene.text.Font?>
+
+<StackPane maxHeight="580.0" maxWidth="770.0" stylesheets="@styles.css" xmlns="http://javafx.com/javafx/11.0.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.gimmick.GimmickController">
+   <children>
+      <ImageView fitHeight="580.0" fitWidth="770.0" pickOnBounds="true" preserveRatio="true">
+         <image>
+            <Image url="@alien.png" />
+         </image>
+      </ImageView>
+      <BorderPane>
+         <center>
+            <ScrollPane fitToHeight="true" fitToWidth="true" vbarPolicy="ALWAYS" BorderPane.alignment="CENTER">
+               <content>
+                  <TilePane fx:id="tilePane" alignment="CENTER" hgap="16.0" opacity="0.85" prefColumns="3" vgap="16.0">
+                     <padding>
+                        <Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
+                     </padding>
+                     <children>
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                        <Rectangle arcHeight="5.0" arcWidth="5.0" fill="DODGERBLUE" height="200.0" stroke="BLACK" strokeType="INSIDE" width="200.0" />
+                     </children>
+                  </TilePane>
+               </content>
+            </ScrollPane>
+         </center>
+         <top>
+            <Label text="The Gimmick Show" BorderPane.alignment="CENTER">
+               <font>
+                  <Font size="32.0" />
+               </font>
+            </Label>
+         </top>
+      </BorderPane>
+   </children>
+</StackPane>
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb06d8873e15f2f9f690347c2298dea696cac12e
Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/hmm.png differ
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png
new file mode 100644
index 0000000000000000000000000000000000000000..643eff6d60113e5e16ab4426c1f3a47ac4878c01
Binary files /dev/null and b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/package.png differ
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..d1e250b8df11265e6345bd142497f6feee8630e8
--- /dev/null
+++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/styles.css	
@@ -0,0 +1,7 @@
+.scroll-pane {
+   -fx-background-color:transparent;
+}
+
+.scroll-pane > .viewport {
+   -fx-background-color: transparent;
+}
diff --git a/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..9a011d163b0ec3090860b42f5c8681417ac75f25
--- /dev/null
+++ b/Project 3/src/main/resources/fi/utu/tech/gui/javafx/zipper.fxml	
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.image.Image?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fi.utu.tech.gui.javafx.zipper.ZipperController">
+   <left>
+      <ImageView fitHeight="368.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER">
+         <image>
+            <Image url="@package.png" />
+         </image>
+         <BorderPane.margin>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </BorderPane.margin>
+      </ImageView>
+   </left>
+   <center>
+      <BorderPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
+         <padding>
+            <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+         </padding>
+         <center>
+            <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="16.0" BorderPane.alignment="CENTER">
+               <padding>
+                  <Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
+               </padding>
+               <children>
+                  <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="200.0">
+                     <children>
+                        <Label prefHeight="30.0" text="Source: " />
+                        <TextField fx:id="sourceField" prefHeight="30.0" HBox.hgrow="ALWAYS">
+                           <HBox.margin>
+                              <Insets left="8.0" right="8.0" />
+                           </HBox.margin>
+                        </TextField>
+                        <Button maxHeight="30.0" maxWidth="30.0" mnemonicParsing="false" onAction="#chooseSource" prefHeight="30.0" prefWidth="30.0" text="..." />
+                     </children>
+                  </HBox>
+                  <HBox alignment="CENTER_LEFT" prefHeight="30.0" prefWidth="200.0">
+                     <children>
+                        <Label prefHeight="30.0" text="Destination:" />
+                        <TextField fx:id="destField" prefHeight="30.0" HBox.hgrow="ALWAYS">
+                           <HBox.margin>
+                              <Insets left="8.0" right="8.0" />
+                           </HBox.margin>
+                        </TextField>
+                        <Button maxHeight="30.0" maxWidth="30.0" mnemonicParsing="false" onAction="#chooseDest" prefHeight="30.0" prefWidth="30.0" text="..." />
+                     </children>
+                  </HBox>
+                  <Button fx:id="zipItButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#zipIt" text="Zip it!" />
+                  <Label fx:id="statusLabel" text="Loading..." />
+               </children></VBox>
+         </center>
+         <top>
+            <Label text="Zip Master 2020" BorderPane.alignment="CENTER">
+               <font>
+                  <Font size="26.0" />
+               </font></Label>
+         </top>
+      </BorderPane>
+   </center>
+</BorderPane>