diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000000000000000000000000000000000000..31241a58a2d6f267db435b5a462f4466f2994fec
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
+		<attributes>
+			<attribute name="test" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path="target/generated-sources/annotations"/>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+		<attributes>
+			<attribute name="test" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+		<attributes>
+			<attribute name="maven.pomderived" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry combineaccessrules="false" kind="src" path="/SharingPluginPlattform">
+		<attributes>
+			<attribute name="module" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/.eslintignore b/.eslintignore
index c8d1f55775fb14da4fe1674ec1d7e7fc1d02f528..baaf044fdf2b2145ceb697978d5a53b3de9274dc 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,4 +1,7 @@
 node_modules/
+src/main/webapp/content/js/bootstrap4.5.2.min.js
+src/main/webapp/content/js/jquery3.5.1.min.js
+src/main/webapp/content/js/popper1.16.0.min.js
 src/main/docker/
 src/test/javascript/protractor.conf.js
 src/test/javascript/jest.conf.js
diff --git a/.eslintrc.json b/.eslintrc.json
index 43c1cf27258c05a548dbb17d85b84cc8ca1e3e12..1ac9ec6ad7b3886cebeac8ceaf2b4a772b8dfb0b 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -6,7 +6,7 @@
   },
   "rules": {
     "@typescript-eslint/tslint/config": [
-      "error",
+      "warn",
       {
         "lintFile": "./tslint.json"
       }
diff --git a/.externalToolBuilders/mvnw clean generate-sources.launch b/.externalToolBuilders/mvnw clean generate-sources.launch
new file mode 100644
index 0000000000000000000000000000000000000000..b62026656baf3b7c682922706ed85d0947c554e3
--- /dev/null
+++ b/.externalToolBuilders/mvnw clean generate-sources.launch	
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
+    <stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;resources&gt;&#13;&#10;&lt;item path=&quot;/gitsearch/target/generated-sources&quot; type=&quot;2&quot;/&gt;&#13;&#10;&lt;item path=&quot;/gitsearch/target/generated-test-sources&quot; type=&quot;2&quot;/&gt;&#13;&#10;&lt;/resources&gt;}"/>
+    <booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
+    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/gitsearch/mvnw.cmd}"/>
+    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
+    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="generate-sources"/>
+    <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
+    <stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/gitsearch}"/>
+</launchConfiguration>
diff --git a/.gitignore b/.gitignore
index 520491fa3c3a9ab8b0583422b7ffb0b78e0fb0ce..eb314f1436cf4862f5d1403630118baaf2a2470c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,7 @@
 ######################
 # Project Specific
 ######################
-/src/main/webapp/content/css/main.css
 /target/classes/static/**
-/src/test/javascript/coverage/
 
 ######################
 # Node
@@ -24,7 +22,6 @@ npm-debug.log.*
 # Eclipse
 ######################
 *.pydevproject
-.project
 .metadata
 tmp/
 tmp/**/*
@@ -33,17 +30,9 @@ tmp/**/*
 *.swp
 *~.nib
 local.properties
-.classpath
-.settings/
 .loadpath
 .factorypath
-/src/main/resources/rebel.xml
 
-# External tool builders
-.externalToolBuilders/**
-
-# Locally stored "Eclipse launch configurations"
-*.launch
 
 # CDT-specific
 .cproject
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 36a94417b77508d5fd8b492a3aa27c6af2be7278..88e49b70e2227204a34c2436ce2657492379acd1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -69,7 +69,7 @@ frontend-test:
 maven-package:
   stage: package
   script:
-    - ./mvnw -ntp verify -Pprod -DskipTests -Dmaven.repo.local=$MAVEN_USER_HOME
+    - ./mvnw -ntp verify -Pstaging -DskipTests -Dmaven.repo.local=$MAVEN_USER_HOME
   artifacts:
     paths:
       - target/*.jar
@@ -80,11 +80,13 @@ docker-push:
     stage: release
     only:
       - master
+      - development
+      - staging
     variables:
         REGISTRY_URL: docker.uibk.ac.at:443
         IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHA
     dependencies:
         - maven-package
     script:
-        - ./mvnw -ntp compile jib:build -Pprod -Djib.to.image=$IMAGE_TAG -Djib.to.auth.username=gitlab-ci-token  -Djib.to.auth.password=$CI_BUILD_TOKEN -Dmaven.repo.local=$MAVEN_USER_HOME
+        - ./mvnw -ntp compile jib:build -Pstaging -Djib.to.image=$IMAGE_TAG -Djib.to.auth.username=gitlab-ci-token  -Djib.to.auth.password=$CI_BUILD_TOKEN -Dmaven.repo.local=$MAVEN_USER_HOME
 
diff --git a/.jhipster/Statistics.json b/.jhipster/Statistics.json
new file mode 100644
index 0000000000000000000000000000000000000000..5a1f86735d3acc18d27516f9bdfa869d1b33d8d6
--- /dev/null
+++ b/.jhipster/Statistics.json
@@ -0,0 +1,29 @@
+{
+  "fluentMethods": true,
+  "clientRootFolder": "",
+  "relationships": [],
+  "fields": [
+    {
+      "fieldName": "views",
+      "fieldType": "Integer"
+    },
+    {
+      "fieldName": "downloads",
+      "fieldType": "Integer",
+      "fieldValidateRules": ["required"]
+    },
+    {
+      "fieldName": "exerciseID",
+      "fieldType": "Integer"
+    }
+  ],
+  "changelogDate": "20210319162139",
+  "dto": "mapstruct",
+  "searchEngine": "elasticsearch",
+  "service": "serviceImpl",
+  "entityTableName": "statistics",
+  "databaseType": "sql",
+  "readOnly": false,
+  "jpaMetamodelFiltering": false,
+  "pagination": "infinite-scroll"
+}
diff --git a/.project b/.project
new file mode 100644
index 0000000000000000000000000000000000000000..1b4a4eb108290395d77debd5e395d53dd3576679
--- /dev/null
+++ b/.project
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>gitsearch</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>LaunchConfigHandle</key>
+					<value>&lt;project&gt;/.externalToolBuilders/mvnw clean generate-sources.launch</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.springframework.ide.eclipse.boot.validation.springbootbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..35dba3ec363f099ccef5253d545e39a9f8768b6d
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/main/webapp/i18n/de/global.json=UTF-8
+encoding//src/main/webapp/i18n/de/search.json=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding//src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content1.json=UTF-8
+encoding//src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content2.json=UTF-8
+encoding//src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content3.json=UTF-8
+encoding/<project>=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..2bd4e77333c71a363a69f94617a46fdac942825d
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,16 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=11
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..eee4bb5e867a6e5e346c24bf93bd6b2df6e6a56d
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=dev
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/.settings/org.springframework.ide.eclipse.boot.prefs b/.settings/org.springframework.ide.eclipse.boot.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..86d14b311e6ef3a59c70540297f1430477abce9c
--- /dev/null
+++ b/.settings/org.springframework.ide.eclipse.boot.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+spring.boot.launch.profile.history=dev;dev,swagger;dev, swagger;prod,staging;prod,stating;staging,prod;
diff --git a/.settings/org.springframework.ide.eclipse.prefs b/.settings/org.springframework.ide.eclipse.prefs
new file mode 100644
index 0000000000000000000000000000000000000000..a12794d68f2d05b47d56cfe2497dbde7752dc695
--- /dev/null
+++ b/.settings/org.springframework.ide.eclipse.prefs
@@ -0,0 +1,2 @@
+boot.validation.initialized=true
+eclipse.preferences.version=1
diff --git a/.yo-rc.json b/.yo-rc.json
index 3fd51cfa3f4e4bebaf24f119a8ae70a6b88fcea4..cd0562ec2ab1ace0a9d95e39a030d2324aad8eff 100644
--- a/.yo-rc.json
+++ b/.yo-rc.json
@@ -38,6 +38,7 @@
     "enableTranslation": true,
     "nativeLanguage": "en",
     "languages": ["en", "de"],
-    "blueprints": []
+    "blueprints": [],
+    "lastLiquibaseTimestamp": 1616170899000
   }
 }
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000000000000000000000000000000000000..dffb84b02f9ba5a91d017dbdce39161a41c80301
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,67 @@
+pipeline {
+  agent any
+  environment { 
+        scannerHome = tool 'SonarQube Scanner'
+    }
+   options {
+       buildDiscarder(logRotator(numToKeepStr: '10', artifactNumToKeepStr: '10'))
+   }
+   tools {
+        // Install the Maven version configured as "M3" and add it to the path.
+        maven "/usr/share/maven"
+        jdk "java-11"
+        nodejs "Node15.11"
+    }
+   stages {
+    stage('Compile and Test') {
+      steps {
+        milestone 1
+                withSonarQubeEnv('SonarQube Production') {
+                    sh "mvn -Dmaven.test.failure.ignore=true  clean verify"
+                }
+      }
+    }
+    stage('Evaluate') {
+      steps {
+        milestone 2
+              junit(testResults: 'target/test-results/test/**/*.xml,target/test-results/integrationTest/**/*.xml', healthScaleFactor: 100)
+              jacoco(execPattern: 'target/jacoco/test/test.exec,target/jacoco/integrationTest/integrationTest.exec',  sourcePattern: 'src/main/java', sourceInclusionPattern: '**/*.java')
+              sh "npm test"
+              sh "mvn jacoco:report"
+      }
+    }
+    stage('SonarAnalysis') {
+      steps {
+        milestone 3
+            
+             withSonarQubeEnv('SonarQube Production') { 
+                 sh "${scannerHome}/bin/sonar-scanner"
+             }                
+           }
+       }
+   }
+
+  post {
+        failure {
+           notifyByEmail("Failure");
+        }
+        unstable  {
+           notifyByEmail("Unstable");
+        }
+        fixed {
+           notifyByEmail("Fixed");
+        }
+    }
+    
+}
+
+    void notifyByEmail(String reason) {
+        def mailRecipients = "michael.breu@arctis.at"
+      emailext (
+          subject: reason + ": Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]'",
+          body: '''${SCRIPT, template="groovy-html.template"}''',
+          mimeType: 'text/html',
+          recipientProviders: [[$class: 'CulpritsRecipientProvider']]
+        )
+    
+    }   
diff --git a/angular.json b/angular.json
index fdb66ac1901d3f0820f1190eeb369fc2c09cd80f..2be4baf4b4764b9df77a05b32b4267ab9baa657d 100644
--- a/angular.json
+++ b/angular.json
@@ -31,6 +31,7 @@
   },
   "defaultProject": "gitsearch",
   "cli": {
-    "packageManager": "npm"
+    "packageManager": "npm",
+    "analytics": "08f60db5-632a-4306-a765-d5491ecb2e82"
   }
-}
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index e5d603c1a9145f790580504c2f05080a663b7973..52cbb55546e8f079be8353b415cd372e08ddc7e3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "gitsearch",
-  "version": "0.0.1-SNAPSHOT",
+  "version": "0.1.0-SNAPSHOT",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -74,6 +74,12 @@
         "uuid": "8.1.0"
       },
       "dependencies": {
+        "ini": {
+          "version": "1.3.5",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+          "dev": true
+        },
         "semver": {
           "version": "7.3.2",
           "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
@@ -2257,6 +2263,12 @@
         "semver-intersect": "1.4.0"
       },
       "dependencies": {
+        "ini": {
+          "version": "1.3.5",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+          "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+          "dev": true
+        },
         "semver": {
           "version": "7.3.2",
           "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
@@ -2483,9 +2495,9 @@
       "dev": true
     },
     "@types/node": {
-      "version": "13.13.4",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
-      "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==",
+      "version": "13.13.41",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.41.tgz",
+      "integrity": "sha512-qLT9IvHiXJfdrje9VmsLzun7cQ65obsBTmtU3EOnCSLFOoSHx1hpiRHoBnpdbyFqnzqdUUIv81JcEJQCB8un9g==",
       "dev": true
     },
     "@types/normalize-package-data": {
@@ -3399,19 +3411,18 @@
       "dev": true
     },
     "axios": {
-      "version": "0.19.0",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
-      "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
+      "version": "0.21.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
+      "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
       "dev": true,
       "requires": {
-        "follow-redirects": "1.5.10",
-        "is-buffer": "^2.0.2"
+        "follow-redirects": "^1.10.0"
       },
       "dependencies": {
-        "is-buffer": {
-          "version": "2.0.5",
-          "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
-          "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+        "follow-redirects": {
+          "version": "1.13.1",
+          "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
+          "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==",
           "dev": true
         }
       }
@@ -3899,9 +3910,9 @@
       "dev": true
     },
     "bootstrap": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
-      "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.2.tgz",
+      "integrity": "sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A=="
     },
     "brace-expansion": {
       "version": "1.1.11",
@@ -4471,9 +4482,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001091",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001091.tgz",
-      "integrity": "sha512-ECd8gfBBpv0GKsEYY5052+8PBjExiugDoi3dfkJcxujh2mf7kiuDvb1o27GXlOOGopKiIPYEX8XDPYj7eo3E9w==",
+      "version": "1.0.30001185",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001185.tgz",
+      "integrity": "sha512-Fpi4kVNtNvJ15H0F6vwmXtb3tukv3Zg3qhKkOGUq7KJ1J6b9kf4dnNgtEAFXhRsJo0gNj9W60+wBvn0JcTvdTg==",
       "dev": true
     },
     "canonical-path": {
@@ -5599,14 +5610,13 @@
       "dev": true
     },
     "css-selector-tokenizer": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz",
-      "integrity": "sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==",
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+      "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
       "dev": true,
       "requires": {
         "cssesc": "^3.0.0",
-        "fastparse": "^1.1.2",
-        "regexpu-core": "^4.6.0"
+        "fastparse": "^1.1.2"
       }
     },
     "css-tree": {
@@ -6572,12 +6582,23 @@
       "dev": true
     },
     "encoding": {
-      "version": "0.1.12",
-      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
-      "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+      "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
       "dev": true,
       "requires": {
-        "iconv-lite": "~0.4.13"
+        "iconv-lite": "^0.6.2"
+      },
+      "dependencies": {
+        "iconv-lite": {
+          "version": "0.6.2",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+          "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+          "dev": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3.0.0"
+          }
+        }
       }
     },
     "end-of-stream": {
@@ -8026,6 +8047,12 @@
         "readable-stream": "^2.0.0"
       }
     },
+    "fs": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.2.tgz",
+      "integrity": "sha1-4fJE7zkzwbKmS9R5kTYGDQ9ZFPg=",
+      "dev": true
+    },
     "fs-extra": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz",
@@ -8540,6 +8567,27 @@
       "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
       "dev": true
     },
+    "handlebars": {
+      "version": "4.7.6",
+      "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
+      "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.0",
+        "source-map": "^0.6.1",
+        "uglify-js": "^3.1.4",
+        "wordwrap": "^1.0.0"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        }
+      }
+    },
     "har-schema": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -8759,12 +8807,29 @@
       "dev": true
     },
     "hosted-git-info": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.4.tgz",
-      "integrity": "sha512-4oT62d2jwSDBbLLFLZE+1vPuQ1h8p9wjrJ8Mqx5TjsyWmBMV5B13eJqn8pvluqubLf3cJPTfiYCIwNwDNmzScQ==",
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz",
+      "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==",
       "dev": true,
       "requires": {
-        "lru-cache": "^5.1.1"
+        "lru-cache": "^6.0.0"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+          "dev": true
+        }
       }
     },
     "hpack.js": {
@@ -9307,9 +9372,9 @@
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "ini": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
       "dev": true
     },
     "inquirer": {
@@ -12620,6 +12685,11 @@
       "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=",
       "dev": true
     },
+    "jquery": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
+      "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
+    },
     "js-object-pretty-print": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/js-object-pretty-print/-/js-object-pretty-print-0.3.0.tgz",
@@ -13192,138 +13262,104 @@
       }
     },
     "localtunnel": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.0.tgz",
-      "integrity": "sha512-g6E0aLgYYDvQDxIjIXkgJo2+pHj3sGg4Wz/XP3h2KtZnRsWPbOQY+hw1H8Z91jep998fkcVE9l+kghO+97vllg==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.1.tgz",
+      "integrity": "sha512-LiaI5wZdz0xFkIQpXbNI62ZnNn8IMsVhwxHmhA+h4vj8R9JG/07bQHWwQlyy7b95/5fVOCHJfIHv+a5XnkvaJA==",
       "dev": true,
       "requires": {
-        "axios": "0.19.0",
-        "debug": "4.1.1",
+        "axios": "0.21.1",
+        "debug": "4.3.1",
         "openurl": "1.1.1",
-        "yargs": "13.3.0"
+        "yargs": "16.2.0"
       },
       "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "cliui": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-          "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
-          "dev": true,
-          "requires": {
-            "string-width": "^3.1.0",
-            "strip-ansi": "^5.2.0",
-            "wrap-ansi": "^5.1.0"
-          }
-        },
-        "emoji-regex": {
-          "version": "7.0.3",
-          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-          "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
-          "dev": true
-        },
-        "find-up": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
           "dev": true,
           "requires": {
-            "locate-path": "^3.0.0"
+            "color-convert": "^2.0.1"
           }
         },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "locate-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+        "cliui": {
+          "version": "7.0.4",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+          "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^7.0.0"
           }
         },
-        "p-locate": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "color-name": "~1.1.4"
           }
         },
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
           "dev": true
         },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+        "debug": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
+          "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
           "dev": true,
           "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
+            "ms": "2.1.2"
           }
         },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
+        "escalade": {
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+          "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+          "dev": true
         },
         "wrap-ansi": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-          "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+          "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^3.2.0",
-            "string-width": "^3.0.0",
-            "strip-ansi": "^5.0.0"
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
           }
         },
+        "y18n": {
+          "version": "5.0.5",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
+          "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==",
+          "dev": true
+        },
         "yargs": {
-          "version": "13.3.0",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
-          "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+          "version": "16.2.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
           "dev": true,
           "requires": {
-            "cliui": "^5.0.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^2.0.1",
+            "cliui": "^7.0.2",
+            "escalade": "^3.1.1",
+            "get-caller-file": "^2.0.5",
             "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^3.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^13.1.1"
+            "string-width": "^4.2.0",
+            "y18n": "^5.0.5",
+            "yargs-parser": "^20.2.2"
           }
         },
         "yargs-parser": {
-          "version": "13.1.2",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
+          "version": "20.2.4",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+          "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+          "dev": true
         }
       }
     },
@@ -15162,10 +15198,28 @@
         "semver": "^7.1.1"
       },
       "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
         "semver": {
-          "version": "7.3.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
-          "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
           "dev": true
         }
       }
@@ -15187,10 +15241,28 @@
         "validate-npm-package-name": "^3.0.0"
       },
       "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
         "semver": {
-          "version": "7.3.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
-          "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
           "dev": true
         }
       }
@@ -15226,10 +15298,28 @@
         "semver": "^7.0.0"
       },
       "dependencies": {
+        "lru-cache": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+          "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+          "dev": true,
+          "requires": {
+            "yallist": "^4.0.0"
+          }
+        },
         "semver": {
-          "version": "7.3.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
-          "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+          "version": "7.3.4",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
+          "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
+          "dev": true,
+          "requires": {
+            "lru-cache": "^6.0.0"
+          }
+        },
+        "yallist": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+          "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
           "dev": true
         }
       }
@@ -15284,9 +15374,9 @@
       }
     },
     "npm-registry-fetch": {
-      "version": "4.0.5",
-      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.5.tgz",
-      "integrity": "sha512-yQ0/U4fYpCCqmueB2g8sc+89ckQ3eXpmU4+Yi2j5o/r0WkKvE2+Y0tK3DEILAtn2UaQTkjTHxIXe2/CSdit+/Q==",
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz",
+      "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
       "dev": true,
       "requires": {
         "JSONStream": "^1.3.4",
@@ -16171,6 +16261,33 @@
       "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
       "dev": true
     },
+    "path": {
+      "version": "0.12.7",
+      "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
+      "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
+      "dev": true,
+      "requires": {
+        "process": "^0.11.1",
+        "util": "^0.10.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        },
+        "util": {
+          "version": "0.10.4",
+          "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+          "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+          "dev": true,
+          "requires": {
+            "inherits": "2.0.3"
+          }
+        }
+      }
+    },
     "path-browserify": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
@@ -17571,14 +17688,13 @@
       }
     },
     "read-package-json": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
-      "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz",
+      "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==",
       "dev": true,
       "requires": {
         "glob": "^7.1.1",
-        "graceful-fs": "^4.1.2",
-        "json-parse-better-errors": "^1.0.1",
+        "json-parse-even-better-errors": "^2.3.0",
         "normalize-package-data": "^2.0.0",
         "npm-normalize-package-bin": "^1.0.0"
       }
@@ -20174,6 +20290,71 @@
         }
       }
     },
+    "swagger-ts-generator": {
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/swagger-ts-generator/-/swagger-ts-generator-1.2.11.tgz",
+      "integrity": "sha512-LgRgE4cG84QTvoye/PEhB2czpKO2IazYuyhalYE7HeHFcf3yZfQu/Hz9lj605TnxtOhXBEqkuzwKgnyGhaTyDQ==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.0.0",
+        "fs": "0.0.2",
+        "handlebars": "^4.7.6",
+        "lodash": "^4.17.15",
+        "moment": "^2.24.0",
+        "path": "^0.12.7"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
     "swagger-ui-dist": {
       "version": "3.25.1",
       "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.25.1.tgz",
@@ -21290,6 +21471,13 @@
       "integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==",
       "dev": true
     },
+    "uglify-js": {
+      "version": "3.12.6",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.6.tgz",
+      "integrity": "sha512-aqWHe3DfQmZUDGWBbabZ2eQnJlQd1fKlMUu7gV+MiTuDzdgDw31bI3wA2jLLsV/hNcDP26IfyEgSVoft5+0SVw==",
+      "dev": true,
+      "optional": true
+    },
     "ultron": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
@@ -21387,9 +21575,9 @@
       },
       "dependencies": {
         "debug": {
-          "version": "3.2.6",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
-          "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+          "version": "3.2.7",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+          "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
           "dev": true,
           "requires": {
             "ms": "^2.1.1"
@@ -23449,6 +23637,12 @@
       "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
       "dev": true
     },
+    "wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+      "dev": true
+    },
     "workbox-build": {
       "version": "5.1.4",
       "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-5.1.4.tgz",
diff --git a/package.json b/package.json
index d767856028bdd2a6d82b4d3f1bdc1374d2cf6c3d..f7e0ab3977726e4b1b89d463ed8e3e4353518b5a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "gitsearch",
-  "version": "0.0.1-SNAPSHOT",
+  "version": "0.1.0-SNAPSHOT",
   "description": "Description for gitsearch",
   "private": true,
   "license": "UNLICENSED",
@@ -10,7 +10,7 @@
   "dependencies": {
     "@angular/common": "10.0.0",
     "@angular/compiler": "10.0.0",
-    "@angular/core": "10.0.0",
+    "@angular/core": "^10.0.0",
     "@angular/forms": "10.0.0",
     "@angular/localize": "10.0.0",
     "@angular/platform-browser": "10.0.0",
@@ -23,10 +23,11 @@
     "@ng-select/ng-select": "^4.0.1",
     "@ngx-translate/core": "12.1.2",
     "@ngx-translate/http-loader": "5.0.0",
-    "bootstrap": "4.5.0",
+    "bootstrap": "^4.5.2",
+    "jquery": "^3.5.1",
     "moment": "2.27.0",
     "ng-jhipster": "0.14.0",
-    "ngx-cookie-service": "3.0.4",
+    "ngx-cookie-service": "^3.0.4",
     "ngx-infinite-scroll": "9.0.0",
     "ngx-webstorage": "5.0.0",
     "prismjs": "^1.20.0",
@@ -45,7 +46,7 @@
     "@types/chai-string": "1.4.2",
     "@types/jest": "26.0.3",
     "@types/mocha": "7.0.2",
-    "@types/node": "13.13.4",
+    "@types/node": "^13.13.41",
     "@types/prismjs": "^1.9.0",
     "@types/selenium-webdriver": "4.0.9",
     "@typescript-eslint/eslint-plugin": "2.30.0",
@@ -89,6 +90,7 @@
     "sass-loader": "8.0.2",
     "simple-progress-webpack-plugin": "1.1.2",
     "style-loader": "1.2.1",
+    "swagger-ts-generator": "^1.2.11",
     "terser-webpack-plugin": "3.0.6",
     "thread-loader": "2.1.3",
     "to-string-loader": "1.1.6",
@@ -137,5 +139,8 @@
   "jestSonar": {
     "reportPath": "target/test-results/jest",
     "reportFile": "TESTS-results-sonar.xml"
+  },
+  "jest": {
+    "testResultsProcessor": "jest-sonar-reporter"
   }
 }
diff --git a/pom.xml b/pom.xml
index 423ac5409bab91870bd1f783543031fa84291f76..3e9f4cbac7056f793a8897120b4622b515ac2037 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,12 +5,17 @@
 
     <groupId>at.ac.uibk.gitsearch</groupId>
     <artifactId>gitsearch</artifactId>
-    <version>0.0.1-SNAPSHOT</version>
+    <version>0.1.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>Gitsearch</name>
 
-    <repositories>
-        <!-- jhipster-needle-maven-repository -->
+  <repositories>
+    <repository>
+      <id>codabilitySharingAPIRepository</id>
+      <name>codability Sharing API Repository via git</name>
+      <url>https://sharing-codeability.uibk.ac.at/development/sharing/codeabilitysharingpluginapi/-/raw/master/target/</url>
+    </repository>
+       <!-- jhipster-needle-maven-repository -->
     </repositories>
 
     <pluginRepositories>
@@ -133,6 +138,11 @@
             <groupId>com.fasterxml.jackson.datatype</groupId>
             <artifactId>jackson-datatype-jsr310</artifactId>
         </dependency>
+		<dependency>
+		    <groupId>org.codeability.sharing</groupId>
+		    <artifactId>SharingPluginPlatformAPI</artifactId>
+		    <version>0.0.2</version>
+		</dependency>
         <dependency>
             <groupId>com.h2database</groupId>
             <artifactId>h2</artifactId>
@@ -224,7 +234,12 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>
         </dependency>
-        <dependency>
+		<dependency>
+		    <groupId>org.gitlab4j</groupId>
+		    <artifactId>gitlab4j-api</artifactId>
+		    <version>4.15.7</version>
+		</dependency>
+		<dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
         </dependency>
@@ -306,6 +321,15 @@
             <groupId>org.zalando</groupId>
             <artifactId>problem-spring-web</artifactId>
         </dependency>
+        <!-- Spring Security OAuth 2.0 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.jsonwebtoken</groupId>
             <artifactId>jjwt-api</artifactId>
@@ -346,6 +370,10 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                </configuration>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -655,6 +683,8 @@
                         <verbose>true</verbose>
                         <logging>debug</logging>
                         <contexts>!test</contexts>
+                        <!-- currently not really relevant ? -->
+                        <diffExcludeObjects>oauth_access_token, oauth_approvals, oauth_client_details, oauth_client_token, oauth_code, oauth_refresh_token</diffExcludeObjects>
                     </configuration>
                     <dependencies>
                         <dependency>
@@ -959,6 +989,98 @@
                 <spring.profiles.active>dev${profile.tls}${profile.no-liquibase}</spring.profiles.active>
             </properties>
         </profile>
+        <profile>
+            <id>staging</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-undertow</artifactId>
+                </dependency>
+            </dependencies>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-clean-plugin</artifactId>
+                        <configuration>
+                            <filesets>
+                                <fileset>
+                                    <directory>target/classes/static/</directory>
+                                </fileset>
+                            </filesets>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>build-info</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>com.github.eirslett</groupId>
+                        <artifactId>frontend-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>install node and npm</id>
+                                <goals>
+                                    <goal>install-node-and-npm</goal>
+                                </goals>
+                                <configuration>
+                                    <nodeVersion>${node.version}</nodeVersion>
+                                    <npmVersion>${npm.version}</npmVersion>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>npm install</id>
+                                <goals>
+                                    <goal>npm</goal>
+                                </goals>
+                                <configuration>
+                                    <arguments>install</arguments>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>webpack build test</id>
+                                <goals>
+                                    <goal>npm</goal>
+                                </goals>
+                                <phase>test</phase>
+                                <configuration>
+                                    <arguments>run webpack:test</arguments>
+                                    <npmInheritsProxyConfigFromMaven>false</npmInheritsProxyConfigFromMaven>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>webpack build prod</id>
+                                <goals>
+                                    <goal>npm</goal>
+                                </goals>
+                                <phase>generate-resources</phase>
+                                <configuration>
+                                    <arguments>run webpack:prod</arguments>
+                                    <environmentVariables>
+                                        <APP_VERSION>${project.version}</APP_VERSION>
+                                    </environmentVariables>
+                                    <npmInheritsProxyConfigFromMaven>false</npmInheritsProxyConfigFromMaven>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>pl.project13.maven</groupId>
+                        <artifactId>git-commit-id-plugin</artifactId>
+                    </plugin>
+                </plugins>
+            </build>
+            <properties>
+                <!-- default Spring profiles -->
+                <spring.profiles.active>staging${profile.swagger}${profile.tls}${profile.no-liquibase}</spring.profiles.active>
+            </properties>
+        </profile>
         <profile>
             <id>prod</id>
             <dependencies>
diff --git a/sonar-project.properties b/sonar-project.properties
index a4e9b65af407a8ccc99f07268b6eeb1039f0a96e..6c97b9ed8c489da138a2ade41b1d1ea1710a5111 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,14 +1,16 @@
-sonar.projectKey=gitsearch
-sonar.projectName=gitsearch generated by jhipster
+sonar.projectKey=GITSEARCH
+sonar.projectName=Sharing Plattform built on jhipster
 sonar.projectVersion=1.0
 
 sonar.sources=src/main/
-sonar.host.url=http://localhost:9001
 
 sonar.tests=src/test/
 sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml
 sonar.java.codeCoveragePlugin=jacoco
-sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest
+sonar.java.sources=src/main/java
+sonar.java.source=11
+sonar.java.binaries=target/classes
+sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest,target/test-results/test,target/test-results/test
 sonar.testExecutionReportPaths=target/test-results/jest/TESTS-results-sonar.xml
 sonar.typescript.lcov.reportPaths=target/test-results/lcov.info
 
diff --git a/src/main/docker/gitsearch.yml b/src/main/docker/gitsearch.yml
index bb5d059965002bbec587a8b3bd136393cbac3b34..9613d87770ca2ad91791b04beeb3821c29a031cf 100644
--- a/src/main/docker/gitsearch.yml
+++ b/src/main/docker/gitsearch.yml
@@ -1,16 +1,26 @@
 version: '3.5'
 services:
   gitsearch-app:
-    image: docker.uibk.ac.at:443/csar9407/gitsearch:master-1bf1f21ceee62f335eeb4b070d93443cdbf4a14b
+    # image: docker.uibk.ac.at:443/csar9407/gitsearch:staging-3c2e4895e32dd72aa90f64d7ec7584d656f4d18b
+    image: gitsearch:latest
     container_name: sharing_search
     environment:
       - _JAVA_OPTIONS=-Xmx512m -Xms256m
-      - SPRING_PROFILES_ACTIVE=prod,swagger
+      - SPRING_PROFILES_ACTIVE=staging,swagger
       - MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true
       - SPRING_DATASOURCE_URL=jdbc:mysql://sharing_mysql:3306/gitsearch?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&createDatabaseIfNotExist=true
       - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application
       - SPRING_DATA_JEST_URI=http://sharing_elasticsearch:9200
       - SPRING_ELASTICSEARCH_REST_URIS=http://sharing_elasticsearch:9200
+      # see https://stackoverflow.com/questions/62676762/how-can-a-variable-set-in-the-docker-env-file-be-used-in-application-yml
+      - JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64SECRET=${JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64SECRET}
+      - SECURITY_OAUTH2_CLIENT_PROVIDER_GITLABOIDC_ISSUERURI=${SECURITY_OAUTH2_CLIENT_PROVIDER_GITLABOIDC_ISSUERURI}
+      - SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTID=${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTID}
+      - SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTSECRET=${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTSECRET}
+      - APPLICATION_GITLAB_GENERALACCESSTOKEN=${APPLICATION_GITLAB_GENERALACCESSTOKEN}
+      - gitBranch=${GIT_BRANCH}
+      - gitCommitId=${COMMIT_ID}
+
     ports:
       - 10084:8080
     networks:
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/ApplicationProperties.java b/src/main/java/at/ac/uibk/gitsearch/config/ApplicationProperties.java
index 09a56352a521ee5faac12b9b21307d38b3df8e70..2fa082c06d94519083161af343aefbfe544c2bc1 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/ApplicationProperties.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/ApplicationProperties.java
@@ -1,5 +1,7 @@
 package at.ac.uibk.gitsearch.config;
 
+import java.util.List;
+
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 /**
@@ -14,8 +16,27 @@ public class ApplicationProperties {
     private Search search;
 
     private GitLab gitLab;
-
-    public Search getSearch() {
+    
+    private DeploymentInfo deploymentInfo;
+    
+    private List<String>  registeredPlugins;
+
+    /**
+     * a list of URLs of plugins to be registered.
+	 * @return the registeredPlugins
+	 */
+	public List<String> getRegisteredPlugins() {
+		return registeredPlugins;
+	}
+
+	/**
+	 * @param registeredPlugins the registeredPlugins to set
+	 */
+	public void setRegisteredPlugins(List<String>  registeredPlugins) {
+		this.registeredPlugins = registeredPlugins;
+	}
+
+	public Search getSearch() {
         return search;
     }
 
@@ -31,7 +52,21 @@ public class ApplicationProperties {
         this.gitLab = gitLab;
     }
 
-    public static class Search {
+    /**
+	 * @return the deploymentInfo
+	 */
+	public DeploymentInfo getDeploymentInfo() {
+		return deploymentInfo;
+	}
+
+	/**
+	 * @param deploymentInfo the deploymentInfo to set
+	 */
+	public void setDeploymentInfo(DeploymentInfo deploymentInfo) {
+		this.deploymentInfo = deploymentInfo;
+	}
+
+	public static class Search {
 
         private String highlightPre;
         private String highlightPost;
@@ -56,7 +91,8 @@ public class ApplicationProperties {
     public static class GitLab {
         private String url;
         private String mainGroup;
-
+        private String generalAccessToken;
+        
         public String getUrl() {
             return url;
         }
@@ -72,5 +108,49 @@ public class ApplicationProperties {
         public void setMainGroup(String mainGroup) {
             this.mainGroup = mainGroup;
         }
+
+		/**
+		 * @return the generalAccessToken
+		 */
+		public String getGeneralAccessToken() {
+			return generalAccessToken;
+		}
+
+		/**
+		 * @param generalAccessToken the generalAccessToken to set
+		 */
+		public void setGeneralAccessToken(String generalAccessToken) {
+			this.generalAccessToken = generalAccessToken;
+		}
+        
+    }
+    
+    public static class DeploymentInfo {
+    	private String commitId;
+    	private String branch;
+		/**
+		 * @return the commitId
+		 */
+		public String getCommitId() {
+			return commitId;
+		}
+		/**
+		 * @param commitId the commitId to set
+		 */
+		public void setCommitId(String commitId) {
+			this.commitId = commitId;
+		}
+		/**
+		 * @return the branch
+		 */
+		public String getBranch() {
+			return branch;
+		}
+		/**
+		 * @param branch the branch to set
+		 */
+		public void setBranch(String branch) {
+			this.branch = branch;
+		}
     }
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/CacheConfiguration.java b/src/main/java/at/ac/uibk/gitsearch/config/CacheConfiguration.java
index 7a7e8453d9edce1fda6e0379fcf9ddfc91f7393a..0992f4fde834d147b11b7d65fa53fa3a2e04f8e8 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/CacheConfiguration.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/CacheConfiguration.java
@@ -48,6 +48,7 @@ public class CacheConfiguration {
             createCache(cm, at.ac.uibk.gitsearch.domain.User.class.getName());
             createCache(cm, at.ac.uibk.gitsearch.domain.Authority.class.getName());
             createCache(cm, at.ac.uibk.gitsearch.domain.User.class.getName() + ".authorities");
+            createCache(cm, at.ac.uibk.gitsearch.domain.Statistics.class.getName());
             // jhipster-needle-ehcache-add-entry
         };
     }
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/ElasticsearchConfiguration.java b/src/main/java/at/ac/uibk/gitsearch/config/ElasticsearchConfiguration.java
index e20a352c14cbee7bcd61b9b78f6e87453ca27d77..138e6818f672b4b4fccdbc47ff3334ef91c9e641 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/ElasticsearchConfiguration.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/ElasticsearchConfiguration.java
@@ -71,7 +71,8 @@ public class ElasticsearchConfiguration {
             return objectMapper.readValue(source, clazz);
         }
 
-        @Override
+        @SuppressWarnings("unchecked")
+		@Override
         public Map<String, Object> mapObject(Object source) {
             try {
                 return objectMapper.readValue(mapToString(source), HashMap.class);
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/LoggingConfiguration.java b/src/main/java/at/ac/uibk/gitsearch/config/LoggingConfiguration.java
index 50a8339c675f53cdf374320838e7f35e0686b04c..f2f72771e4491a00bdc431396740b43847a1875e 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/LoggingConfiguration.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/LoggingConfiguration.java
@@ -1,17 +1,23 @@
 package at.ac.uibk.gitsearch.config;
 
-import ch.qos.logback.classic.LoggerContext;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import io.github.jhipster.config.JHipsterProperties;
+import static io.github.jhipster.config.logging.LoggingUtils.addContextListener;
+import static io.github.jhipster.config.logging.LoggingUtils.addJsonConsoleAppender;
+import static io.github.jhipster.config.logging.LoggingUtils.addLogstashTcpSocketAppender;
+import static io.github.jhipster.config.logging.LoggingUtils.setMetricsMarkerLogbackFilter;
+
+import java.util.HashMap;
+import java.util.Map;
+
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 
-import java.util.HashMap;
-import java.util.Map;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
-import static io.github.jhipster.config.logging.LoggingUtils.*;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.LoggerContext;
+import io.github.jhipster.config.JHipsterProperties;
 
 /*
  * Configures the console and Logstash log appenders from the app properties
@@ -46,5 +52,13 @@ public class LoggingConfiguration {
         if (jHipsterProperties.getMetrics().getLogs().isEnabled()) {
             setMetricsMarkerLogbackFilter(context, loggingProperties.isUseJsonFormat());
         }
+        
+        // disabling some nasty debug logging
+        context.getLogger("io.netty.util.internal.PlatformDependent0").setLevel(Level.INFO);
+        context.getLogger("org.glassfish.jersey.client.ClientExecutorProvidersConfigurator").setLevel(Level.INFO);
+        context.getLogger("org.springframework.boot.liquibase.SpringPackageScanClassResolver").setLevel(Level.INFO); // zu spät hier :-(
+        context.getLogger("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener").setLevel(Level.INFO);
+        context.getLogger("org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener").setLevel(Level.INFO);
+        context.getLogger("io.netty").setLevel(Level.INFO);
     }
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/SecurityConfiguration.java b/src/main/java/at/ac/uibk/gitsearch/config/SecurityConfiguration.java
index 9bd763aa64d735facc446369b6ff506116ccc290..0fd01c3dafa7f3ee20e15751e6fb14e4805ff8da 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/SecurityConfiguration.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/SecurityConfiguration.java
@@ -1,38 +1,109 @@
 package at.ac.uibk.gitsearch.config;
 
-import at.ac.uibk.gitsearch.security.*;
-import at.ac.uibk.gitsearch.security.jwt.*;
+import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
 
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.servlet.DispatcherType;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Import;
+import org.springframework.core.Ordered;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.RequestEntity;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationProvider;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
+import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
+import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtDecoders;
+import org.springframework.security.oauth2.jwt.JwtValidators;
+import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
+import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
+import org.springframework.security.web.csrf.CsrfFilter;
 import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
 import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.filter.ForwardedHeaderFilter;
+import org.springframework.web.util.UriComponentsBuilder;
 import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
 
+import at.ac.uibk.gitsearch.domain.User;
+import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
+import at.ac.uibk.gitsearch.security.jwt.JWTConfigurer;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider;
+import at.ac.uibk.gitsearch.security.oauth2.SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport;
+import at.ac.uibk.gitsearch.security.oauth2.UserDetailsFetcher;
+import at.ac.uibk.gitsearch.service.UserService;
+import at.ac.uibk.gitsearch.service.dto.UserDTO;
+import at.ac.uibk.gitsearch.service.mapper.UserMapper;
+import io.github.jhipster.config.JHipsterProperties;
+
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
 @Import(SecurityProblemSupport.class)
 public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
 
+    @Value("${spring.security.oauth2.client.provider.gitlabOidc.issuer-uri}")
+    private String gitLabIssuerUri;
+    
+    private final JHipsterProperties jHipsterProperties;
+
     private final TokenProvider tokenProvider;
 
     private final CorsFilter corsFilter;
     private final SecurityProblemSupport problemSupport;
+	private UserDetailsFetcher userDetailsFetcher;
+
 
-    public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
+    public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, 
+    		UserDetailsFetcher userDetailsFetcher,
+    		JHipsterProperties jHipsterProperties, SecurityProblemSupport problemSupport
+    		) {
         this.tokenProvider = tokenProvider;
         this.corsFilter = corsFilter;
         this.problemSupport = problemSupport;
+        this.jHipsterProperties = jHipsterProperties;
+        this.userDetailsFetcher = userDetailsFetcher;
     }
 
     @Bean
@@ -56,15 +127,21 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
     public void configure(HttpSecurity http) throws Exception {
         // @formatter:off
         http
-            .csrf()
-            .disable()
-            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
+        .csrf()
+        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
+        .ignoringAntMatchers("/api/pluginIF/**") // Allowing plugin Access. security is checked by token
+	    .and()
+	    .authenticationProvider(customJWTauthenticationProvider())
+	    .addFilterBefore(corsFilter, CsrfFilter.class)
+//            .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
             .exceptionHandling()
                 .authenticationEntryPoint(problemSupport)
                 .accessDeniedHandler(problemSupport)
         .and()
-            .headers()
-            .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:")
+        .addFilterBefore(forwardedHeaderFilter().getFilter(), CsrfFilter.class)
+        .headers()
+        	// allow images from everywhere
+        	.contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src *; font-src 'self' data:")
         .and()
             .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
         .and()
@@ -77,7 +154,13 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
             .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
         .and()
             .authorizeRequests()
+            .antMatchers("/api/statistics/**").permitAll()
+            .antMatchers("/oauth2/**").permitAll()
+            .antMatchers("/api/search/**").permitAll() // search is always allowed, may return more data, if authenticated
+            .antMatchers("/api/pluginIF/**").permitAll() // plugins calls are always allowed, security by tokens
+            .antMatchers("/api/deploymentInfo").permitAll() // everybody may retrieve deployment infos
             .antMatchers("/api/authenticate").permitAll()
+            .antMatchers("/api/refreshToken").permitAll()
             .antMatchers("/api/register").denyAll()
             .antMatchers("/api/activate").permitAll()
             .antMatchers("/api/account/reset-password/init").permitAll()
@@ -90,11 +173,242 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
         .and()
             .httpBasic()
         .and()
-            .apply(securityConfigurerAdapter());
-        // @formatter:on
+        .apply(securityConfigurerAdapter())
+        .and()
+        	.oauth2Login(oauth2 -> oauth2
+                .userInfoEndpoint(userInfo -> userInfo
+                    .oidcUserService(this.oidcUserService())))
+            .oauth2Login().successHandler(getAuthenticationSuccessHandler())
+        .and()
+            .oauth2ResourceServer() // .bearerTokenResolver(bearerTokenResolver)
+                .jwt()
+                  .jwtAuthenticationConverter(authenticationConverter())
+                
+                .and()
+//                .authenticationManagerResolver(authenticationManagerResolver())
+            .and()
+                .oauth2Client();
+
+        http.oauth2Login().tokenEndpoint().accessTokenResponseClient(accessTokenResponseClient());
+
+        	
+//            .apply(securityConfigurerAdapter())  
+              // @formatter:on
     }
 
+    Converter<Jwt, AbstractAuthenticationToken> authenticationConverter() {
+        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
+        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new at.ac.uibk.gitsearch.security.oauth2.JwtGrantedAuthorityConverter());
+        return jwtAuthenticationConverter;
+    }
+    
     private JWTConfigurer securityConfigurerAdapter() {
         return new JWTConfigurer(tokenProvider);
     }
+
+
+    public AuthenticationProvider customJWTauthenticationProvider() {
+    	return new AuthenticationProvider() {
+			
+			@Override
+			public boolean supports(Class<?> authentication) {
+				return BearerTokenAuthenticationToken.class.isAssignableFrom(authentication);
+			}
+			
+			@Override
+			public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+				if(authentication.isAuthenticated()) return authentication;
+				if (authentication instanceof BearerTokenAuthenticationToken) {
+					BearerTokenAuthenticationToken bearerToken = (BearerTokenAuthenticationToken) authentication;
+					if(tokenProvider.validateToken(bearerToken.getToken())) {
+						return tokenProvider.getAuthentication(bearerToken.getToken());
+					}
+					
+				}
+				return null;
+			}
+		};
+    }
+    
+    /**
+     * not used by OidcAuthorizationCodeAuthenticationProvider :-(
+     * @return
+     */
+    @Bean
+    JwtDecoder jwtDecoder() {
+        NimbusJwtDecoder oidcJwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(gitLabIssuerUri);
+
+        OAuth2TokenValidator<Jwt> audienceValidator = new at.ac.uibk.gitsearch.security.oauth2.AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
+        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(gitLabIssuerUri);
+        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
+
+        // TODO with Audience ist merkwürdig/unnötig
+        oidcJwtDecoder.setJwtValidator(withAudience);
+        
+        return oidcJwtDecoder;
+    }
+    
+    // @Bean
+    private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient(){
+        DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = 
+          new DefaultAuthorizationCodeTokenResponseClient(); 
+        accessTokenResponseClient.setRequestEntityConverter(new CustomOAuth2AuthorizationCodeGrantRequestEntityConverter()); 
+        return accessTokenResponseClient;
+    }
+    
+    
+//    @Bean
+    public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
+        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
+        FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
+        registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
+        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
+        registration.setUrlPatterns(Collections.singletonList("/oauth2"));
+        return registration;
+    }
+
+    /**
+     * corrects UTF-8 Handling.
+     * @author Michael Breu
+     *
+     */
+    public static class CustomOAuth2AuthorizationCodeGrantRequestEntityConverter extends OAuth2AuthorizationCodeGrantRequestEntityConverter {
+    
+    	/**
+    	 * Returns the {@link RequestEntity} used for the Access Token Request.
+    	 *
+    	 * @param authorizationCodeGrantRequest the authorization code grant request
+    	 * @return the {@link RequestEntity} used for the Access Token Request
+    	 */
+    	@Override
+    	public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
+    		ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
+
+    		HttpHeaders headers = getTokenRequestHeaders(clientRegistration);
+    		MultiValueMap<String, String> formParameters = this.buildFormParameters(authorizationCodeGrantRequest);
+    		URI uri = UriComponentsBuilder.fromUriString(clientRegistration.getProviderDetails().getTokenUri())
+    				.build()
+    				.toUri();
+
+    		return new RequestEntity<>(formParameters, headers, HttpMethod.POST, uri);
+    	}
+
+    	static HttpHeaders getTokenRequestHeaders(ClientRegistration clientRegistration) {
+    		HttpHeaders headers = new HttpHeaders();
+    		headers.addAll(getDefaultTokenRequestHeaders());
+//    		if (ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
+//    			headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
+//    		}
+    		return headers;
+    	}
+    	
+    	private static HttpHeaders getDefaultTokenRequestHeaders() {
+    		HttpHeaders headers = new HttpHeaders();
+    		headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+    		final MediaType contentType = MediaType.valueOf(APPLICATION_FORM_URLENCODED_VALUE + ";charset=UTF-8");
+    		headers.setContentType(contentType);
+    		return headers;
+    	}
+
+    	/**
+    	 * Returns a {@link MultiValueMap} of the form parameters used for the Access Token Request body.
+    	 *
+    	 * @param authorizationCodeGrantRequest the authorization code grant request
+    	 * @return a {@link MultiValueMap} of the form parameters used for the Access Token Request body
+    	 */
+    	private MultiValueMap<String, String> buildFormParameters(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) {
+    		ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration();
+    		OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange();
+
+    		MultiValueMap<String, String> formParameters = new LinkedMultiValueMap<>();
+    		formParameters.add(OAuth2ParameterNames.GRANT_TYPE, authorizationCodeGrantRequest.getGrantType().getValue());
+    		formParameters.add(OAuth2ParameterNames.CODE, authorizationExchange.getAuthorizationResponse().getCode());
+    		String redirectUri = authorizationExchange.getAuthorizationRequest().getRedirectUri();
+    		String codeVerifier = authorizationExchange.getAuthorizationRequest().getAttribute(PkceParameterNames.CODE_VERIFIER);
+    		if (redirectUri != null) {
+    			formParameters.add(OAuth2ParameterNames.REDIRECT_URI, redirectUri);
+    		}
+
+			formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
+
+//    		if (!ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
+//    			formParameters.add(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
+//    		}
+//    		if (ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) {
+//    			formParameters.add(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
+//    		}
+    		if (codeVerifier != null) {
+    			formParameters.add(PkceParameterNames.CODE_VERIFIER, codeVerifier);
+    		}
+
+    		return formParameters;
+    	}
+    
+
+    }
+    
+   public AuthenticationSuccessHandler getAuthenticationSuccessHandler() {
+	   return new SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport(tokenProvider);
+   }
+   
+   private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
+       final OidcUserService delegate = new OidcUserService();
+
+       final Set<String> defaultAuthorities = new HashSet<>();
+       defaultAuthorities.add(AuthoritiesConstants.USER);
+       final UserService userService = getApplicationContext().getBean(UserService.class);
+       final UserMapper userMapper = getApplicationContext().getBean(UserMapper.class);
+
+       return userRequest -> {
+           // Delegate to the default implementation for loading a user
+           OidcUser oidcUser = delegate.loadUser(userRequest);
+
+           Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
+           mappedAuthorities.addAll(oidcUser.getAuthorities());
+    	
+        	String email = oidcUser.getEmail();
+			
+	        Optional<User> userO = userService.getUserWithAuthoritiesByEmail(email);
+	        
+	        if(userO.isPresent()) {
+	        	UserDTO userDto = userMapper.userToUserDTO(userO.get());
+	        	boolean hasChanged = userDetailsFetcher.updateUserDetails(userDto, oidcUser, userRequest.getAccessToken(), userRequest.getClientRegistration());
+	        	if (hasChanged)
+	        		userService.updateUser(userDto);
+	        	
+	        	userO.get().getAuthorities()
+	        	  .forEach(
+	        			  
+	        			auth -> { 
+	        				if(mappedAuthorities.stream()
+	        			  .filter(existingAuth -> existingAuth.getAuthority().equals(auth.getName()))
+	        			  .findAny().isEmpty()) {
+	        					mappedAuthorities.add(new SimpleGrantedAuthority(auth.getName()));}
+	        				}
+	        			);
+	        } else {
+	        	UserDTO u = new UserDTO();
+	        	u.setFirstName(oidcUser.getGivenName());
+	        	u.setLastName(oidcUser.getFamilyName());
+	        	u.setLogin(email);
+	        	u.setActivated(true);
+	        	u.setLastModifiedBy("system");
+	        	u.setCreatedDate(java.time.Instant.now());
+
+	        	u.setAuthorities(defaultAuthorities);
+	        	u.setEmail(oidcUser.getEmail());
+	        	@SuppressWarnings("unused")
+				boolean hasChanged = userDetailsFetcher.updateUserDetails(u, oidcUser, userRequest.getAccessToken(), userRequest.getClientRegistration());
+	        	userService.createUser(u);
+	        }
+
+			
+	           
+           oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
+
+           return oidcUser;
+       };
+   }
+    
 }
+
diff --git a/src/main/java/at/ac/uibk/gitsearch/config/WebConfigurer.java b/src/main/java/at/ac/uibk/gitsearch/config/WebConfigurer.java
index f66af56540c9c2d5d6b534bc7478f57029092a60..046d99ae0cd6c3daf13cf7d916aacf44e37aea7d 100644
--- a/src/main/java/at/ac/uibk/gitsearch/config/WebConfigurer.java
+++ b/src/main/java/at/ac/uibk/gitsearch/config/WebConfigurer.java
@@ -1,11 +1,20 @@
 package at.ac.uibk.gitsearch.config;
 
-import io.github.jhipster.config.JHipsterConstants;
-import io.github.jhipster.config.JHipsterProperties;
-import io.github.jhipster.config.h2.H2ConfigurationHelper;
+import static java.net.URLDecoder.decode;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.boot.web.server.*;
+import org.springframework.boot.web.server.MimeMappings;
+import org.springframework.boot.web.server.WebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
 import org.springframework.boot.web.servlet.ServletContextInitializer;
 import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
 import org.springframework.context.annotation.Bean;
@@ -17,14 +26,9 @@ import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
 
-import javax.servlet.*;
-import java.io.File;
-import java.io.UnsupportedEncodingException;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Paths;
-import java.util.*;
-
-import static java.net.URLDecoder.decode;
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
+import io.github.jhipster.config.h2.H2ConfigurationHelper;
 
 /**
  * Configuration of web application with Servlet 3.0 APIs.
diff --git a/src/main/java/at/ac/uibk/gitsearch/domain/Statistics.java b/src/main/java/at/ac/uibk/gitsearch/domain/Statistics.java
new file mode 100644
index 0000000000000000000000000000000000000000..35e5e91cdb25c521b94a1e23b40b0e63b0ee6991
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/domain/Statistics.java
@@ -0,0 +1,112 @@
+package at.ac.uibk.gitsearch.domain;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+
+import javax.persistence.*;
+import javax.validation.constraints.*;
+
+import org.springframework.data.elasticsearch.annotations.FieldType;
+import java.io.Serializable;
+
+/**
+ * A Statistics.
+ */
+@Entity
+@Table(name = "statistics")
+@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
+@org.springframework.data.elasticsearch.annotations.Document(indexName = "statistics")
+public class Statistics implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "views")
+    private Integer views;
+
+    @NotNull
+    @Column(name = "downloads", nullable = false)
+    private Integer downloads;
+
+    @Column(name = "exercise_id")
+    private Long exerciseID;
+
+    // jhipster-needle-entity-add-field - JHipster will add fields here
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getViews() {
+        return views;
+    }
+
+    public Statistics views(Integer views) {
+        this.views = views;
+        return this;
+    }
+
+    public void setViews(Integer views) {
+        this.views = views;
+    }
+
+    public Integer getDownloads() {
+        return downloads;
+    }
+
+    public Statistics downloads(Integer downloads) {
+        this.downloads = downloads;
+        return this;
+    }
+
+    public void setDownloads(Integer downloads) {
+        this.downloads = downloads;
+    }
+
+    public Long getExerciseID() {
+        return exerciseID;
+    }
+
+    public Statistics exerciseID(Long exerciseID) {
+        this.exerciseID = exerciseID;
+        return this;
+    }
+
+    public void setExerciseID(Long exerciseID) {
+        this.exerciseID = exerciseID;
+    }
+    // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Statistics)) {
+            return false;
+        }
+        return id != null && id.equals(((Statistics) o).id);
+    }
+
+    @Override
+    public int hashCode() {
+        return 31;
+    }
+
+    // prettier-ignore
+    @Override
+    public String toString() {
+        return "Statistics{" +
+            "id=" + getId() +
+            ", views=" + getViews() +
+            ", downloads=" + getDownloads() +
+            ", exerciseID=" + getExerciseID() +
+            "}";
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/domain/User.java b/src/main/java/at/ac/uibk/gitsearch/domain/User.java
index edbf2d3a1b8b071d2480cb693d976b2d0133a727..424b279a1691425417a1a7b5e8d43c1db6637b01 100644
--- a/src/main/java/at/ac/uibk/gitsearch/domain/User.java
+++ b/src/main/java/at/ac/uibk/gitsearch/domain/User.java
@@ -1,24 +1,33 @@
 package at.ac.uibk.gitsearch.domain;
 
-import at.ac.uibk.gitsearch.config.Constants;
+import java.io.Serializable;
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import org.apache.commons.lang3.StringUtils;
 import org.hibernate.annotations.BatchSize;
 import org.hibernate.annotations.Cache;
 import org.hibernate.annotations.CacheConcurrencyStrategy;
-import org.springframework.data.elasticsearch.annotations.FieldType;
 
-import javax.persistence.*;
-import javax.validation.constraints.Email;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Pattern;
-import javax.validation.constraints.Size;
-import java.io.Serializable;
-import java.time.Instant;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Set;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import at.ac.uibk.gitsearch.config.Constants;
 
 /**
  * A user.
diff --git a/src/main/java/at/ac/uibk/gitsearch/es/model/DocumentInfo.java b/src/main/java/at/ac/uibk/gitsearch/es/model/DocumentInfo.java
index ecc083e54cadcdf8967871a7370e0be107ec2eab..77e15c26d2c8b05e242c7b506fa3091737879b00 100644
--- a/src/main/java/at/ac/uibk/gitsearch/es/model/DocumentInfo.java
+++ b/src/main/java/at/ac/uibk/gitsearch/es/model/DocumentInfo.java
@@ -20,6 +20,7 @@ public class DocumentInfo {
     private ProjectInfo project;
 
     public DocumentInfo() {
+    	//  JSON
     }
 
     @Override
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/CustomAuditEventRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/CustomAuditEventRepository.java
index e47a141190d100d321625da4862c961ab7c62b74..64926e2004024f3b79c1ff8ff8bbc5c524c8a39a 100644
--- a/src/main/java/at/ac/uibk/gitsearch/repository/CustomAuditEventRepository.java
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/CustomAuditEventRepository.java
@@ -55,7 +55,7 @@ public class CustomAuditEventRepository implements AuditEventRepository {
             !Constants.ANONYMOUS_USER.equals(event.getPrincipal())) {
 
             PersistentAuditEvent persistentAuditEvent = new PersistentAuditEvent();
-            persistentAuditEvent.setPrincipal(event.getPrincipal());
+            persistentAuditEvent.setPrincipal(truncatePrincipal(event.getPrincipal()));
             persistentAuditEvent.setAuditEventType(event.getType());
             persistentAuditEvent.setAuditEventDate(event.getTimestamp());
             Map<String, String> eventData = auditEventConverter.convertDataToStrings(event.getData());
@@ -64,6 +64,16 @@ public class CustomAuditEventRepository implements AuditEventRepository {
         }
     }
 
+    /**
+     * an OAuth2-Principal can get very long, so we truncate it here to 50 characters.
+     */
+    private String truncatePrincipal(String principal) {
+    	if(principal==null) return null;
+    	if (principal.length() <= 50) // TODO make a constant
+    		return principal;
+    	else return principal.substring(0,50);
+    				
+    }
     /**
      * Truncate event data that might exceed column length.
      */
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/StatisticsRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/StatisticsRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6665e115aad1ae782beece8c2a1438b271dd3b7f
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/StatisticsRepository.java
@@ -0,0 +1,18 @@
+package at.ac.uibk.gitsearch.repository;
+
+import at.ac.uibk.gitsearch.domain.Statistics;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+import java.util.Optional;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.jpa.repository.*;
+import org.springframework.stereotype.Repository;
+
+/**
+ * Spring Data  repository for the Statistics entity.
+ */
+@Repository
+public interface StatisticsRepository extends JpaRepository<Statistics, Long> {
+
+    Optional<Statistics> findByExerciseID(Long id);
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/gitlab/GitLabRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/gitlab/GitLabRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..f637b4b0c9a03b03967deb1e47945d68eac34ca2
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/gitlab/GitLabRepository.java
@@ -0,0 +1,63 @@
+package at.ac.uibk.gitsearch.repository.gitlab;
+
+import java.util.Optional;
+
+import org.gitlab4j.api.Constants.TokenType;
+import org.gitlab4j.api.GitLabApi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+
+import at.ac.uibk.gitsearch.config.ApplicationProperties;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider.GitLabAccessInfo;
+
+@Repository
+public class GitLabRepository {
+	
+	private final Logger log = LoggerFactory.getLogger(GitLabRepository.class);
+
+	@Autowired 
+	protected TokenProvider tokenProvider;
+	@Autowired
+	protected ApplicationProperties applicationProperties;
+
+	
+	public GitLabApi getGitLabApi() {
+		Optional<String> accessTokenO = getAccessTokenOfCurrentUser();
+		boolean isPresent = accessTokenO.isPresent();
+		GitLabApi gitLabApi = null;
+		if(isPresent) {
+			String idToken = accessTokenO.get();
+			final Optional<String> gitLabAccessIssuer = tokenProvider.getGitLabAccessIssuer();
+			if(! gitLabAccessIssuer.isPresent()) {
+				log.warn("accessToken defined, but no gitlabAccess Issuer found?");
+			} else {
+
+	         gitLabApi = new GitLabApi(gitLabAccessIssuer.get(), TokenType.OAUTH2_ACCESS, idToken);
+
+	        }
+		}
+		return gitLabApi;
+	}
+
+	public GitLabApi getGitLabApi(Optional<GitLabAccessInfo> accessInfo) {
+		if(accessInfo.isPresent()) {
+			 return new GitLabApi(accessInfo.get().getGitLabAccessIssuer(), TokenType.OAUTH2_ACCESS, accessInfo.get().getAccessToken());
+		} 
+		return new GitLabApi(applicationProperties.getGitLab().getUrl(), TokenType.PRIVATE, applicationProperties.getGitLab().getGeneralAccessToken());
+		
+	}
+	private Optional<String> getAccessTokenOfCurrentUser() {
+		Optional<String> accessTokenO = tokenProvider.getGitLabAccessToken();
+		return accessTokenO;
+	}
+	
+    public Optional<GitLabAccessInfo> getGitLabAccessInfo() {
+    	return tokenProvider.getGitLabAccessInfo();
+    }
+
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepository.java
index 4094c248f837eaffc638b342a2a52b5f5b26620e..662c29ef3dbdcc8ec30679db782cca488cf7a3bb 100644
--- a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepository.java
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepository.java
@@ -1,12 +1,11 @@
 package at.ac.uibk.gitsearch.repository.search;
 
+import java.io.IOException;
+
 import at.ac.uibk.gitsearch.service.dto.GitFilesAggregationDTO;
 import at.ac.uibk.gitsearch.service.dto.GitFilesPageDetailsDTO;
 import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
 
-import java.io.IOException;
-import java.util.List;
-
 public interface GitFilesRepository {
 
     GitFilesPageDetailsDTO pageDetails(SearchInputDTO searchInputDTO, int pageSize) throws IOException;
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryConstants.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryConstants.java
index 6d6854e7542b6630a32d3fd33d7a3fa07a779aee..b93a76a252fdd5e2556addc1e1df539b853a4734 100644
--- a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryConstants.java
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryConstants.java
@@ -14,9 +14,9 @@ public final class GitFilesRepositoryConstants {
     public static final String FULLTEXT_REPOSITORY_AGG = "by_project.project_name";
     public static final String FULLTEXT_REPOSITORY = "project.project_name";
 
-    public static final String METADATA_PROGRAMMING_LANGUAGES = "metadata.programming_languages";
-    public static final String METADATA_KEYWORDS = "metadata.keywords";
+    public static final String METADATA_PROGRAMMING_LANGUAGES = "metadata.programmingLanguage";
+    public static final String METADATA_KEYWORDS = "metadata.keyword";
     public static final String METADATA_PROJECT_ID = "project.project_id";
-    public static final String METADATA_AUTHOR = "metadata.authors.name";
+    public static final String METADATA_CREATOR = "metadata.creator.name";
     public static final String METADATA_LICENSE = "metadata.license";
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryImpl.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryImpl.java
index 2b6c76367a30f5c5e45101c245ff9b87bd379546..f2525a904b2363f02da1681ec3e4e1f5af785519 100644
--- a/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryImpl.java
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/GitFilesRepositoryImpl.java
@@ -144,10 +144,10 @@ public class GitFilesRepositoryImpl implements GitFilesRepository {
         }
         SearchRequest searchRequest = new SearchRequest(GitFilesRepositoryConstants.INDEX_METADATA);
         BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
-        addMetadataQueryShould(queryBuilder, searchInputDTO.getMetadataProgrammingLanguage(), GitFilesRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES);
-        addMetadataQueryShould(queryBuilder, searchInputDTO.getMetadataKeywords(), GitFilesRepositoryConstants.METADATA_KEYWORDS);
-        addMetadataQueryMust(queryBuilder, searchInputDTO.getMetadataAuthor(), GitFilesRepositoryConstants.METADATA_AUTHOR);
-        addMetadataQueryMust(queryBuilder, searchInputDTO.getMetadataLicense(), GitFilesRepositoryConstants.METADATA_LICENSE);
+        addMetadataQueryShould(queryBuilder, searchInputDTO.getMetadata().getProgrammingLanguage(), GitFilesRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES);
+        addMetadataQueryShould(queryBuilder, searchInputDTO.getMetadata().getKeyword(), GitFilesRepositoryConstants.METADATA_KEYWORDS);
+        addMetadataQueryMust(queryBuilder, searchInputDTO.getMetadata().getAuthor(), GitFilesRepositoryConstants.METADATA_CREATOR);
+        addMetadataQueryMust(queryBuilder, searchInputDTO.getMetadata().getLicense(), GitFilesRepositoryConstants.METADATA_LICENSE);
         SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
             .query(queryBuilder)
             .fetchSource(GitFilesRepositoryConstants.METADATA_PROJECT_ID, null);
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..97afa652de1637efd7381da609154109af162c2a
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepository.java
@@ -0,0 +1,430 @@
+package at.ac.uibk.gitsearch.repository.search;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.StringTokenizer;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.action.search.SearchRequest;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.MatchQueryBuilder;
+import org.elasticsearch.index.query.MultiMatchQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
+import org.springframework.context.event.ContextStoppedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.stereotype.Repository;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
+import at.ac.uibk.gitsearch.config.ApplicationProperties;
+import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry;
+import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO;
+import at.ac.uibk.gitsearch.service.dto.UserProvidedMetadataDTO.Person;
+
+@Repository
+public class MetaDataRepository {
+
+	private final RestHighLevelClient elasticsearchClient;
+
+	private static final Logger LOGGER = LogManager.getLogger(MetaDataRepository.class);
+
+	private final ApplicationProperties properties;
+
+	private static final int FRAGMENT_SIZE = 5000;
+
+	public MetaDataRepository(RestHighLevelClient elasticsearchClient, ApplicationProperties properties) {
+		this.elasticsearchClient = elasticsearchClient;
+		this.properties = properties;
+
+	}
+
+	/**
+	 * contains for each Field, a list of Strings mapped to a completion. Together
+	 * with hit counts E.g. "metadata.creator" -> Kerstin -> {"Kerstin Limbeck" ->
+	 * 3, "Kerstin Mair" -> 2)}
+	 */
+	private Map<String, Map<String, Map<String, Integer>>> cachedCompletions = null;
+
+	private static final int MAX_AUTO_COMPLETION_RESULTS = 10;
+
+	/**
+	 * returns all keyword autocompletes for keyWord
+	 *
+	 * @param keyWordPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getKeywordsAutoComplete(String keyWordPrefix) throws IOException {
+		return getFieldAutoCompletion(keyWordPrefix, SearchRepositoryConstants.METADATA_KEYWORDS);
+	}
+
+	/**
+	 * returns all programmingLanguage autocompletes for keyWord
+	 *
+	 * @param progammingLanguagePrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(String progammingLanguagePrefix) throws IOException {
+		return getFieldAutoCompletion(progammingLanguagePrefix,
+				SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES);
+	}
+
+	/**
+	 * returns all creator autocompletes
+	 *
+	 * @param creatorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getCreatorAutoComplete(String creatorPrefix) throws IOException {
+		return getFieldAutoCompletion(creatorPrefix, SearchRepositoryConstants.METADATA_CREATOR);
+	}
+
+	/**
+	 * returns all contributor autocompletes
+	 *
+	 * @param contributorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getContributorAutoComplete(String contributorPrefix) throws IOException {
+		return getFieldAutoCompletion(contributorPrefix, SearchRepositoryConstants.METADATA_CONTRIBUTOR);
+	}
+
+	/**
+	 * returns all contributor and creator autocompletes
+	 * TODO could be optimized
+	 * @param contributorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getContributorCreatorAutoComplete(String contributorPrefix) throws IOException {
+		final List<AutoCompleteEntry> contributors = getContributorAutoComplete(contributorPrefix);
+		final List<AutoCompleteEntry> creatorAutoComplete = getCreatorAutoComplete(contributorPrefix);
+		// merging
+		for(AutoCompleteEntry creator: creatorAutoComplete) {
+			contributors.stream().filter(entry -> entry.getTarget().equals(creator.getTarget())).findAny()
+			   .ifPresentOrElse(entry -> {contributors.remove(entry); 
+			                             contributors.add(new AutoCompleteEntry(creator.getTarget(), creator.getHitCount() + entry.getHitCount()));}, 
+					   () -> {contributors.add(creator);});
+		}
+		return contributors;
+	}
+	/**
+	 * returns a list of hits together with the number of occurences
+	 * @param keyWordPrefix
+	 * @param metaDataField
+	 * @return
+	 * @throws IOException
+	 * @throws JsonProcessingException
+	 * @throws JsonMappingException
+	 */
+	private List<AutoCompleteEntry> getFieldAutoCompletion(String keyWordPrefix, final String metaDataField)
+			throws IOException, JsonProcessingException, JsonMappingException {
+		fillAutoCompletion();
+
+		String lcKeyWordPrefix = keyWordPrefix.toLowerCase();
+		// höllisch kompliziert und höllisch unverständlich mit Streams :-)
+		// idea
+		// 1. Filtern nach Prefix
+		// 2. Zusammenzählen der möglichen Hits (Besser Max statt Sum?)
+		// 3. Auf MAX_AUTO_COMPLETION_RESULTS begrenzen und nur die Trefferstrings
+		// ausgeben.
+		final Stream<Entry<String, Map<String, Integer>>> filteredEntries = cachedCompletions.get(metaDataField)
+				.entrySet().stream() // Map<String, Map<String, Integer>> -> Stream<Entry<String, Map<String,
+										// Integer>>
+				.filter(s -> s.getKey().startsWith(lcKeyWordPrefix));
+		final Map<String, Integer> combinedHits = filteredEntries // s = String -> bool -> Stream<Entry<String,
+																	// Map<String, Integer>>
+				.flatMap(e -> e.getValue().entrySet().stream()) // Stream< Map<String, Integer>> with duplicates
+				.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::max));
+		return combinedHits.entrySet().stream().sorted((e1, e2) -> e2.getValue() - e1.getValue())
+				.limit(MAX_AUTO_COMPLETION_RESULTS).map(s -> new AutoCompleteEntry(s.getKey(), s.getValue())).collect(Collectors.toList()); // Map.Entry<String,
+																										// Map<String,
+																										// Integer>>
+
+	}
+
+	private static final long AUTOCOMPLETION_RECHECK_TIMEOUT = 5 * 60 * 1000L; // every 5 Minutes
+	private Timer autocompletionReloadTimer = new Timer("AutocompletionReloadTimer");
+	@PostConstruct
+	private void setUpAutocompletion() {
+		
+		autocompletionReloadTimer.scheduleAtFixedRate(new TimerTask() {
+
+			@Override
+			public void run() {
+				try {
+					updateAutocompletionCache();
+				} catch(IOException e) {
+					LOGGER.warn("Cannot refresh Autocompletion Cache", e);
+				}
+			}
+		}, 0L, AUTOCOMPLETION_RECHECK_TIMEOUT);
+	}
+	
+	/**
+	 * stops the cache reload timer, during shutdown or between tests.
+	 * @param event ContextStoppedEvent
+	 */
+    @EventListener
+    protected void stopReloadTimer(ContextStoppedEvent event) {
+    	autocompletionReloadTimer.cancel();
+    }
+
+
+	private synchronized void fillAutoCompletion() throws IOException, JsonProcessingException, JsonMappingException {
+		if (cachedCompletions == null) {
+			updateAutocompletionCache();
+		}
+	}
+
+	private void updateAutocompletionCache() throws IOException, JsonProcessingException, JsonMappingException {
+		Map<String, Map<String, Map<String, Integer>>> reloadedCachedCompletions = new HashMap<>();
+		reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_KEYWORDS, new TreeMap<>());
+		reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_CREATOR, new TreeMap<>());
+		reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_CONTRIBUTOR, new TreeMap<>());
+		reloadedCachedCompletions.put(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES, new TreeMap<>());
+
+		SearchRequest searchRequest = new SearchRequest(SearchRepositoryConstants.INDEX_METADATA);
+
+		final int pageSize = 20;
+		int currentPage = 0;
+		boolean tryNextPage = true;
+
+		while (tryNextPage) {
+
+			SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().size(pageSize).from(currentPage++*pageSize);
+
+			searchRequest.source(sourceBuilder);
+
+			SearchResponse searchResponse = elasticsearchClient.search(searchRequest, RequestOptions.DEFAULT);
+
+			ObjectMapper objectMapper = new ObjectMapper();
+			objectMapper.registerModule(new JavaTimeModule());
+			objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+			tryNextPage = searchResponse.getHits().getHits().length > 0;
+
+			for (SearchHit searchHit : searchResponse.getHits().getHits()) {
+				final String sourceAsJSON = searchHit.getSourceAsString();
+				SearchResultDTO entry = objectMapper.readValue(sourceAsJSON, SearchResultDTO.class);
+				if (entry.getMetadata().getKeyword() != null)
+					for (String keyWord : entry.getMetadata().getKeyword())
+						addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_KEYWORDS),
+								split(keyWord), keyWord);
+				if (entry.getMetadata().getContributor() != null)
+					for (Person contributor : entry.getMetadata().getContributor())
+						addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CONTRIBUTOR),
+								split(contributor.getName()), contributor.getName());
+				if (entry.getMetadata().getCreator() != null)
+					for (Person creator : entry.getMetadata().getCreator())
+						addTo(reloadedCachedCompletions.get(SearchRepositoryConstants.METADATA_CREATOR),
+								split(creator.getName()), creator.getName());
+				if (entry.getMetadata().getProgrammingLanguage() != null)
+					for (String language : entry.getMetadata().getProgrammingLanguage())
+						addTo(reloadedCachedCompletions
+								.get(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES),
+								Collections.singletonList(language), language);
+			}
+		}
+		cachedCompletions = reloadedCachedCompletions;
+	}
+
+	private static void addTo(Map<String, Map<String, Integer>> completionMap, List<String> tokenList, String target) {
+		tokenList.forEach(token -> {
+			String lowercaseToken = token.toLowerCase();
+			Map<String, Integer> existingTargets = completionMap.get(lowercaseToken);
+			if (existingTargets == null) { // none found
+				existingTargets = new HashMap<>();
+				existingTargets.put(target, 1);
+				completionMap.put(lowercaseToken, existingTargets);
+			} else {
+				final Integer count = existingTargets.get(target);
+				if (count == null) {
+					existingTargets.put(target, 1);
+				} else {
+					existingTargets.put(target, count + 1);
+				}
+
+			}
+//			existingTargets.add(target);
+		});
+	}
+
+	private static List<String> split(String s) {
+		StringTokenizer st = new StringTokenizer(s, " \t\n\r\f");
+		List<String> result = new ArrayList<>();
+		while (st.hasMoreTokens()) {
+			result.add(st.nextToken());
+		}
+		return result;
+	}
+
+	public SearchResultsDTO pageDetails(SearchInputDTO searchInputDTO, int pageSize, Optional<User> user) throws IOException {
+		fillAutoCompletion();
+		
+		SearchRequest searchRequest = new SearchRequest(SearchRepositoryConstants.INDEX_METADATA);
+
+		BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
+
+		BoolQueryBuilder fullTextBuilder = QueryBuilders.boolQuery();
+		forEachTerm(searchInputDTO.getFulltextQuery(),
+				term -> fullTextBuilder.should(QueryBuilders
+						.multiMatchQuery(term, SearchRepositoryConstants.METADATA_DESCRIPTION,
+								SearchRepositoryConstants.METADATA_KEYWORDS, SearchRepositoryConstants.METADATA_TITLE)
+						.type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX)));
+		if(fullTextBuilder.hasClauses())
+			queryBuilder.must(fullTextBuilder);
+
+		BoolQueryBuilder plBuilder = QueryBuilders.boolQuery();
+		forEachTerm(searchInputDTO.getMetadata().getProgrammingLanguage(), term -> plBuilder
+				.should(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_PROGRAMMING_LANGUAGES, term)));
+		if(plBuilder.hasClauses())
+			queryBuilder.must(plBuilder);
+
+		BoolQueryBuilder keywordBuilder = QueryBuilders.boolQuery();
+		forEachTerm(searchInputDTO.getMetadata().getKeyword(), term -> keywordBuilder
+				.should(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_KEYWORDS, term)));
+		if(keywordBuilder.hasClauses())
+			queryBuilder.must(keywordBuilder);
+
+		BoolQueryBuilder authorBuilder = QueryBuilders.boolQuery();
+		if (searchInputDTO.getMetadata().getAuthor() != null)
+			authorBuilder.should(QueryBuilders
+					.multiMatchQuery(searchInputDTO.getMetadata().getAuthor(),
+							SearchRepositoryConstants.METADATA_CREATOR, SearchRepositoryConstants.METADATA_CONTRIBUTOR,
+							SearchRepositoryConstants.METADATA_PUBLISHER)
+					.type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX));
+		if(authorBuilder.hasClauses())
+			queryBuilder.must(authorBuilder);
+		
+		if (searchInputDTO.getMetadata().getTypes()!=null && !searchInputDTO.getMetadata().getTypes().isEmpty()) {
+			BoolQueryBuilder typeBuilder = QueryBuilders.boolQuery();
+			searchInputDTO.getMetadata().getTypes().forEach(
+						type -> typeBuilder.should(QueryBuilders.matchQuery(SearchRepositoryConstants.METADATA_TYPE, type.getExternalName())));
+			if(typeBuilder.hasClauses())
+				queryBuilder.must(typeBuilder);
+		}
+
+		forEachTerm(searchInputDTO.getMetadata().getLicense(),
+				term -> queryBuilder.must(QueryBuilders.prefixQuery(SearchRepositoryConstants.METADATA_LICENSE, term)));
+
+		// Authorization restrictions
+		final MatchQueryBuilder publicQuery = QueryBuilders.matchQuery(SearchRepositoryConstants.PROJECT_VISIBILITY, "public")
+				.fuzzyTranspositions(false).autoGenerateSynonymsPhraseQuery(false).boost(0.0f);
+		if(user.isEmpty()) {
+			 queryBuilder
+				.must(publicQuery);
+		} else {
+			
+			// TODO: this is not yet perfect :-( prefix query on namespace is not correct. We need to check for the longest prefix
+			// better: Make a gitlab api call to all my project I have read access (and that are not public)
+			final Collection<GrantedAuthority> authorities = user.get().getAuthorities();
+			final BoolQueryBuilder authQuery = QueryBuilders.boolQuery().boost(0.0f);
+			final Stream<QueryBuilder> prefixAuthQueries = authorities.stream()
+			  .filter(auth -> !auth.getAuthority().startsWith("ROLE"))
+			  .filter(auth -> !auth.getAuthority().startsWith("SCOPE"))
+			  .map(auth -> QueryBuilders.prefixQuery(SearchRepositoryConstants.PROJECT_NAMESPACE, auth.getAuthority()));
+			prefixAuthQueries.forEach(pq -> {authQuery.should(pq);});
+			if(authQuery.hasClauses())
+				queryBuilder.must(QueryBuilders.boolQuery().should(authQuery).should(publicQuery));
+			else
+				queryBuilder.must(publicQuery);
+		}
+
+		char[] boundaryChars = { '\n' };
+
+		HighlightBuilder highlightBuilder = new HighlightBuilder().highlighterType("plain")
+				.preTags(properties.getSearch().getHighlightPre()).postTags(properties.getSearch().getHighlightPost())
+				.field(SearchRepositoryConstants.METADATA_DESCRIPTION).boundaryChars(boundaryChars)
+				.boundaryScannerType(HighlightBuilder.BoundaryScannerType.SENTENCE).fragmentSize(FRAGMENT_SIZE);
+
+		// String[] includes = {};
+		// String[] excludes = {HighlightRepositoryConstants.CONTENT_FIELD};
+
+		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
+		sourceBuilder.query(queryBuilder).highlighter(highlightBuilder).size(pageSize)
+				.from((searchInputDTO.getPage()) * pageSize);
+		// .fetchSource(includes, excludes);
+
+		searchRequest.source(sourceBuilder);
+
+		SearchResponse searchResponse = elasticsearchClient.search(searchRequest, RequestOptions.DEFAULT);
+
+		return parseSearchResponse(searchResponse);
+	}
+
+	private static SearchResultsDTO parseSearchResponse(final SearchResponse searchResponse) throws JsonProcessingException {
+		
+		float maxScore = searchResponse.getHits().getMaxScore();
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.registerModule(new JavaTimeModule());
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+        final List<SearchResultDTO> searchResults = new ArrayList<>();
+        for (SearchHit searchHit : searchResponse.getHits().getHits()) {
+			float hitScore = searchHit.getScore();
+            final SearchResultDTO parsedSearchHit = parseSearchHit(searchHit, objectMapper);
+        	int ranking5 = 1;
+        	if (maxScore>0.0f) ranking5 = (int) ((hitScore*5.0)/maxScore+0.5f);
+			parsedSearchHit.setRanking5(ranking5);
+			parsedSearchHit.setDownloads(0);
+			parsedSearchHit.setViews(0);
+			searchResults.add(parsedSearchHit);
+        }
+
+        long hitCount = searchResponse.getHits().getTotalHits();
+        final SearchResultsDTO results = new SearchResultsDTO();
+        results.setHitCount(hitCount);
+        results.setSearchResult(searchResults);
+        return results;
+    }
+
+    private static SearchResultDTO parseSearchHit(SearchHit searchHit, ObjectMapper objectMapper) throws JsonProcessingException {
+        return objectMapper.readValue(searchHit.getSourceAsString(), SearchResultDTO.class);
+    }
+
+	/** Helper **/
+	private static void forEachTerm(String searchTerm, Consumer<String> exec) {
+		if (searchTerm == null)
+			return;
+		StringTokenizer st = new StringTokenizer(searchTerm);
+		while (st.hasMoreTokens()) {
+			exec.accept(st.nextToken());
+		}
+	}
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..8712b50c308f65a796a1aff12722c901855e4a94
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/SearchRepositoryConstants.java
@@ -0,0 +1,34 @@
+package at.ac.uibk.gitsearch.repository.search;
+
+public final class SearchRepositoryConstants {
+
+    public static final String INDEX_FULLTEXT = "fulltext";
+    public static final String INDEX_METADATA = "metadata";
+
+    public static final String FULLTEXT_CONTENT = "content";
+    public static final String FULLTEXT_PROJECT_ID = "project.project_id";
+    public static final String FULLTEXT_SUB_GROUP_AGG = "by_project.sub_group";
+    public static final String FULLTEXT_SUB_GROUP = "project.sub_group";
+    public static final String FULLTEXT_FILE_EXTENSION_AGG = "by_file.file_format";
+    public static final String FULLTEXT_FILE_EXTENSION = "file.file_format";
+    public static final String FULLTEXT_REPOSITORY_AGG = "by_project.project_name";
+    public static final String FULLTEXT_REPOSITORY = "project.project_name";
+
+    public static final String METADATA_TITLE = "metadata.title";
+    public static final String METADATA_DESCRIPTION = "metadata.description";
+    public static final String METADATA_KEYWORDS = "metadata.keyword";
+    public static final String METADATA_PROGRAMMING_LANGUAGES = "metadata.programmingLanguage";
+    public static final String METADATA_PROJECT_ID = "project.project_id";
+    public static final String METADATA_CREATOR = "metadata.creator.name";
+    public static final String METADATA_CONTRIBUTOR = "metadata.contributor.name";
+    public static final String METADATA_PUBLISHER = "metadata.publisher.name";
+    public static final String METADATA_LICENSE = "metadata.license";
+    public static final String METADATA_TYPE = "metadata.type";
+
+    public static final String PROJECT_VISIBILITY = "project.visibility";
+    public static final String PROJECT_NAMESPACE = "project.namespace";
+    
+    private SearchRepositoryConstants() {
+    	// just a list of constants
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepository.java b/src/main/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f5af0d21edcc5c599a81dad64c53f9a001725e7
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepository.java
@@ -0,0 +1,11 @@
+package at.ac.uibk.gitsearch.repository.search;
+
+import at.ac.uibk.gitsearch.domain.Statistics;
+import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
+
+
+/**
+ * Spring Data Elasticsearch repository for the {@link Statistics} entity.
+ */
+public interface StatisticsSearchRepository extends ElasticsearchRepository<Statistics, Long> {
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/SecurityUtils.java b/src/main/java/at/ac/uibk/gitsearch/security/SecurityUtils.java
index a4950bb83d0738f44538a83aa07137a84aadd426..f063c2a690e9a9121a8ab099292fee935a54f5b4 100644
--- a/src/main/java/at/ac/uibk/gitsearch/security/SecurityUtils.java
+++ b/src/main/java/at/ac/uibk/gitsearch/security/SecurityUtils.java
@@ -1,13 +1,22 @@
 package at.ac.uibk.gitsearch.security;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
-
-import java.util.Optional;
-import java.util.stream.Stream;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
 
 /**
  * Utility class for Spring Security.
@@ -33,6 +42,13 @@ public final class SecurityUtils {
         } else if (authentication.getPrincipal() instanceof UserDetails) {
             UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
             return springSecurityUser.getUsername();
+        } else if (authentication instanceof JwtAuthenticationToken) {
+            return (String) ((JwtAuthenticationToken)authentication).getToken().getClaims().get("preferred_username");
+        } else if (authentication instanceof OAuth2AuthenticationToken) {
+        	// we use email for username!
+        	return ((OidcUser) ((OAuth2AuthenticationToken) authentication).getPrincipal()).getEmail();
+        } else if (authentication.getPrincipal() instanceof OidcUser) {
+        	return ((OidcUser) authentication.getPrincipal()).getName();
         } else if (authentication.getPrincipal() instanceof String) {
             return (String) authentication.getPrincipal();
         }
@@ -78,8 +94,27 @@ public final class SecurityUtils {
     }
 
     private static Stream<String> getAuthorities(Authentication authentication) {
-        return authentication.getAuthorities().stream()
+        Collection<? extends GrantedAuthority> authorities = authentication instanceof JwtAuthenticationToken ?
+            extractAuthorityFromClaims(((JwtAuthenticationToken) authentication).getToken().getClaims())
+            : authentication.getAuthorities();
+        return authorities.stream()
             .map(GrantedAuthority::getAuthority);
     }
 
+    public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
+        return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
+        return (Collection<String>) claims.getOrDefault("groups",
+            claims.getOrDefault("roles", new ArrayList<>()));
+    }
+
+    private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
+        return roles.stream()
+            .filter(role -> role.startsWith("ROLE_"))
+            .map(SimpleGrantedAuthority::new)
+            .collect(Collectors.toList());
+    }
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/jwt/TokenProvider.java b/src/main/java/at/ac/uibk/gitsearch/security/jwt/TokenProvider.java
index 3fad284bf1dfb48c9dca8d5ad454eb30720e9431..a22f0e4edde88d77aca1e1df1b75f429e575328d 100644
--- a/src/main/java/at/ac/uibk/gitsearch/security/jwt/TokenProvider.java
+++ b/src/main/java/at/ac/uibk/gitsearch/security/jwt/TokenProvider.java
@@ -2,8 +2,14 @@ package at.ac.uibk.gitsearch.security.jwt;
 
 import java.nio.charset.StandardCharsets;
 import java.security.Key;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
+
 import javax.annotation.PostConstruct;
 
 import org.slf4j.Logger;
@@ -12,19 +18,66 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.stereotype.Component;
 import org.springframework.util.StringUtils;
 
 import io.github.jhipster.config.JHipsterProperties;
-import io.jsonwebtoken.*;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.io.Decoders;
 import io.jsonwebtoken.security.Keys;
 
 @Component
 public class TokenProvider {
 
-    private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
+	/**
+	 * encapsulate gitlab access token and access issuer
+	 * @author Michael Breu
+	 *
+	 */
+    public static class GitLabAccessInfo {
+		String accessToken;
+		String gitLabAccessIssuer;
+		Optional<User> user;
+		
+		public GitLabAccessInfo(String accessToken, String gitLabAccessIssuer, Optional<User> user) {
+			super();
+			this.accessToken = accessToken;
+			this.gitLabAccessIssuer = gitLabAccessIssuer;
+			this.user = user;
+		}
+		/**
+		 * @return the accessToken
+		 */
+		public String getAccessToken() {
+			return accessToken;
+		}
+		/**
+		 * @return the gitLabAccessIssuer
+		 */
+		public String getGitLabAccessIssuer() {
+			return gitLabAccessIssuer;
+		}
+		/**
+		 * @return the user
+		 */
+		public Optional<User> getUser() {
+			return user;
+		}
+	}
+
+	public static final String GITLAB_ACCESS_TOKEN = "gitLabAccessToken";
+    public static final String GITLAB_ACCESS_ISSUER = "gitLabAccessIssuer";
+
+	public static final String PRE_TOKEN_CLAIM = "preToken";
+
+	private final Logger log = LoggerFactory.getLogger(TokenProvider.class);
 
     private static final String AUTHORITIES_KEY = "auth";
 
@@ -60,26 +113,60 @@ public class TokenProvider {
                 .getTokenValidityInSecondsForRememberMe();
     }
 
+    /**
+     * creates a token for authentication with validity determined by rememberMe.
+     * @param authentication
+     * @param rememberMe
+     * @return
+     */
     public String createToken(Authentication authentication, boolean rememberMe) {
-        String authorities = authentication.getAuthorities().stream()
+        long validity = rememberMe
+        		?tokenValidityInMillisecondsForRememberMe
+        		:tokenValidityInMilliseconds;
+
+        return createToken(authentication, validity, false);
+    }
+
+    /**
+     * creates a token from authentication given by validity (im msec)
+     * @param authentication the authentication
+     * @param validity validity in msec
+     * @param isPreToken include hint that this token entitles for a long term token
+     * @return
+     */
+	public String createToken(Authentication authentication, long validity, boolean isPreToken) {
+        Date endTime = new Date(System.currentTimeMillis() + validity);
+
+		String authorities = authentication.getAuthorities().stream()
             .map(GrantedAuthority::getAuthority)
             .collect(Collectors.joining(","));
+		JwtBuilder jwtBuilder = Jwts.builder()
+            .setSubject(authentication.getName())
+            .claim(AUTHORITIES_KEY, authorities);
+		// copy from preToken
+		final Object userDetails = authentication.getDetails();
+		String authenticationToken = null;
+		String authenticationIssuer = null;
+		if (userDetails instanceof Map<?,?>) {
+			@SuppressWarnings("unchecked")
+			Map<String, String> userDetailsMap = (Map<String, String>) userDetails;
+			authenticationToken = userDetailsMap.get(GITLAB_ACCESS_TOKEN);
+			authenticationIssuer = userDetailsMap.get(GITLAB_ACCESS_ISSUER);
+		}
+		
+		if (authenticationToken!=null) {
+			jwtBuilder.claim(GITLAB_ACCESS_TOKEN, authenticationToken);
+			jwtBuilder.claim(GITLAB_ACCESS_ISSUER, authenticationIssuer);
+		}
 
-        long now = (new Date()).getTime();
-        Date validity;
-        if (rememberMe) {
-            validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
-        } else {
-            validity = new Date(now + this.tokenValidityInMilliseconds);
+        if(isPreToken) {
+        	jwtBuilder = jwtBuilder.claim(PRE_TOKEN_CLAIM, PRE_TOKEN_CLAIM);
         }
-
-        return Jwts.builder()
-            .setSubject(authentication.getName())
-            .claim(AUTHORITIES_KEY, authorities)
+		return jwtBuilder
             .signWith(key, SignatureAlgorithm.HS512)
-            .setExpiration(validity)
+            .setExpiration(endTime)
             .compact();
-    }
+	}
 
     public Authentication getAuthentication(String token) {
         Claims claims = Jwts.parserBuilder()
@@ -92,10 +179,25 @@ public class TokenProvider {
             Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
                 .map(SimpleGrantedAuthority::new)
                 .collect(Collectors.toList());
+        String preTokenFlag = (String) claims.get(PRE_TOKEN_CLAIM);
+        
 
-        User principal = new User(claims.getSubject(), "", authorities);
+        User principal = new User(claims.getSubject(),"", authorities);
 
-        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
+        
+        final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal, token, authorities);
+    	Map<String, String> detailsMap = new HashMap<>();
+        if(preTokenFlag !=null) {
+        	detailsMap.put(TokenProvider.PRE_TOKEN_CLAIM, preTokenFlag);
+        }
+        String authenticationToken = (String) claims.get(GITLAB_ACCESS_TOKEN);
+        String authenticationIssuer = (String) claims.get(GITLAB_ACCESS_ISSUER);
+      	if(authenticationToken !=null) {
+      		detailsMap.put(GITLAB_ACCESS_TOKEN, authenticationToken);
+  			detailsMap.put(GITLAB_ACCESS_ISSUER, authenticationIssuer);
+      	}
+       	authentication.setDetails(detailsMap);
+		return authentication;
     }
 
     public boolean validateToken(String authToken) {
@@ -108,4 +210,56 @@ public class TokenProvider {
         }
         return false;
     }
+    
+    public Optional<User>  getCurrentPrincipal() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication a = securityContext.getAuthentication();
+        
+        final Object principal = a.getPrincipal();
+        
+        if (principal instanceof User) {
+    		return Optional.ofNullable((User)principal);
+		}
+        return Optional.empty();
+    }
+    
+    public Optional<String>  getGitLabAccessToken() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication authentication = securityContext.getAuthentication();
+		final Object userDetails = authentication.getDetails();
+		if (userDetails instanceof Map<?,?>) {
+			@SuppressWarnings("unchecked")
+			Map<String, String> userDetailsMap = (Map<String, String>) userDetails;
+			return   Optional.ofNullable(userDetailsMap.get(GITLAB_ACCESS_TOKEN));
+		}
+        return Optional.empty();
+    }
+
+    /**
+     * returns the issuer URL for Gitlab API.
+     * @return
+     */
+    public Optional<String>  getGitLabAccessIssuer() {
+        SecurityContext securityContext = SecurityContextHolder.getContext();
+        Authentication authentication = securityContext.getAuthentication();
+		final Object userDetails = authentication.getDetails();
+		if (userDetails instanceof Map<?,?>) {
+			@SuppressWarnings("unchecked")
+			Map<String, String> userDetailsMap = (Map<String, String>) userDetails;
+			return   Optional.ofNullable(userDetailsMap.get(GITLAB_ACCESS_ISSUER));
+		}
+        return Optional.empty();
+    }
+    
+    public Optional<GitLabAccessInfo> getGitLabAccessInfo() {
+    	final Optional<String> accessToken = getGitLabAccessToken();
+    	if(accessToken.isPresent()) {
+        	final Optional<String> accessIssuer = getGitLabAccessIssuer();
+    		if(accessIssuer.isPresent()) {
+    			return Optional.of(new GitLabAccessInfo(accessToken.get(), accessIssuer.get(), getCurrentPrincipal()) );
+    		}
+    	}
+    	return Optional.empty();
+    }
+
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/oauth2/AudienceValidator.java b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/AudienceValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..6677f4955fd73eb3fe2ee1336d34940f2de39a62
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/AudienceValidator.java
@@ -0,0 +1,36 @@
+package at.ac.uibk.gitsearch.security.oauth2;
+
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
+import org.springframework.security.oauth2.jwt.Jwt;
+
+public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
+    private final Logger log = LoggerFactory.getLogger(AudienceValidator.class);
+    private final OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
+
+    private final List<String> allowedAudience;
+
+    public AudienceValidator(List<String> allowedAudience) {
+        // Assert.notEmpty(allowedAudience, "Allowed audience should not be null or empty.");
+        this.allowedAudience = allowedAudience;
+    }
+
+    public OAuth2TokenValidatorResult validate(Jwt jwt) {
+    	if(allowedAudience.isEmpty() ) {
+        	// we do not really care for audience
+    		return OAuth2TokenValidatorResult.success();
+    	}
+        List<String> audience = jwt.getAudience();
+        if (audience.stream().anyMatch(allowedAudience::contains)) {
+            return OAuth2TokenValidatorResult.success();
+        } else {
+            log.warn("Invalid audience: {}", audience);
+            return OAuth2TokenValidatorResult.failure(error);
+        }
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/oauth2/JwtGrantedAuthorityConverter.java b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/JwtGrantedAuthorityConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..185457fef0fe525a9535009d5db38cf2f37a83f9
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/JwtGrantedAuthorityConverter.java
@@ -0,0 +1,23 @@
+package at.ac.uibk.gitsearch.security.oauth2;
+
+import at.ac.uibk.gitsearch.security.SecurityUtils;
+
+import java.util.Collection;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.stereotype.Component;
+
+@Component
+public class JwtGrantedAuthorityConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
+
+    public JwtGrantedAuthorityConverter() {
+        // Bean extracting authority.
+    }
+
+    @Override
+    public Collection<GrantedAuthority> convert(Jwt jwt) {
+        return SecurityUtils.extractAuthorityFromClaims(jwt.getClaims());
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/oauth2/SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport.java b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport.java
new file mode 100644
index 0000000000000000000000000000000000000000..e64cbe0e186e498f30cfddb3f582ecfe9e617bc8
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport.java
@@ -0,0 +1,158 @@
+package at.ac.uibk.gitsearch.security.oauth2;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider;
+
+/**
+ * allows for a redirect and adds a short-lived cookie that encodes a short lived JWT-Token
+ * @author Michael Breu
+ *
+ */
+public class SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport extends SavedRequestAwareAuthenticationSuccessHandler {
+
+	protected TokenProvider tokenProvider;
+	
+	
+	public SavedRequestAwareAuthenticationSuccessHandlerWithJWTSupport(TokenProvider tokenProvider) {
+		super();
+		this.tokenProvider = tokenProvider;
+	}
+
+	
+	private static final int REQUEST_TOKEN_LIVETIME = 10; // seconds
+	@Override
+	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
+			Authentication authentication) throws ServletException, IOException {
+		Authentication authenticationForToken = authentication;
+		if (authentication instanceof OAuth2AuthenticationToken) {
+			OAuth2AuthenticationToken oAuthA = (OAuth2AuthenticationToken) authentication;
+			@SuppressWarnings("unchecked")
+			List<String> gitLabGroups = (List<String>) ((OidcUser) ((OAuth2AuthenticationToken) authentication).getPrincipal()).getClaims().get("groups");
+			final DefaultOidcUser principal = (DefaultOidcUser) oAuthA.getPrincipal();
+			List<GrantedAuthority> roles = new ArrayList<>();
+			roles.addAll(authentication.getAuthorities());
+			for(String gitLabGroup: gitLabGroups) {
+				roles.add(new SimpleGrantedAuthority(gitLabGroup));
+			}
+			
+			DefaultOidcUser emailUser = new DefaultOidcUser(roles, principal.getIdToken(), principal.getUserInfo(),"email");
+
+			OAuth2AuthenticationToken oauth2Authentication = new OAuth2AuthenticationToken(
+					emailUser,
+					roles,
+					oAuthA.getAuthorizedClientRegistrationId());
+
+			authenticationForToken = oauth2Authentication;
+			authenticationForToken.setAuthenticated(authentication.isAuthenticated());
+		}
+		String token = tokenProvider.createToken(authenticationForToken, REQUEST_TOKEN_LIVETIME *1000L, true /* preToken */); // 200 secs (for Debugging)
+		
+		Cookie tempTokenCookie = new Cookie("tempRequestToken", token);
+		tempTokenCookie.setMaxAge(REQUEST_TOKEN_LIVETIME);
+		tempTokenCookie.setPath("/");
+		
+		response.addCookie(tempTokenCookie);
+		
+		super.onAuthenticationSuccess(request, response, authentication);
+	}
+
+
+
+	@Override
+	protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response,
+			Authentication authentication) {
+		return super.determineTargetUrl(request, response, authentication);
+	}
+	
+	
+    public static class SimplePrincipal implements Principal {
+
+    	protected String name;
+    	
+    	
+		public SimplePrincipal(String name) {
+			super();
+			this.name = name;
+		}
+
+		@Override
+		public String getName() {
+			return name;
+		}
+    	
+    }
+    
+    public static class SimpleAuthentication implements Authentication {
+
+    	/**
+		 * 
+		 */
+		private static final long serialVersionUID = -791646857551363545L;
+
+		private Principal principal;
+
+		Collection<? extends GrantedAuthority> authorities;
+
+    	public SimpleAuthentication(Principal principal, Collection<? extends GrantedAuthority> authorities) {
+			super();
+			this.principal = principal;
+			this.authorities = authorities;
+		}
+
+		@Override
+		public String getName() {
+			return principal.getName();
+		}
+
+		@Override
+		public Collection<? extends GrantedAuthority> getAuthorities() {
+			return authorities;
+		}
+
+		@Override
+		public Object getCredentials() {
+			return null;
+		}
+
+		@Override
+		public Object getDetails() {
+			return null;
+		}
+
+		@Override
+		public Object getPrincipal() {
+			return principal;
+		}
+
+		private boolean authenticated = false;
+		@Override
+		public boolean isAuthenticated() {
+			return authenticated;
+		}
+
+		@Override
+		public void setAuthenticated(boolean isAuthenticated) {
+			authenticated = isAuthenticated;
+		}
+    	
+    }
+	
+}
\ No newline at end of file
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/oauth2/TestMetaDataGenerator.java b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/TestMetaDataGenerator.java
new file mode 100644
index 0000000000000000000000000000000000000000..8eddb216cd7b3ac20ecc7ed990336cce8e55ee8d
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/TestMetaDataGenerator.java
@@ -0,0 +1,105 @@
+package at.ac.uibk.gitsearch.security.oauth2;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.gitlab4j.api.Constants.Encoding;
+import org.gitlab4j.api.Constants.SearchScope;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.models.Namespace;
+import org.gitlab4j.api.models.Project;
+import org.gitlab4j.api.models.RepositoryFile;
+
+public class TestMetaDataGenerator {
+
+	private static final Logger logger = LogManager.getLogger(TestMetaDataGenerator.class);
+
+	private static final String METADATA_CONTENT = "metadataVersion: \"0.2\"\r\n" + "type: collection\r\n"
+			+ "collectionContent: \r\n" + "  - src/if-001/if-001.yml\r\n" + "  - src/if-002/if-002.yml\r\n"
+			+ "  - src/if-003/if-003.yml\r\n" + "identifier: javaCourseTUWienTest\r\n"
+			+ "structure: hierarchical # one from atomic, networked, hierarchical, linear\r\n"
+			+ "version: \"1.0\" # just a version tag\r\n"
+			+ "status: final # one fo draft, final, revised, unavalable\r\n" + "title: ${title}\r\n"
+			+ "description: \"Dies sind Teile des Einführungskurses an der TU Wien. \r\n"
+			+ "   Momentan hier genutzt zum Testen der Metadateninfrastruktur. Version 1.xxx\r\n" + "   3. Zeile\"\r\n"
+			+ "programmingLanguage: \r\n" + "  - JAVA\r\n" + "language: [de]\r\n"
+			+ "educationLevel: \"Anfänger, (to be detailed)\"\r\n" + "audience: \"Anfaenger\"\r\n"
+			+ "keyword: [Java, IOTest, latex, ${keyword}]\r\n" + "license: MIT # mandatory\r\n" + "creator: \r\n"
+			+ "  - {name: \"Stefan Podlipnig\", affiliation: \"TU Wien\", email: \"stefan.podlipnig@tuwien.ac.at\"}\r\n"
+			+ "contributor:\r\n"
+			+ "  - {name: \"Daniel Bastta\", affiliation: \"TU Wien\", email: \"daniel.bastta@tuwien.ac.at\"}\r\n"
+			+ "  - {name: \"Andreas Merckel\", affiliation: \"TU Wien\", email: \"andreas.merkel@tuwien.ac.at\"}\r\n"
+			+ "  - {name: \"Kerstin Limbeck\", affiliation: \"TU Wien\", email: \"kerstin.limbeck@tuwien.ac.at\"}\r\n"
+			+ "publisher:\r\n"
+			+ "  - {name: \"Andreas Merckel\", affiliation: \"TU Wien\", email: \"andreas.merkel@tuwien.ac.at\"}\r\n"
+			+ "\r\n" + "format: [latex]\r\n" + "deprecated: false\r\n" + "difficulty: simple\r\n" + "valid:\r\n"
+			+ "  start: 2020-01-01\r\n" + "  end: 2030-12-31\r\n" + "requires: [Java14]\r\n"
+			+ "image: ./courseIcon.png\r\n" + "";
+
+	public String getTestMetaData(int count) {
+		return METADATA_CONTENT.replace("${title}", "Meta Test Data " + count).replace("${keyword}", "testing" + count);
+	}
+
+	/**
+	 * just a weird location to generate Test Data :-(. Should be purged as soon as
+	 * possible .
+	 * 
+	 * @throws GitLabApiException
+	 */
+	public void generateTestData(GitLabApi gitLabApi, String issuerURI, String idToken) throws GitLabApiException {
+		final String projectPrefix = "apiTest";
+		gitLabApi.enableRequestResponseLogging();
+		boolean skipDelete = true;
+		if (!skipDelete) {
+			@SuppressWarnings("unchecked")
+			final List<Project> projects = (List<Project>) gitLabApi.getSearchApi().globalSearch(SearchScope.PROJECTS,
+					projectPrefix);
+			projects.stream().forEach(p -> {
+				try {
+					gitLabApi.getProjectApi().deleteProject(p.getId());
+				} catch (GitLabApiException e) {
+					logger.warn("Cannot delete project {}", p.getName(), e);
+				}
+			});
+		}
+
+		final List<Namespace> namespaces = gitLabApi.getNamespaceApi().getNamespaces();
+		Optional<Namespace> heathCheckNSO = namespaces.stream().filter(ns -> ns.getName().equals("health-check-tests"))
+				.findFirst();
+		if (heathCheckNSO.isEmpty()) {
+			logger.warn("Namespace health-check-tests not found");
+			return;
+		}
+		Namespace healthNS = heathCheckNSO.get();
+
+		for (int i = 1; i <= 100; i++) {
+			Project p = new Project();
+			p.setName("apiTest_" + i + "_" + System.currentTimeMillis());
+			p.setDescription("Automatically generated testapi");
+			p.setNamespace(healthNS);
+			p.setSharedWithGroups(null);
+			
+		
+			logger.info("Creating project {} in {}", p.getName(), healthNS.getFullPath());
+
+			Project createdProject = gitLabApi.getProjectApi().createProject(p);
+
+			gitLabApi.getProjectApi();
+			RepositoryFile rf = new RepositoryFile();
+			rf.setFileName("metadata.yml");
+			String base64encoded = Base64.getEncoder().encodeToString(getTestMetaData(i).getBytes());
+			rf.setContent(base64encoded);
+			rf.setEncoding(Encoding.BASE64);
+			rf.setFilePath("metadata.yml");
+
+			gitLabApi.getRepositoryFileApi().createFile(createdProject.getId(), rf, "master",
+					"automagically generated metadata :-)");
+		}
+
+	}
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/security/oauth2/UserDetailsFetcher.java b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/UserDetailsFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..1947a2a035ae08b27991673646e2206c69b19724
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/security/oauth2/UserDetailsFetcher.java
@@ -0,0 +1,80 @@
+package at.ac.uibk.gitsearch.security.oauth2;
+
+import java.time.Instant;
+import java.util.function.Consumer;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.gitlab4j.api.Constants.TokenType;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.models.User;
+import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.stereotype.Component;
+
+import at.ac.uibk.gitsearch.service.dto.UserDTO;
+
+@Component
+public class UserDetailsFetcher {
+	
+	private static final Logger logger = LogManager.getLogger(UserDetailsFetcher.class);
+	
+	private static final TestMetaDataGenerator testMetaDataGenerator = new TestMetaDataGenerator();
+
+
+	
+	/** fills in data from OIDC repository.
+	 *   returns true, if user data really changed **/
+	public boolean updateUserDetails(UserDTO u, OidcUser oidcUser, OAuth2LoginAuthenticationToken oAuth2LoginAuthenticationToken) {
+			final OAuth2AccessToken accessToken = oAuth2LoginAuthenticationToken.getAccessToken();
+			final ClientRegistration clientRegistration = oAuth2LoginAuthenticationToken.getClientRegistration();
+			return updateUserDetails(u, oidcUser, accessToken, clientRegistration);
+	}
+
+	/**
+	 * fills in date from OIDC repository
+	 * @param u
+	 * @param oidcUser
+	 * @param accessToken
+	 * @param clientRegistration
+	 * @return
+	 */
+	public boolean updateUserDetails(UserDTO u, OidcUser oidcUser, final OAuth2AccessToken accessToken,
+			final ClientRegistration clientRegistration) {
+		String idToken = accessToken.getTokenValue();
+		String issuerURI = (String) clientRegistration.getProviderDetails().getConfigurationMetadata().get("issuer");
+      try( GitLabApi gitLabApi = new GitLabApi(issuerURI, TokenType.OAUTH2_ACCESS, idToken)) {
+		boolean modified = false;
+		gitLabApi.enableRequestResponseLogging();
+		// List<Project> memberProjects = gitLabApi.getProjectApi().getMemberProjects();
+		User gitUser = gitLabApi.getUserApi().getCurrentUser();
+		modified |= updateAttribute(gitUser.getAvatarUrl(),u.getImageUrl(), u::setImageUrl);
+		modified |= updateAttribute(gitUser.getEmail(), u.getEmail(), u::setEmail);
+		modified |= updateAttribute(gitUser.getName(),u.getLastName(), u::setLastName);
+		// modified |= updateAttribute(gitUser.getUsername(),u.getLogin(), u::setLogin);
+		
+		
+		if (modified) u.setLastModifiedDate(Instant.now());
+		// testMetaDataGenerator.generateTestData(gitLabApi, issuerURI, idToken);
+		return modified;
+      } catch (GitLabApiException e) {
+		logger.warn("Cannot fetch details for user {}", oidcUser.getName(), e);
+		
+}
+      return false;
+	}
+	
+	private static boolean updateAttribute (String newString, String oldString, Consumer<String> setter) {
+		if(oldString==null) {
+			if(newString == null) return false;
+		} else if(oldString.equals(newString)) {
+			return false;
+		}
+		setter.accept(newString);
+		return true;
+	}
+	
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java
new file mode 100644
index 0000000000000000000000000000000000000000..8af78b28425b35a2d42e0062a1d74279279232d2
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java
@@ -0,0 +1,131 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.codeability.sharing.plugins.api.ShoppingBasket;
+import org.codeability.sharing.plugins.api.ShoppingBasket.ExerciseInfo;
+import org.codeability.sharing.plugins.api.ShoppingBasket.UserInfo;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+import org.gitlab4j.api.ProjectApi;
+import org.gitlab4j.api.RepositoryApi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider.GitLabAccessInfo;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+
+/**
+ * Service for exercise/course search results
+ * <p>
+ */
+@Service
+public class GitlabService {
+	
+	@Autowired
+	private PluginManagementService pluginManagementService;
+	@Autowired
+	private GitLabRepository gitLabRepository;
+	
+	
+		
+		
+
+	
+
+	private final Logger log = LoggerFactory.getLogger(ShoppingBasketService.class);
+
+
+    public Boolean repositoryExists(String projectID) {
+        final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(Optional.empty());
+		final ProjectApi gitLabProjectApi = gitLabApi.getProjectApi();
+		try{
+			return gitLabProjectApi.getProject(projectID) != null;}
+		catch(GitLabApiException e){
+			log.error(e.getMessage(), e);
+			return false;
+		}
+    }
+    
+	
+	public InputStream getRepositoryZip(String exerciseID) throws GitLabApiException, IOException {
+		
+        final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(Optional.empty());
+		return rePackageGitLabProjectZip(new ZipInputStream(gitLabApi.getRepositoryApi().getRepositoryArchive(exerciseID, "HEAD", "zip")), "from project " + exerciseID);
+	}
+	
+	/**
+	 * chops of the main folder, and brings every file one level up. Also deleting
+	 * all plain files in main folder.
+	 * 
+	 * @param zipIn the zip to be repackaged
+	 * @param originalLocation the original location. For debug purposes only.
+	 * @return
+	 * @throws IOException
+	 */
+	protected InputStream rePackageGitLabProjectZip(ZipInputStream zipIn, String originalLocation) throws IOException {
+		final PipedInputStream pis = new PipedInputStream(1024*256);
+		final PipedOutputStream pos = new PipedOutputStream(pis);
+
+		Runnable rePackage = () -> {
+			try (ZipOutputStream zipOut = new ZipOutputStream(pos)) {
+
+				ZipEntry zipInEntry = zipIn.getNextEntry();
+				String prefix = "";
+				if (zipInEntry.isDirectory()) { // main directory is prefix to all entries
+					prefix = zipInEntry.getName();
+				}
+
+				while (zipInEntry != null) {
+					if (zipInEntry.getName().startsWith(prefix)) {
+						String newName = zipInEntry.getName().substring(prefix.length());
+						if (!StringUtils.isEmpty(newName)) {
+							ZipEntry zipOutEntry = new ZipEntry(newName);
+							if (zipInEntry.isDirectory()) {
+							log.debug("ignoring directory {}", zipInEntry.getName());
+								zipOut.putNextEntry(zipOutEntry);
+							} else {
+								zipOut.putNextEntry(zipOutEntry);
+								StreamUtils.copy(zipIn, zipOut);
+								zipOut.closeEntry();
+							}
+						}}
+					zipInEntry = zipIn.getNextEntry();
+				}
+				zipIn.close();
+			} catch (IOException e) {
+				log.error("Cannot rezip file from {}", originalLocation);
+			}
+		};
+
+		new Thread(rePackage).start();
+
+		return pis;
+
+	}
+	
+	
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/PluginManagementService.java b/src/main/java/at/ac/uibk/gitsearch/service/PluginManagementService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4a09359b02f8bd8b1f0e52a88ce20cdc4b8efd7
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/PluginManagementService.java
@@ -0,0 +1,251 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.annotation.PostConstruct;
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.el.StandardELContext;
+import javax.el.ValueExpression;
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+import org.codeability.sharing.plugins.api.SharingPluginConfig;
+import org.codeability.sharing.plugins.api.SharingPluginConfig.Action;
+import org.glassfish.jersey.client.ClientConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+
+import at.ac.uibk.gitsearch.config.ApplicationProperties;
+import at.ac.uibk.gitsearch.service.PluginManagementService.PluginConfigWrapper.ActionWrapper;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO.PluginActionInfo;
+
+/**
+ * management services for plugins.
+ * 
+ * @author Michael Breu
+ *
+ */
+@Service
+public class PluginManagementService {
+
+	private final class PluginCheckTimer extends TimerTask {
+		private final String registeredPluginURL;
+
+		private PluginCheckTimer(String registeredPlugin) {
+			this.registeredPluginURL = registeredPlugin;
+		}
+
+		@Override
+		public void run() {
+			ClientConfig restClientConfig = new ClientConfig();
+			restClientConfig.register(JacksonJsonProvider.class);
+			Client client = ClientBuilder.newClient(restClientConfig);
+			
+			WebTarget target = client.target(registeredPluginURL);
+			SharingPluginConfig config = null;
+			try {
+				config = target.request().accept(MediaType.APPLICATION_JSON)
+						.get(SharingPluginConfig.class);
+				registeredPluginConfigs.put(config.pluginName, new PluginConfigWrapper(config, registeredPluginURL) );
+			} 
+			catch (ProcessingException ce) {
+				log.warn("Cannot connect to plugin at {}: {}", registeredPluginURL, ce.getMessage());
+			}
+			catch (Exception e) {
+					log.error("Cannot (re-)load plugin at {}", registeredPluginURL, e);
+					final Iterator<Entry<String, PluginConfigWrapper>> pluginIterator = registeredPluginConfigs
+							.entrySet().iterator();
+					while (pluginIterator.hasNext()) {
+						final Entry<String, PluginConfigWrapper> nextEntry = pluginIterator.next();
+						if (nextEntry.getValue().getConfigURL().equals(registeredPluginURL)) {
+							pluginIterator.remove();
+							break;
+						}
+					}
+			}
+			
+		}
+	}
+
+	/**
+	 * this is just a wrapper, to provide a more efficient access to the original
+	 * config.
+	 * 
+	 * @author Michael Breu
+	 *
+	 */
+	public static class PluginConfigWrapper {
+
+		
+		public static class ActionWrapper {
+			protected SharingPluginConfig.Action originalAction;
+			protected ValueExpression filterExpression;
+			protected PluginActionInfo pluginActionInfo;
+			
+			String absoluteActionTransferURL;
+			
+
+			public ActionWrapper(Action action, PluginConfigWrapper plugin) {
+				super();
+				this.originalAction = action;
+				pluginActionInfo = new PluginActionInfo(plugin.getPluginName(), action.actionId, action.actionName);
+
+				try {
+					factory.createValueExpression(factory, Boolean.class);
+					ELContext context = new StandardELContext(factory);
+					filterExpression = factory.createValueExpression(context,
+							"${" + originalAction.filterELExpression + "}", Boolean.class);
+				} catch (Exception e) {
+					log.error("Cannot parse elExpression {} for pluginAction {}:{} ", originalAction.filterELExpression,
+							plugin.getPluginName(), action.actionId);
+					filterExpression = null;
+				}
+				
+				try {
+					URI actionURI = new URI(action.actionTransferURL);
+					if (!actionURI.isAbsolute()) {
+						this.absoluteActionTransferURL = new URI(plugin.getConfigURL()).resolve(actionURI).toASCIIString();
+					}
+				} catch (URISyntaxException e) {
+					log.error("Cannot asolutize url {} from action {} via {}", action.actionTransferURL, plugin.getConfigURL(), plugin.getPluginName(), e);
+				}
+
+
+			}
+
+			
+			/**
+			 * @return the absoluteImportServiceURL
+			 */
+			public String getAbsoluteActionTransferURL() {
+				return absoluteActionTransferURL;
+			}
+
+
+			/**
+			 * @return the originalAction
+			 */
+			public SharingPluginConfig.Action getOriginalAction() {
+				return originalAction;
+			}
+
+			public String getActionId() {
+				return originalAction.actionId;
+			}
+
+			public String getActionName() {
+				return originalAction.actionName;
+			}
+
+			public PluginActionInfo getPluginActionInfo() {
+				return pluginActionInfo;
+			}
+
+			protected static ExpressionFactory factory = ExpressionFactory.newInstance();
+
+			public boolean isApplicable(final SearchResultDTO hit) {
+				if (filterExpression == null)
+					return false;
+				try {
+					StandardELContext context = new StandardELContext(factory);
+					context.getELResolver().setValue(context, null, "metadata", hit.getMetadata());
+					return (boolean) filterExpression.getValue(context);
+				} catch (Exception e) {
+					log.error("Cannot evaluate elExpression {} for pluginAction {}:{} ",
+							originalAction.filterELExpression, originalAction.actionId, e);
+					return false;
+				}
+
+			}
+		}
+
+		protected SharingPluginConfig originalConfig;
+		protected Map<String, ActionWrapper> actions;
+		protected String configURL;
+
+		public PluginConfigWrapper(SharingPluginConfig originalConfig, String configURL) {
+			super();
+			this.originalConfig = originalConfig;
+			this.configURL = configURL;
+			actions = new HashMap<>();
+			for (Action action : originalConfig.actions) {
+				actions.put(action.actionId, new ActionWrapper(action, this));
+			}
+		}
+
+		/**
+		 * @return the baseURL
+		 */
+		public String getConfigURL() {
+			return configURL;
+		}
+
+		public String getPluginName() {
+			return originalConfig.pluginName;
+		}
+
+		public Map<String, ActionWrapper> getActions() {
+			return actions;
+		}
+
+	}
+	
+	@Autowired 
+	private ApplicationProperties applicationProperties;
+
+	private static Map<String, PluginConfigWrapper> registeredPluginConfigs = new HashMap<>();
+
+	private static final Logger log = LoggerFactory.getLogger(PluginManagementService.class);
+
+	private static final long PLUGIN_RECHECK_TIMEOUT = 5 * 60 * 1000L; // every 5 Minutes
+	
+	@PostConstruct
+	public void registerPlugins() {
+		 List<String> registeredPlugins = applicationProperties.getRegisteredPlugins();
+
+		if (registeredPlugins==null) {
+			log.warn("No Plugins registered?");
+			return;
+		}
+
+		for (String registeredPlugin : registeredPlugins) {
+			Timer check = new Timer(registeredPlugin+"-CheckTimer");
+			check.scheduleAtFixedRate(new PluginCheckTimer(registeredPlugin),0L, PLUGIN_RECHECK_TIMEOUT);
+			
+		}
+	}
+
+
+	public String getRedirectURL(String pluginName, String actionId) {
+		final PluginConfigWrapper sharingPluginConfig = registeredPluginConfigs.get(pluginName);
+		if (sharingPluginConfig == null)
+			return null;
+		final ActionWrapper actionWrapper = sharingPluginConfig.actions.get(actionId);
+		if (actionWrapper == null)
+			return null;
+		return actionWrapper.getAbsoluteActionTransferURL();
+	}
+
+	public Collection<PluginConfigWrapper> getRegisteredPluginConfigs() {
+		return registeredPluginConfigs.values();
+	}
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e99536ca3d5c8d6f1b164255d505535b679796e
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/SearchService.java
@@ -0,0 +1,264 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.zip.ZipInputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+
+import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository;
+import at.ac.uibk.gitsearch.repository.search.MetaDataRepository;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider;
+import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry;
+import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO;
+
+/**
+ * Service for exercise/course search results
+ * <p>
+ */
+@Service
+public class SearchService {
+
+	private final Logger log = LoggerFactory.getLogger(SearchService.class);
+
+	@Autowired
+	protected MetaDataRepository metaDataRepository;
+	@Autowired
+	protected TokenProvider tokenProvider;
+	@Autowired
+	protected GitLabRepository gitLabRepository;
+	@Autowired
+	protected PluginManagementService pluginManagementService;
+	@Autowired
+	protected ShoppingBasketService shoppingBasketService;
+
+	/** just for test implementation **/
+	public static final int NUM_TESTRESULTS = 23;
+
+	public SearchService() {
+		// Just for Spring
+	}
+
+	/**
+	 * returns all keyword autocompletes for keyWord
+	 *
+	 * @param keyWordPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getKeywordsAutoComplete(String keyWordPrefix) throws IOException {
+		return metaDataRepository.getKeywordsAutoComplete(keyWordPrefix);
+	}
+
+	/**
+	 * returns all creator autocompletes
+	 *
+	 * @param creatorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getCreatorAutoComplete(String creatorPrefix) throws IOException {
+		return metaDataRepository.getCreatorAutoComplete(creatorPrefix);
+	}
+
+	/**
+	 * returns all contributor autocompletes
+	 *
+	 * @param contributorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getContributorAutoComplete(String contributorPrefix) throws IOException {
+		return metaDataRepository.getContributorAutoComplete(contributorPrefix);
+	}
+
+	/**
+	 * returns all contributor and author autocompletes
+	 *
+	 * @param contributorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getContributorCreatorAutoComplete(String contributorPrefix) throws IOException {
+		return metaDataRepository.getContributorCreatorAutoComplete(contributorPrefix);
+	}
+
+	/**
+	 * returns all programmingLanguage autocompletes for keyWord
+	 *
+	 * @param programmingLanguagePrefix
+	 * @return
+	 * @throws IOException
+	 */
+	public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(String programmingLanguagePrefix)
+			throws IOException {
+		return metaDataRepository.getProgrammingLanguageAutoComplete(programmingLanguagePrefix);
+	}
+
+	/**
+	 * returns the result page
+	 *
+	 * @param searchInput the query definition
+	 * @param first       the index of the first record to be returned
+	 * @param length      the number of records
+	 */
+	public SearchResultsDTO searchResultPage(SearchInputDTO searchInput, long first, int length) throws IOException {
+		log.debug("Searchrequest for {} ", searchInput);
+
+		final SearchResultsDTO pageDetails = metaDataRepository.pageDetails(searchInput, length,
+				tokenProvider.getCurrentPrincipal());
+
+		pageDetails.getSearchResult().stream()
+				.forEach(hit -> pluginManagementService.getRegisteredPluginConfigs().stream()
+						.forEach(config -> config.getActions().values().stream()
+								.filter(action -> action.isApplicable(hit))
+								.forEach(action -> hit.getSupportedActions().add(action.getPluginActionInfo()))));
+		pageDetails.getSearchResult().stream().forEach(this::fixImageURL);
+
+		return pageDetails;
+		// return readTestResults(searchInput.getFulltextQuery(), first, length);
+	}
+
+	private void fixImageURL(SearchResultDTO metaData) {
+
+		String image = metaData.getMetadata().getImage();
+
+		try {
+			if (image != null) {
+				URI url = new URI(image);
+				if (url.isAbsolute())
+					return;
+				String httpUrlToRepo = metaData.getProject().getUrl();
+				String baseRepoURL = httpUrlToRepo + "/-/raw/master/";
+				final URI resolvedImageUrl = new URI(baseRepoURL).resolve(url);
+				metaData.getMetadata().setImage(resolvedImageUrl.toASCIIString());
+				return;
+			}
+
+			final String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
+			final boolean isJava = metaData.getMetadata().getProgrammingLanguage() != null && Arrays
+					.stream(metaData.getMetadata().getProgrammingLanguage()).anyMatch(s -> s.startsWith("JAVA"))
+					&& metaData.hashCode() % 2 == 0; // nur um ein wenig Abwechslung zu bekommen :-)
+			final boolean isLatexFormat = metaData.getMetadata().getFormat() != null
+					&& Arrays.stream(metaData.getMetadata().getFormat()).anyMatch(s -> s.startsWith("latex"));
+			final boolean isPython = metaData.getMetadata().getProgrammingLanguage() != null && Arrays
+					.stream(metaData.getMetadata().getProgrammingLanguage()).anyMatch(s -> s.startsWith("python"));
+			if (isJava) {
+				metaData.getMetadata().setImage(baseUrl + "/content/img/java.png");
+				return;
+			}
+			if (isLatexFormat) {
+				metaData.getMetadata().setImage(baseUrl + "/content/img/latex.png");
+				return;
+			}
+			if (isPython) {
+				metaData.getMetadata().setImage(baseUrl + "/content/img/python.png");
+				return;
+			}
+			metaData.getMetadata().setImage(baseUrl + "/content/img/gitLab.png");
+			return;
+
+		} catch (URISyntaxException e) {
+			log.warn("Cannot parse image url {} for {}", metaData.getMetadata().getImage(),
+					metaData.getProject().getUrl());
+		}
+	}
+
+	/**
+	 * temporary static test data.
+	 *
+	 * @param searchString just a string to add to testdata description.
+	 * @return
+	 */
+	@SuppressWarnings("unused")
+	private SearchResultsDTO readTestResults(String searchString, long first, int length) {
+		ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+		mapper.findAndRegisterModules();
+
+		List<SearchResultDTO> loadedMetaData = new ArrayList<>();
+
+		for (String testFile : new String[] { "metaData1.yaml", "metaData2.yaml" }) {
+			log.debug("reading test data from {} ", testFile);
+
+			final File metaDataFile = new File("src/main/java/at/ac/uibk/gitsearch/service/testData/" + testFile);
+			try {
+				byte[] fileContent = FileCopyUtils.copyToByteArray(metaDataFile);
+				SearchResultDTO searchResultDTO = mapper.readValue(fileContent, SearchResultDTO.class);
+				loadedMetaData.add(searchResultDTO);
+			} catch (IOException e) {
+				log.error("Cannot read test search input from " + testFile, e);
+			}
+		}
+
+		List<SearchResultDTO> result = new ArrayList<>();
+		if (length > 0) {
+			int metadataPos = 0;
+			long recordNr = first;
+			// This loop is only used for testing to get more results.
+			for (int i = 0; i < 12; ++i) {
+				metadataPos = 0; // also for testing, remove later
+				while (metadataPos < length && metadataPos < loadedMetaData.size()) {
+					final SearchResultDTO loadedMetaDataRecord = new SearchResultDTO(loadedMetaData.get(metadataPos++));
+					if (loadedMetaDataRecord.toString().contains(searchString)) {
+						loadedMetaDataRecord.getMetadata()
+								.setTitle("#" + (recordNr + 1) + " " + loadedMetaDataRecord.getMetadata().getTitle());
+						result.add(loadedMetaDataRecord);
+						recordNr++;
+						log.debug("Added MetaData Record {}", loadedMetaData);
+					}
+					log.debug("{} does not contain {}", loadedMetaData, searchString);
+				}
+			}
+
+		}
+		return new SearchResultsDTO(result, NUM_TESTRESULTS, first);
+
+	}
+
+	public File exportExercise(long exerciseId) throws IOException {
+		try{
+		final GitLabApi gitLabApi = gitLabRepository.getGitLabApi(tokenProvider.getGitLabAccessInfo());
+		InputStream inputFile = shoppingBasketService.rePackageGitLabProjectZip(new ZipInputStream(gitLabApi.getRepositoryApi().getRepositoryArchive((int)(long)exerciseId, "HEAD", "zip")), "from project " + String.valueOf(exerciseId));
+		File file = new File("exercise" + String.valueOf(exerciseId) + ".zip");
+
+		return copyInputStreamToFile(inputFile, file);}
+		catch(GitLabApiException exception){
+			log.error(exception.getMessage());
+		}
+		return null;
+	}
+
+	private File copyInputStreamToFile(InputStream inputStream, File file) throws IOException {
+
+		// append = false
+		try (FileOutputStream outputStream = new FileOutputStream(file, false)) {
+			int read;
+			byte[] bytes = new byte[8192];
+			while ((read = inputStream.read(bytes)) != -1) {
+				outputStream.write(bytes, 0, read);
+			}
+			return file;
+		}
+	}
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/ShoppingBasketService.java b/src/main/java/at/ac/uibk/gitsearch/service/ShoppingBasketService.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffa5f97fa844916c0702d8320f838706c577b777
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/ShoppingBasketService.java
@@ -0,0 +1,356 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.codeability.sharing.plugins.api.ShoppingBasket;
+import org.codeability.sharing.plugins.api.ShoppingBasket.ExerciseInfo;
+import org.codeability.sharing.plugins.api.ShoppingBasket.UserInfo;
+import org.gitlab4j.api.GitLabApi;
+import org.gitlab4j.api.GitLabApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import at.ac.uibk.gitsearch.repository.gitlab.GitLabRepository;
+import at.ac.uibk.gitsearch.security.jwt.TokenProvider.GitLabAccessInfo;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+
+/**
+ * Service for exercise/course search results
+ * <p>
+ */
+@Service
+public class ShoppingBasketService {
+	
+	@Autowired
+	private PluginManagementService pluginManagementService;
+	@Autowired
+	private GitLabRepository gitLabRepository;
+	
+	public static class ShoppingBasketRedirectInfoDTO {
+		private String redirectURL;
+		/**
+		 * @return the redirectURL
+		 */
+		public String getRedirectURL() {
+			return redirectURL;
+		}
+		/**
+		 * @param redirectURL the redirectURL to set
+		 */
+		public void setRedirectURL(String redirectURL) {
+			this.redirectURL = redirectURL;
+		}
+	}
+	
+	public static class ShoppingBasketInfoDTO {
+		private SearchResultDTO[] itemInfos;
+		private String plugin;
+		private String action;
+		
+		public ShoppingBasketInfoDTO(SearchResultDTO[] itemInfos) {
+			super();
+			this.itemInfos = itemInfos;
+		}
+		
+		public ShoppingBasketInfoDTO() {
+			// JSON
+		}
+		
+		
+
+		/**
+		 * @return the itemInfos
+		 */
+		public SearchResultDTO[] getItemInfos() {
+			return itemInfos;
+		}
+
+		/**
+		 * @param itemInfos the itemInfos to set
+		 */
+		public void setItemInfos(SearchResultDTO[] itemInfos) {
+			this.itemInfos = itemInfos;
+		}
+
+
+
+		/**
+		 * @return the plugin
+		 */
+		public String getPlugin() {
+			return plugin;
+		}
+
+		/**
+		 * @param plugin the plugin to set
+		 */
+		public void setPlugin(String plugin) {
+			this.plugin = plugin;
+		}
+
+		/**
+		 * @return the action
+		 */
+		public String getAction() {
+			return action;
+		}
+
+		/**
+		 * @param action the action to set
+		 */
+		public void setAction(String action) {
+			this.action = action;
+		}
+
+
+
+		public static class ItemInfoDTO {
+		String exerciseID;
+		String gitLabURL;
+		
+		public ItemInfoDTO(String exerciseID, String gitLabURl) {
+			super();
+			this.exerciseID = exerciseID;
+			this.gitLabURL = gitLabURl;
+		}
+		
+		public ItemInfoDTO() {
+			// JSON
+		}
+		/**
+		 * @return the exerciseIDs
+		 */
+		public String getExerciseID() {
+			return exerciseID;
+		}
+		/**
+		 * @param exerciseIDs the exerciseIDs to set
+		 */
+		public void setExerciseID(String exerciseID) {
+			this.exerciseID = exerciseID;
+		}
+		/**
+		 * @return the gitLabURls
+		 */
+		public String getGitLabURL() {
+			return gitLabURL;
+		}
+		/**
+		 * @param gitLabURL the gitLabURls to set
+		 */
+		public void setGitLabURL(String gitLabURL) {
+			this.gitLabURL = gitLabURL;
+		}
+		}
+	}
+	
+	/**
+	 * holds further info for shopping basket.
+	 * @author Michael Breu
+	 *
+	 */
+	public static class ExtendedShoppingBasket {
+		public ExtendedShoppingBasket(ShoppingBasketInfoDTO shoppingBasket, Optional<GitLabAccessInfo> gitLabAccessInfo, long tokenValidUntil) {
+			super();
+			this.shoppingBasket = shoppingBasket;
+			this.gitLabAccessInfo = gitLabAccessInfo;
+			this.tokenValidUntil = tokenValidUntil;
+		}
+		public ExtendedShoppingBasket() {
+		}
+		
+		private ShoppingBasketInfoDTO shoppingBasket;
+		private Optional<GitLabAccessInfo> gitLabAccessInfo;
+		
+		
+		/**
+		 * token valid until (msecs since 1.1.1970)
+		 */
+		private long tokenValidUntil;
+
+		/**
+		 * @return the shoppingBasket
+		 */
+		public ShoppingBasketInfoDTO getShoppingBasket() {
+			return shoppingBasket;
+		}
+		/**
+		 * @param shoppingBasket the shoppingBasket to set
+		 */
+		public void setShoppingBasket(ShoppingBasketInfoDTO shoppingBasket) {
+			this.shoppingBasket = shoppingBasket;
+		}
+		/**
+		 * @return the gitLabAccessInfo
+		 */
+		public Optional<GitLabAccessInfo> getGitLabAccessInfo() {
+			return gitLabAccessInfo;
+		}
+		/**
+		 * @param gitLabAccessInfo the gitLabAccessInfo to set
+		 */
+		public void setGitLabAccessInfo(Optional<GitLabAccessInfo> gitLabAccessInfo) {
+			this.gitLabAccessInfo = gitLabAccessInfo;
+		}
+		/**
+		 * @return the tokenValidUntil
+		 */
+		public long getTokenValidUntil() {
+			return tokenValidUntil;
+		}
+	}
+	
+
+	private final Logger log = LoggerFactory.getLogger(ShoppingBasketService.class);
+
+
+	public ShoppingBasketService() {
+		// JSON
+	}
+	
+	public ShoppingBasket getBasket(String basketToken) {
+		if(basketToken==null)  {
+			log.warn("Basket for null not found" );
+			return null;
+		}
+		final @Nullable ExtendedShoppingBasket basketInfo = basketCache.getIfPresent(basketToken);
+		if(basketInfo==null) {
+			log.warn("Basket {} not found", basketToken );
+			return null;
+		}
+		
+		List<ExerciseInfo> result = new ArrayList<>();
+		for(SearchResultDTO hit: basketInfo.getShoppingBasket().getItemInfos()) {
+			result.add(convertFrom(hit));
+		}
+		UserInfo userInfo = null;
+		final Optional<GitLabAccessInfo> gitLabAccessInfo = basketInfo.getGitLabAccessInfo();
+		if(gitLabAccessInfo.isPresent() && gitLabAccessInfo.get().getUser().isPresent()) {
+			userInfo = new UserInfo(gitLabAccessInfo.get().getUser().get().getUsername());
+		}
+		final ShoppingBasket shoppingBasket = new ShoppingBasket(userInfo,  result.toArray(new ExerciseInfo[] {}), basketInfo.tokenValidUntil);
+		log.info("Basket {} is delivered", basketToken );
+		return shoppingBasket;
+	}
+	
+	private ExerciseInfo convertFrom(SearchResultDTO hit) {
+		ExerciseInfo ex = new ExerciseInfo();
+		ex.gitLabProjectId = hit.getProject().getProject_id();
+		ex.gitLabURI = hit.getProject().getUrl();
+		ex.keywords = Arrays.asList(hit.getMetadata().getKeyword());
+		ex.title = hit.getMetadata().getTitle();
+		return ex;
+	}
+	
+	public InputStream getRepositoryZip(String basketToken, int position) throws GitLabApiException, IOException {
+		final @Nullable ExtendedShoppingBasket basketInfo = basketCache.getIfPresent(basketToken);
+		ShoppingBasket basket = getBasket( basketToken);
+        final GitLabApi gitLabApi = basketInfo==null?gitLabRepository.getGitLabApi(Optional.empty()):gitLabRepository.getGitLabApi(basketInfo.getGitLabAccessInfo());
+		return rePackageGitLabProjectZip(new ZipInputStream(gitLabApi.getRepositoryApi().getRepositoryArchive(basket.exerciseInfo[position].gitLabProjectId, "HEAD", "zip")), "from project " + basket.exerciseInfo[position].gitLabProjectId);
+	}
+	
+	/**
+	 * chops of the main folder, and brings every file one level up. Also deleting
+	 * all plain files in main folder.
+	 * 
+	 * @param zipIn the zip to be repackaged
+	 * @param originalLocation the original location. For debug purposes only.
+	 * @return
+	 * @throws IOException
+	 */
+	protected InputStream rePackageGitLabProjectZip(ZipInputStream zipIn, String originalLocation) throws IOException {
+		final PipedInputStream pis = new PipedInputStream(1024*256);
+		final PipedOutputStream pos = new PipedOutputStream(pis);
+
+		Runnable rePackage = () -> {
+			try (ZipOutputStream zipOut = new ZipOutputStream(pos)) {
+
+				ZipEntry zipInEntry = zipIn.getNextEntry();
+				String prefix = "";
+				if (zipInEntry.isDirectory()) { // main directory is prefix to all entries
+					prefix = zipInEntry.getName();
+				}
+
+				while (zipInEntry != null) {
+					if (zipInEntry.getName().startsWith(prefix)) {
+						String newName = zipInEntry.getName().substring(prefix.length());
+						if (!StringUtils.isEmpty(newName)) {
+							ZipEntry zipOutEntry = new ZipEntry(newName);
+							if (zipInEntry.isDirectory()) {
+							log.debug("ignoring directory {}", zipInEntry.getName());
+								zipOut.putNextEntry(zipOutEntry);
+							} else {
+								zipOut.putNextEntry(zipOutEntry);
+								StreamUtils.copy(zipIn, zipOut);
+								zipOut.closeEntry();
+							}
+						}}
+					zipInEntry = zipIn.getNextEntry();
+				}
+				zipIn.close();
+			} catch (IOException e) {
+				log.error("Cannot rezip file from {}", originalLocation);
+			}
+		};
+
+		new Thread(rePackage).start();
+
+		return pis;
+
+	}
+	
+	public static final Duration BASKET_EXPIRY_INTERVAL = Duration.ofMinutes(60);
+	/**
+	 * this is a cache for shopping baskets. Entries expires after BASKET_EXPIRY_INTERVAL.
+	 * TODO: Store in Database, to allow multiple application instances.
+	 */
+	protected LoadingCache<String, ExtendedShoppingBasket> basketCache = CacheBuilder.newBuilder()
+		    .maximumSize(10000)
+		    .expireAfterWrite(BASKET_EXPIRY_INTERVAL)
+		    .build(
+		        new CacheLoader<String, ExtendedShoppingBasket>() {
+		          public ExtendedShoppingBasket load(String key)  {
+		        	  return null;
+		          }
+		        });
+	
+	public ShoppingBasketRedirectInfoDTO getRedirectInfo(ShoppingBasketInfoDTO basket, String baseURL) {
+		
+		UUID random = UUID.randomUUID();
+		long tokenValidUntil = System.currentTimeMillis() + BASKET_EXPIRY_INTERVAL.toMillis();
+		basketCache.put(random.toString(), new ExtendedShoppingBasket(basket, gitLabRepository.getGitLabAccessInfo(), tokenValidUntil));
+		
+		ShoppingBasketRedirectInfoDTO result = new ShoppingBasketRedirectInfoDTO();
+		
+		result.setRedirectURL(
+				pluginManagementService.getRedirectURL(basket.getPlugin(), basket.getAction())
+				+ "/"+random.toString()
+				+ "?returnURL=" + baseURL 
+				+ "&apiBaseURL=" + baseURL+"/api/pluginIF/v0.1");
+		
+		return result;
+		
+	}
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/StatisticsService.java b/src/main/java/at/ac/uibk/gitsearch/service/StatisticsService.java
new file mode 100644
index 0000000000000000000000000000000000000000..eac85d46461d86eb97eec6e2614a0af10b895319
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/StatisticsService.java
@@ -0,0 +1,58 @@
+package at.ac.uibk.gitsearch.service;
+
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
+import java.util.Optional;
+
+/**
+ * Service Interface for managing {@link at.ac.uibk.gitsearch.domain.Statistics}.
+ */
+public interface StatisticsService {
+
+    /**
+     * Save a statistics.
+     *
+     * @param statisticsDTO the entity to save.
+     * @return the persisted entity.
+     */
+    StatisticsDTO save(StatisticsDTO statisticsDTO);
+
+    /**
+     * Get all the statistics.
+     *
+     * @param pageable the pagination information.
+     * @return the list of entities.
+     */
+    Page<StatisticsDTO> findAll(Pageable pageable);
+
+
+    /**
+     * Get the "id" statistics.
+     *
+     * @param id the id of the entity.
+     * @return the entity.
+     */
+    Optional<StatisticsDTO> findOne(Long id);
+
+    /**
+     * Delete the "id" statistics.
+     *
+     * @param id the id of the entity.
+     */
+    void delete(Long id);
+
+    /**
+     * Search for the statistics corresponding to the query.
+     *
+     * @param query the query of the search.
+     * 
+     * @param pageable the pagination information.
+     * @return the list of entities.
+     */
+    Page<StatisticsDTO> search(String query, Pageable pageable);
+
+    Optional<StatisticsDTO> findOneByExerciseID(Long id);
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/UserService.java b/src/main/java/at/ac/uibk/gitsearch/service/UserService.java
index 8a0a70b708fc8cb3f7b5a6c5bf3b25b117a443f2..53812dfe261b38d5d0c16430aba07c2f670c8c52 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/UserService.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/UserService.java
@@ -274,6 +274,11 @@ public class UserService {
         return userRepository.findOneWithAuthoritiesByLogin(login);
     }
 
+    @Transactional(readOnly = true)
+    public Optional<User> getUserWithAuthoritiesByEmail(String email) {
+        return userRepository.findOneWithAuthoritiesByEmailIgnoreCase(email);
+    }
+
     @Transactional(readOnly = true)
     public Optional<User> getUserWithAuthorities() {
         return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin);
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..c39397da596acb154a45404b077f4953830847b6
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/AutoCompleteEntry.java
@@ -0,0 +1,41 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+/**
+ * just contains a pair of a target string and the hit count.
+ * @author Michael Breu
+ *
+ */
+public class AutoCompleteEntry {
+	private String target;
+	private int hitCount;
+	
+	public AutoCompleteEntry(String target, int hitCount) {
+		super();
+		this.target = target;
+		this.hitCount = hitCount;
+	}
+
+	public AutoCompleteEntry(String target) {
+		super();
+		this.target = target;
+		this.hitCount = 1;
+	}
+	
+	public void increaseHitCount() {
+		this.hitCount++;
+	}
+	
+	/**
+	 * @return the target
+	 */
+	public String getTarget() {
+		return target;
+	}
+	/**
+	 * @return the hitCount
+	 */
+	public int getHitCount() {
+		return hitCount;
+	}
+
+}
\ No newline at end of file
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/FragmentDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/FragmentDTO.java
index 41617445d882cfd1b3dec8af937d92a777ff1b27..e36e5250828b19a3166074694eaafd5bb9db389c 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/dto/FragmentDTO.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/FragmentDTO.java
@@ -28,7 +28,7 @@ public class FragmentDTO {
             nextNewline = content.indexOf('\n', nextNewline + 1);
         }
 
-        int lastNewline = content.indexOf('\n', idxEnd);;
+        int lastNewline = content.indexOf('\n', idxEnd);
         if (lastNewline == -1) {
             lastNewline = content.length();
         }
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesAggregationDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesAggregationDTO.java
index aafb95c51d09de315244387abb5b248232dbe001..c61fce8441421d6c5b0b79b660e9af8acfcb751c 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesAggregationDTO.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesAggregationDTO.java
@@ -14,9 +14,11 @@ public class GitFilesAggregationDTO {
     private List<FrequencyDTO<String>> repositories;
     private List<FrequencyDTO<String>> university;
 
-    public GitFilesAggregationDTO(Aggregations aggregations) {
+    @SuppressWarnings("unchecked")
+	public GitFilesAggregationDTO(Aggregations aggregations) {
         Terms terms = aggregations.get(GitFilesRepositoryConstants.FULLTEXT_SUB_GROUP_AGG);
-        Collection<Terms.Bucket> buckets = (Collection<Terms.Bucket>) terms.getBuckets();
+
+		Collection<Terms.Bucket> buckets = (Collection<Terms.Bucket>) terms.getBuckets();
         this.university = buckets.stream().map(bucket -> new FrequencyDTO<>(bucket.getKeyAsString(), bucket.getDocCount())).collect(Collectors.toList());
 
         terms = aggregations.get(GitFilesRepositoryConstants.FULLTEXT_REPOSITORY_AGG);
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesDTO.java
index c53ff948e8bfec0f68c3eb725bae2503fdb39156..16c114b034b91a217a664f30c5b41752c63fd5ea 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesDTO.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/GitFilesDTO.java
@@ -25,8 +25,8 @@ public class GitFilesDTO {
         this.content = documentInfo.getContent();
 
         Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
-        String fragment = highlightFields.get(GitFilesRepositoryConstants.FULLTEXT_CONTENT).getFragments()[0].string();
-        this.fragment = new FragmentDTO().matchFillLine(documentInfo.getContent(), fragment, properties.getSearch());
+        String highlightedFragement = highlightFields.get(GitFilesRepositoryConstants.FULLTEXT_CONTENT).getFragments()[0].string();
+        this.fragment = new FragmentDTO().matchFillLine(documentInfo.getContent(), highlightedFragement, properties.getSearch());
 
         this.fileFormat = documentInfo.getFile().getFile_format();
         this.repository = documentInfo.getProject().getProject_name();
@@ -35,7 +35,7 @@ public class GitFilesDTO {
         this.fileExtension = documentInfo.getFile().getExtension();
         this.fileName = documentInfo.getFile().getFilename();
         this.filePath = documentInfo.getProject().getNamespace() + "/" + documentInfo.getFile().getFilename();
-        this.gitUrl = documentInfo.getProject().getUrl() + "/-/blob/master/" + documentInfo.getFile().getCommit_id() + "/" + filePath; // todo: use commit id
+        this.gitUrl = documentInfo.getProject().getUrl() + "/-/blob/master/" + documentInfo.getFile().getCommit_id() + "/" + filePath;
     }
 
     public String getContent() {
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/OAuth2ConfigDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/OAuth2ConfigDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..35bad4547e00991b183e9395ff1a81ae80de23b5
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/OAuth2ConfigDTO.java
@@ -0,0 +1,39 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import java.util.stream.Collectors;
+
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+
+/**
+ * transfers infos of an OAuth2Config to client
+ * @author Michael Breu
+ *
+ */
+public class OAuth2ConfigDTO {
+	
+
+	private String registrationId;
+	private String clientURL;
+	private String scopes;
+	
+	public String getRegistrationId() {
+		return registrationId;
+	}
+	public String getClientURL() {
+		return clientURL;
+	}
+	public String getScopes() {
+		return scopes;
+	}
+	
+	public OAuth2ConfigDTO(ClientRegistration registration) {
+		clientURL = registration.getRedirectUriTemplate();
+		registrationId = registration.getRegistrationId();
+		scopes = registration.getScopes().stream().collect(Collectors.joining( "," ));
+	}
+	
+	@Override
+	public String toString() {
+		return "OAuth2ConfigDTO [for '" + registrationId + "']";
+	}
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputDTO.java
index f5cab72d9f837007e27033a51918e7469264bb7b..86d4ff266a9e93b42631255860390f33872a919f 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputDTO.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputDTO.java
@@ -4,37 +4,28 @@ import java.util.List;
 
 public class SearchInputDTO {
     private String fulltextQuery;
-    private String metadataProgrammingLanguage;
-    private String metadataKeywords;
-    private String metadataNaturalLanguage;
-    private String metadataLicense;
-    private String metadataAuthor;
+    private SearchInputMetadataDTO metadata;
     private List<String> selectedRepository;
     private List<String> selectedUniversity;
     private List<String> selectedFileFormat;
 
     private int page;
+	public static final int PAGE_SIZE = 10;
 
-    public SearchInputDTO(String fulltextQuery, String metadataProgrammingLanguage, String metadataKeywords,
-                          String metadataNaturalLanguage, String metadataLicense, String metadataAuthor,
+    public SearchInputDTO() {
+    	// empty for jackson
+    }
+    public SearchInputDTO(String fulltextQuery, SearchInputMetadataDTO metadata,
                           List<String> selectedRepository, List<String> selectedUniversity,
                           List<String> selectedFileFormat, int page) {
         this.fulltextQuery = fulltextQuery;
-        this.metadataProgrammingLanguage = mapEmptyString(metadataProgrammingLanguage);
-        this.metadataKeywords = mapEmptyString(metadataKeywords);
-        this.metadataNaturalLanguage = mapEmptyString(metadataNaturalLanguage);
-        this.metadataLicense = mapEmptyString(metadataLicense);
-        this.metadataAuthor = mapEmptyString(metadataAuthor);
+        this.metadata = metadata;
         this.selectedRepository = selectedRepository;
         this.selectedUniversity = selectedUniversity;
         this.selectedFileFormat = selectedFileFormat;
         this.page = page;
     }
 
-    private static String mapEmptyString(String str) {
-        return "".equals(str) ? null : str;
-    }
-
     public String getFulltextQuery() {
         return fulltextQuery;
     }
@@ -43,44 +34,8 @@ public class SearchInputDTO {
         this.fulltextQuery = fulltextQuery;
     }
 
-    public String getMetadataProgrammingLanguage() {
-        return metadataProgrammingLanguage;
-    }
-
-    public void setMetadataProgrammingLanguage(String metadataProgrammingLanguage) {
-        this.metadataProgrammingLanguage = metadataProgrammingLanguage;
-    }
-
-    public void setMetadataKeywords(String metadataKeywords) {
-        this.metadataKeywords = metadataKeywords;
-    }
-
-    public String getMetadataKeywords() {
-        return metadataKeywords;
-    }
-
-    public String getMetadataNaturalLanguage() {
-        return metadataNaturalLanguage;
-    }
-
-    public void setMetadataNaturalLanguage(String metadataNaturalLanguage) {
-        this.metadataNaturalLanguage = metadataNaturalLanguage;
-    }
-
-    public String getMetadataLicense() {
-        return metadataLicense;
-    }
-
-    public void setMetadataLicense(String metadataLicense) {
-        this.metadataLicense = metadataLicense;
-    }
-
-    public String getMetadataAuthor() {
-        return metadataAuthor;
-    }
-
-    public void setMetadataAuthor(String metadataAuthor) {
-        this.metadataAuthor = metadataAuthor;
+    public SearchInputMetadataDTO getMetadata() {
+        return metadata;
     }
 
     public List<String> getSelectedRepository() {
@@ -116,9 +71,14 @@ public class SearchInputDTO {
     }
 
     public boolean hasMetadataInput() {
-        return !(metadataProgrammingLanguage == null
-            && metadataKeywords == null
-            && metadataAuthor == null
-            && metadataLicense == null);
+        return this.metadata.hasMetadataInput();
     }
+
+    @Override
+	public String toString() {
+		return "SearchInputDTO [fulltextQuery=" + fulltextQuery + ", selectedRepository=" + selectedRepository + ", selectedUniversity="
+				+ selectedUniversity + ", selectedFileFormat=" + selectedFileFormat + ", page=" + page + ", metadata: {" + metadata + "}]";
+	}
+
+
 }
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputMetadataDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputMetadataDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..5191974219edc29de472204a7b412f0127083a0f
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchInputMetadataDTO.java
@@ -0,0 +1,103 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import java.util.List;
+
+import at.ac.uibk.gitsearch.service.dto.UserProvidedMetadataDTO.ExerciseType;
+
+public class SearchInputMetadataDTO {
+    private String programmingLanguage;
+    private String keyword;
+    private String naturalLanguage;
+    private String license;
+    private String author;
+    private List<ExerciseType> types;
+
+    public SearchInputMetadataDTO() {
+    }
+
+    public SearchInputMetadataDTO(String programmingLanguage,
+                                  String keyword,
+                                  String naturalLanguage,
+                                  String license,
+                                  String author) {
+        this.programmingLanguage = mapEmptyString(programmingLanguage);
+        this.keyword = mapEmptyString(keyword);
+        this.naturalLanguage = mapEmptyString(naturalLanguage);
+        this.license = mapEmptyString(license);
+        this.author = mapEmptyString(author);
+    }
+
+    private static String mapEmptyString(String str) {
+        return "".equals(str) ? null : str;
+    }
+
+    public boolean hasMetadataInput() {
+        return !(programmingLanguage == null
+            && keyword == null
+            && author == null
+            && license == null);
+    }
+
+    public String getProgrammingLanguage() {
+        return programmingLanguage;
+    }
+
+    public void setProgrammingLanguage(String programmingLanguage) {
+        this.programmingLanguage = mapEmptyString(programmingLanguage);
+    }
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    public void setKeyword(String keyword) {
+        this.keyword = mapEmptyString(keyword);
+    }
+
+    public String getNaturalLanguage() {
+        return naturalLanguage;
+    }
+
+    public void setNaturalLanguage(String naturalLanguage) {
+        this.naturalLanguage = mapEmptyString(naturalLanguage);
+    }
+
+    public String getLicense() {
+        return license;
+    }
+
+    public void setLicense(String license) {
+        this.license = mapEmptyString(license);
+    }
+
+    public String getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(String author) {
+        this.author = mapEmptyString(author);
+    }
+
+    /**
+	 * @return the types
+	 */
+	public List<ExerciseType> getTypes() {
+		return types;
+	}
+
+	/**
+	 * @param types the types to set
+	 */
+	public void setTypes(List<ExerciseType> types) {
+		this.types = types;
+	}
+
+	@Override
+    public String toString() {
+        return "programmingLanguage: " + this.programmingLanguage
+            + ", keyword: " + this.keyword
+            + ", naturalLanguage: " + this.naturalLanguage
+            + ", license: " + this.license
+            + ", author: " + this.author;
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..739e5721dc5a4b7467e2b268032ad99632da9a74
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultDTO.java
@@ -0,0 +1,360 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class SearchResultDTO {
+	
+	public static class PluginActionInfo {
+		String plugin;
+		String action;
+		String commandName;
+		
+		public PluginActionInfo(String plugin, String action, String commandName) {
+			super();
+			this.plugin = plugin;
+			this.action = action;
+			this.commandName = commandName;
+		}
+		
+		public PluginActionInfo() {
+			// JSON
+		}
+		/**
+		 * @return the plugin
+		 */
+		public String getPlugin() {
+			return plugin;
+		}
+		/**
+		 * @param plugin the plugin to set
+		 */
+		public void setPlugin(String plugin) {
+			this.plugin = plugin;
+		}
+		/**
+		 * @return the action
+		 */
+		public String getAction() {
+			return action;
+		}
+		/**
+		 * @param action the action to set
+		 */
+		public void setAction(String action) {
+			this.action = action;
+		}
+		/**
+		 * @return the commandName
+		 */
+		public String getCommandName() {
+			return commandName;
+		}
+		/**
+		 * @param commandName the commandName to set
+		 */
+		public void setCommandName(String commandName) {
+			this.commandName = commandName;
+		}
+		
+		
+	}
+
+
+    private GitProject project;
+    private MetadataFile file;
+    private UserProvidedMetadataDTO metadata;
+    
+    private int ranking5;
+	private List<PluginActionInfo> supportedActions = new ArrayList<>(2);
+
+    private Integer views;
+    private Integer downloads;
+
+	public SearchResultDTO() {
+        // default constructor
+    }
+
+    public Integer getDownloads() {
+        return downloads;
+    }
+
+    public void setDownloads(Integer downloads) {
+        this.downloads = downloads;
+    }
+
+    public Integer getViews() {
+        return views;
+    }
+
+    public void setViews(Integer views) {
+        this.views = views;
+    }
+
+    /**
+     * clone constructor
+     *
+     * @param toClone
+     */
+    public SearchResultDTO(SearchResultDTO toClone) {
+        super();
+        this.project = toClone.project;
+        this.file = toClone.file;
+        this.metadata = toClone.metadata;
+    }
+
+    public GitProject getProject() {
+        return project;
+    }
+
+    public void setProject(GitProject project) {
+        this.project = project;
+    }
+
+    public MetadataFile getFile() {
+        return file;
+    }
+
+    public void setFile(MetadataFile file) {
+        this.file = file;
+    }
+
+    public UserProvidedMetadataDTO getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(UserProvidedMetadataDTO metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "SearchResultDTO : { project: " + this.project
+            + ", file: " + this.file
+            + ", metadata: " + this.metadata
+            + " }";
+    }
+
+    @Override
+    public int hashCode() {
+        int prime = 31;
+        int result = this.project.hashCode();
+        result = prime * result + this.file.hashCode();
+        result = prime * result + this.metadata.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof SearchResultDTO)) {
+            return false;
+        }
+        SearchResultDTO other = (SearchResultDTO) obj;
+        return Objects.equals(this.project, other.project)
+            && Objects.equals(this.file, other.file)
+            && Objects.equals(this.metadata, other.metadata);
+    }
+
+    public static class GitProject {
+        private int project_id;
+        private String project_name;
+        private String namespace;
+        private String main_group;
+        private String sub_group;
+        private String url;
+        private String last_activity_at;
+
+        public int getProject_id() {
+            return project_id;
+        }
+
+        public String getLast_activity_at() {
+            return last_activity_at;
+        }
+
+        public void setLast_activity_at(String last_activity_at) {
+            this.last_activity_at = last_activity_at;
+        }
+
+        public void setProject_id(int project_id) {
+            this.project_id = project_id;
+        }
+
+        public String getProject_name() {
+            return project_name;
+        }
+
+        public void setProject_name(String project_name) {
+            this.project_name = project_name;
+        }
+
+        public String getNamespace() {
+            return namespace;
+        }
+
+        public void setNamespace(String namespace) {
+            this.namespace = namespace;
+        }
+
+        public String getMain_group() {
+            return main_group;
+        }
+
+        public void setMain_group(String main_group) {
+            this.main_group = main_group;
+        }
+
+        public String getSub_group() {
+            return sub_group;
+        }
+
+        public void setSub_group(String sub_group) {
+            this.sub_group = sub_group;
+        }
+
+        public String getUrl() {
+            return url;
+        }
+
+        public void setUrl(String url) {
+            this.url = url;
+        }
+
+        public int getProjectId() {
+            return project_id;
+        }
+
+        public void setProjectId(int project_id) {
+            this.project_id = project_id;
+        }
+
+        @Override
+        public int hashCode() {
+            return Integer.hashCode(project_id);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof GitProject)) {
+                return false;
+            }
+            GitProject other = (GitProject) obj;
+            return this.project_id == other.project_id;
+        }
+
+        @Override
+        public String toString() {
+            return  "Project: { project_id: " + project_id
+                + ", project_name: " + project_name
+                + ", namespace: " + namespace
+                + ", main_group: " + main_group
+                + ", sub_group: " + sub_group
+                + ", url: " + url
+                + " }";
+        }
+    }
+    
+    /**
+	 * @return the ranking5
+	 */
+	public int getRanking5() {
+		return ranking5;
+	}
+
+	/**
+	 * @param ranking5 the ranking5 to set
+	 */
+	public void setRanking5(int ranking5) {
+		this.ranking5 = ranking5;
+	}
+
+	/**
+	 * @return the supportedActions
+	 */
+	public List<PluginActionInfo> getSupportedActions() {
+		return supportedActions;
+	}
+
+	/**
+	 * @param supportedActions the supportedActions to set
+	 */
+	public void setSupportedActions(List<PluginActionInfo> supportedActions) {
+		this.supportedActions = supportedActions;
+	}
+
+    public static class MetadataFile {
+        private String filename;
+        private String path;
+        private String commit_id;
+        private String indexing_date;
+
+        public String getFilename() {
+            return filename;
+        }
+
+        public void setFilename(String filename) {
+            this.filename = filename;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public void setPath(String path) {
+            this.path = path;
+        }
+
+        public String getCommit_id() {
+            return commit_id;
+        }
+
+        public void setCommit_id(String commit_id) {
+            this.commit_id = commit_id;
+        }
+
+        public String getIndexing_date() {
+            return indexing_date;
+        }
+
+        public void setIndexing_date(String indexing_date) {
+            this.indexing_date = indexing_date;
+        }
+        
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = this.path.hashCode();
+            result = prime * result + this.commit_id.hashCode();
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof MetadataFile)) {
+                return false;
+            }
+            MetadataFile other = (MetadataFile) obj;
+            return Objects.equals(this.path, other.path)
+                && Objects.equals(this.commit_id, other.commit_id);
+        }
+
+        @Override
+        public String toString() {
+            return "MetadataFile: { filename: " + this.filename
+                + ", path: " + this.path
+                + ", commit_id: " + this.commit_id
+                + ", indexing_date: " + this.indexing_date
+                + " }";
+        }
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultsDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultsDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..9625ca036837065269ffaaf90b2253acd3b2f5b3
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/SearchResultsDTO.java
@@ -0,0 +1,284 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import java.util.Date;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public class SearchResultsDTO {
+
+	@JsonFormat(shape = JsonFormat.Shape.OBJECT)
+	public enum ExerciseType {
+		PROGRAMMING_EXERCISE("programming exercise"), EXERCISE("exercise"), COLLECTION("collection"), OTHER("other");
+
+		ExerciseType(String externalName) {
+			this.externalName = externalName;
+		}
+
+		private String externalName;
+
+		@JsonValue
+		public String getExternalName() {
+			return externalName;
+		}
+	}
+	
+
+	public static class Person {
+		private String name;
+		private String affiliation;
+		private String email;
+
+		public String getName() {
+			return name;
+		}
+
+		public void setName(String name) {
+			this.name = name;
+		}
+
+		public String getAffiliation() {
+			return affiliation;
+		}
+
+		public void setAffiliation(String affiliation) {
+			this.affiliation = affiliation;
+		}
+
+		public String getEmail() {
+			return email;
+		}
+
+		public void setEmail(String email) {
+			this.email = email;
+		}
+
+		@Override
+		public String toString() {
+			return "Person: { name: " + this.name + ", affiliation: " + this.affiliation + ", email: " + this.email
+					+ "}";
+		}
+
+		@Override
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime * result + ((affiliation == null) ? 0 : affiliation.hashCode());
+			result = prime * result + ((email == null) ? 0 : email.hashCode());
+			result = prime * result + ((name == null) ? 0 : name.hashCode());
+			return result;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			Person other = (Person) obj;
+			if (affiliation == null) {
+				if (other.affiliation != null)
+					return false;
+			} else if (!affiliation.equals(other.affiliation))
+				return false;
+			if (email == null) {
+				if (other.email != null)
+					return false;
+			} else if (!email.equals(other.email))
+				return false;
+			if (name == null) {
+				if (other.name != null)
+					return false;
+			} else if (!name.equals(other.name))
+				return false;
+			return true;
+		}
+	}
+	
+	public enum GitProjectVisibility {
+		PRIVATE("private"), PUBLIC("public"), INTERNAL("internal");
+		
+		private String externalName;
+
+		private GitProjectVisibility(String externalName) {
+			this.externalName = externalName;
+		}
+
+		@JsonValue
+		public String getExternalName() {
+			return externalName;
+		}
+
+	}
+
+	public static class GitProject {
+		private int project_id;
+		private Date last_activity_at;
+		private String url;
+		private GitProjectVisibility visibility; 
+		private String namespace;
+		public int getProject_id() {
+			return project_id;
+		}
+
+		public void setProject_id(int project_id) {
+			this.project_id = project_id;
+		}
+
+		public int getProjectId() {
+			return project_id;
+		}
+
+		public void setProjectId(int project_id) {
+			this.project_id = project_id;
+		}
+
+		/**
+		 * @return the last_activity_at
+		 */
+		public Date getLast_activity_at() {
+			return last_activity_at;
+		}
+
+		/**
+		 * @param last_activity_at the last_activity_at to set
+		 */
+		public void setLast_activity_at(Date last_activity_at) {
+			this.last_activity_at = last_activity_at;
+		}
+
+		/**
+		 * @return the url
+		 */
+		public String getUrl() {
+			return url;
+		}
+
+		/**
+		 * @param url the url to set
+		 */
+		public void setUrl(String url) {
+			this.url = url;
+		}
+
+		/**
+		 * @return the visibility
+		 */
+		public GitProjectVisibility getVisibility() {
+			return visibility;
+		}
+
+		/**
+		 * @param visibility the visibility to set
+		 */
+		public void setVisibility(GitProjectVisibility visibility) {
+			this.visibility = visibility;
+		}
+
+		/**
+		 * @return the namespace
+		 */
+		public String getNamespace() {
+			return namespace;
+		}
+
+		/**
+		 * @param namespace the namespace to set
+		 */
+		public void setNamespace(String namespace) {
+			this.namespace = namespace;
+		}
+
+		@Override
+		public int hashCode() {
+			final int prime = 31;
+			int result = 1;
+			result = prime * result + project_id;
+			return result;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			if (this == obj)
+				return true;
+			if (obj == null)
+				return false;
+			if (getClass() != obj.getClass())
+				return false;
+			GitProject other = (GitProject) obj;
+			if (project_id != other.project_id)
+				return false;
+			return true;
+		}
+	}
+
+
+	private List<SearchResultDTO> searchResult;
+
+	/**
+	 *
+	 * @return the list of search results on this page, starting with pageStartIndex
+	 */
+	public List<SearchResultDTO> getSearchResult() {
+		return searchResult;
+	}
+
+	public void setSearchResult(List<SearchResultDTO> searchResult) {
+		this.searchResult = searchResult;
+	}
+
+	long hitCount;
+
+	long pageStartIndex;
+
+	public long getPageStartIndex() {
+		return pageStartIndex;
+	}
+
+	public SearchResultsDTO(List<SearchResultDTO> searchResult, long hitCount, long pageStartIndex) {
+		this.searchResult = searchResult;
+		this.hitCount = hitCount;
+		this.pageStartIndex = pageStartIndex;
+	}
+
+	public SearchResultsDTO() {
+
+	}
+
+	/**
+	 * returns the number of all hits.
+	 *
+	 * @return
+	 */
+	public long getHitCount() {
+		return hitCount;
+	}
+
+	public void setHitCount(long hitCount) {
+		this.hitCount = hitCount;
+	}
+
+	@Override
+	public String toString() {
+		final StringBuilder sb = new StringBuilder();
+		sb.append("SearchResultsDTO:");
+		sb.append(System.lineSeparator());
+		sb.append("hit count: ");
+		sb.append(this.hitCount);
+		sb.append(System.lineSeparator());
+		sb.append("page start index: ");
+		sb.append(this.pageStartIndex);
+		sb.append(System.lineSeparator());
+		sb.append("list of SearchResultsDTOs:");
+		sb.append(System.lineSeparator());
+		for (SearchResultDTO result : this.searchResult) {
+			sb.append(result);
+			sb.append(System.lineSeparator());
+			sb.append(System.lineSeparator());
+		}
+		return sb.toString();
+	}
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..c75f66f92216fce0cd45dadfc1706cbaa1817137
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTO.java
@@ -0,0 +1,79 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import javax.validation.constraints.*;
+import java.io.Serializable;
+
+/**
+ * A DTO for the {@link at.ac.uibk.gitsearch.domain.Statistics} entity.
+ */
+public class StatisticsDTO implements Serializable {
+    
+    private Long id;
+
+    private Integer views;
+
+    private Integer downloads;
+
+    private Long exerciseID;
+
+    
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Integer getViews() {
+        return views;
+    }
+
+    public void setViews(Integer views) {
+        this.views = views;
+    }
+
+    public Integer getDownloads() {
+        return downloads;
+    }
+
+    public void setDownloads(Integer downloads) {
+        this.downloads = downloads;
+    }
+
+    public Long getExerciseID() {
+        return exerciseID;
+    }
+
+    public void setExerciseID(Long exerciseID) {
+        this.exerciseID = exerciseID;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof StatisticsDTO)) {
+            return false;
+        }
+
+        return id != null && id.equals(((StatisticsDTO) o).id);
+    }
+
+    @Override
+    public int hashCode() {
+        return 31;
+    }
+
+    // prettier-ignore
+    @Override
+    public String toString() {
+        return "StatisticsDTO{" +
+            "id=" + getId() +
+            ", views=" + getViews() +
+            ", downloads=" + getDownloads() +
+            ", exerciseID=" + getExerciseID() +
+            "}";
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/dto/UserProvidedMetadataDTO.java b/src/main/java/at/ac/uibk/gitsearch/service/dto/UserProvidedMetadataDTO.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0ca107c2c98beb79d887642e9b96263b932f4ef
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/dto/UserProvidedMetadataDTO.java
@@ -0,0 +1,471 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public class UserProvidedMetadataDTO {
+
+    public UserProvidedMetadataDTO() {
+        // default constructor
+    }
+
+    private String metadataVersion; // just for YAML test data reader
+    private String title;
+    private String identifier;
+    private String version;
+    private String structure;
+
+    private String description;
+    private ExerciseType type;
+    private String license;
+    private String[] keyword;
+
+    private String[] format;
+    private String[] programmingLanguage;
+    private String[] language;
+    private String status;
+
+    private String educationLevel;
+
+    @JsonIgnore
+    private String audience;
+
+    private String timeRequired;
+
+    @JsonIgnore
+    private String[] collectionContent;
+
+    private Person[] creator;
+    private Person[] publisher;
+    private Person[] contributor;
+
+    private boolean deprecated;
+
+    private String difficulty;
+    private String[] source;
+
+    private String[] requires;
+    private String image;
+
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public ExerciseType getType() {
+        return type;
+    }
+
+    public void setType(ExerciseType type) {
+        this.type = type;
+    }
+
+    public String[] getLanguage() {
+        return language;
+    }
+
+    public void setLanguage(String[] language) {
+        this.language = language;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getEducationLevel() {
+        return educationLevel;
+    }
+
+    public void setEducationLevel(String educationLevel) {
+        this.educationLevel = educationLevel;
+    }
+
+    public String getAudience() {
+        return audience;
+    }
+
+    public void setAudience(String audience) {
+        this.audience = audience;
+    }
+
+    public String getTimeRequired() {
+        return timeRequired;
+    }
+
+    public void setTimeRequired(String timeRequired) {
+        this.timeRequired = timeRequired;
+    }
+
+    public Person[] getCreator() {
+        return creator;
+    }
+
+    public void setCreator(Person[] creator) {
+        this.creator = creator;
+    }
+
+    public Person[] getPublisher() {
+        return publisher;
+    }
+
+    public void setPublisher(Person[] publisher) {
+        this.publisher = publisher;
+    }
+
+    public Person[] getContributor() {
+        return contributor;
+    }
+
+    public void setContributor(Person[] contributor) {
+        this.contributor = contributor;
+    }
+
+    public boolean isDeprecated() {
+        return deprecated;
+    }
+
+    public void setDeprecated(boolean deprecated) {
+        this.deprecated = deprecated;
+    }
+
+    public String getDifficulty() {
+        return difficulty;
+    }
+
+    public void setDifficulty(String difficulty) {
+        this.difficulty = difficulty;
+    }
+
+    public String[] getSource() {
+        return source;
+    }
+
+    public void setSource(String[] source) {
+        this.source = source;
+    }
+
+    public String[] getRequires() {
+        return requires;
+    }
+
+    public void setRequires(String[] requires) {
+        this.requires = requires;
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    private String repositoryURL;
+
+    public String getMetadataVersion() {
+        return metadataVersion;
+    }
+
+    public void setMetadataVersion(String metadataVersion) {
+        this.metadataVersion = metadataVersion;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getLicense() {
+        return license;
+    }
+
+    public void setLicense(String license) {
+        this.license = license;
+    }
+
+    public String[] getKeyword() {
+        return keyword;
+    }
+
+    public void setKeyword(String[] keyword) {
+        this.keyword = keyword;
+    }
+
+    public String[] getFormat() {
+        return format;
+    }
+
+    public void setFormat(String[] format) {
+        this.format = format;
+    }
+
+    public String[] getProgrammingLanguage() {
+        return programmingLanguage;
+    }
+
+    public void setProgrammingLanguage(String[] programmingLanguage) {
+        this.programmingLanguage = programmingLanguage;
+    }
+
+    public String getRepositoryURL() {
+        return repositoryURL;
+    }
+
+    public void setRepositoryURL(String repositoryURL) {
+        this.repositoryURL = repositoryURL;
+    }
+
+    public String getStructure() {
+        return structure;
+    }
+
+    public void setStructure(String structure) {
+        this.structure = structure;
+    }
+
+    /**
+     * clone constructor
+     *
+     * @param toClone
+     */
+    public UserProvidedMetadataDTO(UserProvidedMetadataDTO toClone) {
+        super();
+        this.metadataVersion = toClone.metadataVersion;
+        this.title = toClone.title;
+        this.identifier = toClone.identifier;
+        this.version = toClone.version;
+        this.structure = toClone.structure;
+        this.description = toClone.description;
+        this.type = toClone.type;
+        this.license = toClone.license;
+        this.keyword = toClone.keyword;
+        this.format = toClone.format;
+        this.programmingLanguage = toClone.programmingLanguage;
+        this.language = toClone.language;
+        this.status = toClone.status;
+        this.educationLevel = toClone.educationLevel;
+        this.audience = toClone.audience;
+        this.timeRequired = toClone.timeRequired;
+        this.collectionContent = toClone.collectionContent;
+        this.creator = toClone.creator;
+        this.publisher = toClone.publisher;
+        this.contributor = toClone.contributor;
+        this.deprecated = toClone.deprecated;
+        this.difficulty = toClone.difficulty;
+        this.source = toClone.source;
+        this.requires = toClone.requires;
+        this.image = toClone.image;
+        this.repositoryURL = toClone.repositoryURL;
+    }
+
+    @Override
+    public String toString() {
+        return "UserProvidedMetadataDTO { audience: " + audience
+            + ", collectionContent: " + Arrays.toString(collectionContent)
+            + ", contributor: " + Arrays.toString(contributor)
+            + ", creator: " + Arrays.toString(creator)
+            + ", deprecated: " + deprecated
+            + ", description: " + description
+            + ", difficulty: " + difficulty
+            + ", educationLevel: " + educationLevel
+            + ", format: " + Arrays.toString(format)
+            + ", identifier:" + identifier
+            + ", image: " + image
+            + ", keyword: " + Arrays.toString(keyword)
+            + ", language: " + Arrays.toString(language)
+            + ", license: " + license
+            + ", metadataVersion: " + metadataVersion
+            + ", programmingLanguage: " + Arrays.toString(programmingLanguage)
+            + ", publisher: " + Arrays.toString(publisher)
+            + ", repositoryURL: " + repositoryURL
+            + ", requires: " + Arrays.toString(requires)
+            + ", source: " + Arrays.toString(source)
+            + ", status: " + status
+            + ", structure: " + structure
+            + ", timeRequired: " + timeRequired
+            + ", title: " + title
+            + ", type: " + type
+            + ", version: " + version
+            + " }";
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((audience == null) ? 0 : audience.hashCode());
+        result = prime * result + Arrays.hashCode(collectionContent);
+        result = prime * result + Arrays.hashCode(contributor);
+        result = prime * result + Arrays.hashCode(creator);
+        result = prime * result + (deprecated ? 1231 : 1237);
+        result = prime * result + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + ((difficulty == null) ? 0 : difficulty.hashCode());
+        result = prime * result + ((educationLevel == null) ? 0 : educationLevel.hashCode());
+        result = prime * result + Arrays.hashCode(format);
+        result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
+        result = prime * result + ((image == null) ? 0 : image.hashCode());
+        result = prime * result + Arrays.hashCode(keyword);
+        result = prime * result + Arrays.hashCode(language);
+        result = prime * result + ((license == null) ? 0 : license.hashCode());
+        result = prime * result + ((metadataVersion == null) ? 0 : metadataVersion.hashCode());
+        result = prime * result + Arrays.hashCode(programmingLanguage);
+        result = prime * result + Arrays.hashCode(publisher);
+        result = prime * result + ((repositoryURL == null) ? 0 : repositoryURL.hashCode());
+        result = prime * result + Arrays.hashCode(requires);
+        result = prime * result + Arrays.hashCode(source);
+        result = prime * result + ((status == null) ? 0 : status.hashCode());
+        result = prime * result + ((structure == null) ? 0 : structure.hashCode());
+        result = prime * result + ((timeRequired == null) ? 0 : timeRequired.hashCode());
+        result = prime * result + ((title == null) ? 0 : title.hashCode());
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof UserProvidedMetadataDTO)) {
+            return false;
+        }
+        UserProvidedMetadataDTO other = (UserProvidedMetadataDTO) obj;
+        return Objects.equals(this.audience, other.audience)
+            && Arrays.equals(this.collectionContent, other.collectionContent)
+            && Arrays.equals(this.contributor, other.contributor)
+            && Arrays.equals(this.creator, other.creator)
+            && this.deprecated == other.deprecated
+            && Objects.equals(this.description, other.description)
+            && Objects.equals(this.difficulty, other.difficulty)
+            && Objects.equals(this.educationLevel, other.educationLevel)
+            && Arrays.equals(this.format, other.format)
+            && Objects.equals(this.identifier, other.identifier)
+            && Objects.equals(this.image, other.image)
+            && Arrays.equals(this.keyword, other.keyword)
+            && Arrays.equals(this.language, other.language)
+            && Objects.equals(this.license, other.license)
+            && Objects.equals(this.metadataVersion, other.metadataVersion)
+            && Arrays.equals(this.programmingLanguage, other.programmingLanguage)
+            && Arrays.equals(this.publisher, other.publisher)
+            && Objects.equals(this.repositoryURL, other.repositoryURL)
+            && Arrays.equals(this.requires, other.requires)
+            && Arrays.equals(this.source, other.source)
+            && Objects.equals(this.status, other.status)
+            && Objects.equals(this.structure, other.structure)
+            && Objects.equals(this.timeRequired, other.timeRequired)
+            && Objects.equals(this.title, other.title)
+            && this.type == other.type
+            && Objects.equals(this.version, other.version);
+    }
+
+
+    @JsonFormat(shape = JsonFormat.Shape.OBJECT)
+    public enum ExerciseType {
+        PROGRAMMING_EXERCISE("programming exercise"), EXERCISE("exercise"), COLLECTION("collection"), OTHER("other");
+
+        ExerciseType(String externalName) {
+            this.externalName = externalName;
+        }
+
+        private final String externalName;
+
+        @JsonValue
+        public String getExternalName() {
+            return externalName;
+        }
+    }
+
+    public static class Person {
+        private String name;
+        private String affiliation;
+        private String email;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getAffiliation() {
+            return affiliation;
+        }
+
+        public void setAffiliation(String affiliation) {
+            this.affiliation = affiliation;
+        }
+
+        public String getEmail() {
+            return email;
+        }
+
+        public void setEmail(String email) {
+            this.email = email;
+        }
+
+        @Override
+        public String toString() {
+            return "Person: { name: " + this.name
+                + ", affiliation: " + this.affiliation
+                + ", email: " + this.email
+                + " }";
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((affiliation == null) ? 0 : affiliation.hashCode());
+            result = prime * result + ((email == null) ? 0 : email.hashCode());
+            result = prime * result + ((name == null) ? 0 : name.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof Person)) {
+                return false;
+            }
+            Person other = (Person) obj;
+            return Objects.equals(this.affiliation, other.affiliation)
+                && Objects.equals(this.email, other.email)
+                && Objects.equals(this.name, other.name);
+        }
+    }
+}
+
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/impl/StatisticsServiceImpl.java b/src/main/java/at/ac/uibk/gitsearch/service/impl/StatisticsServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..36d5adfb9173abb0106c8c380c66f7c5eccd4cee
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/impl/StatisticsServiceImpl.java
@@ -0,0 +1,91 @@
+package at.ac.uibk.gitsearch.service.impl;
+
+import at.ac.uibk.gitsearch.service.StatisticsService;
+import at.ac.uibk.gitsearch.domain.Statistics;
+import at.ac.uibk.gitsearch.repository.StatisticsRepository;
+import at.ac.uibk.gitsearch.repository.search.StatisticsSearchRepository;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+import at.ac.uibk.gitsearch.service.mapper.StatisticsMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Optional;
+
+import static org.elasticsearch.index.query.QueryBuilders.*;
+
+/**
+ * Service Implementation for managing {@link Statistics}.
+ */
+@Service
+@Transactional
+public class StatisticsServiceImpl implements StatisticsService {
+
+    private final Logger log = LoggerFactory.getLogger(StatisticsServiceImpl.class);
+
+    private final StatisticsRepository statisticsRepository;
+
+    private final StatisticsMapper statisticsMapper;
+
+    private final StatisticsSearchRepository statisticsSearchRepository;
+
+    public StatisticsServiceImpl(StatisticsRepository statisticsRepository, StatisticsMapper statisticsMapper, StatisticsSearchRepository statisticsSearchRepository) {
+        this.statisticsRepository = statisticsRepository;
+        this.statisticsMapper = statisticsMapper;
+        this.statisticsSearchRepository = statisticsSearchRepository;
+    }
+
+    @Override
+    public StatisticsDTO save(StatisticsDTO statisticsDTO) {
+        log.debug("Request to save Statistics : {}", statisticsDTO);
+        Statistics statistics = statisticsMapper.toEntity(statisticsDTO);
+        statistics = statisticsRepository.save(statistics);
+        StatisticsDTO result = statisticsMapper.toDto(statistics);
+        statisticsSearchRepository.save(statistics);
+        return result;
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public Page<StatisticsDTO> findAll(Pageable pageable) {
+        log.debug("Request to get all Statistics");
+        return statisticsRepository.findAll(pageable)
+            .map(statisticsMapper::toDto);
+    }
+
+
+    @Override
+    @Transactional(readOnly = true)
+    public Optional<StatisticsDTO> findOne(Long id) {
+        log.debug("Request to get Statistics : {}", id);
+        return statisticsRepository.findById(id)
+            .map(statisticsMapper::toDto);
+    }
+
+    @Override
+    public Optional<StatisticsDTO> findOneByExerciseID(Long id) {
+        log.debug("Request to get Statistics by ExerciseId : {}", id);
+        return statisticsRepository.findByExerciseID(id)
+            .map(statisticsMapper::toDto);
+    }
+
+    @Override
+    public void delete(Long id) {
+        log.debug("Request to delete Statistics : {}", id);
+        statisticsRepository.deleteById(id);
+        statisticsSearchRepository.deleteById(id);
+    }
+
+    @Override
+    @Transactional(readOnly = true)
+    public Page<StatisticsDTO> search(String query, Pageable pageable) {
+        log.debug("Request to search for a page of Statistics for query {}", query);
+        return statisticsSearchRepository.search(queryStringQuery(query), pageable)
+            .map(statisticsMapper::toDto);
+    }
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/mapper/EntityMapper.java b/src/main/java/at/ac/uibk/gitsearch/service/mapper/EntityMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..8effc6ce964787a43405c608e029914585795eea
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/mapper/EntityMapper.java
@@ -0,0 +1,21 @@
+package at.ac.uibk.gitsearch.service.mapper;
+
+import java.util.List;
+
+/**
+ * Contract for a generic dto to entity mapper.
+ *
+ * @param <D> - DTO type parameter.
+ * @param <E> - Entity type parameter.
+ */
+
+public interface EntityMapper <D, E> {
+
+    E toEntity(D dto);
+
+    D toDto(E entity);
+
+    List <E> toEntity(List<D> dtoList);
+
+    List <D> toDto(List<E> entityList);
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapper.java b/src/main/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..0144416937b2114a1708133c1ef46562205bafa0
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapper.java
@@ -0,0 +1,25 @@
+package at.ac.uibk.gitsearch.service.mapper;
+
+
+import at.ac.uibk.gitsearch.domain.*;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+
+import org.mapstruct.*;
+
+/**
+ * Mapper for the entity {@link Statistics} and its DTO {@link StatisticsDTO}.
+ */
+@Mapper(componentModel = "spring", uses = {})
+public interface StatisticsMapper extends EntityMapper<StatisticsDTO, Statistics> {
+
+
+
+    default Statistics fromId(Long id) {
+        if (id == null) {
+            return null;
+        }
+        Statistics statistics = new Statistics();
+        statistics.setId(id);
+        return statistics;
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/oauth2/OAuth2ConfigService.java b/src/main/java/at/ac/uibk/gitsearch/service/oauth2/OAuth2ConfigService.java
new file mode 100644
index 0000000000000000000000000000000000000000..87ee5cf21b1cbd7c26f974e3fb5c41112eb71591
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/oauth2/OAuth2ConfigService.java
@@ -0,0 +1,53 @@
+package at.ac.uibk.gitsearch.service.oauth2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.oauth2.client.registration.ClientRegistration;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
+import org.springframework.stereotype.Service;
+
+import at.ac.uibk.gitsearch.service.dto.OAuth2ConfigDTO;
+
+/**
+ * provides functions for the retrieval of OAuth2 services
+ * @author Michael Breu
+  */
+@Service
+
+public class OAuth2ConfigService {
+	
+    private final Logger log = LoggerFactory.getLogger(OAuth2ConfigService.class);
+	
+	@Autowired
+	private ClientRegistrationRepository oauth2ClientRegistrationRepositories;
+
+	public OAuth2ConfigDTO findByRegistrationId(String registrationId) {
+		final ClientRegistration registration = oauth2ClientRegistrationRepositories.findByRegistrationId(registrationId);
+		if(registration==null) return null;
+		return buildConfigFromRegistration(registration);
+	}
+	
+	public List<OAuth2ConfigDTO> getPublicRegistrations() {
+    	if (oauth2ClientRegistrationRepositories instanceof InMemoryClientRegistrationRepository) {
+			InMemoryClientRegistrationRepository imOAuth2ClientRegistrationRepositories = (InMemoryClientRegistrationRepository) oauth2ClientRegistrationRepositories;
+			final List<OAuth2ConfigDTO> result = new ArrayList<>();
+			imOAuth2ClientRegistrationRepositories.iterator().forEachRemaining(registration -> result.add(buildConfigFromRegistration(registration)));
+			return result;
+    	}
+    	else {
+    		log.warn("Cannot find any OAuth2 Registrations");
+    		return Collections.emptyList();
+    	}
+	}
+
+	private OAuth2ConfigDTO buildConfigFromRegistration(ClientRegistration registration) {
+		return new OAuth2ConfigDTO(registration);
+	}
+	
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/service/testSearchResults.xml b/src/main/java/at/ac/uibk/gitsearch/service/testSearchResults.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3992f45f4f3f32d176f54cef28390e10bfba902d
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/service/testSearchResults.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- can be moved to tests, after correct search results are implemented  -->
+<SearchResultsDTO>
+	<SearchResultDTO title="Result 1" programmingLanguage="Java" language="de">
+		<description>Search result one is a mocked search result. This description should fill some space to test the formatting.</description>
+		
+	</SearchResultDTO>
+</SearchResultsDTO>
\ No newline at end of file
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/AccountResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/AccountResource.java
index 2dd871fc3222ddadbbc6f005f7c9ebbd018c1622..94fef9aebda0a4318ea04b1270eaf87094bf6b1a 100644
--- a/src/main/java/at/ac/uibk/gitsearch/web/rest/AccountResource.java
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/AccountResource.java
@@ -1,5 +1,22 @@
 package at.ac.uibk.gitsearch.web.rest;
 
+import java.util.Optional;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
 import at.ac.uibk.gitsearch.domain.User;
 import at.ac.uibk.gitsearch.repository.UserRepository;
 import at.ac.uibk.gitsearch.security.SecurityUtils;
@@ -7,20 +24,12 @@ import at.ac.uibk.gitsearch.service.MailService;
 import at.ac.uibk.gitsearch.service.UserService;
 import at.ac.uibk.gitsearch.service.dto.PasswordChangeDTO;
 import at.ac.uibk.gitsearch.service.dto.UserDTO;
-import at.ac.uibk.gitsearch.web.rest.errors.*;
+import at.ac.uibk.gitsearch.web.rest.errors.EmailAlreadyUsedException;
+import at.ac.uibk.gitsearch.web.rest.errors.InvalidPasswordException;
+import at.ac.uibk.gitsearch.web.rest.errors.LoginAlreadyUsedException;
 import at.ac.uibk.gitsearch.web.rest.vm.KeyAndPasswordVM;
 import at.ac.uibk.gitsearch.web.rest.vm.ManagedUserVM;
 
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.validation.Valid;
-import java.util.*;
-
 /**
  * REST controller for managing the current user's account.
  */
@@ -29,7 +38,12 @@ import java.util.*;
 public class AccountResource {
 
     private static class AccountResourceException extends RuntimeException {
-        private AccountResourceException(String message) {
+        /**
+		 * 
+		 */
+		private static final long serialVersionUID = -4660672207242510582L;
+
+		private AccountResourceException(String message) {
             super(message);
         }
     }
@@ -58,15 +72,15 @@ public class AccountResource {
      * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already used.
      */
     @PostMapping("/register")
-    // @ResponseStatus(HttpStatus.CREATED)
-    @ResponseStatus(HttpStatus.FORBIDDEN)
+    @ResponseStatus(HttpStatus.CREATED)
+    //@ResponseStatus(HttpStatus.FORBIDDEN)
     public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
         return;
-//        if (!checkPasswordLength(managedUserVM.getPassword())) {
-//            throw new InvalidPasswordException();
-//        }
-//        User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());
-//        mailService.sendActivationEmail(user);
+    //    if (!checkPasswordLength(managedUserVM.getPassword())) {
+    //        throw new InvalidPasswordException();
+    //    }
+    //    User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());
+    //    mailService.sendActivationEmail(user);
     }
 
     /**
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/ApplicationInfoResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/ApplicationInfoResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..5093d04aa23b2aa576f486be9ed6e839e0efb588
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/ApplicationInfoResource.java
@@ -0,0 +1,42 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import at.ac.uibk.gitsearch.config.ApplicationProperties;
+import at.ac.uibk.gitsearch.config.ApplicationProperties.DeploymentInfo;
+
+/**
+ * REST controller to retrieve deployment infos.
+ */
+@RestController
+@RequestMapping("/api")
+public class ApplicationInfoResource {
+
+	
+    @SuppressWarnings("unused")
+	private final Logger log = LoggerFactory.getLogger(ApplicationInfoResource.class);
+
+    private final ApplicationProperties applicationProperties;
+
+    public ApplicationInfoResource(ApplicationProperties applicationProperties) {
+    	this.applicationProperties = applicationProperties;
+    }
+
+    /**
+     * {@code GET  /deploymentInfo} : get generic deployment info.
+     *
+     */
+    @GetMapping("/deploymentInfo")
+    public ApplicationProperties.DeploymentInfo getApplicationInfo() {
+        final DeploymentInfo deploymentInfo = applicationProperties.getDeploymentInfo();
+        if(deploymentInfo==null || "${git.branch}".equals(deploymentInfo.getBranch())) {
+        	return new ApplicationProperties.DeploymentInfo(); // application info is not initialized properly! Return an empty info
+        }
+		return deploymentInfo;
+    }
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/GitFilesResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/GitFilesResource.java
index 59fb41529170f63a2e5ba2bfddedf49b8a17f8a1..9c27564fa459bd8664aa089b305acb2c5ef5d11b 100644
--- a/src/main/java/at/ac/uibk/gitsearch/web/rest/GitFilesResource.java
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/GitFilesResource.java
@@ -1,10 +1,7 @@
 package at.ac.uibk.gitsearch.web.rest;
 
-import at.ac.uibk.gitsearch.es.model.DocumentInfo;
-import at.ac.uibk.gitsearch.repository.search.GitFilesRepository;
-import at.ac.uibk.gitsearch.service.dto.GitFilesAggregationDTO;
-import at.ac.uibk.gitsearch.service.dto.GitFilesPageDetailsDTO;
-import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
+import java.io.IOException;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
@@ -14,8 +11,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
-import java.io.IOException;
-import java.util.List;
+import at.ac.uibk.gitsearch.es.model.DocumentInfo;
+import at.ac.uibk.gitsearch.repository.search.GitFilesRepository;
+import at.ac.uibk.gitsearch.service.dto.GitFilesAggregationDTO;
 
 /**
  * REST controller for managing {@link DocumentInfo}.
@@ -44,6 +42,7 @@ public class GitFilesResource {
      * //     * @return the result of the search.
      * //
      */
+    /*
     @GetMapping("/_search/git-files/page-details")
     public GitFilesPageDetailsDTO
     gitFilesPageDetails(@RequestParam String fulltextQuery, @RequestParam String metadataProgrammingLanguage,
@@ -59,6 +58,7 @@ public class GitFilesResource {
         // return searchResultSearchRepository.findByContent(query);
         return gitFilesRepository.pageDetails(searchInput, pageSize);
     }
+    */
 
     /**
      * //     * {@code SEARCH  /_search/git-files/aggregation?query=:query} : search for the searchResult corresponding
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/OAuth2ConfigResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/OAuth2ConfigResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..668693a8d704fd116d8ccf45512e03853749d640
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/OAuth2ConfigResource.java
@@ -0,0 +1,39 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import java.util.List;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import at.ac.uibk.gitsearch.service.dto.OAuth2ConfigDTO;
+import at.ac.uibk.gitsearch.service.oauth2.OAuth2ConfigService;
+
+/**
+ * Controller to authenticate users.
+ */
+@RestController
+@RequestMapping("/oauth2Config")
+public class OAuth2ConfigResource {
+
+	private OAuth2ConfigService oAuth2ConfigService;
+
+    public OAuth2ConfigResource(OAuth2ConfigService oAuth2ConfigService) {
+    	this.oAuth2ConfigService = oAuth2ConfigService;
+    }
+
+    @GetMapping("/config/{configId}")
+    public ResponseEntity<OAuth2ConfigDTO> getOAuth2Config(@PathVariable(value = "configId") String configId) {
+
+    	return new ResponseEntity<>(oAuth2ConfigService.findByRegistrationId(configId), HttpStatus.OK);
+    }
+    
+    @GetMapping("/allConfigs")
+    public ResponseEntity<List<OAuth2ConfigDTO>> getOAuth2Configs() {
+    	return new ResponseEntity<>(oAuth2ConfigService.getPublicRegistrations(), HttpStatus.OK);
+    }
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/PluginInterfaceResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/PluginInterfaceResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e11fd48f7f346ea62b3b524eca410a11342d106
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/PluginInterfaceResource.java
@@ -0,0 +1,82 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.codeability.sharing.plugins.api.ShoppingBasket;
+import org.gitlab4j.api.GitLabApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+
+import at.ac.uibk.gitsearch.es.model.DocumentInfo;
+import at.ac.uibk.gitsearch.service.ShoppingBasketService;
+import at.ac.uibk.gitsearch.service.ShoppingBasketService.ShoppingBasketInfoDTO;
+import at.ac.uibk.gitsearch.service.ShoppingBasketService.ShoppingBasketRedirectInfoDTO;
+
+/**
+ * REST controller for managing {@link DocumentInfo}.
+ */
+@RestController
+@RequestMapping("/api")
+@Transactional
+public class PluginInterfaceResource {
+
+	private final Logger log = LoggerFactory.getLogger(PluginInterfaceResource.class);
+
+	@Autowired
+	private ShoppingBasketService basketService;
+	
+	/**
+	 * assigns a shopping basket on server, and provides a redirect link together with the shopping basket token.
+	 * @param basketInfo
+	 * @return
+	 */
+    @PostMapping("/pluginIF/getPluginRedirectInfos") 
+	public ShoppingBasketRedirectInfoDTO getRedirectInfos(@RequestBody ShoppingBasketInfoDTO basketInfo) {
+    	final String baseUrl = 
+    			ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
+    	return basketService.getRedirectInfo(basketInfo, baseUrl);
+    }
+    
+    
+	
+    /**
+     * {@code SEARCH  /search/page-details} : search for the searchResults corresponding
+     *  to the query.
+     *
+	 * @param query the query of the searchResult search.
+	 * @return the result of the search.
+     */
+    @GetMapping("/pluginIF/v0.1/basket/{basketToken}") 
+    public ShoppingBasket getBasket(
+    		@PathVariable("basketToken") String basketToken) throws IOException {
+        log.debug("REST request for basket  {}", basketToken);
+        return basketService.getBasket(basketToken);
+    }
+
+    @GetMapping("/pluginIF/v0.1/basket/{basketToken}/repository/{exerciseId}") 
+    public ResponseEntity<?> getRepositoryZip(@PathVariable String basketToken, @PathVariable int exerciseId) throws IOException {
+    	InputStream zip;
+		try {
+			zip = basketService.getRepositoryZip(basketToken, exerciseId);
+		} catch (GitLabApiException e) {
+			return ResponseEntity.unprocessableEntity().body(e.getMessage());
+		}
+    	InputStreamResource resource = new InputStreamResource(zip);
+        return ResponseEntity.ok().contentType(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM).header("filename", "Repository"+exerciseId).body(resource);
+
+    }
+
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..5b0f6039680d9beb0defa35e7c3170b95add95f9
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/SearchResource.java
@@ -0,0 +1,195 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import org.gitlab4j.api.GitLabApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.core.io.Resource;
+
+import at.ac.uibk.gitsearch.es.model.DocumentInfo;
+import at.ac.uibk.gitsearch.service.SearchService;
+import at.ac.uibk.gitsearch.service.StatisticsService;
+import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry;
+import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+import at.ac.uibk.gitsearch.web.util.HeaderUtil;
+
+/**
+ * REST controller for managing {@link DocumentInfo}.
+ */
+@RestController
+@RequestMapping("/api")
+@Transactional
+public class SearchResource {
+
+    @Value("${jhipster.clientApp.name}")
+    private String applicationName;
+
+    private static final String ENTITY_NAME = "SearchResource";
+
+
+    private final Logger log = LoggerFactory.getLogger(SearchResource.class);
+
+    private final SearchService searchService;
+
+    private final StatisticsService statisticsService;
+
+    public SearchResource(SearchService searchService, StatisticsService statisticsService) {
+        this.searchService = searchService;
+        this.statisticsService = statisticsService;
+    }
+
+    /**
+     * {@code SEARCH  /search/page-details} : search for the searchResults corresponding
+     * to the query.
+     *
+     * @param query the query of the searchResult search.
+     * @return the result of the search.
+     */
+    @PostMapping("/search/page-details")
+    public SearchResultsDTO
+    searchPageDetails(@RequestBody SearchInputDTO query) throws IOException {
+        log.debug("REST request to search  {}", query);
+        return searchService.searchResultPage(query, (long) SearchInputDTO.PAGE_SIZE * (long) query.getPage(), SearchInputDTO.PAGE_SIZE);
+    }
+
+    /**
+     * returns all keyword autocompletes for keyWord
+     *
+     * @param keyWordPrefix
+     * @return
+     * @throws IOException
+     */
+    @GetMapping("/search/keywordsAutoComplete")
+    public List<AutoCompleteEntry> getKeywordsAutoComplete(@RequestParam String keyWordPrefix) throws IOException {
+        return searchService.getKeywordsAutoComplete(keyWordPrefix);
+    }
+
+    /**
+     * returns all creator autocompletes
+     *
+     * @param creatorPrefix
+     * @return
+     * @throws IOException
+     */
+    @GetMapping("/search/creatorAutoComplete")
+    public List<AutoCompleteEntry> getCreatorAutoComplete(@RequestParam String creatorPrefix) throws IOException {
+        return searchService.getCreatorAutoComplete(creatorPrefix);
+    }
+
+    /**
+     * returns all contributor autocompletes
+     *
+     * @param contributorPrefix
+     * @return
+     * @throws IOException
+     */
+    @GetMapping("/search/contributorAutoComplete")
+    public List<AutoCompleteEntry> getContributorAutoComplete(@RequestParam String contributorPrefix) throws IOException {
+        return searchService.getContributorAutoComplete(contributorPrefix);
+    }
+
+	/**
+	 * returns all contributor and author autocompletes
+	 *
+	 * @param contributorPrefix
+	 * @return
+	 * @throws IOException
+	 */
+    @GetMapping("/search/contributorCreatorAutoComplete")
+	public List<AutoCompleteEntry> getContributorCreatorAutoComplete(String contributorPrefix) throws IOException {
+		return searchService.getContributorCreatorAutoComplete(contributorPrefix);
+	}
+
+    /**
+     * returns all programmingLanguage autocompletes for keyWord
+     *
+     * @param programmingLanguagePrefix
+     * @return
+     * @throws IOException
+     */
+    @GetMapping("/search/programmingLanguageAutoComplete")
+    public List<AutoCompleteEntry> getProgrammingLanguageAutoComplete(@RequestParam String programmingLanguagePrefix) throws IOException {
+        return searchService.getProgrammingLanguageAutoComplete(programmingLanguagePrefix);
+    }
+
+
+        /**
+     * POST /programming-exercises/:exerciseId/export-programmingExercise : sends all repositories and details of the programming exercise as zip
+     *
+     * @param exerciseId the id of the exercise to get the repos from
+     * ResponseEntity with status
+     * @throws IOException if something during the zip process went wrong
+     */
+    @PostMapping("/programming-exercises/{exerciseId}/export-programming-exercise")
+    public ResponseEntity<Resource> exportProgrammingExercise(@PathVariable long exerciseId) throws IOException {
+
+        File zipFile = searchService.exportExercise(exerciseId);
+        
+        if (zipFile == null) {
+            return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(applicationName, true, ENTITY_NAME, "internalServerError",
+                    "There was an error on the server and the zip file could not be created.")).body(null);
+        }
+
+        /*
+         * Customized FileInputStream to delete and therefore clean up the returned files
+         */
+        class NewFileInputStream extends FileInputStream {
+
+            File file;
+
+            public NewFileInputStream(File file) throws FileNotFoundException {
+                super(file);
+                this.file = file;
+            }
+
+            public void close() throws IOException {
+                super.close();
+                file.delete();
+            }
+
+        }
+        InputStreamResource resource = new InputStreamResource(new NewFileInputStream(zipFile));
+
+        log.debug("REST request to get Statistics for ExerciseID : {}", exerciseId);
+        Optional<StatisticsDTO> statisticsDTO = statisticsService.findOneByExerciseID(exerciseId);
+        if(statisticsDTO.isPresent()){
+            StatisticsDTO newStats = statisticsDTO.get();
+            newStats.setDownloads(newStats.getDownloads() + 1);
+            statisticsService.save(newStats);
+            log.debug("REST increased number of downloads for ExerciseID : {}", exerciseId);
+        }
+        if(!statisticsDTO.isPresent()){
+            StatisticsDTO newStats = new StatisticsDTO();
+            newStats.setDownloads(1);
+            newStats.setViews(1);
+            newStats.setExerciseID(exerciseId);
+            statisticsService.save(newStats);
+            log.debug("Created new statistics entry for exerciseID: {}", exerciseId);
+        }
+
+
+        return ResponseEntity.ok().contentLength(zipFile.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).header("filename", zipFile.getName()).body(resource);
+    }
+
+
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..aff0f6d515fd975a8b9cfbe8d218a3f34849686d
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/StatisticsResource.java
@@ -0,0 +1,203 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import at.ac.uibk.gitsearch.service.GitlabService;
+
+import at.ac.uibk.gitsearch.service.StatisticsService;
+import at.ac.uibk.gitsearch.web.rest.errors.BadRequestAlertException;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+
+import io.github.jhipster.web.util.HeaderUtil;
+import io.github.jhipster.web.util.PaginationUtil;
+import io.github.jhipster.web.util.ResponseUtil;
+
+import org.gitlab4j.api.GitLabApiException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import javax.validation.Valid;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.StreamSupport;
+
+import static org.elasticsearch.index.query.QueryBuilders.*;
+
+/**
+ * REST controller for managing {@link at.ac.uibk.gitsearch.domain.Statistics}.
+ */
+@RestController
+@RequestMapping("/api")
+public class StatisticsResource {
+
+    private final Logger log = LoggerFactory.getLogger(StatisticsResource.class);
+
+    private static final String ENTITY_NAME = "statistics";
+
+    @Value("${jhipster.clientApp.name}")
+    private String applicationName;
+
+    private final StatisticsService statisticsService;
+
+    @Autowired
+    private final GitlabService gitlabService;
+
+    public StatisticsResource(StatisticsService statisticsService, GitlabService gitlabService) {
+        this.statisticsService = statisticsService;
+        this.gitlabService = gitlabService;
+    }
+
+    /**
+     * {@code POST  /statistics} : Create a new statistics.
+     *
+     * @param statisticsDTO the statisticsDTO to create.
+     * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with
+     *         body the new statisticsDTO, or with status {@code 400 (Bad Request)}
+     *         if the statistics has already an ID.
+     * @throws URISyntaxException if the Location URI syntax is incorrect.
+     */
+    @PostMapping("/statistics")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<StatisticsDTO> createStatistics(@Valid @RequestBody StatisticsDTO statisticsDTO)
+            throws URISyntaxException {
+        log.debug("REST request to save Statistics : {}", statisticsDTO);
+        if (statisticsDTO.getId() != null) {
+            throw new BadRequestAlertException("A new statistics cannot already have an ID", ENTITY_NAME, "idexists");
+        }
+        StatisticsDTO result = statisticsService.save(statisticsDTO);
+        return ResponseEntity
+                .created(new URI("/api/statistics/" + result.getId())).headers(HeaderUtil
+                        .createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString()))
+                .body(result);
+    }
+
+    /**
+     * {@code PUT  /statistics} : Updates an existing statistics.
+     *
+     * @param statisticsDTO the statisticsDTO to update.
+     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body
+     *         the updated statisticsDTO, or with status {@code 400 (Bad Request)}
+     *         if the statisticsDTO is not valid, or with status
+     *         {@code 500 (Internal Server Error)} if the statisticsDTO couldn't be
+     *         updated.
+     * @throws URISyntaxException if the Location URI syntax is incorrect.
+     */
+    @PutMapping("/statistics")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<StatisticsDTO> updateStatistics(@Valid @RequestBody StatisticsDTO statisticsDTO)
+            throws URISyntaxException {
+        log.debug("REST request to update Statistics : {}", statisticsDTO);
+        if (statisticsDTO.getId() == null) {
+            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
+        }
+        StatisticsDTO result = statisticsService.save(statisticsDTO);
+        return ResponseEntity.ok().headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME,
+                statisticsDTO.getId().toString())).body(result);
+    }
+
+    /**
+     * {@code GET  /statistics} : get all the statistics.
+     *
+     * @param pageable the pagination information.
+     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list
+     *         of statistics in body.
+     */
+    @GetMapping("/statistics")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<List<StatisticsDTO>> getAllStatistics(Pageable pageable) {
+        log.debug("REST request to get a page of Statistics");
+        Page<StatisticsDTO> page = statisticsService.findAll(pageable);
+        HttpHeaders headers = PaginationUtil
+                .generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
+        return ResponseEntity.ok().headers(headers).body(page.getContent());
+    }
+
+    /**
+     * {@code GET  /statistics/:id} : get the "id" statistics.
+     *
+     * @param id the id of the statisticsDTO to retrieve.
+     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body
+     *         the statisticsDTO, or with status {@code 404 (Not Found)}.
+     */
+    @GetMapping("/statistics/{id}")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<StatisticsDTO> getStatistics(@PathVariable Long id) {
+        log.debug("REST request to get Statistics : {}", id);
+        Optional<StatisticsDTO> statisticsDTO = statisticsService.findOne(id);
+        return ResponseUtil.wrapOrNotFound(statisticsDTO);
+    }
+
+    @GetMapping("/statistics/exercise/{id}")
+    public ResponseEntity<StatisticsDTO> getStatisticsByExerciseId(@PathVariable Long id) {
+
+        log.debug("REST request to get Statistics for ExerciseID : {}", id);
+        Optional<StatisticsDTO> statisticsDTO = statisticsService.findOneByExerciseID(id);
+        Boolean repoExists = gitlabService.repositoryExists(id.toString());
+
+        if (repoExists) {
+            if (statisticsDTO.isPresent()) {
+                StatisticsDTO newStats = statisticsDTO.get();
+                newStats.setViews(newStats.getViews() + 1);
+                statisticsService.save(newStats);
+            }
+            if (!statisticsDTO.isPresent()) {
+                StatisticsDTO newStats = new StatisticsDTO();
+                newStats.setDownloads(0);
+                newStats.setViews(1);
+                newStats.setExerciseID(id);
+                statisticsService.save(newStats);
+                log.debug("Created new statistics entry for exerciseID: {}", id);
+                statisticsDTO = Optional.of(newStats);
+            }
+
+            return ResponseUtil.wrapOrNotFound(statisticsDTO);
+        }
+        else{
+            return null;
+        }
+    }
+
+    /**
+     * {@code DELETE  /statistics/:id} : delete the "id" statistics.
+     *
+     * @param id the id of the statisticsDTO to delete.
+     * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
+     */
+    @DeleteMapping("/statistics/{id}")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<Void> deleteStatistics(@PathVariable Long id) {
+        log.debug("REST request to delete Statistics : {}", id);
+        statisticsService.delete(id);
+        return ResponseEntity.noContent()
+                .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString()))
+                .build();
+    }
+
+    /**
+     * {@code SEARCH  /_search/statistics?query=:query} : search for the statistics
+     * corresponding to the query.
+     *
+     * @param query    the query of the statistics search.
+     * @param pageable the pagination information.
+     * @return the result of the search.
+     */
+    @GetMapping("/_search/statistics")
+    @PreAuthorize("hasAnyRole('ADMIN')")
+    public ResponseEntity<List<StatisticsDTO>> searchStatistics(@RequestParam String query, Pageable pageable) {
+        log.debug("REST request to search for a page of Statistics for query {}", query);
+        Page<StatisticsDTO> page = statisticsService.search(query, pageable);
+        HttpHeaders headers = PaginationUtil
+                .generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
+        return ResponseEntity.ok().headers(headers).body(page.getContent());
+    }
+}
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/UserJWTController.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/UserJWTController.java
index aef04b589c81c4485349813ea6f58a1444a51b58..4dbaa4b3b37ccad14129803d8f6731e668b2055e 100644
--- a/src/main/java/at/ac/uibk/gitsearch/web/rest/UserJWTController.java
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/UserJWTController.java
@@ -15,6 +15,8 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Map;
+
 import javax.validation.Valid;
 
 /**
@@ -47,6 +49,26 @@ public class UserJWTController {
         httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
         return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
     }
+    
+    @PostMapping("/refreshToken")
+    public ResponseEntity<JWTToken> refreshToken(@RequestParam("token") String token) {
+    	if(!tokenProvider.validateToken(token)) {
+    		return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED);
+    	} else {
+    		 Authentication authentication = tokenProvider.getAuthentication(token);
+    		 @SuppressWarnings("unchecked")
+			Map<String, String> details = (Map<String, String>) authentication.getDetails();
+    		 if(!details.containsKey(TokenProvider.PRE_TOKEN_CLAIM)) {
+    	    		return new ResponseEntity<>(null, HttpStatus.UNAUTHORIZED);
+    		 }
+   	         SecurityContextHolder.getContext().setAuthentication(authentication);
+   	        String jwt = tokenProvider.createToken(authentication, false);
+   	        HttpHeaders httpHeaders = new HttpHeaders();
+   	        httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
+   	        return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
+    	}
+    }
+    
     /**
      * Object to return as body in JWT Authentication.
      */
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/util/HeaderUtil.java b/src/main/java/at/ac/uibk/gitsearch/web/util/HeaderUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e9fc019b0a76cf8f4f49937ec512dbf32792c3e
--- /dev/null
+++ b/src/main/java/at/ac/uibk/gitsearch/web/util/HeaderUtil.java
@@ -0,0 +1,63 @@
+package at.ac.uibk.gitsearch.web.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+/**
+ * Utility class for HTTP headers creation.
+ */
+public final class HeaderUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
+
+    private HeaderUtil() {
+    }
+
+    public static HttpHeaders createAlert(String applicationName, String message, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createAlert(applicationName, message, param);
+    }
+
+    public static HttpHeaders createEntityCreationAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityCreationAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createEntityUpdateAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityUpdateAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createEntityDeletionAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityDeletionAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createFailureAlert(String applicationName, boolean enableTranslation, String entityName, String errorKey, String defaultMessage) {
+        HttpHeaders headers = io.github.jhipster.web.util.HeaderUtil.createFailureAlert(applicationName, enableTranslation, entityName, errorKey, defaultMessage);
+        headers.add("X-" + applicationName + "-message", defaultMessage);
+        return headers;
+    }
+
+    /**
+     * Creates a authorization headers for a given username and password
+     * @param username the username
+     * @param password the password
+     * @return the acceptHeader
+     */
+    public static HttpHeaders createAuthorization(String username, String password) {
+        HttpHeaders acceptHeaders = new HttpHeaders() {
+
+            {
+                set(com.google.common.net.HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString());
+                set(com.google.common.net.HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString());
+            }
+        };
+        String authorization = username + ":" + password;
+        String basic = new String(Base64.getEncoder().encode(authorization.getBytes(StandardCharsets.UTF_8)));
+        acceptHeaders.set("Authorization", "Basic " + basic);
+
+        return acceptHeaders;
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/.h2.server.properties b/src/main/resources/.h2.server.properties
index 8642f2b2838a135d70afdc49a9d491d5aa0c0f48..877fe1ca4b39aa5ecfa03cc8fe03facaf9a357f9 100644
--- a/src/main/resources/.h2.server.properties
+++ b/src/main/resources/.h2.server.properties
@@ -1,6 +1,6 @@
 #H2 Server Properties
-#Fri Nov 20 12:38:10 CET 2020
+#Wed Mar 24 16:32:43 CET 2021
 0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./target/h2db/db/gitsearch|gitsearch
+webSSL=false
 webAllowOthers=true
 webPort=8082
-webSSL=false
diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..aa370797fbfaa7f72fa747b294f6a52b305e560d
--- /dev/null
+++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -0,0 +1,7 @@
+{"properties": [
+  {
+    "name": "spring.security.oauth2.client.gitlabOidc.icon",
+    "type": "java.lang.String",
+    "description": "custom configurations: Icon for oauth2 client"
+  }
+]}
\ No newline at end of file
diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt
index e0bc55aaffa9b6c51d63a1a67ecf8d533f51e0d2..5046c498f99fcd4c07bef65a386f8d8552eb18ec 100644
--- a/src/main/resources/banner.txt
+++ b/src/main/resources/banner.txt
@@ -1,10 +1,11 @@
 
-  ${AnsiColor.GREEN}      ██╗${AnsiColor.RED} ██╗   ██╗ ████████╗ ███████╗   ██████╗ ████████╗ ████████╗ ███████╗
-  ${AnsiColor.GREEN}      ██║${AnsiColor.RED} ██║   ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗
-  ${AnsiColor.GREEN}      ██║${AnsiColor.RED} ████████║    ██║    ███████╔╝ ╚█████╗     ██║    ██████╗   ███████╔╝
-  ${AnsiColor.GREEN}██╗   ██║${AnsiColor.RED} ██╔═══██║    ██║    ██╔════╝   ╚═══██╗    ██║    ██╔═══╝   ██╔══██║
-  ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║   ██║ ████████╗ ██║       ██████╔╝    ██║    ████████╗ ██║  ╚██╗
-  ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝   ╚═╝ ╚═══════╝ ╚═╝       ╚═════╝     ╚═╝    ╚═══════╝ ╚═╝   ╚═╝
-
-${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓  :: Running Spring Boot ${spring-boot.version} ::
+  ${AnsiColor.GREEN}  ::::::::  :::    :::     :::     :::::::::  ::::::::::: ::::    :::  ::::::::  
+  ${AnsiColor.GREEN} :+:    :+: :+:    :+:   :+: :+:   :+:    :+:     :+:     :+:+:   :+: :+:    :+: 
+  ${AnsiColor.GREEN} +:+        +:+    +:+  +:+   +:+  +:+    +:+     +:+     :+:+:+  +:+ +:+        
+  ${AnsiColor.GREEN} +#++:++#++ +#++:++#++ +#++:++#++: +#++:++#:      +#+     +#+ +:+ +#+ :#:        
+  ${AnsiColor.GREEN}        +#+ +#+    +#+ +#+     +#+ +#+    +#+     +#+     +#+  +#+#+# +#+   +#+# 
+  ${AnsiColor.GREEN} #+#    #+# #+#    #+# #+#     #+# #+#    #+#     #+#     #+#   #+#+# #+#    #+# 
+  ${AnsiColor.GREEN}  ########  ###    ### ###     ### ###    ### ########### ###    ####  ########  
+ 
+${AnsiColor.BRIGHT_BLUE}:: Sharing Plattform 🤓  :: Running Spring Boot ${spring-boot.version} ::
 :: https://www.jhipster.tech ::${AnsiColor.DEFAULT}
diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml
index 73e633c9e885407bf5067616646b696977810a96..1b933ff69b3d7c142ba4370017a9eff85e71793d 100644
--- a/src/main/resources/config/application-dev.yml
+++ b/src/main/resources/config/application-dev.yml
@@ -33,7 +33,7 @@ spring:
       #- tls
   devtools:
     restart:
-      enabled: true
+      enabled: false
       additional-exclude: static/**,.h2.server.properties
     livereload:
       enabled: false # we use Webpack dev server + BrowserSync for livereload
@@ -71,6 +71,16 @@ spring:
     cache-duration: PT1S # 1 second, see the ISO 8601 standard
   thymeleaf:
     cache: false
+  security:
+    oauth2:
+      client:
+        provider:
+          gitlabOidc:
+            issuer-uri: https://sharing.codeability-austria.uibk.ac.at
+        registration:
+          gitlabOidc:
+            client-id: 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24
+            client-secret: 6f480635241f420a361581f4837594ea6f48f5ee6f515c1aa89f325dd922dbb0
 
 server:
   port: 8080
@@ -95,6 +105,10 @@ jhipster:
     allow-credentials: true
     max-age: 1800
   security:
+    oauth2:
+      audience:
+# TODO: audience seems not really relevant, could be omitted? It is identical with client-id above
+#        - 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24
     authentication:
       jwt:
         # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one)
@@ -127,4 +141,10 @@ jhipster:
 # https://www.jhipster.tech/common-application-properties/
 # ===================================================================
 
-# application:
+application:
+  registeredPlugins:
+   - "http://localhost:8081/api/sharing/config" 
+   - "http://localhost:8082/api/sharingPluginConfig"
+  gitlab:
+   url: https://sharing.codeability-austria.uibk.ac.at/
+   generalAccessToken: zPxPmJE3UXAZJpBzxqej
\ No newline at end of file
diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml
index 50305bc6c0ef9dd6cd0148fa9b988a1a93bf6447..5e9c46f28a0fcb14ee670081b29c1ca35f297be1 100644
--- a/src/main/resources/config/application-prod.yml
+++ b/src/main/resources/config/application-prod.yml
@@ -63,6 +63,17 @@ spring:
     password:
   thymeleaf:
     cache: true
+  security:
+    oauth2:
+      client:
+        provider:
+          gitlabOidc:
+            issuer-uri: TODO https://sharing.codeability-austria.uibk.ac.at
+        registration:
+          gitlabOidc:
+            client-id: TODO 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24
+            client-secret: TODO 6f480635241f420a361581f4837594ea6f48f5ee6f515c1aa89f325dd922dbb0
+
 
 # ===================================================================
 # To enable TLS in production, generate a certificate using:
@@ -116,6 +127,10 @@ jhipster:
         # Token is valid 24 hours
         token-validity-in-seconds: 86400
         token-validity-in-seconds-for-remember-me: 2592000
+    oauth2:
+# TODO: audience seems not really relevant, could be omitted? It is identical with client-id above
+      audience:
+        - TODO or omit
   mail: # specific JHipster mail property, for standard properties see MailProperties
     base-url: http://my-server-url-to-change # Modify according to your server's URL
   metrics:
@@ -141,4 +156,10 @@ jhipster:
 # https://www.jhipster.tech/common-application-properties/
 # ===================================================================
 
-# application:
+application:
+  registeredPlugins:
+   - "http://localhost:8081/api/sharing/config" 
+   - "http://localhost:8082/api/sharingPluginConfig"
+  gitlab:
+   url: https://sharing-codeability.uibk.ac.at/
+   generalAccessToken: ${APPLICATION_GITLAB_GENERALACCESSTOKEN}
\ No newline at end of file
diff --git a/src/main/resources/config/application-staging.yml b/src/main/resources/config/application-staging.yml
new file mode 100644
index 0000000000000000000000000000000000000000..21e59b7c299881df306ed38c155c1c5c5323357c
--- /dev/null
+++ b/src/main/resources/config/application-staging.yml
@@ -0,0 +1,160 @@
+# ===================================================================
+# Spring Boot configuration for the "staging" profile.
+#
+# This configuration overrides the application.yml file.
+#
+# More information on profiles: https://www.jhipster.tech/profiles/
+# More information on configuration properties: https://www.jhipster.tech/common-application-properties/
+# ===================================================================
+
+# ===================================================================
+# Standard Spring Boot properties.
+# Full reference is available at:
+# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
+# ===================================================================
+
+logging:
+  level:
+    ROOT: INFO
+    io.github.jhipster: INFO
+    at.ac.uibk.gitsearch: INFO
+
+management:
+  metrics:
+    export:
+      prometheus:
+        enabled: false
+
+spring:
+  devtools:
+    restart:
+      enabled: false
+    livereload:
+      enabled: false
+  datasource:
+    type: com.zaxxer.hikari.HikariDataSource
+    url: jdbc:mysql://localhost:3306/gitsearch?useUnicode=true&characterEncoding=utf8&useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&createDatabaseIfNotExist=true
+    username: root
+    password:
+    hikari:
+      poolName: Hikari
+      auto-commit: false
+      data-source-properties:
+        cachePrepStmts: true
+        prepStmtCacheSize: 250
+        prepStmtCacheSqlLimit: 2048
+        useServerPrepStmts: true
+  jpa:
+    show-sql: false
+  data:
+    jest:
+      uri: http://localhost:9200
+  # see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-nosql.html#boot-features-connecting-to-elasticsearch-jest
+  elasticsearch:
+    rest:
+      uris: http://localhost:9200
+  # Replace by 'prod, faker' to add the faker context and have sample data loaded in production
+  liquibase:
+    contexts: prod
+  mail:
+    host: localhost
+    port: 25
+    username:
+    password:
+  thymeleaf:
+    cache: true
+  security:
+    oauth2:
+      client:
+        provider:
+          gitlabOidc:
+            issuer-uri: ${SECURITY_OAUTH2_CLIENT_PROVIDER_GITLABOIDC_ISSUERURI}
+        registration:
+          gitlabOidc:
+            client-id: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTID}
+            client-secret: ${SECURITY_OAUTH2_CLIENT_REGISTRATION_GITLABOIDC_CLIENTSECRET}
+
+# ===================================================================
+# To enable TLS in staging, generate a certificate using:
+# keytool -genkey -alias gitsearch -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
+#
+# You can also use Let's Encrypt:
+# https://maximilian-boehm.com/hp2121/Create-a-Java-Keystore-JKS-from-Let-s-Encrypt-Certificates.htm
+#
+# Then, modify the server.ssl properties so your "server" configuration looks like:
+#
+# server:
+#   port: 443
+#   ssl:
+#     key-store: classpath:config/tls/keystore.p12
+#     key-store-password: password
+#     key-store-type: PKCS12
+#     key-alias: selfsigned
+#     # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/)
+#     ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
+# ===================================================================
+server:
+  port: 8080
+  compression:
+    enabled: true
+    mime-types: text/html,text/xml,text/plain,text/css, application/javascript, application/json
+    min-response-size: 1024
+
+# ===================================================================
+# JHipster specific properties
+#
+# Full reference is available at: https://www.jhipster.tech/common-application-properties/
+# ===================================================================
+
+jhipster:
+  http:
+    cache: # Used by the CachingHttpHeadersFilter
+      timeToLiveInDays: 1461
+  cache: # Cache configuration
+    ehcache: # Ehcache configuration
+      time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache
+      max-entries: 1000 # Number of objects in each cache entry
+  security:
+    authentication:
+      jwt:
+        # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one)
+        # As this is the STAGING configuration, you MUST change the default key, and store it securely:
+        # - In the JHipster Registry (which includes a Spring Cloud Config server)
+        # - In a separate `application-staging.yml` file, in the same folder as your executable JAR file
+        # - In the `JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET` environment variable
+        base64-secret: ${JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64SECRET}
+        # Token is valid 24 hours
+        token-validity-in-seconds: 86400
+        token-validity-in-seconds-for-remember-me: 2592000
+  mail: # specific JHipster mail property, for standard properties see MailProperties
+    base-url: http://my-server-url-to-change # Modify according to your server's URL
+  metrics:
+    logs: # Reports metrics in the logs
+      enabled: false
+      report-frequency: 60 # in seconds
+  logging:
+    use-json-format: false # By default, logs are not in Json format
+    logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration
+      enabled: false
+      host: localhost
+      port: 5000
+      queue-size: 512
+  audit-events:
+    retention-period: 30 # Number of days before audit events are deleted.
+
+# ===================================================================
+# Application specific properties
+# Add your own application properties here, see the ApplicationProperties class
+# to have type-safe configuration, like in the JHipsterProperties above
+#
+# More documentation is available at:
+# https://www.jhipster.tech/common-application-properties/
+# ===================================================================
+
+application:
+  registeredPlugins:
+   - "https://artemis.codeability-austria.uibk.ac.at/api/sharing/config" 
+  gitlab:
+   url: https://sharing.codeability-austria.uibk.ac.at/
+   generalAccessToken: ${APPLICATION_GITLAB_GENERALACCESSTOKEN}
+   
\ No newline at end of file
diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml
index 448de84fa391a74452a71c9bf47e09d72c221fae..c126a39945ca61c24151b7ce635eb3ce1d8c1333 100644
--- a/src/main/resources/config/application.yml
+++ b/src/main/resources/config/application.yml
@@ -22,7 +22,7 @@ management:
         include: ['configprops', 'env', 'health', 'info', 'jhimetrics', 'logfile', 'loggers', 'prometheus', 'threaddump']
   endpoint:
     health:
-      show-details: when_authorized
+      show-details: WHEN_AUTHORIZED
       roles: 'ROLE_ADMIN'
     jhimetrics:
       enabled: true
@@ -148,13 +148,16 @@ jhipster:
     default-include-pattern: /api/.*
     title: gitsearch API
     description: gitsearch API documentation
-    version: 0.0.1
+    version: 0.1.0
     terms-of-service-url:
     contact-name:
     contact-url:
     contact-email:
     license: unlicensed
     license-url:
+    protocols: 
+     - http
+     - https
 # ===================================================================
 # Application specific properties
 # Add your own application properties here, see the ApplicationProperties class
@@ -168,6 +171,6 @@ application:
   search:
     highlight-pre: <mark><strong>
     highlight-post: </strong></mark>
-  gitlab:
-    url: http://dev-exchange.codeability.org
-    mainGroup: exchange
+  deployment-info:
+    commit-id: "${gitCommitId}"
+    branch: "${gitBranch}"
\ No newline at end of file
diff --git a/src/main/resources/config/liquibase/changelog/20210319162139_added_entity_Statistics.xml b/src/main/resources/config/liquibase/changelog/20210319162139_added_entity_Statistics.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3f1b43bff6a312a5d98ebda5bd38aa373b4c49ee
--- /dev/null
+++ b/src/main/resources/config/liquibase/changelog/20210319162139_added_entity_Statistics.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<databaseChangeLog
+    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.9.xsd
+                        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
+
+        <property name="autoIncrement" value="true"/>
+
+    <!--
+        Added the entity Statistics.
+    -->
+    <changeSet id="20210319162139-1" author="jhipster">
+        <createTable tableName="statistics">
+            <column name="id" type="bigint" autoIncrement="${autoIncrement}">
+                <constraints primaryKey="true" nullable="false"/>
+            </column>
+            <column name="views" type="integer">
+                <constraints nullable="true" />
+            </column>
+            <column name="downloads" type="integer">
+                <constraints nullable="false" />
+            </column>
+            <column name="exercise_id" type="bigint">
+                <constraints nullable="true" />
+            </column>
+            <!-- jhipster-needle-liquibase-add-column - JHipster will add columns here -->
+        </createTable>
+    </changeSet>
+
+    <changeSet id="20210319162139-1-relations" author="jhipster">
+
+    </changeSet>
+    <!-- jhipster-needle-liquibase-add-changeset - JHipster will add changesets here -->
+
+    <!--
+        Load sample data generated with Faker.js
+        - This data can be easily edited using a CSV editor (or even MS Excel) and
+          is located in the 'src/main/resources/config/liquibase/fake-data' directory
+        - By default this data is applied when running with the JHipster 'dev' profile.
+          This can be customized by adding or removing 'faker' in the 'spring.liquibase.contexts'
+          Spring Boot configuration key.
+    -->
+    <changeSet id="20210319162139-1-data" author="jhipster" context="faker">
+        <loadData
+                  file="config/liquibase/fake-data/statistics.csv"
+                  separator=";"
+                  tableName="statistics">
+            <column name="id" type="numeric"/>
+            <column name="views" type="numeric"/>
+            <column name="downloads" type="numeric"/>
+            <column name="exercise_id" type="numeric"/>
+            <!-- jhipster-needle-liquibase-add-loadcolumn - JHipster (and/or extensions) can add load columns here -->
+        </loadData>
+    </changeSet>
+
+</databaseChangeLog>
diff --git a/src/main/resources/config/liquibase/fake-data/statistics.csv b/src/main/resources/config/liquibase/fake-data/statistics.csv
new file mode 100644
index 0000000000000000000000000000000000000000..acf46c8bd297845b0748e759ca4f9510cf566e0b
--- /dev/null
+++ b/src/main/resources/config/liquibase/fake-data/statistics.csv
@@ -0,0 +1,11 @@
+id;views;downloads;exercise_id
+1;28654;425;57423
+2;13381;97074;76629
+3;48989;23775;33451
+4;45574;12711;64602
+5;8149;3891;3865
+6;79424;6522;94307
+7;87891;8136;34539
+8;57940;77167;46436
+9;49135;24678;26221
+10;629;91062;58843
diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml
index 6b937fedf6b3688ce313de683dbc15fb1a457a5b..b81304dbc3992aea7a207b527e932b2b6b710005 100644
--- a/src/main/resources/config/liquibase/master.xml
+++ b/src/main/resources/config/liquibase/master.xml
@@ -14,6 +14,7 @@
     <property name="uuidType" value="varchar(36)" dbms="h2, mysql, mariadb"/>
 
     <include file="config/liquibase/changelog/00000000000000_initial_schema.xml" relativeToChangelogFile="false"/>
+    <include file="config/liquibase/changelog/20210319162139_added_entity_Statistics.xml" relativeToChangelogFile="false"/>
     <!-- jhipster-needle-liquibase-add-changelog - JHipster will add liquibase changelogs here -->
     <!-- jhipster-needle-liquibase-add-constraints-changelog - JHipster will add liquibase constraints changelogs here -->
     <!-- jhipster-needle-liquibase-add-incremental-changelog - JHipster will add incremental liquibase changelogs here -->
diff --git a/src/main/resources/swagger-ts-generator/.gitignore b/src/main/resources/swagger-ts-generator/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..398f81b25abe5e341600b5b7d600c24f39b83b8b
--- /dev/null
+++ b/src/main/resources/swagger-ts-generator/.gitignore
@@ -0,0 +1,2 @@
+/models/
+/enums.ts
diff --git a/src/main/resources/swagger-ts-generator/generate.js b/src/main/resources/swagger-ts-generator/generate.js
new file mode 100644
index 0000000000000000000000000000000000000000..26efd3340af8fc20274055dac439bb483c36fb38
--- /dev/null
+++ b/src/main/resources/swagger-ts-generator/generate.js
@@ -0,0 +1,16 @@
+/* eslint @typescript-eslint/no-var-requires: "off" */
+const { generateTSFiles } = require('swagger-ts-generator');
+ 
+// const config = {
+//    file: __dirname + '\\swagger.json'
+// };
+ 
+generateTSFiles(
+	// Better read from URL http://localhost:8080/v2/api-docs
+    {"swagger":"2.0","info":{"description":"gitsearch API documentation","version":"0.1.0","title":"gitsearch API","contact":{},"license":{"name":"unlicensed"}},"host":"localhost:8080","basePath":"/","tags":[{"name":"account-resource","description":"Account Resource"},{"name":"git-files-resource","description":"Git Files Resource"},{"name":"search-resource","description":"Search Resource"},{"name":"user-jwt-controller","description":"User JWT Controller"},{"name":"user-resource","description":"User Resource"}],"schemes":["http","https"],"paths":{"/api/_search/git-files/aggregation":{"get":{"tags":["git-files-resource"],"summary":"gitFilesAggregation","operationId":"gitFilesAggregationUsingGET","produces":["*/*"],"parameters":[{"name":"query","in":"query","description":"query","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GitFilesAggregationDTO"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/_search/git-files/page-details":{"get":{"tags":["git-files-resource"],"summary":"gitFilesPageDetails","operationId":"gitFilesPageDetailsUsingGET","produces":["*/*"],"parameters":[{"name":"fulltextQuery","in":"query","description":"fulltextQuery","required":true,"type":"string"},{"name":"metadataAuthor","in":"query","description":"metadataAuthor","required":true,"type":"string"},{"name":"metadataKeywords","in":"query","description":"metadataKeywords","required":true,"type":"string"},{"name":"metadataLicense","in":"query","description":"metadataLicense","required":true,"type":"string"},{"name":"metadataNaturalLanguage","in":"query","description":"metadataNaturalLanguage","required":true,"type":"string"},{"name":"metadataProgrammingLanguage","in":"query","description":"metadataProgrammingLanguage","required":true,"type":"string"},{"name":"page","in":"query","description":"page","required":true,"type":"integer","format":"int32"},{"name":"pageSize","in":"query","description":"pageSize","required":true,"type":"integer","format":"int32"},{"name":"selectedFileFormat","in":"query","description":"selectedFileFormat","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"selectedRepository","in":"query","description":"selectedRepository","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"},{"name":"selectedUniversity","in":"query","description":"selectedUniversity","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/GitFilesPageDetailsDTO"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/_search/users/{query}":{"get":{"tags":["user-resource"],"summary":"search","operationId":"searchUsingGET","produces":["*/*"],"parameters":[{"name":"query","in":"path","description":"query","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/User"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/account":{"get":{"tags":["account-resource"],"summary":"getAccount","operationId":"getAccountUsingGET","produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/UserDTO"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"post":{"tags":["account-resource"],"summary":"saveAccount","operationId":"saveAccountUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"userDTO","description":"userDTO","required":true,"schema":{"$ref":"#/definitions/UserDTO"}}],"responses":{"200":{"description":"OK"},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/account/change-password":{"post":{"tags":["account-resource"],"summary":"changePassword","operationId":"changePasswordUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"passwordChangeDto","description":"passwordChangeDto","required":true,"schema":{"$ref":"#/definitions/PasswordChangeDTO"}}],"responses":{"200":{"description":"OK"},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/account/reset-password/finish":{"post":{"tags":["account-resource"],"summary":"finishPasswordReset","operationId":"finishPasswordResetUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"keyAndPassword","description":"keyAndPassword","required":true,"schema":{"$ref":"#/definitions/KeyAndPasswordVM"}}],"responses":{"200":{"description":"OK"},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/account/reset-password/init":{"post":{"tags":["account-resource"],"summary":"requestPasswordReset","operationId":"requestPasswordResetUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"mail","description":"mail","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/activate":{"get":{"tags":["account-resource"],"summary":"activateAccount","operationId":"activateAccountUsingGET","produces":["*/*"],"parameters":[{"name":"key","in":"query","description":"key","required":true,"type":"string"}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/authenticate":{"get":{"tags":["account-resource"],"summary":"isAuthenticated","operationId":"isAuthenticatedUsingGET","produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"string"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"post":{"tags":["user-jwt-controller"],"summary":"authorize","operationId":"authorizeUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"loginVM","description":"loginVM","required":true,"schema":{"$ref":"#/definitions/LoginVM"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/JWTToken"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/register":{"post":{"tags":["account-resource"],"summary":"registerAccount","operationId":"registerAccountUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"managedUserVM","description":"managedUserVM","required":true,"schema":{"$ref":"#/definitions/ManagedUserVM"}}],"responses":{"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/search/page-details":{"post":{"tags":["search-resource"],"summary":"searchPageDetails","operationId":"searchPageDetailsUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"query","description":"query","required":true,"schema":{"$ref":"#/definitions/SearchInputDTO"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/SearchResultsDTO"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/users":{"get":{"tags":["user-resource"],"summary":"getAllUsers","operationId":"getAllUsersUsingGET","produces":["*/*"],"parameters":[{"name":"page","in":"query","description":"Page number of the requested page","required":false,"type":"integer","format":"int32"},{"name":"size","in":"query","description":"Size of a page","required":false,"type":"integer","format":"int32"},{"name":"sort","in":"query","description":"Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.","required":false,"type":"array","items":{"type":"string"},"collectionFormat":"multi"}],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"$ref":"#/definitions/UserDTO"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"post":{"tags":["user-resource"],"summary":"createUser","operationId":"createUserUsingPOST","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"userDTO","description":"userDTO","required":true,"schema":{"$ref":"#/definitions/UserDTO"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/User"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"put":{"tags":["user-resource"],"summary":"updateUser","operationId":"updateUserUsingPUT","consumes":["application/json"],"produces":["*/*"],"parameters":[{"in":"body","name":"userDTO","description":"userDTO","required":true,"schema":{"$ref":"#/definitions/UserDTO"}}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/UserDTO"}},"201":{"description":"Created"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/users/authorities":{"get":{"tags":["user-resource"],"summary":"getAuthorities","operationId":"getAuthoritiesUsingGET","produces":["*/*"],"responses":{"200":{"description":"OK","schema":{"type":"array","items":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false}},"/api/users/{login:^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$}":{"get":{"tags":["user-resource"],"summary":"getUser","operationId":"getUserUsingGET","produces":["*/*"],"parameters":[{"name":"login","in":"path","description":"login","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/UserDTO"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}},"deprecated":false},"delete":{"tags":["user-resource"],"summary":"deleteUser","operationId":"deleteUserUsingDELETE","produces":["*/*"],"parameters":[{"name":"login","in":"path","description":"login","required":true,"type":"string"}],"responses":{"200":{"description":"OK"},"204":{"description":"No Content"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"}},"deprecated":false}}},"definitions":{"ExerciseType":{"type":"object","title":"ExerciseType"},"FragmentDTO":{"type":"object","properties":{"firstLineNumber":{"type":"integer","format":"int32"},"fragment":{"type":"string"}},"title":"FragmentDTO"},"FrequencyDTOOfstring":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"integer","format":"int64"}},"title":"FrequencyDTOOfstring"},"GitFilesAggregationDTO":{"type":"object","properties":{"fileFormat":{"type":"array","items":{"$ref":"#/definitions/FrequencyDTOOfstring"}},"repositories":{"type":"array","items":{"$ref":"#/definitions/FrequencyDTOOfstring"}},"university":{"type":"array","items":{"$ref":"#/definitions/FrequencyDTOOfstring"}}},"title":"GitFilesAggregationDTO"},"GitFilesDTO":{"type":"object","properties":{"content":{"type":"string"},"fileExtension":{"type":"string"},"fileFormat":{"type":"string"},"fileName":{"type":"string"},"filePath":{"type":"string"},"fragment":{"$ref":"#/definitions/FragmentDTO"},"gitUrl":{"type":"string"},"repository":{"type":"string"},"subGroup":{"type":"string"},"url":{"type":"string"}},"title":"GitFilesDTO"},"GitFilesPageDetailsDTO":{"type":"object","properties":{"gitFiles":{"type":"array","items":{"$ref":"#/definitions/GitFilesDTO"}},"hitCount":{"type":"integer","format":"int64"}},"title":"GitFilesPageDetailsDTO"},"JWTToken":{"type":"object","properties":{"id_token":{"type":"string"}},"title":"JWTToken"},"KeyAndPasswordVM":{"type":"object","properties":{"key":{"type":"string"},"newPassword":{"type":"string"}},"title":"KeyAndPasswordVM"},"LoginVM":{"type":"object","required":["password","username"],"properties":{"password":{"type":"string","minLength":4,"maxLength":100},"rememberMe":{"type":"boolean"},"username":{"type":"string","minLength":1,"maxLength":50}},"title":"LoginVM"},"ManagedUserVM":{"type":"object","properties":{"activated":{"type":"boolean"},"authorities":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string"},"createdDate":{"type":"string","format":"date-time"},"email":{"type":"string","minLength":5,"maxLength":254},"firstName":{"type":"string","minLength":0,"maxLength":50},"id":{"type":"integer","format":"int64"},"imageUrl":{"type":"string","minLength":0,"maxLength":256},"langKey":{"type":"string","minLength":2,"maxLength":10},"lastModifiedBy":{"type":"string"},"lastModifiedDate":{"type":"string","format":"date-time"},"lastName":{"type":"string","minLength":0,"maxLength":50},"login":{"type":"string","minLength":1,"maxLength":50,"pattern":"^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$"},"password":{"type":"string","minLength":4,"maxLength":100}},"title":"ManagedUserVM"},"PasswordChangeDTO":{"type":"object","properties":{"currentPassword":{"type":"string"},"newPassword":{"type":"string"}},"title":"PasswordChangeDTO"},"Person":{"type":"object","properties":{"affiliation":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"}},"title":"Person"},"SearchInputDTO":{"type":"object","properties":{"fulltextQuery":{"type":"string"},"metadataAuthor":{"type":"string"},"metadataKeywords":{"type":"string"},"metadataLicense":{"type":"string"},"metadataNaturalLanguage":{"type":"string"},"metadataProgrammingLanguage":{"type":"string"},"page":{"type":"integer","format":"int32"},"selectedFileFormat":{"type":"array","items":{"type":"string"}},"selectedRepository":{"type":"array","items":{"type":"string"}},"selectedUniversity":{"type":"array","items":{"type":"string"}}},"title":"SearchInputDTO"},"SearchResultDTO":{"type":"object","properties":{"contributor":{"type":"array","items":{"$ref":"#/definitions/Person"}},"creator":{"type":"array","items":{"$ref":"#/definitions/Person"}},"deprecated":{"type":"boolean"},"description":{"type":"string"},"difficulty":{"type":"string"},"educationLevel":{"type":"string"},"format":{"type":"string"},"identifier":{"type":"string"},"image":{"type":"string"},"keyword":{"type":"array","items":{"type":"string"}},"language":{"type":"array","items":{"type":"string"}},"license":{"type":"string"},"metadataVersion":{"type":"string"},"programmingLanguage":{"type":"array","items":{"type":"string"}},"publisher":{"type":"array","items":{"$ref":"#/definitions/Person"}},"repositoryURL":{"type":"string"},"requires":{"type":"string"},"source":{"type":"array","items":{"type":"string"}},"status":{"type":"string"},"structure":{"type":"string"},"timeRequired":{"type":"string"},"title":{"type":"string"},"type":{"$ref":"#/definitions/ExerciseType"},"version":{"type":"string"}},"title":"SearchResultDTO"},"SearchResultsDTO":{"type":"object","properties":{"hitCount":{"type":"integer","format":"int64"},"searchResult":{"type":"array","items":{"$ref":"#/definitions/SearchResultDTO"}}},"title":"SearchResultsDTO"},"User":{"type":"object","required":["activated","login"],"properties":{"activated":{"type":"boolean"},"email":{"type":"string","minLength":5,"maxLength":254},"firstName":{"type":"string","minLength":0,"maxLength":50},"id":{"type":"integer","format":"int64"},"imageUrl":{"type":"string","minLength":0,"maxLength":256},"langKey":{"type":"string","minLength":2,"maxLength":10},"lastName":{"type":"string","minLength":0,"maxLength":50},"login":{"type":"string","minLength":1,"maxLength":50,"pattern":"^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$"},"resetDate":{"type":"string","format":"date-time"}},"title":"User"},"UserDTO":{"type":"object","properties":{"activated":{"type":"boolean"},"authorities":{"type":"array","items":{"type":"string"}},"createdBy":{"type":"string"},"createdDate":{"type":"string","format":"date-time"},"email":{"type":"string","minLength":5,"maxLength":254},"firstName":{"type":"string","minLength":0,"maxLength":50},"id":{"type":"integer","format":"int64"},"imageUrl":{"type":"string","minLength":0,"maxLength":256},"langKey":{"type":"string","minLength":2,"maxLength":10},"lastModifiedBy":{"type":"string"},"lastModifiedDate":{"type":"string","format":"date-time"},"lastName":{"type":"string","minLength":0,"maxLength":50},"login":{"type":"string","minLength":1,"maxLength":50,"pattern":"^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$"}},"title":"UserDTO"}}},
+    {
+        modelFolder: './src/main/resources/swagger-ts-generator/models',
+        enumTSFile: './src/main/resources/swagger-ts-generator/enums.ts'
+        // + optionally more configuration
+    }
+);
\ No newline at end of file
diff --git a/src/main/webapp/app/account/register/register.component.html b/src/main/webapp/app/account/register/register.component.html
index 19aede971030f41fb79d7b4b17a1a6bc15a01b22..70792e111d5093fedc372d41b41c5b5d2b51149c 100644
--- a/src/main/webapp/app/account/register/register.component.html
+++ b/src/main/webapp/app/account/register/register.component.html
@@ -6,161 +6,162 @@
     </div>
 </div>
 
-<!--<div>-->
-<!--    <div class="row justify-content-center">-->
-<!--        <div class="col-md-8">-->
-<!--            <h1 jhiTranslate="register.title">Registration</h1>-->
-
-<!--            <div class="alert alert-success" *ngIf="success" jhiTranslate="register.messages.success">-->
-<!--                <strong>Registration saved!</strong> Please check your email for confirmation.-->
-<!--            </div>-->
-
-<!--            <div class="alert alert-danger" *ngIf="error" jhiTranslate="register.messages.error.fail">-->
-<!--                <strong>Registration failed!</strong> Please try again later.-->
-<!--            </div>-->
-
-<!--            <div class="alert alert-danger" *ngIf="errorUserExists" jhiTranslate="register.messages.error.userexists">-->
-<!--                <strong>Login name already registered!</strong> Please choose another one.-->
-<!--            </div>-->
-
-<!--            <div class="alert alert-danger" *ngIf="errorEmailExists" jhiTranslate="register.messages.error.emailexists">-->
-<!--                <strong>Email is already in use!</strong> Please choose another one.-->
-<!--            </div>-->
-
-<!--            <div class="alert alert-danger" *ngIf="doNotMatch" jhiTranslate="global.messages.error.dontmatch">-->
-<!--                The password and its confirmation do not match!-->
-<!--            </div>-->
-<!--        </div>-->
-<!--    </div>-->
-
-<!--    <div class="row justify-content-center">-->
-<!--        <div class="col-md-8">-->
-<!--            <form name="form" role="form" (ngSubmit)="register()" [formGroup]="registerForm" *ngIf="!success">-->
-<!--                <div class="form-group">-->
-<!--                    <label class="form-control-label" for="login" jhiTranslate="global.form.username.label">Username</label>-->
-<!--                    <input type="text" class="form-control" id="login" name="login" placeholder="{{ 'global.form.username.placeholder' | translate }}"-->
-<!--                           formControlName="login" #login>-->
-
-<!--                    <div *ngIf="registerForm.get('login')!.invalid && (registerForm.get('login')!.dirty || registerForm.get('login')!.touched)">-->
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('login')?.errors?.required"-->
-<!--                               jhiTranslate="register.messages.validate.login.required">-->
-<!--                            Your username is required.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('login')?.errors?.minlength"-->
-<!--                               jhiTranslate="register.messages.validate.login.minlength">-->
-<!--                            Your username is required to be at least 1 character.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('login')?.errors?.maxlength"-->
-<!--                               jhiTranslate="register.messages.validate.login.maxlength">-->
-<!--                            Your username cannot be longer than 50 characters.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('login')?.errors?.pattern"-->
-<!--                               jhiTranslate="register.messages.validate.login.pattern">-->
-<!--                            Your username can only contain letters and digits.-->
-<!--                        </small>-->
-<!--                    </div>-->
-<!--                </div>-->
-
-<!--                <div class="form-group">-->
-<!--                    <label class="form-control-label" for="email" jhiTranslate="global.form.email.label">Email</label>-->
-<!--                    <input type="email" class="form-control" id="email" name="email" placeholder="{{ 'global.form.email.placeholder' | translate }}"-->
-<!--                             formControlName="email">-->
-
-<!--                    <div *ngIf="registerForm.get('email')!.invalid && (registerForm.get('email')!.dirty || registerForm.get('email')!.touched)">-->
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('email')?.errors?.required"-->
-<!--                               jhiTranslate="global.messages.validate.email.required">-->
-<!--                            Your email is required.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('email')?.errors?.invalid"-->
-<!--                               jhiTranslate="global.messages.validate.email.invalid">-->
-<!--                            Your email is invalid.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('email')?.errors?.minlength"-->
-<!--                               jhiTranslate="global.messages.validate.email.minlength">-->
-<!--                            Your email is required to be at least 5 characters.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('email')?.errors?.maxlength"-->
-<!--                               jhiTranslate="global.messages.validate.email.maxlength">-->
-<!--                            Your email cannot be longer than 100 characters.-->
-<!--                        </small>-->
-<!--                    </div>-->
-<!--                </div>-->
-
-<!--                <div class="form-group">-->
-<!--                    <label class="form-control-label" for="password" jhiTranslate="global.form.newpassword.label">New password</label>-->
-<!--                    <input type="password" class="form-control" id="password" name="password" placeholder="{{ 'global.form.newpassword.placeholder' | translate }}"-->
-<!--                            formControlName="password">-->
-
-<!--                    <div *ngIf="registerForm.get('password')!.invalid && (registerForm.get('password')!.dirty || registerForm.get('password')!.touched)">-->
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('password')?.errors?.required"-->
-<!--                               jhiTranslate="global.messages.validate.newpassword.required">-->
-<!--                            Your password is required.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('password')?.errors?.minlength"-->
-<!--                               jhiTranslate="global.messages.validate.newpassword.minlength">-->
-<!--                            Your password is required to be at least 4 characters.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('password')?.errors?.maxlength"-->
-<!--                               jhiTranslate="global.messages.validate.newpassword.maxlength">-->
-<!--                            Your password cannot be longer than 50 characters.-->
-<!--                        </small>-->
-<!--                    </div>-->
-
-<!--                    <jhi-password-strength-bar [passwordToCheck]="registerForm.get('password')!.value"></jhi-password-strength-bar>-->
-<!--                </div>-->
-
-<!--                <div class="form-group">-->
-<!--                    <label class="form-control-label" for="confirmPassword" jhiTranslate="global.form.confirmpassword.label">New password confirmation</label>-->
-<!--                    <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{ 'global.form.confirmpassword.placeholder' | translate }}"-->
-<!--                            formControlName="confirmPassword">-->
-
-<!--                    <div *ngIf="registerForm.get('confirmPassword')!.invalid && (registerForm.get('confirmPassword')!.dirty || registerForm.get('confirmPassword')!.touched)">-->
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('confirmPassword')?.errors?.required"-->
-<!--                               jhiTranslate="global.messages.validate.confirmpassword.required">-->
-<!--                            Your confirmation password is required.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('confirmPassword')?.errors?.minlength"-->
-<!--                               jhiTranslate="global.messages.validate.confirmpassword.minlength">-->
-<!--                            Your confirmation password is required to be at least 4 characters.-->
-<!--                        </small>-->
-
-<!--                        <small class="form-text text-danger"-->
-<!--                               *ngIf="registerForm.get('confirmPassword')?.errors?.maxlength"-->
-<!--                               jhiTranslate="global.messages.validate.confirmpassword.maxlength">-->
-<!--                            Your confirmation password cannot be longer than 50 characters.-->
-<!--                        </small>-->
-<!--                    </div>-->
-<!--                </div>-->
-
-<!--                <button type="submit" [disabled]="registerForm.invalid" class="btn btn-primary" jhiTranslate="register.form.button">Register</button>-->
-<!--            </form>-->
-
-<!--            <div class="mt-3 alert alert-warning">-->
-<!--                <span jhiTranslate="global.messages.info.authenticated.prefix">If you want to </span>-->
-<!--                <a class="alert-link" (click)="openLogin()" jhiTranslate="global.messages.info.authenticated.link">sign in</a><span jhiTranslate="global.messages.info.authenticated.suffix">, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").</span>-->
-<!--            </div>-->
-<!--        </div>-->
-<!--    </div>-->
-<!--</div>-->
+<!-- 
+<div>
+   <div class="row justify-content-center">
+       <div class="col-md-8">
+           <h1 jhiTranslate="register.title">Registration</h1>
+
+           <div class="alert alert-success" *ngIf="success" jhiTranslate="register.messages.success">
+               <strong>Registration saved!</strong> Please check your email for confirmation.
+           </div>
+
+           <div class="alert alert-danger" *ngIf="error" jhiTranslate="register.messages.error.fail">
+               <strong>Registration failed!</strong> Please try again later.
+           </div>
+
+           <div class="alert alert-danger" *ngIf="errorUserExists" jhiTranslate="register.messages.error.userexists">
+               <strong>Login name already registered!</strong> Please choose another one.
+           </div>
+
+           <div class="alert alert-danger" *ngIf="errorEmailExists" jhiTranslate="register.messages.error.emailexists">
+               <strong>Email is already in use!</strong> Please choose another one.
+           </div>
+
+           <div class="alert alert-danger" *ngIf="doNotMatch" jhiTranslate="global.messages.error.dontmatch">
+               The password and its confirmation do not match!
+           </div>
+       </div>
+   </div>
+
+   <div class="row justify-content-center">
+       <div class="col-md-8">
+           <form name="form" role="form" (ngSubmit)="register()" [formGroup]="registerForm" *ngIf="!success">
+               <div class="form-group">
+                   <label class="form-control-label" for="login" jhiTranslate="global.form.username.label">Username</label>
+                   <input type="text" class="form-control" id="login" name="login" placeholder="{{ 'global.form.username.placeholder' | translate }}"
+                          formControlName="login" #login>
+
+                   <div *ngIf="registerForm.get('login')!.invalid && (registerForm.get('login')!.dirty || registerForm.get('login')!.touched)">
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('login')?.errors?.required"
+                              jhiTranslate="register.messages.validate.login.required">
+                           Your username is required.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('login')?.errors?.minlength"
+                              jhiTranslate="register.messages.validate.login.minlength">
+                           Your username is required to be at least 1 character.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('login')?.errors?.maxlength"
+                              jhiTranslate="register.messages.validate.login.maxlength">
+                           Your username cannot be longer than 50 characters.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('login')?.errors?.pattern"
+                              jhiTranslate="register.messages.validate.login.pattern">
+                           Your username can only contain letters and digits.
+                       </small>
+                   </div>
+               </div>
+
+               <div class="form-group">
+                   <label class="form-control-label" for="email" jhiTranslate="global.form.email.label">Email</label>
+                   <input type="email" class="form-control" id="email" name="email" placeholder="{{ 'global.form.email.placeholder' | translate }}"
+                            formControlName="email">
+
+                   <div *ngIf="registerForm.get('email')!.invalid && (registerForm.get('email')!.dirty || registerForm.get('email')!.touched)">
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('email')?.errors?.required"
+                              jhiTranslate="global.messages.validate.email.required">
+                           Your email is required.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('email')?.errors?.invalid"
+                              jhiTranslate="global.messages.validate.email.invalid">
+                           Your email is invalid.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('email')?.errors?.minlength"
+                              jhiTranslate="global.messages.validate.email.minlength">
+                           Your email is required to be at least 5 characters.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('email')?.errors?.maxlength"
+                              jhiTranslate="global.messages.validate.email.maxlength">
+                           Your email cannot be longer than 100 characters.
+                       </small>
+                   </div>
+               </div>
+
+               <div class="form-group">
+                   <label class="form-control-label" for="password" jhiTranslate="global.form.newpassword.label">New password</label>
+                   <input type="password" class="form-control" id="password" name="password" placeholder="{{ 'global.form.newpassword.placeholder' | translate }}"
+                           formControlName="password">
+
+                   <div *ngIf="registerForm.get('password')!.invalid && (registerForm.get('password')!.dirty || registerForm.get('password')!.touched)">
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('password')?.errors?.required"
+                              jhiTranslate="global.messages.validate.newpassword.required">
+                           Your password is required.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('password')?.errors?.minlength"
+                              jhiTranslate="global.messages.validate.newpassword.minlength">
+                           Your password is required to be at least 4 characters.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('password')?.errors?.maxlength"
+                              jhiTranslate="global.messages.validate.newpassword.maxlength">
+                           Your password cannot be longer than 50 characters.
+                       </small>
+                   </div>
+
+                   <jhi-password-strength-bar [passwordToCheck]="registerForm.get('password')!.value"></jhi-password-strength-bar>
+               </div>
+
+               <div class="form-group">
+                   <label class="form-control-label" for="confirmPassword" jhiTranslate="global.form.confirmpassword.label">New password confirmation</label>
+                   <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{ 'global.form.confirmpassword.placeholder' | translate }}"
+                           formControlName="confirmPassword">
+
+                   <div *ngIf="registerForm.get('confirmPassword')!.invalid && (registerForm.get('confirmPassword')!.dirty || registerForm.get('confirmPassword')!.touched)">
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('confirmPassword')?.errors?.required"
+                              jhiTranslate="global.messages.validate.confirmpassword.required">
+                           Your confirmation password is required.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('confirmPassword')?.errors?.minlength"
+                              jhiTranslate="global.messages.validate.confirmpassword.minlength">
+                           Your confirmation password is required to be at least 4 characters.
+                       </small>
+
+                       <small class="form-text text-danger"
+                              *ngIf="registerForm.get('confirmPassword')?.errors?.maxlength"
+                              jhiTranslate="global.messages.validate.confirmpassword.maxlength">
+                           Your confirmation password cannot be longer than 50 characters.
+                       </small>
+                   </div>
+               </div>
+
+               <button type="submit" [disabled]="registerForm.invalid" class="btn btn-primary" jhiTranslate="register.form.button">Register</button>
+           </form>
+
+           <div class="mt-3 alert alert-warning">
+               <span jhiTranslate="global.messages.info.authenticated.prefix">If you want to </span>
+               <a class="alert-link" (click)="openLogin()" jhiTranslate="global.messages.info.authenticated.link">sign in</a><span jhiTranslate="global.messages.info.authenticated.suffix">, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").</span>
+           </div>
+       </div>
+   </div>
+</div> -->
diff --git a/src/main/webapp/app/app-routing.module.ts b/src/main/webapp/app/app-routing.module.ts
index 7fd899906936159a2c354116b06f09727e73350a..02ced562804b5fa524373d1d5607ba72fc8a7331 100644
--- a/src/main/webapp/app/app-routing.module.ts
+++ b/src/main/webapp/app/app-routing.module.ts
@@ -1,13 +1,14 @@
-import {NgModule} from '@angular/core';
-import {RouterModule} from '@angular/router';
-import {errorRoute} from './layouts/error/error.route';
-import {navbarRoute} from './layouts/navbar/navbar.route';
-import {DEBUG_INFO_ENABLED} from 'app/app.constants';
-import {Authority} from 'app/shared/constants/authority.constants';
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { errorRoute } from './layouts/error/error.route';
+import { navbarRoute } from './layouts/navbar/navbar.route';
+import { SEARCH_ROUTE } from 'app/search/search.route';
+import { DEBUG_INFO_ENABLED } from 'app/app.constants';
+import { Authority } from 'app/shared/constants/authority.constants';
 
-import {UserRouteAccessService} from 'app/core/auth/user-route-access-service';
+import { UserRouteAccessService } from 'app/core/auth/user-route-access-service';
 
-const LAYOUT_ROUTES = [navbarRoute, ...errorRoute];
+const LAYOUT_ROUTES = [navbarRoute, SEARCH_ROUTE, ...errorRoute];
 
 @NgModule({
   imports: [
@@ -27,14 +28,13 @@ const LAYOUT_ROUTES = [navbarRoute, ...errorRoute];
         },
         {
           path: 'search',
-          loadChildren: () => import('./search/search.module').then(m => m.SearchModule)
+          loadChildren: () => import('app/search/search.module').then(m => m.SearchModule),
         },
         ...LAYOUT_ROUTES,
       ],
-      {enableTracing: DEBUG_INFO_ENABLED}
+      { enableTracing: DEBUG_INFO_ENABLED }
     ),
   ],
   exports: [RouterModule],
 })
-export class GitSearchV2AppRoutingModule {
-}
+export class GitSearchV2AppRoutingModule {}
diff --git a/src/main/webapp/app/app.module.ts b/src/main/webapp/app/app.module.ts
index 975845dacbc4710b8399e1faf6ed64815bdb23be..d49706cf19782651136f0db01f3d1c10791d85d3 100644
--- a/src/main/webapp/app/app.module.ts
+++ b/src/main/webapp/app/app.module.ts
@@ -1,5 +1,6 @@
 import { NgModule } from '@angular/core';
 import { BrowserModule } from '@angular/platform-browser';
+import { CommonModule } from '@angular/common';
 
 import './vendor';
 import { GitSearchV2SharedModule } from 'app/shared/shared.module';
@@ -14,11 +15,12 @@ import { FooterComponent } from './layouts/footer/footer.component';
 import { PageRibbonComponent } from './layouts/profiles/page-ribbon.component';
 import { ActiveMenuDirective } from './layouts/navbar/active-menu.directive';
 import { ErrorComponent } from './layouts/error/error.component';
-import {QueryParamModule} from "@ngqp/core";
+import { QueryParamModule } from '@ngqp/core';
 
 @NgModule({
   imports: [
     BrowserModule,
+    CommonModule,
     GitSearchV2SharedModule,
     GitSearchV2CoreModule,
     GitSearchV2HomeModule,
@@ -26,9 +28,14 @@ import {QueryParamModule} from "@ngqp/core";
     GitSearchV2EntityModule,
     GitSearchV2AppRoutingModule,
     QueryParamModule,
-
   ],
-  declarations: [MainComponent, NavbarComponent, ErrorComponent, PageRibbonComponent, ActiveMenuDirective, FooterComponent],
+  declarations: [
+	    MainComponent, 
+    NavbarComponent, 
+    ErrorComponent, 
+    PageRibbonComponent, 
+    ActiveMenuDirective, 
+    FooterComponent],
   bootstrap: [MainComponent],
 })
 export class GitSearchV2AppModule {}
diff --git a/src/main/webapp/app/core/application/applicationInfo.service.ts b/src/main/webapp/app/core/application/applicationInfo.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0ea20bbd8b6075ece7e809a657f89f5d8bc6253
--- /dev/null
+++ b/src/main/webapp/app/core/application/applicationInfo.service.ts
@@ -0,0 +1,38 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable, } from 'rxjs';
+
+import { SERVER_API_URL } from 'app/app.constants';
+
+export class DeploymentInfo {
+    branch = "";
+    commitId = "";
+}
+@Injectable({ providedIn: 'root' })
+export class ApplicationInfoService {
+
+  cachedDeploymentInfo: DeploymentInfo;
+  inLoading = false;
+  constructor(
+    private http: HttpClient,
+    ) {
+    this.cachedDeploymentInfo = {} as DeploymentInfo;
+    }
+
+  private loadDeploymentInfo(): Observable<DeploymentInfo> {
+    return this.http.get<DeploymentInfo>(SERVER_API_URL + 'api/deploymentInfo');
+  }
+  
+  public getDeploymentInfo(): DeploymentInfo {
+    if(!this.cachedDeploymentInfo.branch && !this.inLoading) {
+        this.inLoading = true;
+        this.loadDeploymentInfo().subscribe((res) => {
+            this.cachedDeploymentInfo = res;
+            this.inLoading = false;
+        });
+    }
+    return this.cachedDeploymentInfo;
+    }
+  
+
+}
diff --git a/src/main/webapp/app/core/auth/auth-jwt.service.ts b/src/main/webapp/app/core/auth/auth-jwt.service.ts
index 5fb3e6f8e0cd68227f1c3deff604f6580d5b57c9..90564bb460fd02593b34e28c9495763c24bd100d 100644
--- a/src/main/webapp/app/core/auth/auth-jwt.service.ts
+++ b/src/main/webapp/app/core/auth/auth-jwt.service.ts
@@ -1,5 +1,5 @@
 import { Injectable } from '@angular/core';
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpParams } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
@@ -25,6 +25,13 @@ export class AuthServerProvider {
       .pipe(map(response => this.authenticateSuccess(response, credentials.rememberMe)));
   }
 
+  refreshToken(tokenX: string): Observable<void> {
+	const tokenParam = new HttpParams().set('token', tokenX);
+    return this.http
+      .post<JwtToken>(SERVER_API_URL + 'api/refreshToken', tokenParam )
+      .pipe(map(response => this.authenticateSuccess(response, false)));
+	}
+
   logout(): Observable<void> {
     return new Observable(observer => {
       this.$localStorage.clear('authenticationToken');
diff --git a/src/main/webapp/app/core/auth/oauth2-config.model.ts b/src/main/webapp/app/core/auth/oauth2-config.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f30e6945df8281773738850647f75ba28be27e0
--- /dev/null
+++ b/src/main/webapp/app/core/auth/oauth2-config.model.ts
@@ -0,0 +1,7 @@
+export class OAuth2Config {
+  constructor(
+	public registrationId: string,
+	public clientURL: string,
+	public scopes: string,
+  ) {}
+}
diff --git a/src/main/webapp/app/core/auth/oauth2-config.service.ts b/src/main/webapp/app/core/auth/oauth2-config.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08cdc87640fafa025ca2865561fb0fc41688cdd9
--- /dev/null
+++ b/src/main/webapp/app/core/auth/oauth2-config.service.ts
@@ -0,0 +1,25 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { OAuth2Config} from './oauth2-config.model';
+import { Observable } from 'rxjs';
+import { SERVER_API_URL } from 'app/app.constants';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class OAuth2ConfigService {
+
+  constructor(
+	    private http: HttpClient,
+	) { }
+
+  get(clientId: string): Observable<OAuth2Config> {
+    return this.http.get<OAuth2Config>(SERVER_API_URL + 'oauth2/oauth2Config' + `/${clientId}`);
+  }
+
+  getAllConfigs(): Observable<OAuth2Config[]> {
+    return this.http.get<OAuth2Config[]>(SERVER_API_URL + 'oauth2Config/allConfigs');
+	
+}
+
+}
diff --git a/src/main/webapp/app/core/icons/font-awesome-icons.ts b/src/main/webapp/app/core/icons/font-awesome-icons.ts
index 90c6366f7bdfd2be5563c9d2283daaea4a6bedcf..46617c114e871ef590f14cd64b545c1efe49d70e 100644
--- a/src/main/webapp/app/core/icons/font-awesome-icons.ts
+++ b/src/main/webapp/app/core/icons/font-awesome-icons.ts
@@ -32,7 +32,9 @@ import {
   faTrashAlt,
   faAsterisk,
   faTasks,
-  faHome, faLanguage,
+  faHome,
+  faLanguage,
+  faDownload,
   // jhipster-needle-add-icon-import
 } from '@fortawesome/free-solid-svg-icons';
 
@@ -72,6 +74,7 @@ export const fontAwesomeIcons = [
   faTrashAlt,
   faAsterisk,
   faLanguage,
-  faBook
+  faBook,
+  faDownload,
   // jhipster-needle-add-icon-import
 ];
diff --git a/src/main/webapp/app/entities/entity.module.ts b/src/main/webapp/app/entities/entity.module.ts
index 5740cfedce7ae695e3f64286e3d7bc0fdf0027fb..9d17fe8a5988ff30e2e1e99b581c9fc736775963 100644
--- a/src/main/webapp/app/entities/entity.module.ts
+++ b/src/main/webapp/app/entities/entity.module.ts
@@ -4,6 +4,10 @@ import { RouterModule } from '@angular/router';
 @NgModule({
   imports: [
     RouterModule.forChild([
+      {
+        path: 'statistics',
+        loadChildren: () => import('./statistics/statistics.module').then(m => m.GitsearchStatisticsModule),
+      },
       /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */
     ]),
   ],
diff --git a/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.html b/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..b6a356dce7d0c3fa9c903e02491b63d2ced0c588
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.html
@@ -0,0 +1,24 @@
+<form *ngIf="statistics" name="deleteForm" (ngSubmit)="confirmDelete(statistics?.id!)">
+    <div class="modal-header">
+        <h4 class="modal-title" jhiTranslate="entity.delete.title">Confirm delete operation</h4>
+
+        <button type="button" class="close" data-dismiss="modal" aria-hidden="true"
+                (click)="cancel()">&times;</button>
+    </div>
+
+    <div class="modal-body">
+        <jhi-alert-error></jhi-alert-error>
+
+        <p id="jhi-delete-statistics-heading" jhiTranslate="gitsearchApp.statistics.delete.question" [translateValues]="{ id: statistics.id }">Are you sure you want to delete this Statistics?</p>
+    </div>
+
+    <div class="modal-footer">
+        <button type="button" class="btn btn-secondary" data-dismiss="modal" (click)="cancel()">
+            <fa-icon icon="ban"></fa-icon>&nbsp;<span jhiTranslate="entity.action.cancel">Cancel</span>
+        </button>
+
+        <button id="jhi-confirm-delete-statistics" type="submit" class="btn btn-danger">
+            <fa-icon icon="times"></fa-icon>&nbsp;<span jhiTranslate="entity.action.delete">Delete</span>
+        </button>
+    </div>
+</form>
diff --git a/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.ts b/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c15c50ed9935db3864f6c55ddcde4457817b75b8
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-delete-dialog.component.ts
@@ -0,0 +1,30 @@
+import { Component } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { JhiEventManager } from 'ng-jhipster';
+
+import { IStatistics } from 'app/shared/model/statistics.model';
+import { StatisticsService } from './statistics.service';
+
+@Component({
+  templateUrl: './statistics-delete-dialog.component.html',
+})
+export class StatisticsDeleteDialogComponent {
+  statistics?: IStatistics;
+
+  constructor(
+    protected statisticsService: StatisticsService,
+    public activeModal: NgbActiveModal,
+    protected eventManager: JhiEventManager
+  ) {}
+
+  cancel(): void {
+    this.activeModal.dismiss();
+  }
+
+  confirmDelete(id: number): void {
+    this.statisticsService.delete(id).subscribe(() => {
+      this.eventManager.broadcast('statisticsListModification');
+      this.activeModal.close();
+    });
+  }
+}
diff --git a/src/main/webapp/app/entities/statistics/statistics-detail.component.html b/src/main/webapp/app/entities/statistics/statistics-detail.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a8d3c44402eb9f6a2b109d67da4921fbf9a4f5f9
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-detail.component.html
@@ -0,0 +1,38 @@
+<div class="row justify-content-center">
+    <div class="col-8">
+        <div *ngIf="statistics">
+            <h2><span jhiTranslate="gitsearchApp.statistics.detail.title">Statistics</span> {{ statistics.id }}</h2>
+
+            <hr>
+
+            <jhi-alert-error></jhi-alert-error>
+
+            <dl class="row-md jh-entity-details">
+                <dt><span jhiTranslate="gitsearchApp.statistics.views">Views</span></dt>
+                <dd>
+                    <span>{{ statistics.views }}</span>
+                </dd>
+                <dt><span jhiTranslate="gitsearchApp.statistics.downloads">Downloads</span></dt>
+                <dd>
+                    <span>{{ statistics.downloads }}</span>
+                </dd>
+                <dt><span jhiTranslate="gitsearchApp.statistics.exerciseID">Exercise ID</span></dt>
+                <dd>
+                    <span>{{ statistics.exerciseID }}</span>
+                </dd>
+            </dl>
+
+            <button type="submit"
+                    (click)="previousState()"
+                    class="btn btn-info">
+                <fa-icon icon="arrow-left"></fa-icon>&nbsp;<span jhiTranslate="entity.action.back">Back</span>
+            </button>
+
+            <button type="button"
+                    [routerLink]="['/statistics', statistics.id, 'edit']"
+                    class="btn btn-primary">
+                <fa-icon icon="pencil-alt"></fa-icon>&nbsp;<span jhiTranslate="entity.action.edit">Edit</span>
+            </button>
+        </div>
+    </div>
+</div>
diff --git a/src/main/webapp/app/entities/statistics/statistics-detail.component.ts b/src/main/webapp/app/entities/statistics/statistics-detail.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abc3bbfdfd0b453aa21d28dc10b16ac0a6da9b21
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-detail.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { IStatistics } from 'app/shared/model/statistics.model';
+
+@Component({
+  selector: 'jhi-statistics-detail',
+  templateUrl: './statistics-detail.component.html',
+})
+export class StatisticsDetailComponent implements OnInit {
+  statistics: IStatistics | null = null;
+
+  constructor(protected activatedRoute: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.activatedRoute.data.subscribe(({ statistics }) => (this.statistics = statistics));
+  }
+
+  previousState(): void {
+    window.history.back();
+  }
+}
diff --git a/src/main/webapp/app/entities/statistics/statistics-update.component.html b/src/main/webapp/app/entities/statistics/statistics-update.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..08aa08dc4e23383fdb33cb5a1943f9f7b0c732ef
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-update.component.html
@@ -0,0 +1,54 @@
+<div class="row justify-content-center">
+    <div class="col-8">
+        <form name="editForm" role="form" novalidate (ngSubmit)="save()" [formGroup]="editForm">
+            <h2 id="jhi-statistics-heading" jhiTranslate="gitsearchApp.statistics.home.createOrEditLabel">Create or edit a Statistics</h2>
+
+            <div>
+                <jhi-alert-error></jhi-alert-error>
+
+                <div class="form-group" [hidden]="!editForm.get('id')!.value">
+                    <label for="id" jhiTranslate="global.field.id">ID</label>
+                    <input type="text" class="form-control" id="id" name="id" formControlName="id" readonly />
+                </div>
+
+                <div class="form-group">
+                    <label class="form-control-label" jhiTranslate="gitsearchApp.statistics.views" for="field_views">Views</label>
+                    <input type="number" class="form-control" name="views" id="field_views"
+                           formControlName="views"/>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-control-label" jhiTranslate="gitsearchApp.statistics.downloads" for="field_downloads">Downloads</label>
+                    <input type="number" class="form-control" name="downloads" id="field_downloads"
+                           formControlName="downloads"/>
+                    <div *ngIf="editForm.get('downloads')!.invalid && (editForm.get('downloads')!.dirty || editForm.get('downloads')!.touched)">
+                        <small class="form-text text-danger"
+                               *ngIf="editForm.get('downloads')?.errors?.required" jhiTranslate="entity.validation.required">
+                        This field is required.
+                        </small>
+                        <small class="form-text text-danger"
+                            [hidden]="!editForm.get('downloads')?.errors?.number" jhiTranslate="entity.validation.number">
+                            This field should be a number.
+                        </small>
+                    </div>
+                </div>
+
+                <div class="form-group">
+                    <label class="form-control-label" jhiTranslate="gitsearchApp.statistics.exerciseID" for="field_exerciseID">Exercise ID</label>
+                    <input type="number" class="form-control" name="exerciseID" id="field_exerciseID"
+                           formControlName="exerciseID"/>
+                </div>
+            </div>
+
+            <div>
+                <button type="button" id="cancel-save" class="btn btn-secondary" (click)="previousState()">
+                    <fa-icon icon="ban"></fa-icon>&nbsp;<span jhiTranslate="entity.action.cancel">Cancel</span>
+                </button>
+
+                <button type="submit" id="save-entity" [disabled]="editForm.invalid || isSaving" class="btn btn-primary">
+                    <fa-icon icon="save"></fa-icon>&nbsp;<span jhiTranslate="entity.action.save">Save</span>
+                </button>
+            </div>
+        </form>
+    </div>
+</div>
diff --git a/src/main/webapp/app/entities/statistics/statistics-update.component.ts b/src/main/webapp/app/entities/statistics/statistics-update.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3a82dc8f9f3c0664ef442dc5cba0716e10f61e5d
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics-update.component.ts
@@ -0,0 +1,81 @@
+import { Component, OnInit } from '@angular/core';
+import { HttpResponse } from '@angular/common/http';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { FormBuilder, Validators } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
+import { Observable } from 'rxjs';
+
+import { IStatistics, Statistics } from 'app/shared/model/statistics.model';
+import { StatisticsService } from './statistics.service';
+
+@Component({
+  selector: 'jhi-statistics-update',
+  templateUrl: './statistics-update.component.html',
+})
+export class StatisticsUpdateComponent implements OnInit {
+  isSaving = false;
+
+  editForm = this.fb.group({
+    id: [],
+    views: [],
+    downloads: [null, [Validators.required]],
+    exerciseID: [],
+  });
+
+  constructor(protected statisticsService: StatisticsService, protected activatedRoute: ActivatedRoute, private fb: FormBuilder) {}
+
+  ngOnInit(): void {
+    this.activatedRoute.data.subscribe(({ statistics }) => {
+      this.updateForm(statistics);
+    });
+  }
+
+  updateForm(statistics: IStatistics): void {
+    this.editForm.patchValue({
+      id: statistics.id,
+      views: statistics.views,
+      downloads: statistics.downloads,
+      exerciseID: statistics.exerciseID,
+    });
+  }
+
+  previousState(): void {
+    window.history.back();
+  }
+
+  save(): void {
+    this.isSaving = true;
+    const statistics = this.createFromForm();
+    if (statistics.id !== undefined) {
+      this.subscribeToSaveResponse(this.statisticsService.update(statistics));
+    } else {
+      this.subscribeToSaveResponse(this.statisticsService.create(statistics));
+    }
+  }
+
+  private createFromForm(): IStatistics {
+    return {
+      ...new Statistics(),
+      id: this.editForm.get(['id'])!.value,
+      views: this.editForm.get(['views'])!.value,
+      downloads: this.editForm.get(['downloads'])!.value,
+      exerciseID: this.editForm.get(['exerciseID'])!.value,
+    };
+  }
+
+  protected subscribeToSaveResponse(result: Observable<HttpResponse<IStatistics>>): void {
+    result.subscribe(
+      () => this.onSaveSuccess(),
+      () => this.onSaveError()
+    );
+  }
+
+  protected onSaveSuccess(): void {
+    this.isSaving = false;
+    this.previousState();
+  }
+
+  protected onSaveError(): void {
+    this.isSaving = false;
+  }
+}
diff --git a/src/main/webapp/app/entities/statistics/statistics.component.html b/src/main/webapp/app/entities/statistics/statistics.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ffcd96190aa472acdf99cdc02132c317ba6cdd65
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics.component.html
@@ -0,0 +1,83 @@
+<div>
+    <h2 id="page-heading">
+        <span jhiTranslate="gitsearchApp.statistics.home.title">Statistics</span>
+
+        <button id="jh-create-entity" class="btn btn-primary float-right jh-create-entity create-statistics" [routerLink]="['/statistics/new']">
+            <fa-icon icon="plus"></fa-icon>
+            <span class="hidden-sm-down"  jhiTranslate="gitsearchApp.statistics.home.createLabel">
+            Create a new Statistics
+            </span>
+        </button>
+    </h2>
+
+    <jhi-alert-error></jhi-alert-error>
+
+    <jhi-alert></jhi-alert>
+
+    <div class="row">
+        <div class="col-sm-12">
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" [(ngModel)]="currentSearch" id="currentSearch" name="currentSearch" placeholder="{{ 'gitsearchApp.statistics.home.search' | translate }}">
+
+                    <button class="input-group-append btn btn-info" (click)="search(currentSearch)">
+                        <fa-icon icon="search"></fa-icon>
+                    </button>
+
+                    <button class="input-group-append btn btn-danger" (click)="search('')" *ngIf="currentSearch">
+                        <fa-icon icon="trash-alt"></fa-icon>
+                    </button>
+                </div>
+            </form>
+        </div>
+    </div>
+
+    <div class="alert alert-warning" id="no-result" *ngIf="statistics?.length === 0">
+        <span jhiTranslate="gitsearchApp.statistics.home.notFound">No statistics found</span>
+    </div>
+
+    <div class="table-responsive" id="entities" *ngIf="statistics && statistics.length > 0">
+        <table class="table table-striped" aria-describedby="page-heading">
+            <thead>
+                <tr jhiSort [(predicate)]="predicate" [(ascending)]="ascending" [callback]="reset.bind(this)">
+                    <th scope="col"  jhiSortBy="id"><span jhiTranslate="global.field.id">ID</span> <fa-icon icon="sort"></fa-icon></th>
+                    <th scope="col"  jhiSortBy="views"><span jhiTranslate="gitsearchApp.statistics.views">Views</span> <fa-icon icon="sort"></fa-icon></th>
+                    <th scope="col"  jhiSortBy="downloads"><span jhiTranslate="gitsearchApp.statistics.downloads">Downloads</span> <fa-icon icon="sort"></fa-icon></th>
+                    <th scope="col"  jhiSortBy="exerciseID"><span jhiTranslate="gitsearchApp.statistics.exerciseID">Exercise ID</span> <fa-icon icon="sort"></fa-icon></th>
+                    <th scope="col"></th>
+                </tr>
+            </thead>
+            <tbody infinite-scroll (scrolled)="loadPage(page + 1)" [infiniteScrollDisabled]="page >= links['last']" [infiniteScrollDistance]="0">
+                <tr *ngFor="let statistics of statistics ;trackBy: trackId">
+                    <td><a [routerLink]="['/statistics', statistics.id, 'view']">{{ statistics.id }}</a></td>
+                    <td>{{ statistics.views }}</td>
+                    <td>{{ statistics.downloads }}</td>
+                    <td>{{ statistics.exerciseID }}</td>
+                    <td class="text-right">
+                        <div class="btn-group">
+                            <button type="submit"
+                                    [routerLink]="['/statistics', statistics.id, 'view']"
+                                    class="btn btn-info btn-sm">
+                                <fa-icon icon="eye"></fa-icon>
+                                <span class="d-none d-md-inline" jhiTranslate="entity.action.view">View</span>
+                            </button>
+
+                            <button type="submit"
+                                    [routerLink]="['/statistics', statistics.id, 'edit']"
+                                    class="btn btn-primary btn-sm">
+                                <fa-icon icon="pencil-alt"></fa-icon>
+                                <span class="d-none d-md-inline" jhiTranslate="entity.action.edit">Edit</span>
+                            </button>
+
+                            <button type="submit" (click)="delete(statistics)"
+                                    class="btn btn-danger btn-sm">
+                                <fa-icon icon="times"></fa-icon>
+                                <span class="d-none d-md-inline" jhiTranslate="entity.action.delete">Delete</span>
+                            </button>
+                        </div>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
diff --git a/src/main/webapp/app/entities/statistics/statistics.component.ts b/src/main/webapp/app/entities/statistics/statistics.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4941244aa6bd84ef9040f8352be0764db3fc237
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics.component.ts
@@ -0,0 +1,141 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { HttpHeaders, HttpResponse } from '@angular/common/http';
+import { ActivatedRoute } from '@angular/router';
+import { Subscription } from 'rxjs';
+import { JhiEventManager, JhiParseLinks } from 'ng-jhipster';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+
+import { IStatistics } from 'app/shared/model/statistics.model';
+
+import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants';
+import { StatisticsService } from './statistics.service';
+import { StatisticsDeleteDialogComponent } from './statistics-delete-dialog.component';
+
+@Component({
+  selector: 'jhi-statistics',
+  templateUrl: './statistics.component.html',
+})
+export class StatisticsComponent implements OnInit, OnDestroy {
+  statistics: IStatistics[];
+  eventSubscriber?: Subscription;
+  itemsPerPage: number;
+  links: any;
+  page: number;
+  predicate: string;
+  ascending: boolean;
+  currentSearch: string;
+
+  constructor(
+    protected statisticsService: StatisticsService,
+    protected eventManager: JhiEventManager,
+    protected modalService: NgbModal,
+    protected parseLinks: JhiParseLinks,
+    protected activatedRoute: ActivatedRoute
+  ) {
+    this.statistics = [];
+    this.itemsPerPage = ITEMS_PER_PAGE;
+    this.page = 0;
+    this.links = {
+      last: 0,
+    };
+    this.predicate = 'id';
+    this.ascending = true;
+    this.currentSearch =
+      this.activatedRoute.snapshot && this.activatedRoute.snapshot.queryParams['search']
+        ? this.activatedRoute.snapshot.queryParams['search']
+        : '';
+  }
+
+  loadAll(): void {
+    if (this.currentSearch) {
+      this.statisticsService
+        .search({
+          query: this.currentSearch,
+          page: this.page,
+          size: this.itemsPerPage,
+          sort: this.sort(),
+        })
+        .subscribe((res: HttpResponse<IStatistics[]>) => this.paginateStatistics(res.body, res.headers));
+      return;
+    }
+
+    this.statisticsService
+      .query({
+        page: this.page,
+        size: this.itemsPerPage,
+        sort: this.sort(),
+      })
+      .subscribe((res: HttpResponse<IStatistics[]>) => this.paginateStatistics(res.body, res.headers));
+  }
+
+  reset(): void {
+    this.page = 0;
+    this.statistics = [];
+    this.loadAll();
+  }
+
+  loadPage(page: number): void {
+    this.page = page;
+    this.loadAll();
+  }
+
+  search(query: string): void {
+    this.statistics = [];
+    this.links = {
+      last: 0,
+    };
+    this.page = 0;
+    if (query) {
+      this.predicate = '_score';
+      this.ascending = false;
+    } else {
+      this.predicate = 'id';
+      this.ascending = true;
+    }
+    this.currentSearch = query;
+    this.loadAll();
+  }
+
+  ngOnInit(): void {
+    this.loadAll();
+    this.registerChangeInStatistics();
+  }
+
+  ngOnDestroy(): void {
+    if (this.eventSubscriber) {
+      this.eventManager.destroy(this.eventSubscriber);
+    }
+  }
+
+  trackId(index: number, item: IStatistics): number {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+    return item.id!;
+  }
+
+  registerChangeInStatistics(): void {
+    this.eventSubscriber = this.eventManager.subscribe('statisticsListModification', () => this.reset());
+  }
+
+  delete(statistics: IStatistics): void {
+    const modalRef = this.modalService.open(StatisticsDeleteDialogComponent, { size: 'lg', backdrop: 'static' });
+    modalRef.componentInstance.statistics = statistics;
+  }
+
+  sort(): string[] {
+    const result = [this.predicate + ',' + (this.ascending ? 'asc' : 'desc')];
+    if (this.predicate !== 'id') {
+      result.push('id');
+    }
+    return result;
+  }
+
+  protected paginateStatistics(data: IStatistics[] | null, headers: HttpHeaders): void {
+    const headersLink = headers.get('link');
+    this.links = this.parseLinks.parse(headersLink ? headersLink : '');
+    if (data) {
+      for (let i = 0; i < data.length; i++) {
+        this.statistics.push(data[i]);
+      }
+    }
+  }
+}
diff --git a/src/main/webapp/app/entities/statistics/statistics.module.ts b/src/main/webapp/app/entities/statistics/statistics.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7abdc749b10ce67d545830af3aaf2f8739943a78
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { GitSearchV2SharedModule } from 'app/shared/shared.module';
+import { StatisticsComponent } from './statistics.component';
+import { StatisticsDetailComponent } from './statistics-detail.component';
+import { StatisticsUpdateComponent } from './statistics-update.component';
+import { StatisticsDeleteDialogComponent } from './statistics-delete-dialog.component';
+import { statisticsRoute } from './statistics.route';
+
+@NgModule({
+  imports: [GitSearchV2SharedModule, RouterModule.forChild(statisticsRoute)],
+  declarations: [StatisticsComponent, StatisticsDetailComponent, StatisticsUpdateComponent, StatisticsDeleteDialogComponent],
+  entryComponents: [StatisticsDeleteDialogComponent],
+})
+export class GitsearchStatisticsModule {}
diff --git a/src/main/webapp/app/entities/statistics/statistics.route.ts b/src/main/webapp/app/entities/statistics/statistics.route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ad3d51e9812f710d9c231bb72ee953eff3f3d660
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics.route.ts
@@ -0,0 +1,83 @@
+import { Injectable } from '@angular/core';
+import { HttpResponse } from '@angular/common/http';
+import { Resolve, ActivatedRouteSnapshot, Routes, Router } from '@angular/router';
+import { Observable, of, EMPTY } from 'rxjs';
+import { flatMap } from 'rxjs/operators';
+
+import { Authority } from 'app/shared/constants/authority.constants';
+import { UserRouteAccessService } from 'app/core/auth/user-route-access-service';
+import { IStatistics, Statistics } from 'app/shared/model/statistics.model';
+import { StatisticsService } from './statistics.service';
+import { StatisticsComponent } from './statistics.component';
+import { StatisticsDetailComponent } from './statistics-detail.component';
+import { StatisticsUpdateComponent } from './statistics-update.component';
+
+@Injectable({ providedIn: 'root' })
+export class StatisticsResolve implements Resolve<IStatistics> {
+  constructor(private service: StatisticsService, private router: Router) {}
+
+  resolve(route: ActivatedRouteSnapshot): Observable<IStatistics> | Observable<never> {
+    const id = route.params['id'];
+    if (id) {
+      return this.service.find(id).pipe(
+        flatMap((statistics: HttpResponse<Statistics>) => {
+          if (statistics.body) {
+            return of(statistics.body);
+          } else {
+            this.router.navigate(['404']);
+            return EMPTY;
+          }
+        })
+      );
+    }
+    return of(new Statistics());
+  }
+}
+
+export const statisticsRoute: Routes = [
+  {
+    path: '',
+    component: StatisticsComponent,
+    data: {
+      authorities: [Authority.USER],
+      pageTitle: 'gitsearchApp.statistics.home.title',
+    },
+    canActivate: [UserRouteAccessService],
+  },
+  {
+    path: ':id/view',
+    component: StatisticsDetailComponent,
+    resolve: {
+      statistics: StatisticsResolve,
+    },
+    data: {
+      authorities: [Authority.USER],
+      pageTitle: 'gitsearchApp.statistics.home.title',
+    },
+    canActivate: [UserRouteAccessService],
+  },
+  {
+    path: 'new',
+    component: StatisticsUpdateComponent,
+    resolve: {
+      statistics: StatisticsResolve,
+    },
+    data: {
+      authorities: [Authority.USER],
+      pageTitle: 'gitsearchApp.statistics.home.title',
+    },
+    canActivate: [UserRouteAccessService],
+  },
+  {
+    path: ':id/edit',
+    component: StatisticsUpdateComponent,
+    resolve: {
+      statistics: StatisticsResolve,
+    },
+    data: {
+      authorities: [Authority.USER],
+      pageTitle: 'gitsearchApp.statistics.home.title',
+    },
+    canActivate: [UserRouteAccessService],
+  },
+];
diff --git a/src/main/webapp/app/entities/statistics/statistics.service.ts b/src/main/webapp/app/entities/statistics/statistics.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8c87e953bf2d66dfe4a321ac09f0a069ca46e749
--- /dev/null
+++ b/src/main/webapp/app/entities/statistics/statistics.service.ts
@@ -0,0 +1,44 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpResponse } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+import { SERVER_API_URL } from 'app/app.constants';
+import { createRequestOption, SearchWithPagination } from 'app/shared/util/request-util';
+import { IStatistics } from 'app/shared/model/statistics.model';
+
+type EntityResponseType = HttpResponse<IStatistics>;
+type EntityArrayResponseType = HttpResponse<IStatistics[]>;
+
+@Injectable({ providedIn: 'root' })
+export class StatisticsService {
+  public resourceUrl = SERVER_API_URL + 'api/statistics';
+  public resourceSearchUrl = SERVER_API_URL + 'api/_search/statistics';
+
+  constructor(protected http: HttpClient) {}
+
+  create(statistics: IStatistics): Observable<EntityResponseType> {
+    return this.http.post<IStatistics>(this.resourceUrl, statistics, { observe: 'response' });
+  }
+
+  update(statistics: IStatistics): Observable<EntityResponseType> {
+    return this.http.put<IStatistics>(this.resourceUrl, statistics, { observe: 'response' });
+  }
+
+  find(id: number): Observable<EntityResponseType> {
+    return this.http.get<IStatistics>(`${this.resourceUrl}/${id}`, { observe: 'response' });
+  }
+
+  query(req?: any): Observable<EntityArrayResponseType> {
+    const options = createRequestOption(req);
+    return this.http.get<IStatistics[]>(this.resourceUrl, { params: options, observe: 'response' });
+  }
+
+  delete(id: number): Observable<HttpResponse<{}>> {
+    return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' });
+  }
+
+  search(req: SearchWithPagination): Observable<EntityArrayResponseType> {
+    const options = createRequestOption(req);
+    return this.http.get<IStatistics[]>(this.resourceSearchUrl, { params: options, observe: 'response' });
+  }
+}
diff --git a/src/main/webapp/app/exercise/exercise-card/exercise-card.component.html b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..a49cae3466c77e3b2d6e2769ab701982b096b1c8
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.html
@@ -0,0 +1,47 @@
+<div *ngIf="exercise" style="height: 100%;">
+    <div class="card">
+        <div class="card-body" style="display: flex; flex-direction: column;">
+            <img class="card-img-top" src="{{exercise.imageURL}}" (error)="correctImageURL($event)" alt="exercise image"
+                 style="width: auto; max-width: 100%; max-height: 70px; margin-left: auto; margin-right: auto; margin-bottom: 20px; margin-top: 5px; box-shadow: 3px;">
+            <h5 class="card-title">{{exercise.title}}</h5>
+            <p class="card-text" style="text-align: left;">{{exercise.description}}</p>
+
+            <div style="margin-top: auto; width: 100%; padding-left: 30px; padding-right: 30px;">
+                <!-- card rating-->
+                <div style="margin-bottom: 20px;">
+                    <div style="float: left;">
+                        <p class="card-text" jhiTranslate="exercise.details.rating"></p>
+                    </div>
+                    <div class="star-container">
+                            <span class="star"
+                                  *ngFor="let starNumber of [1,2,3,4,5]"
+                                  [ngClass]="{'star-marked': exercise.rating >= starNumber,
+                                              'star-unmarked': exercise.rating < starNumber}">
+                            </span>
+                    </div>
+                </div> <!-- card rating end-->
+
+                <!-- card bookmark-->
+                <div style="float: left; width: 100%;">
+                    <div style="float: left;">
+                        <p class="card-text" jhiTranslate="exercise.details.bookmark"></p>
+                    </div>
+                    <div class="form-check" style="float: right; padding-right: 10px;">
+                        <input class="form-check-input" type="checkbox" value="" id="card-defaultCheck1">
+                        <label class="form-check-label" for="card-defaultCheck1"></label>
+                    </div>
+                </div> <!-- card bookmark end-->
+
+                <!-- Button to Open the Modal -->
+                <button type="button"
+                        class="btn btn-outline-secondary"
+                        style="margin-top: 20px;"
+                        data-toggle="modal"
+                        data-target="#myModal"
+                        (click)="selectExercise()"
+                        jhiTranslate="exercise.more">
+                </button>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/main/webapp/app/exercise/exercise-card/exercise-card.component.scss b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c22d4bb5ba740d15a4c3cb23c5abc24ee6b60db7
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.scss
@@ -0,0 +1,32 @@
+.card {
+  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+  text-align: center;
+  justify-content: center;
+  height: 100%;
+}
+
+.card-title {
+  padding-bottom: 15px;
+}
+
+.card-text {
+  text-align: left;
+}
+
+.star-container {
+  float: right;
+  height: auto;
+  padding-right: 10px;
+}
+
+.star::before {
+  content: '★';
+}
+
+.star-marked {
+  color: #ffc700;
+}
+
+.star-unmarked {
+  color: #ccc;
+}
diff --git a/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..674f385364cba75ec6f52d4e9b0751ec8d1c8544
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-card/exercise-card.component.ts
@@ -0,0 +1,44 @@
+import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { Exercise } from 'app/shared/model/exercise.model';
+import { SearchService } from 'app/search/service/search-service';
+import { Statistics } from 'app/shared/model/statistics.model';
+
+@Component({
+  selector: 'jhi-exercise-card',
+  templateUrl: './exercise-card.component.html',
+  styleUrls: ['./exercise-card.component.scss'],
+})
+export class ExerciseCardComponent implements OnInit {
+  @Input() exercise: Exercise | undefined;
+
+  @Output() exerciseSelectionEvent = new EventEmitter<Exercise>();
+
+  constructor(protected searchService: SearchService) {}
+
+  ngOnInit(): void {}
+
+  selectExercise(): void {
+    if (this.exercise !== undefined) {
+      this.exercise.views = 0;
+      this.exercise.downloads = 0;
+      this.searchService.getStatisticsForExercise(this.exercise.originalResult.project.project_id).subscribe(
+        (data: Statistics) => {
+          this.exercise!.views = data.views!;
+          this.exercise!.downloads = data.downloads!;
+        },
+        () => alert('Request failed')
+      );
+      this.exercise.lastUpdate = this.exercise.lastUpdate.split('.')[0];
+    }
+    this.exerciseSelectionEvent.emit(this.exercise);
+  }
+
+  /**
+   * correct missing image urls
+   */
+  correctImageURL(event: Event): void {
+    if (event.srcElement) {
+      event.srcElement['src'] = '/content/img/gitLab.png';
+    }
+  }
+}
diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.html b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..02479d7f5461cae043e0a33a74fbe7891322728e
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.html
@@ -0,0 +1,347 @@
+<div *ngIf="exercise">
+    <div class="modal fade" id="myModal">
+        <div class="modal-dialog modal-lg modal-dialog-centered">
+            <div class="modal-content">
+
+                <!-- Modal Header -->
+                <div class="modal-header">
+                    <img class="card-img-top col-md-3" src="{{exercise.imageURL}}" alt="exercise image"
+                         (error)="correctImageURL($event)"
+                        style="height: auto; float: left;">
+                    <h4 class="modal-title">{{exercise.title}}</h4>
+                    <button type="button" class="close" data-dismiss="modal">&times;</button>
+                </div>
+
+                <!-- Modal body -->
+                <div class="modal-body">
+                    <div class="container-fluid">
+                        <div *ngIf="exercise.description" class="row">
+                            <p style="padding-bottom: 15px; text-align: justify;">{{exercise.description}}</p>
+                        </div>
+                        <div class="row">
+                            <div class="col-6 col-md-4" style="margin-bottom: 15px;">
+                                <p style="text-align: left;"><strong jhiTranslate="exercise.details.details"></strong>
+                                </p>
+                                <hr>
+
+                                <!-- modal rating-->
+                                <div *ngIf="exercise.rating"
+                                    style="float: left; width: 100%; margin-bottom: 5px; padding-top: 15px;">
+                                    <div style="float: left;">
+                                        <p class="card-text" jhiTranslate="exercise.details.rating"></p>
+                                    </div>
+                                    <div class="star-container">
+                                        <span class="star" *ngFor="let starNumber of [1,2,3,4,5]" [ngClass]="{'star-marked': exercise.rating >= starNumber,
+                                                              'star-unmarked': exercise.rating < starNumber}">
+                                        </span>
+                                    </div>
+                                </div> <!-- modal rating end-->
+
+                                <div *ngIf="exercise.rating"
+                                    style="float: left; width: 100%; margin-bottom: 5px; padding-top: 15px;">
+                                    <div style="float: left;">
+                                        <div><fa-icon icon="eye"></fa-icon></div>
+                                        <!-- <div class="onhoverIconDisplay"><p class="card-text" jhiTranslate="exercise.details.views"></p></div> -->
+
+                                    </div>
+                                    <div class="star-container">
+                                        <span>
+                                            {{exercise.views}}
+                                        </span>
+                                    </div>
+                                </div> <!-- modal views end-->
+
+                                <div *ngIf="exercise.rating"
+                                    style="float: left; width: 100%; margin-bottom: 5px; padding-top: 15px;">
+                                    <div style="float: left;">
+                                        <fa-icon icon="download"></fa-icon>
+                                    </div>
+                                    <div class="star-container">
+                                        <span>
+                                            {{exercise.downloads}}
+                                        </span>
+                                    </div>
+                                </div> <!-- modal views end-->
+
+
+                                <!-- modal bookmark-->
+                                <div style="float: left; width: 100%; padding-top: 15px; margin-bottom: 25px;">
+                                    <div style="float: left;">
+                                        <p class="card-text" jhiTranslate="exercise.details.bookmark"></p>
+                                    </div>
+                                    <div class="form-check" style="float: right; padding-right: 10px;">
+                                        <input class="form-check-input" type="checkbox" value=""
+                                            id="modal-defaultCheck1">
+                                        <label class="form-check-label" for="modal-defaultCheck1"></label>
+                                    </div>
+                                </div> <!-- modal bookmark end-->
+
+                                <!-- <button type="button" class="btn btn-outline-secondary"
+                                    style="display: block; margin-bottom: 5px;"
+                                    jhiTranslate="exercise.details.hitDetails">
+                                </button> -->
+
+                                <button *ngIf="exercise.type == exerciseType.COLLECTION" type="button" class="btn btn-outline-secondary"
+                                    style="display: block; margin-bottom: 5px;"
+                                    jhiTranslate="exercise.details.allExercises"
+                                    disabled>
+                                </button>
+
+                                <button type="button" class="btn btn-outline-secondary" style="display: block;"
+                                    (click)="openLink(exercise.gitlabURL)"
+                                    jhiTranslate="exercise.details.git">
+                                </button>
+                            </div>
+
+                            <div class="col-12 col-md-8">
+                                <p style="text-align: left;">
+                                    <strong jhiTranslate="exercise.metadata.metadata"></strong>
+                                </p>
+                                <hr>
+
+                                <table class="metadata-table">
+
+                                    <ng-container *ngIf="exercise.creators">
+                                        <tr *ngIf="exercise.creators.length > 0">
+                                            <td *ngIf="exercise.creators.length === 1"
+                                                jhiTranslate="exercise.metadata.creatorSingular"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="exercise.creators.length > 1"
+                                                jhiTranslate="exercise.metadata.creatorsPlural"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="isAuthenticated()" class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.creators.map(getPersonDetailsWithEmail))">
+                                            </td>
+                                            <td *ngIf="!isAuthenticated()" class="metadata-table-value"
+                                            [innerHTML]="arrayToString(exercise.creators.map(getPersonDetails))">
+                                        </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.contributor">
+                                        <tr *ngIf="exercise.contributor.length > 0">
+                                            <td *ngIf="exercise.contributor.length === 1"
+                                                jhiTranslate="exercise.metadata.contributorSingular"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="exercise.contributor.length > 1"
+                                                jhiTranslate="exercise.metadata.contributorsPlural"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="isAuthenticated()" class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.contributor.map(getPersonDetailsWithEmail))">
+                                            </td>
+                                            <td *ngIf="!isAuthenticated()" class="metadata-table-value"
+                                            [innerHTML]="arrayToString(exercise.contributor.map(getPersonDetails))">
+                                        </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.publisher">
+                                        <tr *ngIf="exercise.publisher.length > 0">
+                                            <td *ngIf="exercise.publisher.length === 1"
+                                                jhiTranslate="exercise.metadata.publisherSingular"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="exercise.publisher.length > 1"
+                                                jhiTranslate="exercise.metadata.publisherPlural"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="isAuthenticated()" class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.publisher.map(getPersonDetailsWithEmail))">
+                                            </td>
+                                            <td *ngIf="!isAuthenticated()" class="metadata-table-value"
+                                            [innerHTML]="arrayToString(exercise.publisher.map(getPersonDetails))">
+                                        </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.keyword">
+                                        <tr *ngIf="exercise.keyword.length > 0">
+                                            <td jhiTranslate="exercise.metadata.keywords"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.keyword)">
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.format">
+                                        <tr *ngIf="exercise.format.length > 0">
+                                            <td jhiTranslate="exercise.metadata.format"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.format)">
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.programmingLanguages">
+                                        <tr *ngIf="exercise.programmingLanguages.length > 0">
+                                            <td *ngIf="exercise.programmingLanguages.length === 1"
+                                                jhiTranslate="exercise.metadata.programmingLanguageSingular"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td *ngIf="exercise.programmingLanguages.length > 1"
+                                                jhiTranslate="exercise.metadata.programmingLanguagesPlural"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.programmingLanguages)">
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <ng-container *ngIf="exercise.requires">
+                                        <tr *ngIf="exercise.requires.length > 0">
+                                            <td jhiTranslate="exercise.metadata.requires"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value"
+                                                [innerHTML]="arrayToString(exercise.requires)">
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+
+                                    <tr *ngIf="exercise.license.length > 0">
+                                        <td jhiTranslate="exercise.metadata.license" class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{exercise.license}}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.difficulty">
+                                        <td jhiTranslate="exercise.metadata.difficulty"
+                                            class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{'exercise.metadata.' + exercise.difficulty | translate }}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.timeRequired">
+                                        <td jhiTranslate="exercise.metadata.timeRequired"
+                                            class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{exercise.timeRequired}}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.educationLevel">
+                                        <td jhiTranslate="exercise.metadata.educationLevel"
+                                            class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{exercise.educationLevel}}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.type">
+                                        <td jhiTranslate="exercise.metadata.type" class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{'exercise.metadata.' + exercise.type | translate }}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.structure">
+                                        <td jhiTranslate="exercise.metadata.structure" class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{'exercise.metadata.' + exercise.structure | translate }}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.status">
+                                        <td jhiTranslate="exercise.metadata.status" class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{'exercise.metadata.' + exercise.status | translate }}
+                                        </td>
+                                    </tr>
+
+                                    <tr *ngIf="exercise.deprecated">
+                                        <td jhiTranslate="exercise.metadata.deprecated"
+                                            class="metadata-table-description">
+                                        </td>
+                                        <td class="metadata-table-value">
+                                            {{exercise.deprecated}}
+                                        </td>
+                                    </tr>
+
+                                    <ng-container *ngIf="exercise.lastUpdate">
+                                        <tr *ngIf="exercise.lastUpdate.length > 0">
+                                            <td jhiTranslate="exercise.metadata.lastUpdate"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value">
+                                                {{exercise.lastUpdate}}
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+                                    <ng-container *ngIf="exercise.version">
+                                        <tr>
+                                            <td jhiTranslate="exercise.metadata.version"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value">
+                                                {{exercise.version}}
+                                            </td>
+                                        </tr>
+                                    </ng-container>
+                                    <!-- <ng-container *ngIf="exercise.version">
+                                        <tr>
+                                            <td jhiTranslate="exercise.metadata.metadataVersion"
+                                                class="metadata-table-description">
+                                            </td>
+                                            <td class="metadata-table-value">
+                                                {{exercise.metadataVersion}}
+                                            </td>
+                                        </tr>
+                                    </ng-container> -->
+
+
+
+                                </table>
+
+                            </div>
+
+                            <div class="col-6 col-md-4"></div>
+                            <div class="col-12 col-md-8">
+                                <p style="text-align: left; margin-top: 20px;"><strong jhiTranslate="exercise.export.export"></strong>
+                                </p>
+                                <hr>
+                                <a *ngFor="let action of exercise.originalResult.supportedActions" class="btn btn-outline-secondary" role="button" aria-pressed="true"
+                                    style="float: left; margin-right: 5px; margin-top: 5px;" (click)="startAction(action, exercise)"
+                                    >{{action.commandName}}</a>
+                                <button *ngIf="exercise.type == exerciseType.COLLECTION" type="button" class="btn btn-outline-secondary"
+                                    style="float: left; margin-right: 5px; margin-top: 5px;"
+                                    jhiTranslate="exercise.export.latex"
+                                    disabled>
+                                </button>
+
+                                <a class="btn btn-outline-secondary" role="button" aria-pressed="true"
+                                    style="float: left; margin-right: 5px; margin-top: 5px;"
+                                    (click)="download()"
+                                    jhiTranslate="exercise.export.download"></a>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <!-- Modal footer -->
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal"
+                        jhiTranslate="exercise.close"></button>
+                </div>
+
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.scss b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..4e3112f21acce9b58bdb5b1b3bef120ae30ccdae
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.scss
@@ -0,0 +1,49 @@
+.star-container {
+  float: right;
+  height: auto;
+  padding-right: 10px;
+}
+
+.star::before {
+  content: '★';
+}
+
+.star-marked {
+  color: #ffc700;
+}
+
+.star-unmarked {
+  color: #ccc;
+}
+
+.metadata-table {
+  width: 100%;
+}
+
+.metadata-table-description {
+  padding: 3px;
+}
+
+.metadata-table-value {
+  padding: 3px;
+}
+
+.blackLink {
+  color: black !important;
+}
+
+.modal-title {
+  padding-top: 5px;
+}
+
+.card-text {
+  text-align: left;
+}
+
+// .onhoverIcon:hover ~ .onhoverIconDisplay {
+//   visibility: visible;
+// }
+
+// .onhoverIconDisplay {
+//   visibility: hidden;
+// }
diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..109966ff106134e19841da8c02f99fca88e4b533
--- /dev/null
+++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts
@@ -0,0 +1,136 @@
+import { Component, OnInit, Input, OnDestroy } from '@angular/core';
+import { Exercise, IExerciseType } from 'app/shared/model/exercise.model';
+import { Person } from 'app/shared/model/person.model';
+import { Subscription } from 'rxjs';
+import { PluginActionInfo } from 'app/shared/model/search/search-result-dto.model';
+import { PluginService } from 'app/shared/service/plugin-service';
+import { ShoppingBasketInfo, ShoppingBasketRedirectInfoDTO } from 'app/shared/model/basket/shopping-basket-info.model';
+import { HttpErrorResponse } from '@angular/common/http';
+
+import { AccountService } from 'app/core/auth/account.service';
+import { Account } from 'app/core/user/account.model';
+import { SearchService } from 'app/search/service/search-service.ts';
+import { HttpResponse } from '@angular/common/http';
+import { JhiAlertService } from 'ng-jhipster';
+
+@Component({
+  selector: 'jhi-exercise-details',
+  templateUrl: './exercise-details.component.html',
+  styleUrls: ['./exercise-details.component.scss'],
+})
+export class ExerciseDetailsComponent implements OnInit, OnDestroy {
+  @Input() exercise: Exercise | undefined;
+  account: Account | null = null;
+  authSubscription?: Subscription;
+
+  constructor(
+    private accountService: AccountService,
+    protected pluginService: PluginService,
+    private searchService: SearchService,
+    private jhiAlertService: JhiAlertService
+  ) {}
+
+  ngOnInit(): void {
+    this.authSubscription = this.accountService.getAuthenticationState().subscribe(account => (this.account = account));
+  }
+
+  public get exerciseType(): typeof IExerciseType {
+    return IExerciseType;
+  }
+
+  public isAuthenticated(): boolean {
+    return this.accountService.isAuthenticated();
+  }
+
+  public getPersonName(person: Person): string {
+    return person.name;
+  }
+
+  public getPersonDetails(person: Person): string {
+    return person.name + ', ' + person.affiliation;
+  }
+
+  public getPersonDetailsWithEmail(person: Person): string {
+    return "<a class='text-dark' href= 'mailto:'" + person.email + '>' + person.name + ', ' + person.affiliation + '</a>';
+  }
+
+  public arrayToString(array: string[]): string {
+    let result = '';
+    let i = 1;
+    array.forEach(element => {
+      if (array.length > 1 && array.length !== i) {
+        result += element + ', ';
+      } else {
+        result += element;
+      }
+      if (i % 5 === 0) {
+        result += '<br>';
+      }
+      i++;
+    });
+    return result;
+  }
+
+    public startAction(action: PluginActionInfo, exercise: Exercise): void {
+        const basketInfo: ShoppingBasketInfo = {
+            plugin: action.plugin,
+            action: action.action,
+            itemInfos: [
+                exercise.originalResult
+            ]
+        };
+        this.pluginService.getRedirectLink(basketInfo).subscribe(
+            (redirectInfo: ShoppingBasketRedirectInfoDTO) => {
+                // alert('redirecting to ' + redirectInfo.redirectURL);
+                // location.href = redirectInfo.redirectURL;
+                	window.open(redirectInfo.redirectURL, action.action);
+            },
+            () => alert('Search failed'))
+    }
+
+  public download(): void {
+    this.exportExercise(Number(this.exercise!.originalResult.project.project_id));
+  }
+
+  exportExercise(projectId: number) {
+    return this.searchService.exportExercise(projectId).subscribe(
+      (response: HttpResponse<Blob>) => {
+        this.jhiAlertService.success('artemisApp.programmingExercise.export.successMessage');
+        if (response.body) {
+          const zipFile = new Blob([response.body], { type: 'application/zip' });
+          const url = window.URL.createObjectURL(zipFile);
+          const link = document.createElement('a');
+          link.setAttribute('href', url);
+          link.setAttribute('download', response.headers.get('filename')!);
+          document.body.appendChild(link); // Required for FF
+          link.click();
+          window.URL.revokeObjectURL(url);
+        }
+      },
+      (error: HttpErrorResponse) => this.jhiAlertService.error('Unable to export exercise. Error: ' + error.message)
+    );
+  }
+
+  public getViews(): number {
+    return 5;
+  }
+
+  openLink(link: string): void {
+    window.open(link);
+  }
+
+  ngOnDestroy(): void {
+    if (this.authSubscription) {
+      this.authSubscription.unsubscribe();
+    }
+  }
+
+  /**
+   * correct missing image urls
+   */
+  correctImageURL(event: Event): void {
+    if (event.srcElement) {
+      event.srcElement['src'] = '/content/img/gitLab.png';
+    }
+  }
+}
diff --git a/src/main/webapp/app/home/home.component.html b/src/main/webapp/app/home/home.component.html
index a7d0f91f38efbeb923416680824cdfbb978a69a5..00ca0f7434cab455a5e4c2b4737d22c8eaad4452 100644
--- a/src/main/webapp/app/home/home.component.html
+++ b/src/main/webapp/app/home/home.component.html
@@ -1,8 +1,18 @@
+<div><!--  just a wrapper  -->
 <div class="row">
     <div class="col-md-12">
-        <h1 class="display-4" jhiTranslate="home.title">Welcome!</h1>
+    	<h1 style="padding: 50px 0px 15px 0px;text-align:center" jhiTranslate="home.title">Browse our popular content</h1>
+		<!-- <h4 style="margin-bottom: 50px" align="center" jhiTranslate="home.subtitle">An optional subheadline for additional information</h4>  -->
+		<div jhiTranslate="home.teaser" style="width: 80%; margin-left: auto; margin-right: auto;">teaser text </div>
+        <div [ngSwitch]="isAuthenticated()">
+            <div class="alert alert-success" *ngSwitchCase="true">
+                <span id="home-logged-message" *ngIf="account" jhiTranslate="home.logged.message"
+                    [translateValues]="{ username: account.login }">You are logged in as user "{{ account.login }}".</span>
+            </div>
+
+        </div>
 
-        <div>
+<!--         <div>
             <span jhiTranslate="global.messages.info.sharing.platform">
                 The CodeAbility Sharing Platform provides an infrastructure that enables the exchange of learning content in the field of programming. The content includes programming exercises, lecture materials, and collections of links on all topics related to the acquisition of programming skills. This search engine provides a quick and easy way for users to search for content published on the sharing platform. The search results can be further restricted by various filter options such as licenses, programming languages, and authors.
             </span>&nbsp;
@@ -24,6 +34,11 @@
                 <span jhiTranslate="global.messages.info.registration.disabled">Registration of new users is currently disabled. Please, contact the administrator if you want to test the system.</span>
             </div>
         </div>
+        -->
+        <div><jhi-teaser-content></jhi-teaser-content></div>
 
     </div>
 </div>
+
+
+</div>
\ No newline at end of file
diff --git a/src/main/webapp/app/home/home.module.ts b/src/main/webapp/app/home/home.module.ts
index 2d5feee3d7c82e64d1bb55a1510311ca2294f5e3..64cea3c5012b3454bf47b88099c3c84e12537fbf 100644
--- a/src/main/webapp/app/home/home.module.ts
+++ b/src/main/webapp/app/home/home.module.ts
@@ -1,5 +1,6 @@
 import {NgModule} from '@angular/core';
 import {RouterModule} from '@angular/router';
+import { TeaserContentComponent } from 'app/teaserContent/teaserContent.component';
 
 import {GitSearchV2SharedModule} from 'app/shared/shared.module';
 import {HOME_ROUTE} from './home.route';
@@ -7,7 +8,7 @@ import {HomeComponent} from './home.component';
 
 @NgModule({
   imports: [GitSearchV2SharedModule, RouterModule.forChild([HOME_ROUTE])],
-  declarations: [HomeComponent],
+  declarations: [HomeComponent, TeaserContentComponent],
 })
 export class GitSearchV2HomeModule {
 }
diff --git a/src/main/webapp/app/home/home.route.ts b/src/main/webapp/app/home/home.route.ts
index e70e6b0a7af9a77b7307beef331c6bd76541a416..05da318242e4fc46de35351c4b996b2ad11a5ec5 100644
--- a/src/main/webapp/app/home/home.route.ts
+++ b/src/main/webapp/app/home/home.route.ts
@@ -7,6 +7,6 @@ export const HOME_ROUTE: Route = {
   component: HomeComponent,
   data: {
     authorities: [],
-    pageTitle: 'home.title',
+    pageTitle: 'global.title',
   },
 };
diff --git a/src/main/webapp/app/home/prism.scss b/src/main/webapp/app/home/prism.scss
index bd7e88cd2aac85a79ad37590ffbebc26eb404a82..cd17dbc8a68c555c8b3c168f142c23e07eab1698 100644
--- a/src/main/webapp/app/home/prism.scss
+++ b/src/main/webapp/app/home/prism.scss
@@ -42,7 +42,7 @@ https://prismjs.com/download.html#themes=prism&languages=css */
   background: #b3d4fc;
 }
 
-::ng-deep @media print {
+@media print {
   ::ng-deep code[class*="language-"],
   ::ng-deep pre[class*="language-"] {
     text-shadow: none;
diff --git a/src/main/webapp/app/layouts/footer/footer.component.html b/src/main/webapp/app/layouts/footer/footer.component.html
index eca9a65a6261983cbadc4faa49c70aaa86a71b18..e61a21f8647001829d157b0a152713c380dcc7f9 100644
--- a/src/main/webapp/app/layouts/footer/footer.component.html
+++ b/src/main/webapp/app/layouts/footer/footer.component.html
@@ -1,3 +1,29 @@
 <div class="footer">
-    <p jhiTranslate="footer">More information can be found <a href="https://sharing-codeability.uibk.ac.at/static/SharingCodeAbility.html">here</a>!</p>
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-4">
+          <div class="logo-container">
+            <img style="padding-left: 25px; padding-top: 15px; float: left;" src="/content/img/logo-footer.png" alt="logoFooter" width="128" height="auto">
+          </div>
+        </div>
+        <div class="col-8">
+          <div style="float: left; padding-top: 15px;">
+            <ul>
+              <li><a href="https://codeability.uibk.ac.at/" jhiTranslate="global.footer.about">About codeAbility</a></li>
+            </ul>
+          </div>
+          <div style="float: left; padding-top: 15px;">
+            <ul>
+              <li><a href="https://www.uibk.ac.at/informatik/"  jhiTranslate="global.footer.imprint">Imprint</a></li>
+            </ul>
+          </div>
+          <div style="float: left; padding-top: 15px;">
+            <ul>
+              <li><a href="https://artemis.codeability.uibk.ac.at/#/datenschutz"  jhiTranslate="global.footer.privacy">Privacy</a></li>
+            </ul>
+          </div>
+           <div style="float: right; font-size: 50%;padding-top: 15px;" *ngIf="applicationInfoService.getDeploymentInfo().branch">{{applicationInfoService.getDeploymentInfo().branch}}/{{applicationInfoService.getDeploymentInfo().commitId}}</div>
+      </div>
+    </div>
+  </div>
 </div>
diff --git a/src/main/webapp/app/layouts/footer/footer.component.ts b/src/main/webapp/app/layouts/footer/footer.component.ts
index 7c640ec8af4302bd0e5914d0172021b03e591717..2a54d1bda5dc31c3227322f20b51d8c37e095c9d 100644
--- a/src/main/webapp/app/layouts/footer/footer.component.ts
+++ b/src/main/webapp/app/layouts/footer/footer.component.ts
@@ -1,7 +1,10 @@
 import { Component } from '@angular/core';
+import { ApplicationInfoService } from 'app/core/application/applicationInfo.service'
 
 @Component({
   selector: 'jhi-footer',
   templateUrl: './footer.component.html',
 })
-export class FooterComponent {}
+export class FooterComponent {
+    constructor(public applicationInfoService: ApplicationInfoService) {}
+}
diff --git a/src/main/webapp/app/layouts/main/main.component.html b/src/main/webapp/app/layouts/main/main.component.html
index d8bcc2219cc79f0d145395f71f2c309d7ae64153..1d291a79b8c8cdefa50deb22e606da4c6b05d9fe 100644
--- a/src/main/webapp/app/layouts/main/main.component.html
+++ b/src/main/webapp/app/layouts/main/main.component.html
@@ -1,13 +1,25 @@
-<jhi-page-ribbon></jhi-page-ribbon>
+<div style="display: flex; flex-direction: column; min-height: 100vh;">
 
-<div>
-    <router-outlet name="navbar"></router-outlet>
-</div>
+    <!-- codeability bar -->
+    <div id="top-space">
+        <img src="/content/img/logo-top.png" alt="codeAbility" width="168px">
+    </div>
+
+    <div>
+        <router-outlet name="navbar"></router-outlet>
+    </div>
+
+    <div class="container-fluid">
+
+        <div class="pagewrapper"></div>
+
+        <!-- Main content -->
+            <router-outlet></router-outlet>
+
+        <div class="pagewrapper"></div>
 
-<div class="container-fluid">
-    <div class="card jh-card">
-        <router-outlet></router-outlet>
+        <jhi-page-ribbon></jhi-page-ribbon>
     </div>
 
-    <jhi-footer></jhi-footer>
+    <jhi-footer style="margin-top:auto"></jhi-footer>
 </div>
diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts
index a9c86b649cd2c2cc5eda0e9c5f48826251b1d3ba..137458c2b8eae0170906ed89a7539d0905e6239b 100644
--- a/src/main/webapp/app/layouts/main/main.component.ts
+++ b/src/main/webapp/app/layouts/main/main.component.ts
@@ -4,6 +4,9 @@ import { Router, ActivatedRouteSnapshot, NavigationEnd, NavigationError } from '
 import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
 
 import { AccountService } from 'app/core/auth/account.service';
+import { AuthServerProvider } from 'app/core/auth/auth-jwt.service';
+import { CookieService } from 'ngx-cookie-service';
+// import { AlertErrorComponent } from 'app/shared/alert/alert-error.component';
 
 @Component({
   selector: 'jhi-main',
@@ -17,12 +20,16 @@ export class MainComponent implements OnInit {
     private titleService: Title,
     private router: Router,
     private translateService: TranslateService,
-    rootRenderer: RendererFactory2
-  ) {
+    rootRenderer: RendererFactory2,
+    private authServerProvider: AuthServerProvider,
+    private cookieService: CookieService
+  ) //	private alertErrorComponent: AlertErrorComponent
+  {
     this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null);
   }
 
   ngOnInit(): void {
+    this.checkRequestToken();
     // try to log in automatically
     this.accountService.identity().subscribe();
 
@@ -42,6 +49,22 @@ export class MainComponent implements OnInit {
     });
   }
 
+  private checkRequestToken(): void {
+    const tokenCookie = this.cookieService.get('tempRequestToken');
+    if (tokenCookie) {
+      if (tokenCookie.length > 20)
+        this.authServerProvider.refreshToken(tokenCookie).subscribe(
+          () => {
+            this.accountService.identity(true).subscribe();
+            this.router.navigate(['']);
+          },
+          () => {
+            //						 this.alertErrorComponent.addErrorAlert('OAuth2 authentication failed', 'Authentication failed');
+          }
+        );
+    }
+  }
+
   private getPageTitle(routeSnapshot: ActivatedRouteSnapshot): string {
     let title: string = routeSnapshot.data && routeSnapshot.data['pageTitle'] ? routeSnapshot.data['pageTitle'] : '';
     if (routeSnapshot.firstChild) {
diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.html b/src/main/webapp/app/layouts/navbar/navbar.component.html
index 880d7329987c29806f94eb3a48a2e926c5e6c6c7..4985e5c35f9fa5b83e155706b46fdb12f17ad635 100644
--- a/src/main/webapp/app/layouts/navbar/navbar.component.html
+++ b/src/main/webapp/app/layouts/navbar/navbar.component.html
@@ -1,160 +1,296 @@
-<nav class="navbar navbar-dark navbar-expand-md bg-dark">
-    <a class="navbar-brand logo" routerLink="/" (click)="collapseNavbar()">
-        <span class="logo-img"></span>
-        <span jhiTranslate="global.title" class="navbar-title">Sharing Platform</span> <span class="navbar-version">{{ version }}</span>
-    </a>
-    <a class="navbar-toggler d-lg-none" href="javascript:void(0);" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation" (click)="toggleNavbar()">
-        <fa-icon icon="bars"></fa-icon>
-    </a>
-    <div class="navbar-collapse collapse" id="navbarResponsive" [ngbCollapse]="isNavbarCollapsed" [ngSwitch]="isAuthenticated()">
-        <ul class="navbar-nav ml-auto">
-            <li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
-                <a class="nav-link" routerLink="/" (click)="collapseNavbar()">
-                    <span>
-                        <fa-icon icon="home"></fa-icon>
-                        <span jhiTranslate="global.menu.home">Home</span>
-                    </span>
-                </a>
-            </li>
-            <li class="nav-item" *ngSwitchCase="true" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
-                <a class="nav-link" routerLink="/search" (click)="collapseNavbar()">
-                    <span>
-                        <fa-icon icon="search"></fa-icon>
-                        <span jhiTranslate="global.menu.search">Search</span>
+<!-- Navigation -->
+<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
+    <div class="container-fluid">
+
+        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
+            <span class="navbar-toggler-icon"></span>
+        </button>
+        <div class="collapse navbar-collapse" id="collapsibleNavbar">
+            <div class="navbar-nav" [ngSwitch]="isAuthenticated()">
+                <!-- Menu Home -->
+                <div class="nav-item">
+                    <span class="nav-link">
+                        <a routerLink="/">
+                            <!-- Icon -->
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                                 class="bi bi-house-fill" viewBox="0 0 16 16">
+                                <path fill-rule="evenodd"
+                                      d="M8 3.293l6 6V13.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 13.5V9.293l6-6zm5-.793V6l-2-2V2.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5z"/>
+                                <path fill-rule="evenodd"
+                                      d="M7.293 1.5a1 1 0 0 1 1.414 0l6.647 6.646a.5.5 0 0 1-.708.708L8 2.207 1.354 8.854a.5.5 0 1 1-.708-.708L7.293 1.5z"/>
+                            </svg>
+                            <!-- Icon End -->
+                            <span jhiTranslate="global.menu.home">Home</span>
+                        </a>
                     </span>
-                </a>
-            </li>
-            <!-- jhipster-needle-add-element-to-menu - JHipster will add new menu items here -->
-            <li *ngSwitchCase="true" ngbDropdown class="nav-item dropdown pointer" display="dynamic" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
-                <a class="nav-link dropdown-toggle" ngbDropdownToggle href="javascript:void(0);" id="entity-menu">
-                    <span>
-                        <fa-icon icon="th-list"></fa-icon>
-                        <span jhiTranslate="global.menu.entities.main">
-                            Entities
+                </div>
+
+                <div class="nav-item dropdown" *ngIf="languages && languages.length > 1">
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="javascript:void(0);">
+                        <span>
+                            <fa-icon icon="flag"></fa-icon>
+                            <span jhiTranslate="global.menu.language">Language</span>
                         </span>
-                    </span>
-                </a>
-                <ul class="dropdown-menu" ngbDropdownMenu aria-labelledby="entity-menu">
-                    <!-- jhipster-needle-add-entity-to-menu - JHipster will add entities to the menu here -->
-                </ul>
-            </li>
-            <li *jhiHasAnyAuthority="'ROLE_ADMIN'" ngbDropdown class="nav-item dropdown pointer" display="dynamic" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
-                <a class="nav-link dropdown-toggle" ngbDropdownToggle href="javascript:void(0);" id="admin-menu">
-                    <span>
-                        <fa-icon icon="user-plus"></fa-icon>
-                        <span jhiTranslate="global.menu.admin.main">Administration</span>
-                    </span>
-                </a>
-                <ul class="dropdown-menu" ngbDropdownMenu aria-labelledby="admin-menu">
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/user-management" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="user" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.userManagement">User management</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/metrics" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="tachometer-alt" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.metrics">Metrics</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/health" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="heart" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.health">Health</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/configuration" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="list" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.configuration">Configuration</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/audits" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="bell" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.audits">Audits</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" routerLink="admin/logs" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="tasks" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.logs">Logs</span>
-                        </a>
-                    </li>
-                    <li *ngIf="swaggerEnabled">
-                        <a class="dropdown-item" routerLink="admin/docs" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="book" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.apidocs">API</span>
+                    </a>
+                    <ul class="dropdown-menu">
+                        <li *ngFor="let language of languages">
+                            <a class="dropdown-item" [jhiActiveMenu]="language" href="javascript:void(0);"
+                               (click)="changeLanguage(language);collapseNavbar();">{{ language | findLanguageFromKey }}</a>
+                        </li>
+                    </ul>
+                </div>
+
+
+                <!-- Menu Entities -->
+                <!-- 
+                <div class="nav-item dropdown">
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">
+                        <!-X- Icon -X->
+                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                             class="bi bi-collection-fill" viewBox="0 0 16 16">
+                            <path
+                                d="M0 13a1.5 1.5 0 0 0 1.5 1.5h13A1.5 1.5 0 0 0 16 13V6a1.5 1.5 0 0 0-1.5-1.5h-13A1.5 1.5 0 0 0 0 6v7zM2 3a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 0-1h-11A.5.5 0 0 0 2 3zm2-2a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 0-1h-7A.5.5 0 0 0 4 1z"/>
+                        </svg>
+                        <!-X- Icon End -X->
+                        Entities
+                    </a> (Vorlage)
+                    <div class="dropdown-menu">
+                        <a class="dropdown-item" href="#">Menu Item 1</a>
+                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                        <a class="dropdown-item" href="#">Menu Item 2</a>
+                        <span class="spawn-submenu">This is a short description of the link 2</span>
+                        <a class="dropdown-item" href="#">Menu Item 3</a>
+                        <span class="spawn-submenu">This is a short description of the link 3</span>
+                    </div>
+                </div>
+				 -->
+                <!-- Menu Programs -->
+                <!-- 
+                <div class="nav-item dropdown">
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">
+                        <!-X- Icon -X->
+                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                             class="bi bi-grid-3x3-gap-fill" viewBox="0 0 16 16">
+                            <path
+                                d="M1 2a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V2zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V2zM1 7a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V7zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V7zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1V7zM1 12a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-2zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1v-2zm5 0a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-2z"/>
+                        </svg>
+                        <! -X- Icon End -X- >
+                        Programs
+                    </a> (Vorlage)
+                    <div class="dropdown-menu dropdown-large">
+                        <div class="row flex-container">
+                            <div class="col-md-6 flex-item-left">
+                                <h6 class="headline">Category 1</h6>
+                                <ul class="list-unstyled">
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 1</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 2</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 3</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                </ul>
+                            </div>
+                            <div class="col-md-6 flex-item-left">
+                                <h6 class="headline">Category 2</h6>
+                                <ul class="list-unstyled">
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 1</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 2</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                    <li class="nav-item">
+                                        <a class="dropdown-item" href="#">Menu Item 3</a>
+                                        <span class="spawn-submenu">This is a short description of the link 1</span>
+                                    </li>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+				 -->
+                <!-- Search -->
+                <div class="nav-item" >
+                    <span class="nav-link">
+                        <a routerLink="/search">
+                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                                 class="bi bi-search"
+                                 viewBox="0 0 16 16">
+                                <path
+                                    d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
+                            </svg>
+                            <span jhiTranslate="global.menu.search"></span>
                         </a>
-                    </li>
-                    <!-- jhipster-needle-add-element-to-admin-menu - JHipster will add entities to the admin menu here -->
-                    <li *ngIf="!inProduction">
-                        <a class="dropdown-item" href='./h2-console' target="_tab" (click)="collapseNavbar()">
-                            <fa-icon icon="hdd" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.admin.database">Database</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-            <li ngbDropdown class="nav-item dropdown pointer" display="dynamic" *ngIf="languages && languages.length > 1">
-                <a class="nav-link dropdown-toggle" ngbDropdownToggle href="javascript:void(0);" id="languagesnavBarDropdown">
-                    <span>
-                        <fa-icon icon="flag"></fa-icon>
-                        <span jhiTranslate="global.menu.language">Language</span>
                     </span>
-                </a>
-                <ul class="dropdown-menu" ngbDropdownMenu aria-labelledby="languagesnavBarDropdown">
-                    <li *ngFor="let language of languages">
-                        <a class="dropdown-item" [jhiActiveMenu]="language" href="javascript:void(0);" (click)="changeLanguage(language);collapseNavbar();">{{ language | findLanguageFromKey }}</a>
-                    </li>
-                </ul>
-            </li>
-            <li ngbDropdown class="nav-item dropdown pointer" display="dynamic" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
-                <a class="nav-link dropdown-toggle" ngbDropdownToggle href="javascript:void(0);" id="account-menu">
-                    <span *ngIf="!getImageUrl()">
-                        <fa-icon icon="user"></fa-icon>
-                        <span jhiTranslate="global.menu.account.main">
-                            Account
+                </div>
+                <!-- End search -->
+
+                <div *jhiHasAnyAuthority="'ROLE_ADMIN'" ngbDropdown class="nav-item dropdown pointer" display="dynamic"
+                    routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" ngbDropdownToggle
+                       href="javascript:void(0);"
+                       id="admin-menu">
+                        <span>
+                              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
+                                   class="bi bi-globe" viewBox="0 0 16 16">
+                                  <path
+                                    d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855.173-.324.33-.682.468-1.068H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"/>
+                              </svg>
+                            <span jhiTranslate="global.menu.admin.main">Administration</span>
                         </span>
-                    </span>
-                    <span *ngIf="getImageUrl()">
-                        <img [src]="getImageUrl()" class="profile-image rounded-circle" alt="Avatar">
-                    </span>
-                </a>
-                <ul class="dropdown-menu" ngbDropdownMenu aria-labelledby="account-menu">
-                    <li *ngSwitchCase="true">
-                        <a class="dropdown-item" routerLink="account/settings" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="wrench" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.account.settings">Settings</span>
-                        </a>
-                    </li>
-                    <li *ngSwitchCase="true">
-                        <a class="dropdown-item" routerLink="account/password" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="lock" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.account.password">Password</span>
-                        </a>
-                    </li>
-                    <li *ngSwitchCase="true">
-                        <a class="dropdown-item" (click)="logout()" id="logout">
-                            <fa-icon icon="sign-out-alt" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.account.logout">Sign out</span>
-                        </a>
-                    </li>
-                    <li *ngSwitchCase="false">
-                        <a class="dropdown-item" (click)="login()" id="login">
-                            <fa-icon icon="sign-in-alt" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.account.login">Sign in</span>
-                        </a>
-                    </li>
-                    <li *ngSwitchCase="false">
-                        <a class="dropdown-item" routerLink="account/register" routerLinkActive="active" (click)="collapseNavbar()">
-                            <fa-icon icon="user-plus" [fixedWidth]="true"></fa-icon>
-                            <span jhiTranslate="global.menu.account.register">Register</span>
-                        </a>
-                    </li>
-                </ul>
-            </li>
-        </ul>
+                    </a>
+                    <ul class="dropdown-menu" ngbDropdownMenu aria-labelledby="admin-menu">
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/user-management" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="user" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.userManagement">User management</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/metrics" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="tachometer-alt" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.metrics">Metrics</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/health" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="heart" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.health">Health</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/configuration" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="list" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.configuration">Configuration</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/audits" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="bell" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.audits">Audits</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a class="dropdown-item" routerLink="admin/logs" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="tasks" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.logs">Logs</span>
+                            </a>
+                        </li>
+                        <li *ngIf="swaggerEnabled">
+                            <a class="dropdown-item" routerLink="admin/docs" routerLinkActive="active"
+                               (click)="collapseNavbar()">
+                                <fa-icon icon="book" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.apidocs">API</span>
+                            </a>
+                        </li>
+                        <!-- jhipster-needle-add-element-to-admin-menu - JHipster will add entities to the admin menu here -->
+                        <li *ngIf="!inProduction">
+                            <a class="dropdown-item" href='./h2-console' target="_tab" (click)="collapseNavbar()">
+                                <fa-icon icon="hdd" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.admin.database">Database</span>
+                            </a>
+                        </li>
+                    </ul>
+                </div>
+
+                <!-- Login -->
+                <div class="nav-item dropdown pointer" ngbDropdown>
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#">
+                        <!-- Icon -->
+                        <span *ngIf="!getImageUrl()">
+                            <fa-icon icon="user"></fa-icon>
+                            <span jhiTranslate="global.menu.account.main">Account</span>
+                        </span>
+                        <span *ngIf="getImageUrl()">
+                            <img [src]="getImageUrl()" class="profile-image rounded-circle" alt="Avatar"/>
+                        </span>
+                        <!-- Icon End -->
+                    </a>
+                    <div class="dropdown-menu">
+                        <div class="search-container" *ngSwitchCase="false">
+                            <div *ngSwitchCase="false" >
+                                <!-- Quick Search -->
+                                <div class="dropdown-item" *ngFor="let config of configs">
+                                    <img src="{{'oauth2.'+config.registrationId + '.icon'| translate}}" alt="oAuth2Login" style="width: 50px;"/>
+                                    <button type="submit" class="btn btn-outline-secondary"
+                                            *ngSwitchCase="false" (click)="loginWithGitLab(config.registrationId)" 
+                                            jhiTranslate="oauth2.{{config.registrationId}}.text">Sign in with GitLab
+                                        Account 
+                                    </button>
+                                </div>
+                                <!-- Menu Divider -->
+                                <div class="dropdown-divider"></div>
+                                <div class="dropdown-item">
+                                     <form class="form" role="form" (ngSubmit)="login()" [formGroup]="loginForm">
+                                    <h5 style="color: #b3b3b3; margin-bottom: 25px;">Sign in locally</h5>
+                                    <div class="input-group mb-3">
+                                        <input type="text" class="form-control"
+                                               name="username" id="username" formControlName="username" placeholder="{{ 'global.form.username.placeholder' | translate }}" #username>
+                                    </div>
+                                    <div class="input-group mb-3">
+                                        <input type="password" class="form-control" formControlName="password"
+                                               name="password" id="password" placeholder="{{ 'login.form.password.placeholder' | translate }}">
+                                    </div>
+						            <div class="alert alert-danger" *ngIf="authenticationError" jhiTranslate="login.messages.error.authentication">
+						                <strong>Failed to sign in!</strong> Please check your credentials and try again.
+						            </div>
+                                    <button type="submit" class="btn btn-outline-secondary" (click)="login()"
+                                            style="border-color: #d0d0d0" jhiTranslate="login.form.button">Sign in
+                                    </button>
+                                    </form>
+                                    <p style="margin-top: 25px;">
+                                        <a href="#">Did you forget your password?</a>
+                                    </p>
+                                </div>
+                                <a class="dropdown-item" (click)="login()" id="login">
+                                    <fa-icon icon="sign-in-alt" [fixedWidth]="true"></fa-icon>
+                                    <span jhiTranslate="global.menu.account.login">Sign in</span>
+                                </a>
+                            </div>
+                        </div>
+                        <div>
+                            <a class="dropdown-item" routerLink="account/settings" routerLinkActive="active"
+                               *ngSwitchCase="true" (click)="collapseNavbar()">
+                                <fa-icon icon="wrench" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.account.settings">Settings</span>
+                            </a>
+                            <span class="spawn-submenu" jhiTranslate="global.menu.account.settingsDescription"
+                                  *ngSwitchCase="true">Manage your user settings</span>
+
+                            <a class="dropdown-item" routerLink="account/password" routerLinkActive="active"
+                               *ngSwitchCase="true" (click)="collapseNavbar()">
+                                <fa-icon icon="lock" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.account.password">Password</span>
+                            </a>
+                            <span class="spawn-submenu" jhiTranslate="global.menu.account.passwordDescription"
+                                  *ngSwitchCase="true">Change password</span>
+
+                            <a class="dropdown-item" (click)="logout()" id="logout" *ngSwitchCase="true">
+                                <fa-icon icon="sign-out-alt" [fixedWidth]="true"></fa-icon>
+                                <span jhiTranslate="global.menu.account.logout">Sign out</span>
+                            </a>
+                            <span class="spawn-submenu" jhiTranslate="global.menu.account.logoutDescription"
+                                  *ngSwitchCase="true">Quit session</span>
+                        </div>
+                    </div>
+                </div> <!-- End Login -->
+            </div>
+        </div>
     </div>
 </nav>
+<div class="myBar"></div>
+
diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.ts b/src/main/webapp/app/layouts/navbar/navbar.component.ts
index 1e5ffa1207b5db12abc4cf63c126475fd3b552a7..6ecde617eaef756fcb9c9b9adf9c88a479534e9c 100644
--- a/src/main/webapp/app/layouts/navbar/navbar.component.ts
+++ b/src/main/webapp/app/layouts/navbar/navbar.component.ts
@@ -1,4 +1,5 @@
 import { Component, OnInit } from '@angular/core';
+import { FormBuilder } from '@angular/forms';
 import { Router } from '@angular/router';
 import { JhiLanguageService } from 'ng-jhipster';
 import { SessionStorageService } from 'ngx-webstorage';
@@ -6,9 +7,12 @@ import { SessionStorageService } from 'ngx-webstorage';
 import { VERSION } from 'app/app.constants';
 import { LANGUAGES } from 'app/core/language/language.constants';
 import { AccountService } from 'app/core/auth/account.service';
-import { LoginModalService } from 'app/core/login/login-modal.service';
 import { LoginService } from 'app/core/login/login.service';
 import { ProfileService } from 'app/layouts/profiles/profile.service';
+import { OAuth2ConfigService } from 'app/core/auth/oauth2-config.service';
+import { OAuth2Config } from 'app/core/auth/oauth2-config.model';
+
+import { Location } from '@angular/common';
 
 @Component({
   selector: 'jhi-navbar',
@@ -22,15 +26,34 @@ export class NavbarComponent implements OnInit {
   swaggerEnabled?: boolean;
   version: string;
 
+  authenticationError = false;
+
+  loginForm = this.fb.group({
+    username: [''],
+    password: [''],
+    rememberMe: [false],
+  });
+
+  public configs: OAuth2Config[];
+
   constructor(
     private loginService: LoginService,
     private languageService: JhiLanguageService,
     private sessionStorage: SessionStorageService,
     private accountService: AccountService,
-    private loginModalService: LoginModalService,
+    private fb: FormBuilder,
+    //    private loginModalService: LoginModalService,
     private profileService: ProfileService,
-    private router: Router
+    private router: Router,
+    private location: Location, // for redirect in OAuth2
+    public oAuth2ConfigService: OAuth2ConfigService
   ) {
+    this.configs = [];
+
+    this.oAuth2ConfigService.getAllConfigs().subscribe((loadedConfigs: OAuth2Config[]) => {
+      this.configs = loadedConfigs;
+    });
+
     this.version = VERSION ? (VERSION.toLowerCase().startsWith('v') ? VERSION : 'v' + VERSION) : '';
   }
 
@@ -54,8 +77,36 @@ export class NavbarComponent implements OnInit {
     return this.accountService.isAuthenticated();
   }
 
+  //  login(): void {
+  //    this.loginModalService.open();
+  //  }
+
   login(): void {
-    this.loginModalService.open();
+    this.loginService
+      .login({
+        username: this.loginForm.get('username')!.value,
+        password: this.loginForm.get('password')!.value,
+        rememberMe: this.loginForm.get('rememberMe')!.value,
+      })
+      .subscribe(
+        () => {
+          this.authenticationError = false;
+          if (
+            this.router.url === '/account/register' ||
+            this.router.url.startsWith('/account/activate') ||
+            this.router.url.startsWith('/account/reset/')
+          ) {
+            this.router.navigate(['']);
+          }
+        },
+        () => (this.authenticationError = true)
+      );
+  }
+
+  loginWithGitLab(registrationId: string): void {
+    location.href = `${location.origin}${this.location.prepareExternalUrl('oauth2/authorization/' + registrationId)}`;
+    // If you have configured multiple OIDC providers, then, you can update this URL to /login.
+    // It will show a Spring Security generated login page with links to configured OIDC providers.
   }
 
   logout(): void {
diff --git a/src/main/webapp/app/search/highlighting/highlighting.component.html b/src/main/webapp/app/search-old/highlighting/highlighting.component.html
similarity index 100%
rename from src/main/webapp/app/search/highlighting/highlighting.component.html
rename to src/main/webapp/app/search-old/highlighting/highlighting.component.html
diff --git a/src/main/webapp/app/search/highlighting/highlighting.component.ts b/src/main/webapp/app/search-old/highlighting/highlighting.component.ts
similarity index 79%
rename from src/main/webapp/app/search/highlighting/highlighting.component.ts
rename to src/main/webapp/app/search-old/highlighting/highlighting.component.ts
index 177ce9666f77ca2b33a62738ad930438be5b0eb7..e9f7737dd3e5f03f89d375b835dce98f745165c2 100644
--- a/src/main/webapp/app/search/highlighting/highlighting.component.ts
+++ b/src/main/webapp/app/search-old/highlighting/highlighting.component.ts
@@ -1,13 +1,13 @@
-import {Component, Input, OnInit} from '@angular/core';
-import * as Prism from "prismjs";
+import { Component, Input, OnInit } from '@angular/core';
+import * as Prism from 'prismjs';
 import 'prismjs/components/prism-scss';
 import 'prismjs/components/prism-c';
 import 'prismjs/components/prism-cpp';
 import 'prismjs/components/prism-java';
 import 'prismjs/components/prism-python';
 import 'prismjs/components/prism-markdown';
-import {LanguageDefinition} from "prismjs";
-import {IGitFiles} from "app/shared/model/git-files.model";
+import { LanguageDefinition } from 'prismjs';
+import { IGitFiles } from 'app/shared/model/git-files.model';
 
 @Component({
   selector: 'jhi-home-highlighting',
@@ -15,8 +15,8 @@ import {IGitFiles} from "app/shared/model/git-files.model";
   styleUrls: ['./highlighting.scss'],
 })
 export class HighlightingComponent implements OnInit {
-  @Input() gitFiles: IGitFiles | undefined
-  public language: LanguageDefinition | undefined
+  @Input() gitFiles: IGitFiles | undefined;
+  public language: LanguageDefinition | undefined;
 
   private static highlight(content: string, language: LanguageDefinition): string {
     const elements = content.split(/(<mark><strong>|<\/strong><\/mark>)/g);
@@ -32,27 +32,27 @@ export class HighlightingComponent implements OnInit {
   ngOnInit(): void {
     this.language = undefined;
     switch (this.gitFiles?.fileFormat) {
-      case "C":
+      case 'C':
         this.language = Prism.languages.c;
         break;
-      case "C++":
+      case 'C++':
         this.language = Prism.languages.cpp;
         break;
-      case "Java":
+      case 'Java':
         this.language = Prism.languages.java;
         break;
-      case "Python":
+      case 'Python':
         this.language = Prism.languages.python;
         break;
-      case "Markdown":
+      case 'Markdown':
         this.language = Prism.languages.markdown;
         break;
     }
-  };
+  }
 
   public highlightCode(line: string): string {
     if (!this.gitFiles) {
-      return "";
+      return '';
     }
     if (this.language) {
       return HighlightingComponent.highlight(line, this.language);
@@ -75,6 +75,6 @@ export class HighlightingComponent implements OnInit {
     if (!this.gitFiles) {
       return;
     }
-    window.location.href = this.gitFiles.gitUrl + "#L" + this.lineNumber(i);
+    window.location.href = this.gitFiles.gitUrl + '#L' + this.lineNumber(i);
   }
 }
diff --git a/src/main/webapp/app/search/highlighting/highlighting.scss b/src/main/webapp/app/search-old/highlighting/highlighting.scss
similarity index 91%
rename from src/main/webapp/app/search/highlighting/highlighting.scss
rename to src/main/webapp/app/search-old/highlighting/highlighting.scss
index 6f8c64a4b90d0001e199079bb39c0c5a4f03f7d7..1c122a94e417035f23e47bc79fe54815f158d391 100644
--- a/src/main/webapp/app/search/highlighting/highlighting.scss
+++ b/src/main/webapp/app/search-old/highlighting/highlighting.scss
@@ -1,5 +1,5 @@
 li.meta:hover {
-  background-color: #CACACA;
+  background-color: #cacaca;
 }
 li.meta {
   list-style-type: none;
@@ -46,9 +46,8 @@ code.line-number {
   color: #7f7f7f;
 }
 
-
 ::ng-deep code > mark {
-  background-color: rgba(255,245,80,0.53);
+  background-color: rgba(255, 245, 80, 0.53);
 }
 
 ::ng-deep mark > strong {
diff --git a/src/main/webapp/app/search/prism.scss b/src/main/webapp/app/search-old/prism.scss
similarity index 70%
rename from src/main/webapp/app/search/prism.scss
rename to src/main/webapp/app/search-old/prism.scss
index bd7e88cd2aac85a79ad37590ffbebc26eb404a82..c4a2416982f5c6c8802f05698e9b7efddb4af870 100644
--- a/src/main/webapp/app/search/prism.scss
+++ b/src/main/webapp/app/search-old/prism.scss
@@ -6,8 +6,8 @@ https://prismjs.com/download.html#themes=prism&languages=css */
  * @author Lea Verou
  */
 
-::ng-deep code[class*="language-"],
-::ng-deep pre[class*="language-"] {
+::ng-deep code[class*='language-'],
+::ng-deep pre[class*='language-'] {
   color: black;
   background: none;
   text-shadow: 0 1px white;
@@ -30,41 +30,45 @@ https://prismjs.com/download.html#themes=prism&languages=css */
   hyphens: none;
 }
 
-::ng-deep pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
-::ng-deep code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
+::ng-deep pre[class*='language-']::-moz-selection,
+pre[class*='language-'] ::-moz-selection,
+::ng-deep code[class*='language-']::-moz-selection,
+code[class*='language-'] ::-moz-selection {
   text-shadow: none;
   background: #b3d4fc;
 }
 
-::ng-deep pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
-::ng-deep code[class*="language-"]::selection, code[class*="language-"] ::selection {
+::ng-deep pre[class*='language-']::selection,
+pre[class*='language-'] ::selection,
+::ng-deep code[class*='language-']::selection,
+code[class*='language-'] ::selection {
   text-shadow: none;
   background: #b3d4fc;
 }
 
-::ng-deep @media print {
-  ::ng-deep code[class*="language-"],
-  ::ng-deep pre[class*="language-"] {
+@media print {
+  ::ng-deep code[class*='language-'],
+  ::ng-deep pre[class*='language-'] {
     text-shadow: none;
   }
 }
 
 /* Code blocks */
-::ng-deep pre[class*="language-"] {
+::ng-deep pre[class*='language-'] {
   padding: 1em;
-  margin: .5em 0;
+  margin: 0.5em 0;
   overflow: auto;
 }
 
-::ng-deep :not(pre) > code[class*="language-"],
-::ng-deep pre[class*="language-"] {
+::ng-deep :not(pre) > code[class*='language-'],
+::ng-deep pre[class*='language-'] {
   background: #f5f2f0;
 }
 
 /* Inline code */
-::ng-deep :not(pre) > code[class*="language-"] {
-  padding: .1em;
-  border-radius: .3em;
+::ng-deep :not(pre) > code[class*='language-'] {
+  padding: 0.1em;
+  border-radius: 0.3em;
   white-space: normal;
 }
 
@@ -80,7 +84,7 @@ https://prismjs.com/download.html#themes=prism&languages=css */
 }
 
 ::ng-deep .token.namespace {
-  opacity: .7;
+  opacity: 0.7;
 }
 
 ::ng-deep .token.property,
@@ -109,7 +113,7 @@ https://prismjs.com/download.html#themes=prism&languages=css */
 ::ng-deep .style .token.string {
   color: #9a6e3a;
   /* This background color was intended by the author of this theme. */
-  background: hsla(0, 0%, 100%, .5);
+  background: hsla(0, 0%, 100%, 0.5);
 }
 
 ::ng-deep .token.atrule,
@@ -120,7 +124,7 @@ https://prismjs.com/download.html#themes=prism&languages=css */
 
 .token.function,
 .token.class-name {
-  color: #DD4A68;
+  color: #dd4a68;
 }
 
 .token.regex,
@@ -140,4 +144,3 @@ https://prismjs.com/download.html#themes=prism&languages=css */
 .token.entity {
   cursor: help;
 }
-
diff --git a/src/main/webapp/app/search-old/search.component.html b/src/main/webapp/app/search-old/search.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f47f591ab333520fc21381a5e49c799b9853ea31
--- /dev/null
+++ b/src/main/webapp/app/search-old/search.component.html
@@ -0,0 +1,160 @@
+<div class="row">
+    <div class="col-md-12">
+        <h2 class="display-4" jhiTranslate="search.title">Welcome!</h2>
+
+        <span jhiTranslate="search.usage">The search mask allows you to search for full texts in the sharing platform based on various search criteria (e.g., full-text search, programming languages, keywords, etc.). Boolean operators can be used in the search mask for full texts. Apart from the full-text search, no further fields are to be specified.</span>&nbsp;
+
+        <div [ngSwitch]="isAuthenticated()">
+            <ng-container [queryParamGroup]="paramGroup">
+                <div class="row">
+                    <div class="col-sm-12">
+                        <form name="searchForm" class="form-inline">
+                            <div class="input-group w-100 mt-3">
+                                <input type="text" class="form-control" queryParamName="searchText"
+                                       placeholder="{{ 'search.filters.search' | translate }}"/>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+
+                <div class="row">
+                    <div class="col-sm-3">
+                        <form name="searchForm" class="form-inline">
+                            <div class="input-group w-100 mt-3">
+                                <input type="text" class="form-control" queryParamName="programmingLanguage"
+                                       placeholder="{{ 'search.filters.programmingLanguage' | translate }}"/>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="col-sm-3">
+                        <form name="searchForm" class="form-inline">
+                            <div class="input-group w-100 mt-3">
+                                <input type="text" class="form-control" queryParamName="keyword"
+                                       placeholder="{{ 'search.filters.keywords' | translate }}"/>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="col-sm-3">
+                        <form name="searchForm" class="form-inline">
+                            <div class="input-group w-100 mt-3">
+                                <input type="text" class="form-control" queryParamName="author"
+                                       placeholder="{{ 'search.filters.author' | translate }}"/>
+                            </div>
+                        </form>
+                    </div>
+                    <div class="col-sm-3">
+                        <form name="searchForm" class="form-inline">
+                            <div class="input-group w-100 mt-3">
+                                <input type="text" class="form-control" queryParamName="license"
+                                       placeholder="{{ 'search.filters.license' | translate }}"/>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </ng-container>
+
+            <div class="row">
+                <div class="col-2">
+                    <ng-container *ngIf="gitFilesAggregation">
+                        <br>
+                        <h3 jhiTranslate="search.metadata.filter">Search Filter</h3>
+
+                        <div class="filter">
+                            <ng-select
+                                [hidden]="gitFilesAggregation?.repositories == null"
+                                [items]="repos"
+                                bindLabel="key"
+                                [multiple]="true"
+                                (change)="filter()"
+                                [(ngModel)]="searchInput.fulltextSelection.repository"
+                                placeholder="{{ 'search.metadata.repository' | translate }}">
+                            </ng-select>
+                        </div>
+
+                        <div class="filter">
+                            <ng-select
+                                [hidden]="gitFilesAggregation?.university == null"
+                                [items]="university"
+                                bindLabel="key"
+                                [multiple]="true"
+                                (change)="filter()"
+                                [(ngModel)]="searchInput.fulltextSelection.university"
+                                placeholder="{{ 'search.metadata.university' | translate }}">
+                            </ng-select>
+                        </div>
+
+                        <div class="filter">
+                            <ng-select
+                                [hidden]="gitFilesAggregation?.fileFormat == null"
+                                [items]="fileFormat"
+                                bindLabel="key"
+                                [multiple]="true"
+                                (change)="filter()"
+                                [(ngModel)]="searchInput.fulltextSelection.fileFormat"
+                                placeholder="{{ 'search.metadata.fileFormat' | translate }}">
+                            </ng-select>
+                        </div>
+
+                        <hr>
+
+                        <h3 jhiTranslate="search.metadata.information">Search information</h3>
+
+                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.repositories"
+                                           [parameter]="'repository'"></jhi-home-metadata>
+                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.university"
+                                           [parameter]="'university'"></jhi-home-metadata>
+                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.fileFormat"
+                                           [parameter]="'fileFormat'"></jhi-home-metadata>
+                    </ng-container>
+                </div>
+
+                <div class="col-10">
+                    <ng-container *ngIf="gitFilesPageDetails">
+                        <hr>
+                        <h2 id="home-logged-message" jhiTranslate="search.searchResult.title"
+                            [translateValues]="{ results: gitFilesPageDetails?.hitCount || 0 }">search
+                            results</h2>
+                    </ng-container>
+                    <hr [hidden]="gitFilesAggregation == null">
+
+                    <div *ngIf="gitFilesPageDetails?.gitFiles !== null">
+                        <div
+                            *ngFor="let gitFile of gitFiles">
+                            <a (click)="onClickMe(gitFile)">{{ gitFile.filePath}}</a>
+                            <div class="solid">
+                                <div class="row">
+                                    <jhi-home-highlighting [gitFiles]="gitFile"></jhi-home-highlighting>
+                                </div>
+                            </div>
+
+                            <div class="row">
+                                <div class="col-sm">
+                                <span class="info"><fa-icon icon="language"></fa-icon>
+                                    {{gitFile.fileFormat}}</span>
+                                </div>
+                                <div class="col-sm">
+                                <span class="info"><fa-icon icon="book"></fa-icon>
+                                    {{gitFile.repository}}</span>
+                                </div>
+                            </div>
+
+                            <hr>
+                        </div>
+                    </div>
+                    <div class="d-flex justify-content-center">
+                        <ngb-pagination
+                            (pageChange)="onPageChange($event)"
+                            [hidden]="hitCount == 0"
+                            [(page)]="searchInput.page"
+                            [pageSize]="pageSize"
+                            [boundaryLinks]="true"
+                            [maxSize]="5"
+                            [rotate]="true"
+                            [collectionSize]="hitCount"></ngb-pagination>
+                    </div>
+
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/main/webapp/app/search-old/search.component.ts b/src/main/webapp/app/search-old/search.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a26765d0798ec8aca15c26f5d0f67fd92ccf3c6a
--- /dev/null
+++ b/src/main/webapp/app/search-old/search.component.ts
@@ -0,0 +1,271 @@
+// currently unused
+/*
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Subject, Subscription } from 'rxjs';
+
+import { LoginModalService } from 'app/core/login/login-modal.service';
+import { AccountService } from 'app/core/auth/account.service';
+import { Account } from 'app/core/user/account.model';
+import { HttpResponse } from '@angular/common/http';
+import { ActivatedRoute, Router } from '@angular/router';
+import { QueryParam, QueryParamBuilder, QueryParamGroup } from '@ngqp/core';
+import { takeUntil } from 'rxjs/operators';
+import { IFrequency } from 'app/shared/model/frequency.model';
+import { IGitFilesPageDetails, GitFilesPageDetails } from 'app/shared/model/git-files-page-details.model';
+import { IGitFilesAggregation } from 'app/shared/model/git-files-aggregation';
+import { IGitFiles } from 'app/shared/model/git-files.model';
+import { SearchInput } from 'app/shared/model/search/search-input.model';
+import { SearchResultService } from 'app/search/search/search-result.service';
+import { MetadataMessageService } from 'app/search/metadata/metadata-message.service';
+import { IMetadataSelection } from 'app/search/metadata/metadata-selection.component';
+
+@Component({
+  selector: 'jhi-search',
+  templateUrl: './search.component.html',
+  styleUrls: ['search.scss'],
+})
+export class SearchComponent implements OnInit, OnDestroy {
+  private static DEBOUNCE_TIME = 200;
+  public pageSize = 4;
+
+  account: Account | null = null;
+  authSubscription?: Subscription;
+
+  // Stores all search results for the current query.
+  // Used to locally apply filters for e.g. repository and file format.
+  // When local filters change, the results are recalculated from this variable.
+  private queryResult?: IGitFilesPageDetails;
+  gitFilesPageDetails?: IGitFilesPageDetails;
+  gitFilesAggregation?: IGitFilesAggregation;
+
+  private filterSelectionSubscription: Subscription = new Subscription();
+  private selectedRepositories = new Set<string>();
+  private selectedUniversities = new Set<string>();
+  private selectedFileFormats = new Set<string>();
+
+  public paramGroup: QueryParamGroup;
+  private componentDestroyed$ = new Subject<void>();
+
+  public searchInput: SearchInput;
+
+  constructor(
+    protected searchResultService: SearchResultService,
+    private accountService: AccountService,
+    private loginModalService: LoginModalService,
+    protected activatedRoute: ActivatedRoute,
+    private router: Router,
+    private qpb: QueryParamBuilder,
+    private filterSelectionService: MetadataMessageService
+  ) {
+    this.searchInput = new SearchInput();
+
+    this.paramGroup = qpb.group({
+      searchText: qpb.stringParam('q', {
+        debounceTime: SearchComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      page: qpb.numberParam('page', {
+        emptyOn: 1,
+      }),
+      programmingLanguage: qpb.stringParam('pl', {
+        debounceTime: SearchComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      keyword: qpb.stringParam('kw', {
+        debounceTime: SearchComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      author: qpb.stringParam('a', {
+        debounceTime: SearchComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      license: qpb.stringParam('l', {
+        debounceTime: SearchComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+    });
+
+    this.paramGroup.valueChanges.pipe(takeUntil(this.componentDestroyed$)).subscribe(value => {
+      this.searchInput.setValues(value);
+      this.search();
+    });
+  }
+
+  filter(): void {
+    if (!this.gitFilesPageDetails?.hitCount || this.gitFilesPageDetails.hitCount <= 0) {
+      return;
+    }
+    this.loadPageDetails();
+  }
+
+  search(): void {
+    if (this.searchInput.fulltextQuery === '') {
+      this.gitFilesAggregation = undefined;
+      this.gitFilesPageDetails = undefined;
+      return;
+    }
+    this.loadAggregation();
+    this.loadPageDetails();
+  }
+
+  ngOnInit(): void {
+    this.loadAggregation();
+    this.loadPageDetails();
+    this.authSubscription = this.accountService.getAuthenticationState().subscribe(account => (this.account = account));
+    this.filterSelectionSubscription.add(
+      this.filterSelectionService.filterSelection$.subscribe(selection => this.updateSearchInfoFilter(selection))
+    );
+  }
+
+  loadAggregation(): void {
+    if (this.searchInput.fulltextQuery) {
+      this.searchResultService
+        .searchAggregation({
+          query: this.searchInput.fulltextQuery,
+        })
+        .subscribe((res: HttpResponse<IGitFilesAggregation>) => {
+          this.gitFilesAggregation = res.body || undefined;
+        });
+    }
+  }
+
+  loadPageDetails(): void {
+    if (this.searchInput.fulltextQuery) {
+      this.searchResultService.searchPageDetails(this.searchInput, this.pageSize).subscribe((res: HttpResponse<IGitFilesPageDetails>) => {
+        if (res.body === null) {
+          this.queryResult = undefined;
+        } else {
+          this.queryResult = new GitFilesPageDetails(res.body.gitFiles, res.body.gitFiles.length);
+          this.updatePageDetails();
+        }
+      });
+    }
+  }
+
+  // Applies local filters to search results
+  // and sets this.gitFilesPageDetails accordingly.
+  private updatePageDetails(): void {
+    if (this.queryResult !== undefined) {
+      const matchingResults = this.queryResult.gitFiles.filter(element => this.matchesSelection(element));
+      if (matchingResults.length > 0) {
+        this.gitFilesPageDetails = new GitFilesPageDetails(matchingResults, matchingResults.length);
+      } else {
+        this.gitFilesPageDetails = undefined;
+      }
+    }
+  }
+
+  // When the user updates the metadata filter selection
+  // this function is called.
+  // It updates the local filters.
+  // Currently, for any category (at the moment repository and file format)
+  // there is a set of allowed values.
+  // If the selected item is not in the corresponding set it is added.
+  // Otherwise it is removed.
+  // If a set is empty the corresponding filter is not applied.
+  // For non-empty sets, the results which will be displayed must match one of the set's elements.
+  // (e.g. if the selectedRepositories set contains "foo" and "bar",
+  // only results from these two repos will be shown.)
+  public updateSearchInfoFilter(selection: IMetadataSelection): void {
+    switch (selection.category) {
+      case 'repository': {
+        if (this.selectedRepositories.has(selection.value)) {
+          this.selectedRepositories.delete(selection.value);
+        } else {
+          this.selectedRepositories.add(selection.value);
+        }
+        break;
+      }
+      case 'university': {
+        // University seems to be a derived attribute.
+        // Currently not implemented.
+        alert('Filtering by university is not supported at the moment. Cause: University is not in the interface IGitFiles.');
+*/
+/* if (this.selectedUniversities.has(selection.value)) {
+          this.selectedUniversities.delete(selection.value);
+        } else {
+          this.selectedUniversities.add(selection.value);
+        } */
+/*        break;
+      }
+      case 'fileFormat': {
+        if (this.selectedFileFormats.has(selection.value)) {
+          this.selectedFileFormats.delete(selection.value);
+        } else {
+          this.selectedFileFormats.add(selection.value);
+        }
+        break;
+      }
+    }
+    this.updatePageDetails();
+  }
+
+  // Checks if a given file matches the local filters.
+  matchesSelection(file: IGitFiles): boolean {
+    if (this.selectedRepositories.size > 0 && !this.selectedRepositories.has(file.repository)) {
+      return false;
+    }
+*/
+/* if (this.selectedUniversities.size > 0 && !this.selectedUniversities.has(file.university)) {
+       return false;
+     } */
+/*
+    if (this.selectedFileFormats.size > 0 && !this.selectedFileFormats.has(file.fileFormat)) {
+      return false;
+    }
+    return true;
+  }
+
+  isAuthenticated(): boolean {
+    return this.accountService.isAuthenticated();
+  }
+
+  login(): void {
+    this.loginModalService.open();
+  }
+
+  ngOnDestroy(): void {
+    if (this.authSubscription) {
+      this.authSubscription.unsubscribe();
+    }
+    if (this.filterSelectionSubscription) {
+      this.filterSelectionSubscription.unsubscribe();
+    }
+    this.componentDestroyed$.next();
+    this.componentDestroyed$.complete();
+  }
+
+  onClickMe(gitFiles: IGitFiles): void {
+    window.location.href = gitFiles.gitUrl;
+  }
+
+  public get gitFiles(): IGitFiles[] {
+    return this.gitFilesPageDetails?.gitFiles || [];
+  }
+
+  public get repos(): IFrequency<string>[] {
+    return this.gitFilesAggregation?.repositories || [];
+  }
+
+  public get university(): IFrequency<string>[] {
+    return this.gitFilesAggregation?.university || [];
+  }
+
+  public get fileFormat(): IFrequency<string>[] {
+    return this.gitFilesAggregation?.fileFormat || [];
+  }
+
+  public get pageParam(): QueryParam<number> {
+    return this.paramGroup.get('page') as QueryParam<number>;
+  }
+
+  public onPageChange(page: number): void {
+    this.pageParam.setValue(page);
+  }
+
+  public get hitCount(): number {
+    const hits = this.gitFilesPageDetails?.hitCount;
+    return hits ? hits : 0;
+  }
+}
+*/
diff --git a/src/main/webapp/app/search-old/search.module.ts b/src/main/webapp/app/search-old/search.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..388e64b99dd7c1f0364ea30af84f1649f3c40427
--- /dev/null
+++ b/src/main/webapp/app/search-old/search.module.ts
@@ -0,0 +1,21 @@
+// currently unused
+/*
+import { NgModule } from '@angular/core';
+
+import { GitSearchV2SharedModule } from 'app/shared/shared.module';
+import { SearchComponent } from 'app/search/search.component';
+import { MetadataComponent } from 'app/search/metadata/metadata.component';
+import { HighlightingComponent } from 'app/search/highlighting/highlighting.component';
+import { QueryParamModule } from '@ngqp/core';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { MetadataMessageService } from 'app/search/metadata/metadata-message.service';
+import { SearchDropdownComponent } from './search-dropdown.component';
+
+@NgModule({
+  imports: [GitSearchV2SharedModule, QueryParamModule, NgSelectModule],
+  declarations: [SearchComponent, MetadataComponent, HighlightingComponent, SearchDropdownComponent],
+  exports: [SearchComponent],
+  providers: [MetadataMessageService],
+})
+export class SearchModule {}
+*/
diff --git a/src/main/webapp/app/search-old/search.scss b/src/main/webapp/app/search-old/search.scss
new file mode 100644
index 0000000000000000000000000000000000000000..36f06a08646ba858ccb3884153ac46fe663cb35b
--- /dev/null
+++ b/src/main/webapp/app/search-old/search.scss
@@ -0,0 +1,183 @@
+/* ==========================================================================
+Main page styles
+========================================================================== */
+.hipster {
+  display: inline-block;
+  width: 347px;
+  height: 497px;
+  background: url('../../content/images/jhipster_family_member_2.svg') no-repeat center top;
+  background-size: contain;
+}
+
+/* wait autoprefixer update to allow simple generation of high pixel density media query */
+@media only screen and (-webkit-min-device-pixel-ratio: 2),
+  only screen and (-moz-min-device-pixel-ratio: 2),
+  only screen and (-o-min-device-pixel-ratio: 2/1),
+  only screen and (min-resolution: 192dpi),
+  only screen and (min-resolution: 2dppx) {
+  .hipster {
+    background: url('../../content/images/jhipster_family_member_2.svg') no-repeat center top;
+    background-size: contain;
+  }
+}
+
+div.solid {
+  border-style: solid;
+  border-width: 1pt;
+  padding: 10px;
+  border-color: rgb(223, 226, 230);
+}
+
+span.info {
+  font-size: 13px;
+  color: #7f7f7f;
+}
+
+div.filter {
+  margin-bottom: 20px;
+  margin-top: 20px;
+}
+
+::ng-deep code[class*='language-'],
+::ng-deep pre[class*='language-'] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  font-size: 1em;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+::ng-deep pre[class*='language-']::-moz-selection,
+pre[class*='language-'] ::-moz-selection,
+::ng-deep code[class*='language-']::-moz-selection,
+code[class*='language-'] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+::ng-deep pre[class*='language-']::selection,
+pre[class*='language-'] ::selection,
+::ng-deep code[class*='language-']::selection,
+code[class*='language-'] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+  ::ng-deep code[class*='language-'],
+  ::ng-deep pre[class*='language-'] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+::ng-deep pre[class*='language-'] {
+  padding: 1em;
+  margin: 0.5em 0;
+  overflow: auto;
+}
+
+::ng-deep :not(pre) > code[class*='language-'],
+::ng-deep pre[class*='language-'] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+::ng-deep :not(pre) > code[class*='language-'] {
+  padding: 0.1em;
+  border-radius: 0.3em;
+  white-space: normal;
+}
+
+::ng-deep .token.comment,
+::ng-deep .token.prolog,
+::ng-deep .token.doctype,
+::ng-deep .token.cdata {
+  color: slategray;
+}
+
+::ng-deep .token.punctuation {
+  color: #999;
+}
+
+::ng-deep .token.namespace {
+  opacity: 0.7;
+}
+
+::ng-deep .token.property,
+::ng-deep .token.tag,
+::ng-deep .token.boolean,
+::ng-deep .token.number,
+::ng-deep .token.constant,
+::ng-deep .token.symbol,
+::ng-deep .token.deleted {
+  color: #905;
+}
+
+::ng-deep .token.selector,
+::ng-deep .token.attr-name,
+::ng-deep .token.string,
+::ng-deep .token.char,
+::ng-deep .token.builtin,
+::ng-deep .token.inserted {
+  color: #690;
+}
+
+::ng-deep .token.operator,
+::ng-deep .token.entity,
+::ng-deep .token.url,
+::ng-deep .language-css .token.string,
+::ng-deep .style .token.string {
+  color: #9a6e3a;
+  /* This background color was intended by the author of this theme. */
+  background: hsla(0, 0%, 100%, 0.5);
+}
+
+::ng-deep .token.atrule,
+::ng-deep .token.attr-value,
+::ng-deep .token.keyword {
+  color: #07a;
+}
+
+::ng-deep .token.function,
+::ng-deep .token.class-name {
+  color: #dd4a68;
+}
+
+::ng-deep .token.regex,
+::ng-deep .token.important,
+::ng-deep .token.variable {
+  color: #e90;
+}
+
+::ng-deep .token.important,
+::ng-deep .token.bold {
+  font-weight: bold;
+}
+
+::ng-deep .token.italic {
+  font-style: italic;
+}
+
+::ng-deep .token.entity {
+  cursor: help;
+}
+
+::ng-deep .strong {
+  font-weight: 600 !important;
+}
diff --git a/src/main/webapp/app/search/search/search-result.service.ts b/src/main/webapp/app/search-old/search/search-result.service.ts
similarity index 95%
rename from src/main/webapp/app/search/search/search-result.service.ts
rename to src/main/webapp/app/search-old/search/search-result.service.ts
index 049af91fad282b9cceb8a21291c11bdb85e68b49..83c954ef627642dbe0ef159945ebe1b330ed0dec 100644
--- a/src/main/webapp/app/search/search/search-result.service.ts
+++ b/src/main/webapp/app/search-old/search/search-result.service.ts
@@ -1,3 +1,5 @@
+// currently unused
+/*
 import { Injectable } from '@angular/core';
 import { HttpClient, HttpResponse } from '@angular/common/http';
 import { Observable } from 'rxjs';
@@ -6,7 +8,7 @@ import { SERVER_API_URL } from 'app/app.constants';
 import { createRequestOption } from 'app/shared/util/request-util';
 import { IGitFilesPageDetails } from 'app/shared/model/git-files-page-details.model';
 import { IGitFilesAggregation } from 'app/shared/model/git-files-aggregation';
-import {SearchInput} from "app/shared/model/search-input.model";
+import { SearchInput } from 'app/shared/model/search/search-input.model';
 
 type EntityResponseTypePageDetails = HttpResponse<IGitFilesPageDetails>;
 type EntityResponseTypeAggregation = HttpResponse<IGitFilesAggregation>;
@@ -40,7 +42,7 @@ export class SearchResultService {
     const req = {
       fulltextQuery: searchInput.fulltextQuery,
       metadataProgrammingLanguage: searchInput.metadata.programmingLanguage,
-      metadataKeywords: searchInput.metadata.keywords,
+      metadataKeywords: searchInput.metadata.keyword,
       metadataNaturalLanguage: searchInput.metadata.naturalLanguage,
       metadataLicense: searchInput.metadata.license,
       metadataAuthor: searchInput.metadata.author,
@@ -48,8 +50,8 @@ export class SearchResultService {
       selectedUniversity: searchInput.fulltextSelection.university.map(value => value.key),
       selectedFileFormat: searchInput.fulltextSelection.fileFormat.map(value => value.key),
       page: searchInput.page,
-      pageSize: page
-    }
+      pageSize: page,
+    };
     const options = createRequestOption(req);
     return this.http
       .get<IGitFilesPageDetails>(this.resourceSearchUrlPageDetails, { params: options, observe: 'response' })
@@ -62,8 +64,8 @@ export class SearchResultService {
       .get<IGitFilesAggregation>(this.resourceSearchUrlAggregation, { params: options, observe: 'response' })
       .pipe();
   }
-
-  /*
+*/
+/*
   protected convertDateFromClient(searchResult: ISearchResult): ISearchResult {
     const copy: ISearchResult = Object.assign({}, searchResult, {
       lastModified: searchResult.lastModified && searchResult.lastModified.isValid() ? searchResult.lastModified.toJSON() : undefined,
@@ -93,4 +95,4 @@ export class SearchResultService {
     return res;
   }
   */
-}
+// }
diff --git a/src/main/webapp/app/search/metadata/metadata-message.service.ts b/src/main/webapp/app/search/metadata/metadata-message.service.ts
deleted file mode 100644
index 35de7f04681f4b109972eba97c3f215e6ba126de..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/metadata/metadata-message.service.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-// The code in this file and the files using this file is partially taken from
-// https://stackoverflow.com/questions/46487255/pass-observable-data-from-parent-component-to-a-child-component-created-with-com
-import { Injectable } from '@angular/core';
-import { Subject, Observable } from 'rxjs';
-import { IMetadataSelection, MetadataSelection } from 'app/search/metadata/metadata-selection.component';
-
-@Injectable()
-export class MetadataMessageService {
-  filterSelection$: Observable<IMetadataSelection>;
-  private filterSelectionSubject: Subject<IMetadataSelection> = new Subject<IMetadataSelection>();
-
-  constructor() {
-    this.filterSelection$ = this.filterSelectionSubject.asObservable();
-  }
-
-  public updateFilterSelection(metadataCategory: string, value: string): void {
-    this.filterSelectionSubject.next(new MetadataSelection(metadataCategory, value));
-  }
-}
diff --git a/src/main/webapp/app/search/metadata/metadata-selection.component.ts b/src/main/webapp/app/search/metadata/metadata-selection.component.ts
deleted file mode 100644
index 4e04945a7957117ec4a357912f9b31039d21ce60..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/metadata/metadata-selection.component.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export interface IMetadataSelection {
-  category: string;
-  value: string;
-}
-
-export class MetadataSelection implements IMetadataSelection {
-  constructor(public category: string, public value: string) {}
-}
diff --git a/src/main/webapp/app/search/metadata/metadata.component.html b/src/main/webapp/app/search/metadata/metadata.component.html
deleted file mode 100644
index c78bd7a3ca83da5b3298ce9ea8c936592a1eb486..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/metadata/metadata.component.html
+++ /dev/null
@@ -1,14 +0,0 @@
-<div class="solid" [hidden]="frequencies == null">
-    <span class="meta" jhiTranslate="search.metadata.{{parameter}}">file format</span>
-    <span class="clear" float="right" jhiTranslate="search.metadata.clearLocalFilters"
-        (click)="clearFilters()">clear all</span>
-    <li class="unselected" *ngFor="let frequency of frequencies"
-        (click)="toggleSelection(frequency.key)"
-        [ngClass]="isSelected(frequency.key) ? 'selected' : 'unselected'">
-        <div>
-            <a class="meta">{{frequency.key}}
-                <span class="count">{{frequency.value}}</span>
-            </a>
-        </div>
-    </li>
-</div>
diff --git a/src/main/webapp/app/search/metadata/metadata.component.ts b/src/main/webapp/app/search/metadata/metadata.component.ts
deleted file mode 100644
index 7cf291e16c120f147d49490e735eab39237c6ea9..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/metadata/metadata.component.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Component, Input } from '@angular/core';
-import { IFrequency } from 'app/shared/model/frequency.model';
-import { MetadataMessageService } from 'app/search/metadata/metadata-message.service';
-
-@Component({
-  selector: 'jhi-home-metadata',
-  templateUrl: './metadata.component.html',
-  styleUrls: ['./metadata.scss'],
-})
-export class MetadataComponent {
-  @Input() frequencies!: IFrequency<string>[] | undefined;
-  @Input() parameter!: string;
-
-  testString = '';
-  selectedItems = new Set<string>();
-
-  constructor(public messageService: MetadataMessageService) {}
-
-  toggleSelection(name: string): void {
-    this.messageService.updateFilterSelection(this.parameter, name);
-    if (this.selectedItems.has(name)) {
-      this.selectedItems.delete(name);
-    } else {
-      this.selectedItems.add(name);
-    }
-  }
-
-  isSelected(name: string): boolean {
-    return this.selectedItems.has(name);
-  }
-
-  clearFilters(): void {
-    for (const selectedItem of this.selectedItems) {
-      this.messageService.updateFilterSelection(this.parameter, selectedItem);
-    }
-    this.selectedItems = new Set<string>();
-  }
-}
diff --git a/src/main/webapp/app/search/metadata/metadata.scss b/src/main/webapp/app/search/metadata/metadata.scss
deleted file mode 100644
index e8f038dcc005afdf068e50a9b7ef92ba21a1352d..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/metadata/metadata.scss
+++ /dev/null
@@ -1,46 +0,0 @@
-li.meta:hover {
-  background-color: #cacaca;
-}
-li.meta {
-  list-style-type: none;
-  padding-left: 20px;
-  padding-right: 20px;
-}
-a.meta {
-  font-weight: normal;
-}
-span.meta {
-  font-weight: bold;
-  margin-bottom: 50px;
-}
-span.count {
-  float: right;
-}
-div.solid {
-  border-style: solid;
-  border-width: 1pt;
-  padding: 10px;
-  border-color: rgb(223, 226, 230);
-  margin-bottom: 20px;
-}
-span.clear {
-  float: right;
-  padding-left: 10px;
-  padding-right: 10px;
-  cursor: pointer;
-  font-size: 0.9em;
-}
-span.clear:hover {
-  background-color: rgba(0, 0, 0, 0.1);
-}
-li.selected {
-  list-style-type: none;
-  padding-left: 20px;
-  padding-right: 20px;
-  background-color: rgb(200, 200, 200);
-}
-li.unselected {
-  list-style-type: none;
-  padding-left: 20px;
-  padding-right: 20px;
-}
diff --git a/src/main/webapp/app/search/search-input/search-input.component.html b/src/main/webapp/app/search/search-input/search-input.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..01598fb83500c3a8bc9dd1612f734057f6f28ca2
--- /dev/null
+++ b/src/main/webapp/app/search/search-input/search-input.component.html
@@ -0,0 +1,78 @@
+<div class="card-container">
+    <h2 class="display-4" style="width: 100%;" jhiTranslate="search.title">Search!</h2>
+    <ng-container [queryParamGroup]="paramGroup">
+        <div>
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" queryParamName="searchText"
+                           placeholder="{{ 'search.filters.search' | translate }}"/>
+                    <ng-template #helpFulltext> {{ 'search.help.fulltext' | translate}}</ng-template>
+                    <fa-icon style="padding: 10px 0px 0px 10px;" [ngbTooltip]="helpFulltext" [icon]="questionIcon"></fa-icon>
+                </div>
+            </form>
+        </div>
+
+        <div>
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" queryParamName="programmingLanguage" [ngbTypeahead]="autoCompleteProgrammingLanguage" 
+                           placeholder="{{ 'search.filters.programmingLanguage' | translate }}"/>
+                    <ng-template #helpPL> {{ 'search.help.programmingLanguage' | translate}}</ng-template>
+                    <fa-icon style="padding: 10px 0px 0px 10px;" [ngbTooltip]="helpPL" [icon]="questionIcon"></fa-icon>
+                </div>
+            </form>
+        </div>
+
+        <div>
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" queryParamName="keyword" [ngbTypeahead]="autoCompleteKeyWords"
+                           placeholder="{{ 'search.filters.keywords' | translate }}"/>
+                    <ng-template #helpKeyword> {{ 'search.help.keywords' | translate}}</ng-template>
+                    <fa-icon style="padding: 10px 0px 0px 10px;" [ngbTooltip]="helpKeyword" [icon]="questionIcon"></fa-icon>
+                </div>
+            </form>
+        </div>
+
+        <div>
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" queryParamName="author" name="author" [ngbTypeahead]="autoCompleteContributorCreator" 
+                           placeholder="{{ 'search.filters.author' | translate }}"/>
+                    <ng-template #helpauthorContritbutors> {{ 'search.help.authorContritbutors' | translate}}</ng-template>
+                    <fa-icon style="padding: 10px 0px 0px 10px;" [ngbTooltip]="helpauthorContritbutors" [icon]="questionIcon"></fa-icon>
+                </div>
+            </form>
+        </div>
+
+        <div>
+            <form name="searchForm" class="form-inline">
+                <div class="input-group w-100 mt-3">
+                    <input type="text" class="form-control" queryParamName="license"
+                           placeholder="{{ 'search.filters.license' | translate }}"/>
+                    <ng-template #helpLicense> {{ 'search.help.license' | translate}}</ng-template>
+                    <fa-icon style="padding: 10px 0px 0px 10px;" [ngbTooltip]="helpLicense" [icon]="questionIcon"></fa-icon>
+                </div>
+            </form>
+        </div>
+        <div>
+            <form name="searchForm" class="form-inline">
+                 <div class="nav-item dropdown">
+                    <a class="nav-link dropdown-toggle" data-toggle="dropdown" href="javascript:void(0);">
+                       <span jhiTranslate="exercise.metadata.type">Type</span>
+                    </a><span *ngFor="let type of getSelectedTypes(), let isLast=last">{{'exercise.metadata.' + type | translate}}{{isLast ? '' : ', '}}</span>
+                    <div class="dropdown-menu" style="padding-left: 10px;">
+                    <table><tr *ngFor="let type of typeValues"><td>
+                        <input class="form-check-input" [value]="type" type="checkbox" queryParamName="{{type}}" id="type">
+                        </td><td style="text-align: left;">
+                        <label class="form-check-label" style="display: inline-block;align-items: left" for="type">{{'exercise.metadata.' + type | translate}}</label>
+                        </td></table>
+                    </div>
+                </div>
+            
+            </form>
+        </div>
+    </ng-container>
+</div>
+
+
diff --git a/src/main/webapp/app/search/search-input/search-input.component.scss b/src/main/webapp/app/search/search-input/search-input.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..93991f1f8f8f4370c2ce4ca150fdac6f51d5ae3f
--- /dev/null
+++ b/src/main/webapp/app/search/search-input/search-input.component.scss
@@ -0,0 +1,6 @@
+.card-container {
+  padding: 20px;
+  position: -webkit-sticky;
+  position: sticky;
+  top: 0;
+}
diff --git a/src/main/webapp/app/search/search-input/search-input.component.ts b/src/main/webapp/app/search/search-input/search-input.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54e9438adc537e7be72ae8d8df83b26646ac6e00
--- /dev/null
+++ b/src/main/webapp/app/search/search-input/search-input.component.ts
@@ -0,0 +1,193 @@
+import { Component, OnDestroy, OnInit, Output, EventEmitter } from '@angular/core';
+import { Subject } from 'rxjs';
+import { ActivatedRoute, Router } from '@angular/router';
+import { QueryParam, QueryParamBuilder, QueryParamGroup } from '@ngqp/core';
+import { takeUntil } from 'rxjs/operators';
+import { SearchInput } from 'app/shared/model/search/search-input.model';
+import { SearchService } from '../service/search-service'
+import { faQuestion } from '@fortawesome/free-solid-svg-icons';
+
+import {Observable} from 'rxjs';
+import {debounceTime, distinctUntilChanged, map, switchMap} from 'rxjs/operators';
+
+import {IExerciseType } from 'app/shared/model/exercise.model';
+
+interface LooseObject {
+    [key: string]: any
+}
+
+@Component({
+  selector: 'jhi-search-input',
+  templateUrl: './search-input.component.html',
+  styleUrls: ['./search-input.component.scss'],
+})
+export class SearchInputComponent implements OnInit, OnDestroy {
+  private static DEBOUNCE_TIME = 200;
+
+  @Output() searchInputEvent = new EventEmitter<SearchInput>();
+  public pageSize = 4;
+  
+  typeValues = Object.keys(IExerciseType);
+  
+  questionIcon = faQuestion;
+  public paramGroup: QueryParamGroup;
+  private componentDestroyed$ = new Subject<void>();
+
+  public searchInput: SearchInput;
+
+  public showSearchUsage = false;
+
+  private states = ['Alabama', 'Alaska', 'American Samoa', 'Arizona', 'Arkansas', 'California', 'Colorado',
+  'Connecticut', 'Delaware', 'District Of Columbia', 'Federated States Of Micronesia', 'Florida', 'Georgia',
+  'Guam', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine',
+  'Marshall Islands', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana',
+  'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'North Dakota',
+  'Northern Mariana Islands', 'Ohio', 'Oklahoma', 'Oregon', 'Palau', 'Pennsylvania', 'Puerto Rico', 'Rhode Island',
+  'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virgin Islands', 'Virginia',
+  'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
+
+
+  constructor(
+    protected activatedRoute: ActivatedRoute,
+    private router: Router,
+    private qpb: QueryParamBuilder,
+    private searchService: SearchService
+  ) {
+    this.searchInput = new SearchInput();
+
+    const groupDef: LooseObject = {
+      searchText: qpb.stringParam('q', {
+        debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      page: qpb.numberParam('page', {
+        emptyOn: 1,
+      }),
+      programmingLanguage: qpb.stringParam('pl', {
+        debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      keyword: qpb.stringParam('kw', {
+        debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      author: qpb.stringParam('a', {
+        debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+      license: qpb.stringParam('l', {
+        debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+        emptyOn: '',
+      }),
+    };
+    
+    // add type checkbox support
+    this.typeValues.forEach(
+        function(type: string): void {
+            const typeName = type.toString();
+            groupDef[typeName] = qpb.stringParam(typeName, {
+                debounceTime: SearchInputComponent.DEBOUNCE_TIME,
+                emptyOn: '',
+              });
+        }
+    );
+
+    this.paramGroup = qpb.group(groupDef);
+
+    this.paramGroup.valueChanges.pipe(takeUntil(this.componentDestroyed$)).subscribe(value => {
+      this.searchInput.setValues(value);
+      this.searchInputEvent.emit(this.searchInput);
+    });
+
+  }
+  
+  getSelectedTypes(): string[] {
+    const result: string[] = [];
+    this.typeValues.forEach(
+        type => {if(this.paramGroup.queryParams[type].value) result.push(type)});
+    return result;
+}
+
+  ngOnInit(): void {}
+
+   
+  ngOnDestroy(): void {
+    this.componentDestroyed$.next();
+    this.componentDestroyed$.complete();
+  }
+
+  public get pageParam(): QueryParam<number> {
+    return this.paramGroup.get('page') as QueryParam<number>;
+  }
+
+  public onPageChange(page: number): void {
+    this.pageParam.setValue(page);
+  }
+  
+/*
+  public itemSelected(): void {
+          this.searchInputEvent.emit(this.searchInput);
+  }
+*/
+   autoCompleteContributorCreator = (text$: Observable<string>) =>
+    text$.pipe(
+      debounceTime(200),
+      distinctUntilChanged(),
+      switchMap((searchText) => {
+        if(searchText.length <= 2) return [];
+        return this.searchService.getContributorCreatorAutoComplete( searchText).pipe(map(
+            ace => (ace.map(ac => ac.target)) ) ); }
+            )
+     );
+      
+   autoCompleteKeyWords = (text$: Observable<string>) =>
+    text$.pipe(
+      debounceTime(200),
+      distinctUntilChanged(),
+      switchMap((searchText) => 
+        this.searchService.getKeywordsAutoComplete( searchText).pipe(map(
+            ace => (ace.map(ac => ac.target)) ) ))
+     );
+
+   autoCompleteProgrammingLanguage = (text$: Observable<string>) =>
+    text$.pipe(
+      debounceTime(200),
+      distinctUntilChanged(),
+      switchMap((searchText) => 
+        this.searchService.getProgrammingLanguageAutoComplete( searchText).pipe(map(
+            ace => (ace.map(ac => ac.target)) ) ))
+     );
+     
+  /* searchChanged(): void {
+    const fullTextSearch = this.searchInput.fulltextQuery;
+    this.searchFullText(fullTextSearch);
+  }
+
+  searchFullText(fullText: string): void {
+    const searchInput = new SearchInput();
+    searchInput.page = 1; // just test for the second page :-)
+    searchInput.fulltextQuery = fullText;
+    this.searchService.searchPageDetails(searchInput).subscribe(
+      (data: SearchResultsDTO) => {
+        let hits = '';
+        data.searchResult.forEach(function (hit): void {
+          hits = hits + hit.title + ':' + '\n   ';
+        });
+
+        alert(
+          'Success! ' +
+            data.hitCount +
+            ' hits: Showing ' +
+            data.pageStartIndex +
+            '-' +
+            (data.pageStartIndex + data.searchResult.length - 1) +
+            '\n   ' +
+            hits
+        );
+      },
+      () => {
+        alert('Search failed');
+      }
+    );
+  } */
+}
diff --git a/src/main/webapp/app/search/search-routing.module.ts b/src/main/webapp/app/search/search-routing.module.ts
deleted file mode 100644
index 48e9f345cf2c9a3ac8675148280b38526c6911ea..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/search/search-routing.module.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Route } from '@angular/router';
-import {SearchComponent} from "app/search/search.component";
-
-
-export const SEARCH_ROUTE: Route = {
-  path: '',
-  component: SearchComponent,
-  data: {
-    authorities: [],
-    pageTitle: 'search.tabTitle',
-  },
-};
diff --git a/src/main/webapp/app/search/search.component.html b/src/main/webapp/app/search/search.component.html
index bd8b35c458546cd50ed75daa5b6e14d4c016c12f..aeadad21ac5af7b053ec3dfc4432bb27b84d32a5 100644
--- a/src/main/webapp/app/search/search.component.html
+++ b/src/main/webapp/app/search/search.component.html
@@ -1,160 +1,30 @@
-<div class="row">
-    <div class="col-md-12">
-        <h2 class="display-4" jhiTranslate="search.title">Welcome!</h2>
-
-        <span jhiTranslate="search.usage">The search mask allows you to search for full texts in the sharing platform based on various search criteria (e.g., full-text search, programming languages, keywords, etc.). Boolean operators can be used in the search mask for full texts. Apart from the full-text search, no further fields are to be specified.</span>&nbsp;
-
-        <div [ngSwitch]="isAuthenticated()">
-            <ng-container [queryParamGroup]="paramGroup">
-                <div class="row">
-                    <div class="col-sm-12">
-                        <form name="searchForm" class="form-inline">
-                            <div class="input-group w-100 mt-3">
-                                <input type="text" class="form-control" queryParamName="searchText"
-                                       placeholder="{{ 'search.filters.search' | translate }}"/>
-                            </div>
-                        </form>
-                    </div>
-                </div>
-
-                <div class="row">
-                    <div class="col-sm-3">
-                        <form name="searchForm" class="form-inline">
-                            <div class="input-group w-100 mt-3">
-                                <input type="text" class="form-control" queryParamName="programmingLanguage"
-                                       placeholder="{{ 'search.filters.programmingLanguage' | translate }}"/>
-                            </div>
-                        </form>
-                    </div>
-                    <div class="col-sm-3">
-                        <form name="searchForm" class="form-inline">
-                            <div class="input-group w-100 mt-3">
-                                <input type="text" class="form-control" queryParamName="keywords"
-                                       placeholder="{{ 'search.filters.keywords' | translate }}"/>
-                            </div>
-                        </form>
-                    </div>
-                    <div class="col-sm-3">
-                        <form name="searchForm" class="form-inline">
-                            <div class="input-group w-100 mt-3">
-                                <input type="text" class="form-control" queryParamName="author"
-                                       placeholder="{{ 'search.filters.author' | translate }}"/>
-                            </div>
-                        </form>
-                    </div>
-                    <div class="col-sm-3">
-                        <form name="searchForm" class="form-inline">
-                            <div class="input-group w-100 mt-3">
-                                <input type="text" class="form-control" queryParamName="license"
-                                       placeholder="{{ 'search.filters.license' | translate }}"/>
-                            </div>
-                        </form>
-                    </div>
-                </div>
-            </ng-container>
-
-            <div class="row">
-                <div class="col-2">
-                    <ng-container *ngIf="gitFilesAggregation">
-                        <br>
-                        <h3 jhiTranslate="search.metadata.filter">Search Filter</h3>
-
-                        <div class="filter">
-                            <ng-select
-                                [hidden]="gitFilesAggregation?.repositories == null"
-                                [items]="repos"
-                                bindLabel="key"
-                                [multiple]="true"
-                                (change)="filter()"
-                                [(ngModel)]="searchInput.fulltextSelection.repository"
-                                placeholder="{{ 'search.metadata.repository' | translate }}">
-                            </ng-select>
-                        </div>
-
-                        <div class="filter">
-                            <ng-select
-                                [hidden]="gitFilesAggregation?.university == null"
-                                [items]="university"
-                                bindLabel="key"
-                                [multiple]="true"
-                                (change)="filter()"
-                                [(ngModel)]="searchInput.fulltextSelection.university"
-                                placeholder="{{ 'search.metadata.university' | translate }}">
-                            </ng-select>
-                        </div>
-
-                        <div class="filter">
-                            <ng-select
-                                [hidden]="gitFilesAggregation?.fileFormat == null"
-                                [items]="fileFormat"
-                                bindLabel="key"
-                                [multiple]="true"
-                                (change)="filter()"
-                                [(ngModel)]="searchInput.fulltextSelection.fileFormat"
-                                placeholder="{{ 'search.metadata.fileFormat' | translate }}">
-                            </ng-select>
-                        </div>
-
-                        <hr>
-
-                        <h3 jhiTranslate="search.metadata.information">Search information</h3>
-
-                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.repositories"
-                                           [parameter]="'repository'"></jhi-home-metadata>
-                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.university"
-                                           [parameter]="'university'"></jhi-home-metadata>
-                        <jhi-home-metadata [frequencies]="gitFilesAggregation?.fileFormat"
-                                           [parameter]="'fileFormat'"></jhi-home-metadata>
-                    </ng-container>
-                </div>
-
-                <div class="col-10">
-                    <ng-container *ngIf="gitFilesPageDetails">
-                        <hr>
-                        <h2 id="home-logged-message" jhiTranslate="search.searchResult.title"
-                            [translateValues]="{ results: gitFilesPageDetails?.hitCount || 0 }">search
-                            results</h2>
-                    </ng-container>
-                    <hr [hidden]="gitFilesAggregation == null">
-
-                    <div *ngIf="gitFilesPageDetails?.gitFiles !== null">
-                        <div
-                            *ngFor="let gitFile of gitFiles">
-                            <a (click)="onClickMe(gitFile)">{{ gitFile.filePath}}</a>
-                            <div class="solid">
-                                <div class="row">
-                                    <jhi-home-highlighting [gitFiles]="gitFile"></jhi-home-highlighting>
-                                </div>
-                            </div>
-
-                            <div class="row">
-                                <div class="col-sm">
-                                <span class="info"><fa-icon icon="language"></fa-icon>
-                                    {{gitFile.fileFormat}}</span>
-                                </div>
-                                <div class="col-sm">
-                                <span class="info"><fa-icon icon="book"></fa-icon>
-                                    {{gitFile.repository}}</span>
-                                </div>
-                            </div>
-
-                            <hr>
-                        </div>
-                    </div>
-                    <div class="d-flex justify-content-center">
-                        <ngb-pagination
-                            (pageChange)="onPageChange($event)"
-                            [hidden]="hitCount == 0"
-                            [(page)]="searchInput.page"
-                            [pageSize]="pageSize"
-                            [boundaryLinks]="true"
-                            [maxSize]="5"
-                            [rotate]="true"
-                            [collectionSize]="hitCount"></ngb-pagination>
-                    </div>
-
-                </div>
-            </div>
+<div class="row"
+     infiniteScroll
+     [infiniteScrollDistance]="2"
+     [infiniteScrollThrottle]="50"
+     (scrolled)="onScroll()">
+
+    <jhi-search-input
+        class="col-12 col-md-5 col-lg-4 col-xl-3"
+        (searchInputEvent)="updateSearchInput($event)">
+    </jhi-search-input>
+    <div class="col-12 col-md-7 col-lg-8 col-xl-9">
+        <div *ngIf="results.length === 0" >
+            <span jhiTranslate="search.noResults">No results found</span>
+            <span *ngIf="!accountService.isAuthenticated()" jhiTranslate="search.noResultsAnonymous">Anmelden?</span>
+        </div>
+        <div *ngIf="hitCount !== 0" >
+            <span>{{ 'search.numberResults' | translate:{'length': hitCount} }}</span>
+        </div>
+        <div class="row">
+            <jhi-exercise-card *ngFor="let result of results"
+                               class="card-container col-12 col-lg-6 col-xl-4"
+                               [exercise]="result"
+                               (exerciseSelectionEvent)="selectExercise($event)">
+            </jhi-exercise-card>
         </div>
     </div>
 </div>
+
+<jhi-exercise-details [exercise]="selectedResult"></jhi-exercise-details>
+
diff --git a/src/main/webapp/app/search/search.component.ts b/src/main/webapp/app/search/search.component.ts
index 587fd2fa2eace9e0310bb98126422250dae8a2f6..9e7407b6a2d1f1951ec077e0c2f65a800419b801 100644
--- a/src/main/webapp/app/search/search.component.ts
+++ b/src/main/webapp/app/search/search.component.ts
@@ -1,265 +1,96 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
-import { Subject, Subscription } from 'rxjs';
-
-import { LoginModalService } from 'app/core/login/login-modal.service';
+import { Component, OnInit } from '@angular/core';
+import { Exercise } from 'app/shared/model/exercise.model';
+import { SearchInput } from 'app/shared/model/search/search-input.model';
+import { SearchService } from 'app/search/service/search-service';
+import { SearchResultsDTO } from 'app/shared/model/search/search-results-dto.model';
+import { SearchResultDTO } from 'app/shared/model/search/search-result-dto.model';
 import { AccountService } from 'app/core/auth/account.service';
-import { Account } from 'app/core/user/account.model';
-import { HttpResponse } from '@angular/common/http';
-import { ActivatedRoute, Router } from '@angular/router';
-import { QueryParam, QueryParamBuilder, QueryParamGroup } from '@ngqp/core';
-import { takeUntil } from 'rxjs/operators';
-import { IFrequency } from 'app/shared/model/frequency.model';
-import { IGitFilesPageDetails, GitFilesPageDetails } from 'app/shared/model/git-files-page-details.model';
-import { IGitFilesAggregation } from 'app/shared/model/git-files-aggregation';
-import { IGitFiles } from 'app/shared/model/git-files.model';
-import { SearchInput } from 'app/shared/model/search-input.model';
-import { SearchResultService } from 'app/search/search/search-result.service';
-import { MetadataMessageService } from 'app/search/metadata/metadata-message.service';
-import { IMetadataSelection } from 'app/search/metadata/metadata-selection.component';
 
 @Component({
   selector: 'jhi-search',
   templateUrl: './search.component.html',
-  styleUrls: ['search.scss'],
+  styleUrls: ['./search.scss'],
 })
-export class SearchComponent implements OnInit, OnDestroy {
-  private static DEBOUNCE_TIME = 200;
-  public pageSize = 4;
-
-  account: Account | null = null;
-  authSubscription?: Subscription;
-
-  // Stores all search results for the current query.
-  // Used to locally apply filters for e.g. repository and file format.
-  // When local filters change, the results are recalculated from this variable.
-  private queryResult?: IGitFilesPageDetails;
-  gitFilesPageDetails?: IGitFilesPageDetails;
-  gitFilesAggregation?: IGitFilesAggregation;
-
-  private filterSelectionSubscription: Subscription = new Subscription();
-  private selectedRepositories = new Set<string>();
-  private selectedUniversities = new Set<string>();
-  private selectedFileFormats = new Set<string>();
-
-  public paramGroup: QueryParamGroup;
-  private componentDestroyed$ = new Subject<void>();
-
-  public searchInput: SearchInput;
-
-  constructor(
-    protected searchResultService: SearchResultService,
-    private accountService: AccountService,
-    private loginModalService: LoginModalService,
-    protected activatedRoute: ActivatedRoute,
-    private router: Router,
-    private qpb: QueryParamBuilder,
-    private filterSelectionService: MetadataMessageService
-  ) {
-    this.searchInput = new SearchInput();
-
-    this.paramGroup = qpb.group({
-      searchText: qpb.stringParam('q', {
-        debounceTime: SearchComponent.DEBOUNCE_TIME,
-        emptyOn: '',
-      }),
-      page: qpb.numberParam('page', {
-        emptyOn: 1,
-      }),
-      programmingLanguage: qpb.stringParam('pl', {
-        debounceTime: SearchComponent.DEBOUNCE_TIME,
-        emptyOn: '',
-      }),
-      keywords: qpb.stringParam('kw', {
-        debounceTime: SearchComponent.DEBOUNCE_TIME,
-        emptyOn: '',
-      }),
-      author: qpb.stringParam('a', {
-        debounceTime: SearchComponent.DEBOUNCE_TIME,
-        emptyOn: '',
-      }),
-      license: qpb.stringParam('l', {
-        debounceTime: SearchComponent.DEBOUNCE_TIME,
-        emptyOn: '',
-      }),
-    });
-
-    this.paramGroup.valueChanges.pipe(takeUntil(this.componentDestroyed$)).subscribe(value => {
-      this.searchInput.setValues(value);
-      this.search();
-    });
-  }
-
-  filter(): void {
-    if (!this.gitFilesPageDetails?.hitCount || this.gitFilesPageDetails.hitCount <= 0) {
-      return;
-    }
-    this.loadPageDetails();
-  }
-
-  search(): void {
-    if (this.searchInput.fulltextQuery === '') {
-      this.gitFilesAggregation = undefined;
-      this.gitFilesPageDetails = undefined;
-      return;
-    }
-    this.loadAggregation();
-    this.loadPageDetails();
-  }
-
-  ngOnInit(): void {
-    this.loadAggregation();
-    this.loadPageDetails();
-    this.authSubscription = this.accountService.getAuthenticationState().subscribe(account => (this.account = account));
-    this.filterSelectionSubscription.add(
-      this.filterSelectionService.filterSelection$.subscribe(selection => this.updateSearchInfoFilter(selection))
-    );
-  }
-
-  loadAggregation(): void {
-    if (this.searchInput.fulltextQuery) {
-      this.searchResultService
-        .searchAggregation({
-          query: this.searchInput.fulltextQuery,
-        })
-        .subscribe((res: HttpResponse<IGitFilesAggregation>) => {
-          this.gitFilesAggregation = res.body || undefined;
-        });
-    }
-  }
-
-  loadPageDetails(): void {
-    if (this.searchInput.fulltextQuery) {
-      this.searchResultService.searchPageDetails(this.searchInput, this.pageSize).subscribe((res: HttpResponse<IGitFilesPageDetails>) => {
-        if (res.body === null) {
-          this.queryResult = undefined;
-        } else {
-          this.queryResult = new GitFilesPageDetails(res.body.gitFiles, res.body.gitFiles.length);
-          this.updatePageDetails();
-        }
-      });
-    }
-  }
-
-  // Applies local filters to search results
-  // and sets this.gitFilesPageDetails accordingly.
-  private updatePageDetails(): void {
-    if (this.queryResult !== undefined) {
-      const matchingResults = this.queryResult.gitFiles.filter(element => this.matchesSelection(element));
-      if (matchingResults.length > 0) {
-        this.gitFilesPageDetails = new GitFilesPageDetails(matchingResults, matchingResults.length);
-      } else {
-        this.gitFilesPageDetails = undefined;
-      }
-    }
-  }
-
-  // When the user updates the metadata filter selection
-  // this function is called.
-  // It updates the local filters.
-  // Currently, for any category (at the moment repository and file format)
-  // there is a set of allowed values.
-  // If the selected item is not in the corresponding set it is added.
-  // Otherwise it is removed.
-  // If a set is empty the corresponding filter is not applied.
-  // For non-empty sets, the results which will be displayed must match one of the set's elements.
-  // (e.g. if the selectedRepositories set contains "foo" and "bar",
-  // only results from these two repos will be shown.)
-  public updateSearchInfoFilter(selection: IMetadataSelection): void {
-    switch (selection.category) {
-      case 'repository': {
-        if (this.selectedRepositories.has(selection.value)) {
-          this.selectedRepositories.delete(selection.value);
-        } else {
-          this.selectedRepositories.add(selection.value);
-        }
-        break;
-      }
-      case 'university': {
-        // University seems to be a derived attribute.
-        // Currently not implemented.
-        alert('Filtering by university is not supported at the moment. Cause: University is not in the interface IGitFiles.');
-        /* if (this.selectedUniversities.has(selection.value)) {
-          this.selectedUniversities.delete(selection.value);
-        } else {
-          this.selectedUniversities.add(selection.value);
-        } */
-        break;
-      }
-      case 'fileFormat': {
-        if (this.selectedFileFormats.has(selection.value)) {
-          this.selectedFileFormats.delete(selection.value);
-        } else {
-          this.selectedFileFormats.add(selection.value);
-        }
-        break;
-      }
-    }
-    this.updatePageDetails();
-  }
-
-  // Checks if a given file matches the local filters.
-  matchesSelection(file: IGitFiles): boolean {
-    if (this.selectedRepositories.size > 0 && !this.selectedRepositories.has(file.repository)) {
-      return false;
-    }
-    /* if (this.selectedUniversities.size > 0 && !this.selectedUniversities.has(file.university)) {
-       return false;
-     } */
-    if (this.selectedFileFormats.size > 0 && !this.selectedFileFormats.has(file.fileFormat)) {
-      return false;
-    }
-    return true;
-  }
-
-  isAuthenticated(): boolean {
-    return this.accountService.isAuthenticated();
-  }
-
-  login(): void {
-    this.loginModalService.open();
-  }
-
-  ngOnDestroy(): void {
-    if (this.authSubscription) {
-      this.authSubscription.unsubscribe();
-    }
-    if (this.filterSelectionSubscription) {
-      this.filterSelectionSubscription.unsubscribe();
+export class SearchComponent implements OnInit {
+  results: Exercise[] = [];
+  selectedResult?: Exercise;
+  hitCount = 0;
+
+  private searchInput: SearchInput = new SearchInput();
+
+  constructor(protected searchService: SearchService, public accountService: AccountService) {}
+
+  ngOnInit(): void {}
+
+  selectExercise(exercise: Exercise): void {
+    this.selectedResult = exercise;
+  }
+
+  updateSearchInput(searchInput: SearchInput): void {
+    searchInput.page = 0;
+    this.searchInput = searchInput;
+    this.results = [];
+    this.search();
+  }
+
+  private search(): void {
+    if (
+      this.searchInput.fulltextQuery ||
+      this.searchInput.metadata.programmingLanguage ||
+      this.searchInput.metadata.keyword ||
+      this.searchInput.metadata.author ||
+      this.searchInput.metadata.license
+    ) {
+      this.searchService.searchPageDetails(this.searchInput).subscribe(
+        (data: SearchResultsDTO) => {
+          const searchResults = this.parseSearchResultsDTO(data);
+          this.results = this.results.concat(searchResults);
+          ++this.searchInput.page;
+        },
+        () => alert('Search failed')
+      );
     }
-    this.componentDestroyed$.next();
-    this.componentDestroyed$.complete();
-  }
-
-  onClickMe(gitFiles: IGitFiles): void {
-    window.location.href = gitFiles.gitUrl;
-  }
-
-  public get gitFiles(): IGitFiles[] {
-    return this.gitFilesPageDetails?.gitFiles || [];
-  }
-
-  public get repos(): IFrequency<string>[] {
-    return this.gitFilesAggregation?.repositories || [];
-  }
-
-  public get university(): IFrequency<string>[] {
-    return this.gitFilesAggregation?.university || [];
-  }
-
-  public get fileFormat(): IFrequency<string>[] {
-    return this.gitFilesAggregation?.fileFormat || [];
-  }
-
-  public get pageParam(): QueryParam<number> {
-    return this.paramGroup.get('page') as QueryParam<number>;
-  }
-
-  public onPageChange(page: number): void {
-    this.pageParam.setValue(page);
   }
 
-  public get hitCount(): number {
-    const hits = this.gitFilesPageDetails?.hitCount;
-    return hits ? hits : 0;
+  private parseSearchResultsDTO(searchResultsDTO: SearchResultsDTO): Exercise[] {
+    this.hitCount = searchResultsDTO.hitCount;
+    return searchResultsDTO.searchResult.map(this.searchResultToExercise);
+  }
+
+  private searchResultToExercise(searchResult: SearchResultDTO): Exercise {
+    return {
+      title: searchResult.metadata.title,
+      license: searchResult.metadata.license,
+      description: searchResult.metadata.description,
+      programmingLanguages: searchResult.metadata.programmingLanguage,
+      languages: searchResult.metadata.language,
+      creators: searchResult.metadata.creator,
+      contributor: searchResult.metadata.contributor,
+      publisher: searchResult.metadata.publisher,
+      imageURL: searchResult.metadata.image,
+      rating: searchResult.ranking5,
+      lastUpdate: searchResult.project.last_activity_at,
+      timeRequired: searchResult.metadata.timeRequired,
+      requires: searchResult.metadata.requires,
+      deprecated: searchResult.metadata.deprecated,
+      difficulty: searchResult.metadata.difficulty,
+      educationLevel: searchResult.metadata.educationLevel,
+      format: searchResult.metadata.format,
+      keyword: searchResult.metadata.keyword,
+      status: searchResult.metadata.status,
+      type: searchResult.metadata.type,
+      structure: searchResult.metadata.structure,
+      version: searchResult.metadata.version,
+      metadataVersion: searchResult.metadata.metadataVersion,
+
+      gitlabURL: searchResult.project.url,
+      originalResult: searchResult,
+      views: searchResult.views,
+      downloads: searchResult.downloads,
+    };
+  }
+
+  onScroll(): void {
+    this.search();
   }
 }
diff --git a/src/main/webapp/app/search/search.module.ts b/src/main/webapp/app/search/search.module.ts
index 55f4c05cd9aa6724996018bbd405b7beb409ef88..8ac1e9ca18b723ab5ab4b042ba2c9bdd5a9ea186 100644
--- a/src/main/webapp/app/search/search.module.ts
+++ b/src/main/webapp/app/search/search.module.ts
@@ -1,18 +1,18 @@
 import { NgModule } from '@angular/core';
 import { RouterModule } from '@angular/router';
+import { InfiniteScrollModule } from 'ngx-infinite-scroll';
 
+import { SearchComponent } from './search.component';
+import { SEARCH_ROUTE } from './search.route';
 import { GitSearchV2SharedModule } from 'app/shared/shared.module';
-import { SearchComponent } from 'app/search/search.component';
-import { SEARCH_ROUTE } from 'app/search/search-routing.module';
-import { MetadataComponent } from 'app/search/metadata/metadata.component';
-import { HighlightingComponent } from 'app/search/highlighting/highlighting.component';
+import { ExerciseDetailsComponent } from '../exercise/exercise-details/exercise-details.component';
+import { ExerciseCardComponent } from '../exercise/exercise-card/exercise-card.component';
+import { SearchInputComponent } from './search-input/search-input.component';
 import { QueryParamModule } from '@ngqp/core';
-import { NgSelectModule } from '@ng-select/ng-select';
-import { MetadataMessageService } from 'app/search/metadata/metadata-message.service';
 
 @NgModule({
-  imports: [GitSearchV2SharedModule, RouterModule.forChild([SEARCH_ROUTE]), QueryParamModule, NgSelectModule],
-  declarations: [SearchComponent, MetadataComponent, HighlightingComponent],
-  providers: [MetadataMessageService],
+  imports: [RouterModule.forChild([SEARCH_ROUTE]), GitSearchV2SharedModule, QueryParamModule, InfiniteScrollModule],
+  declarations: [SearchComponent, ExerciseDetailsComponent, ExerciseCardComponent, SearchInputComponent],
+  exports: [SearchComponent],
 })
 export class SearchModule {}
diff --git a/src/main/webapp/app/search/search.route.ts b/src/main/webapp/app/search/search.route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1beca8002c0e5fe1c1748d4e6cf0076e1a4feeeb
--- /dev/null
+++ b/src/main/webapp/app/search/search.route.ts
@@ -0,0 +1,8 @@
+import { Route } from '@angular/router';
+
+import { SearchComponent } from './search.component';
+
+export const SEARCH_ROUTE: Route = {
+  path: '',
+  component: SearchComponent,
+};
diff --git a/src/main/webapp/app/search/search.scss b/src/main/webapp/app/search/search.scss
index 9379f6e16e0c13a968034890de3311792e0fed20..02444ff655864fe74e9696544ad53fc2f891301c 100644
--- a/src/main/webapp/app/search/search.scss
+++ b/src/main/webapp/app/search/search.scss
@@ -1,180 +1,5 @@
-/* ==========================================================================
-Main page styles
-========================================================================== */
-.hipster {
-  display: inline-block;
-  width: 347px;
-  height: 497px;
-  background: url('../../content/images/jhipster_family_member_2.svg') no-repeat center top;
-  background-size: contain;
-}
-
-/* wait autoprefixer update to allow simple generation of high pixel density media query */
-@media only screen and (-webkit-min-device-pixel-ratio: 2),
-only screen and (-moz-min-device-pixel-ratio: 2),
-only screen and (-o-min-device-pixel-ratio: 2/1),
-only screen and (min-resolution: 192dpi),
-only screen and (min-resolution: 2dppx) {
-  .hipster {
-    background: url('../../content/images/jhipster_family_member_2.svg') no-repeat center top;
-    background-size: contain;
-  }
-}
-
-div.solid {
-  border-style: solid;
-  border-width: 1pt;
-  padding: 10px;
-  border-color: rgb(223, 226, 230);
-}
-
-span.info {
-  font-size: 13px;
-  color: #7f7f7f;
-}
-
-div.filter {
-  margin-bottom: 20px;
-  margin-top: 20px;
-}
-
-
-::ng-deep code[class*="language-"],
-::ng-deep pre[class*="language-"] {
-  color: black;
-  background: none;
-  text-shadow: 0 1px white;
-  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
-  font-size: 1em;
-  text-align: left;
-  white-space: pre;
-  word-spacing: normal;
-  word-break: normal;
-  word-wrap: normal;
-  line-height: 1.5;
-
-  -moz-tab-size: 4;
-  -o-tab-size: 4;
-  tab-size: 4;
-
-  -webkit-hyphens: none;
-  -moz-hyphens: none;
-  -ms-hyphens: none;
-  hyphens: none;
-}
-
-::ng-deep pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
-::ng-deep code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
-  text-shadow: none;
-  background: #b3d4fc;
-}
-
-::ng-deep pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
-::ng-deep code[class*="language-"]::selection, code[class*="language-"] ::selection {
-  text-shadow: none;
-  background: #b3d4fc;
-}
-
-@media print {
-  ::ng-deep code[class*="language-"],
-  ::ng-deep pre[class*="language-"] {
-    text-shadow: none;
-  }
-}
-
-/* Code blocks */
-::ng-deep pre[class*="language-"] {
-  padding: 1em;
-  margin: .5em 0;
-  overflow: auto;
-}
-
-::ng-deep :not(pre) > code[class*="language-"],
-::ng-deep pre[class*="language-"] {
-  background: #f5f2f0;
-}
-
-/* Inline code */
-::ng-deep :not(pre) > code[class*="language-"] {
-  padding: .1em;
-  border-radius: .3em;
-  white-space: normal;
-}
-
-::ng-deep .token.comment,
-::ng-deep .token.prolog,
-::ng-deep .token.doctype,
-::ng-deep .token.cdata {
-  color: slategray;
-}
-
-::ng-deep .token.punctuation {
-  color: #999;
-}
-
-::ng-deep .token.namespace {
-  opacity: .7;
-}
-
-::ng-deep .token.property,
-::ng-deep .token.tag,
-::ng-deep .token.boolean,
-::ng-deep .token.number,
-::ng-deep .token.constant,
-::ng-deep .token.symbol,
-::ng-deep .token.deleted {
-  color: #905;
-}
-
-::ng-deep .token.selector,
-::ng-deep .token.attr-name,
-::ng-deep .token.string,
-::ng-deep .token.char,
-::ng-deep .token.builtin,
-::ng-deep .token.inserted {
-  color: #690;
-}
-
-::ng-deep .token.operator,
-::ng-deep .token.entity,
-::ng-deep .token.url,
-::ng-deep .language-css .token.string,
-::ng-deep .style .token.string {
-  color: #9a6e3a;
-  /* This background color was intended by the author of this theme. */
-  background: hsla(0, 0%, 100%, .5);
-}
-
-::ng-deep .token.atrule,
-::ng-deep .token.attr-value,
-::ng-deep .token.keyword {
-  color: #07a;
-}
-
-::ng-deep .token.function,
-::ng-deep .token.class-name {
-  color: #DD4A68;
-}
-
-::ng-deep .token.regex,
-::ng-deep .token.important,
-::ng-deep .token.variable {
-  color: #e90;
-}
-
-::ng-deep .token.important,
-::ng-deep .token.bold {
-  font-weight: bold;
-}
-
-::ng-deep .token.italic {
-  font-style: italic;
-}
-
-::ng-deep .token.entity {
-  cursor: help;
-}
-
-::ng-deep .strong {
-  font-weight: 600 !important;
+.card-container {
+  padding: 20px;
+  position: -webkit-sticky;
+  position: sticky;
 }
diff --git a/src/main/webapp/app/search/service/search-service.ts b/src/main/webapp/app/search/service/search-service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..096425a46930084f2e65d9b7240781bc1869559e
--- /dev/null
+++ b/src/main/webapp/app/search/service/search-service.ts
@@ -0,0 +1,99 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+import { SERVER_API_URL } from 'app/app.constants';
+import { SearchResultsDTO } from 'app/shared/model/search/search-results-dto.model';
+import { SearchInput } from 'app/shared/model/search/search-input.model';
+import { Statistics } from 'app/shared/model/statistics.model';
+
+@Injectable({ providedIn: 'root' })
+export class SearchService {
+  public resourceSearchUrlPageDetails = SERVER_API_URL + 'api/search/page-details';
+  public resourceSearchUrlStatisticsForExercise = SERVER_API_URL + 'api/statistics/exercise/';
+  public resourceKeywordsAutoCompleteDetails = SERVER_API_URL + 'api/search/keywordsAutoComplete';
+  public resourceProgrammingLanguageAutoCompleteDetails = SERVER_API_URL + 'api/search/programmingLanguageAutoComplete';
+  public resourceContributorAutoCompleteDetails = SERVER_API_URL + 'api/search/contributorAutoComplete';
+  public resourceContributorCreatorAutoCompleteDetails = SERVER_API_URL + 'api/search/contributorCreatorAutoComplete';
+  public resourceCreatorAutoCompleteDetails = SERVER_API_URL + 'api/search/creatorAutoComplete';
+
+  constructor(protected http: HttpClient) {}
+
+  searchPageDetails(searchInput: SearchInput): Observable<SearchResultsDTO> {
+    return this.http.post<SearchResultsDTO>(this.resourceSearchUrlPageDetails, searchInput);
+  }
+
+  getStatisticsForExercise(exerciseID: string): Observable<Statistics> {
+    return this.http.get<Statistics>(this.resourceSearchUrlStatisticsForExercise + exerciseID);
+  }
+
+  /**
+   * Used to export an exercise as a compressed zip file
+   * The file will contain all the three repositories as well as the exercise text and further metadata
+   * @param exerciseId
+   */
+  exportExercise(exerciseId: number): Observable<HttpResponse<Blob>> {
+    return this.http.post(SERVER_API_URL + `/api/programming-exercises/${exerciseId}/export-programming-exercise`, '', {
+      observe: 'response',
+      responseType: 'blob',
+    });
+  }
+
+  downloadFile(projectID: string): Observable<Object> {
+    return this.http.post(SERVER_API_URL + 'download/${projectID}', {
+      observe: 'response',
+      responseType: 'blob',
+    });
+  }
+
+  getKeywordsAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> {
+    const options: HttpParams = new HttpParams();
+    options.append('keyWordPrefix', prefix);
+    return this.http.get<Array<AutoCompletionEntry>>(this.resourceKeywordsAutoCompleteDetails, {
+      params: new HttpParams().set('keyWordPrefix', prefix),
+    });
+  }
+
+  getProgrammingLanguageAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> {
+    const options: HttpParams = new HttpParams();
+    options.append('keyWordPrefix', prefix);
+    return this.http.get<Array<AutoCompletionEntry>>(this.resourceProgrammingLanguageAutoCompleteDetails, {
+      params: new HttpParams().set('programmingLanguagePrefix', prefix),
+    });
+  }
+
+  getContributorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> {
+    const options: HttpParams = new HttpParams();
+    options.append('keyWordPrefix', prefix);
+    return this.http.get<Array<AutoCompletionEntry>>(this.resourceContributorAutoCompleteDetails, {
+      params: new HttpParams().set('contributorPrefix', prefix),
+    });
+  }
+  
+   getContributorCreatorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> {
+    const options: HttpParams = new HttpParams();
+    options.append('keyWordPrefix', prefix);
+    return this.http.get<Array<AutoCompletionEntry>>(this.resourceContributorCreatorAutoCompleteDetails, {
+      params: new HttpParams().set('contributorPrefix', prefix),
+    });
+  }
+
+  getCreatorAutoComplete(prefix: string): Observable<Array<AutoCompletionEntry>> {
+    const options: HttpParams = new HttpParams();
+    options.append('keyWordPrefix', prefix);
+    return this.http.get<Array<AutoCompletionEntry>>(this.resourceCreatorAutoCompleteDetails, {
+      params: new HttpParams().set('contributorPrefix', prefix),
+    });
+  }
+
+}
+
+export class AutoCompletionEntry {
+  public target: String;
+  public hitCount: Number;
+
+  constructor() {
+    this.target = '';
+    this.hitCount = 0;
+  }
+}
diff --git a/src/main/webapp/app/shared/login/login.component.html b/src/main/webapp/app/shared/login/login.component.html
index dcd23c1327e99ae129768fe2c89b6dd5807ca53a..4fd8e77d08a6f12606c11049919b05b5df766003 100644
--- a/src/main/webapp/app/shared/login/login.component.html
+++ b/src/main/webapp/app/shared/login/login.component.html
@@ -37,7 +37,11 @@
 
                 <button type="submit" class="btn btn-primary" jhiTranslate="login.form.button">Sign in</button>
             </form>
-
+            <br/>
+            <div *ngFor="let config of configs">
+            	<img src="{{'oauth2.'+config.registrationId + '.icon'| translate}}" alt="oAuth2Image"/>
+	            <button type="submit" class="btn btn-primary"  (click)="loginWithGitLab(config.registrationId)" jhiTranslate="oauth2.{{config.registrationId}}.text">Login With GitLab</button>
+			</div>
             <div class="mt-3 alert alert-warning">
                 <a class="alert-link" (click)="requestResetPassword()" jhiTranslate="login.password.forgot">Did you forget your password?</a>
             </div>
diff --git a/src/main/webapp/app/shared/login/login.component.ts b/src/main/webapp/app/shared/login/login.component.ts
index 26592223e7671eb7f04b7097c86557335fddb555..6493d643d2ff6519a690d22bd5974dd9a5cb00aa 100644
--- a/src/main/webapp/app/shared/login/login.component.ts
+++ b/src/main/webapp/app/shared/login/login.component.ts
@@ -3,7 +3,11 @@ import { FormBuilder } from '@angular/forms';
 import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
 import { Router } from '@angular/router';
 
+import { Location } from '@angular/common';
+
 import { LoginService } from 'app/core/login/login.service';
+import { OAuth2ConfigService } from 'app/core/auth/oauth2-config.service';
+import { OAuth2Config } from 'app/core/auth/oauth2-config.model';
 
 @Component({
   selector: 'jhi-login-modal',
@@ -13,6 +17,8 @@ export class LoginModalComponent implements AfterViewInit {
   @ViewChild('username', { static: false })
   username?: ElementRef;
 
+  public configs:	OAuth2Config[];
+
   authenticationError = false;
 
   loginForm = this.fb.group({
@@ -21,7 +27,17 @@ export class LoginModalComponent implements AfterViewInit {
     rememberMe: [false],
   });
 
-  constructor(private loginService: LoginService, private router: Router, public activeModal: NgbActiveModal, private fb: FormBuilder) {}
+  constructor(private loginService: LoginService, private router: Router,
+      public activeModal: NgbActiveModal, private fb: FormBuilder, private location: Location,
+	  public oAuth2ConfigService: OAuth2ConfigService) {
+		this.configs = [];
+		
+		this.oAuth2ConfigService.getAllConfigs().subscribe(
+		(loadedConfigs: OAuth2Config[]) => {
+			this.configs = loadedConfigs;
+		});
+
+	}
 
   ngAfterViewInit(): void {
     if (this.username) {
@@ -66,6 +82,13 @@ export class LoginModalComponent implements AfterViewInit {
     this.router.navigate(['/account/register']);
   }
 
+  loginWithGitLab(registrationId:string): void {
+  		  location.href = `${location.origin}${this.location.prepareExternalUrl('oauth2/authorization/'+ registrationId)}`;
+    // If you have configured multiple OIDC providers, then, you can update this URL to /login.
+    // It will show a Spring Security generated login page with links to configured OIDC providers.
+
+ }
+
   requestResetPassword(): void {
     this.activeModal.dismiss('to state requestReset');
     this.router.navigate(['/account/reset', 'request']);
diff --git a/src/main/webapp/app/shared/model/basket/shopping-basket-info.model.ts b/src/main/webapp/app/shared/model/basket/shopping-basket-info.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..41b44d07c688009a41eaf8828097f3196bd70748
--- /dev/null
+++ b/src/main/webapp/app/shared/model/basket/shopping-basket-info.model.ts
@@ -0,0 +1,11 @@
+import { SearchResultDTO } from 'app/shared/model/search/search-result-dto.model';
+
+export interface ShoppingBasketInfo {
+	itemInfos: SearchResultDTO[];
+	plugin: string;
+	action: string;
+}
+
+export interface ShoppingBasketRedirectInfoDTO {
+	redirectURL: string;
+}
\ No newline at end of file
diff --git a/src/main/webapp/app/shared/model/exercise.model.ts b/src/main/webapp/app/shared/model/exercise.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a54565fd2419a3d6f138d4d44941e6578bd3edf
--- /dev/null
+++ b/src/main/webapp/app/shared/model/exercise.model.ts
@@ -0,0 +1,62 @@
+import { Person } from 'app/shared/model/person.model';
+import { SearchResultDTO } from 'app/shared/model/search/search-result-dto.model';
+
+export enum IExerciseType {
+  COLLECTION = 'collection',
+  PROGRAMMING_EXERCISE = 'programming exercise',
+  EXERCISE = 'exercise',
+  OTHER = 'other',
+}
+
+// just a cheap trick, to make enum available to HTML.
+// see https://stackoverflow.com/questions/35835984/how-to-use-a-typescript-enum-value-in-an-angular2-ngswitch-statement
+// export function IExerciseTypeAware(constructor: Function) {
+//    constructor.prototype.IExerciseType = IExerciseType;
+// }
+
+// export class IExerciseTypeClass {
+//     myEnum: typeof IExerciseType;
+//     myEnumField: IExerciseType;
+// }
+
+export interface Exercise {
+  // from metadata (required)
+  title: string;
+  license: string;
+
+  // from metadata (optional)
+  description: string;
+  programmingLanguages: string[];
+  languages: string[];
+  creators: Person[];
+  publisher: Person[];
+  contributor: Person[];
+  requires: string[];
+  imageURL: string;
+  timeRequired: string;
+  deprecated: boolean;
+  difficulty: string;
+  educationLevel: string;
+  format: Array<string>;
+  keyword: Array<string>;
+  // language: Array<string>;
+  // repositoryURL: string;
+  // source: Array<string>;
+  status: string;
+  // structure: string;
+  type: IExerciseType;
+  structure: string;
+  version: string;
+  metadataVersion: string;
+
+  gitlabURL: string;
+
+  // not in metadata
+  rating: number;
+  lastUpdate: string;
+  originalResult: SearchResultDTO;
+
+  //thesis
+  views: number;
+  downloads: number;
+}
diff --git a/src/main/webapp/app/shared/model/fulltext-search-selection.model.ts b/src/main/webapp/app/shared/model/fulltext-search-selection.model.ts
index 31da8a4814ec4a29328ee4b49adbfaeca361259a..238cd15e5fd8c450692031b84104ea6e202ea8f6 100644
--- a/src/main/webapp/app/shared/model/fulltext-search-selection.model.ts
+++ b/src/main/webapp/app/shared/model/fulltext-search-selection.model.ts
@@ -1,4 +1,5 @@
-import {IFrequency} from "app/shared/model/frequency.model";
+// currently unused
+/* import {IFrequency} from "app/shared/model/frequency.model";
 
 export class FulltextSearchSelection {
   public repository: IFrequency<string>[];
@@ -12,3 +13,4 @@ export class FulltextSearchSelection {
   }
 
 }
+*/
diff --git a/src/main/webapp/app/shared/model/person.model.ts b/src/main/webapp/app/shared/model/person.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53665142ac715c03d2a460ffacd14ae2148cb90b
--- /dev/null
+++ b/src/main/webapp/app/shared/model/person.model.ts
@@ -0,0 +1,5 @@
+export interface Person {
+  name: string;
+  email: string;
+  affiliation: string;
+}
diff --git a/src/main/webapp/app/shared/model/search-input.model.ts b/src/main/webapp/app/shared/model/search-input.model.ts
deleted file mode 100644
index ced51a711c35d5f7264ebbc76294ccb092e59534..0000000000000000000000000000000000000000
--- a/src/main/webapp/app/shared/model/search-input.model.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import {MedataSearchInput} from "app/shared/model/metadata-search-input.model";
-import {FulltextSearchSelection} from "app/shared/model/fulltext-search-selection.model";
-
-export class SearchInput {
-
-  public fulltextQuery: string;
-  public fulltextSelection: FulltextSearchSelection;
-  public metadata: MedataSearchInput;
-  public page: number;
-
-  public constructor() {
-    this.fulltextQuery = '';
-    this.fulltextSelection = new FulltextSearchSelection();
-    this.metadata = new MedataSearchInput();
-    this.page = 1;
-  }
-
-  setValues(value: Record<string, any>): void {
-    this.fulltextQuery = value.searchText;
-    this.page = value.page;
-    this.metadata.programmingLanguage = value.programmingLanguage;
-    this.metadata.keywords = value.keywords;
-    this.metadata.author = value.author;
-    this.metadata.license = value.license;
-  }
-}
diff --git a/src/main/webapp/app/shared/model/search/metadata-file-dto.model.ts b/src/main/webapp/app/shared/model/search/metadata-file-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f92b39828aa08b108b0aae156ac2687aeb93a8c
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/metadata-file-dto.model.ts
@@ -0,0 +1,6 @@
+export interface MetadataFileDTO {
+  filename: string;
+  path: string;
+  commit_id: string;
+  indexing_date: string;
+}
diff --git a/src/main/webapp/app/shared/model/metadata-search-input.model.ts b/src/main/webapp/app/shared/model/search/metadata-search-input.model.ts
similarity index 56%
rename from src/main/webapp/app/shared/model/metadata-search-input.model.ts
rename to src/main/webapp/app/shared/model/search/metadata-search-input.model.ts
index 3a0eed4a93a863585c87783e666cec304c53b113..d8322b3f9e301f3db47fab5a84b7d74c7f501040 100644
--- a/src/main/webapp/app/shared/model/metadata-search-input.model.ts
+++ b/src/main/webapp/app/shared/model/search/metadata-search-input.model.ts
@@ -1,17 +1,19 @@
-export class MedataSearchInput {
+import {IExerciseType } from 'app/shared/model/exercise.model';
 
-  public keywords: string;
+export class MetadataSearchInput {
+  public keyword: string;
   public programmingLanguage: string;
   public naturalLanguage: string;
   public license: string;
   public author: string;
+  public types: Array<IExerciseType>
 
   public constructor() {
-    this.keywords = '';
+    this.keyword = '';
     this.programmingLanguage = '';
     this.naturalLanguage = '';
     this.license = '';
     this.author = '';
+    this.types = [];
   }
-
 }
diff --git a/src/main/webapp/app/shared/model/search/project-dto.model.ts b/src/main/webapp/app/shared/model/search/project-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9dc1390e1d1ab762d8fe71beada546f43c167d2a
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/project-dto.model.ts
@@ -0,0 +1,9 @@
+export interface ProjectDTO {
+  project_id: string;
+  project_name: string;
+  namespace: string;
+  main_group: string;
+  sub_group: string;
+  url: string;
+  last_activity_at: string;
+}
diff --git a/src/main/webapp/app/shared/model/search/search-input.model.ts b/src/main/webapp/app/shared/model/search/search-input.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29694f4a5a0b7ac92cdea106999fb0266ac31960
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/search-input.model.ts
@@ -0,0 +1,32 @@
+import { MetadataSearchInput } from 'app/shared/model/search/metadata-search-input.model';
+import {IExerciseType } from 'app/shared/model/exercise.model';
+
+export class SearchInput {
+  public fulltextQuery: string;
+  public metadata: MetadataSearchInput;
+  public page: number;
+
+  public constructor() {
+    this.fulltextQuery = '';
+    this.metadata = new MetadataSearchInput();
+    this.page = 0;
+  }
+
+  setValues(value: Record<string, any>): void {
+    this.fulltextQuery = value.searchText;
+    this.page = value.page;
+    this.metadata.programmingLanguage = value.programmingLanguage;
+    this.metadata.keyword = value.keyword;
+    this.metadata.author = value.author;
+    this.metadata.license = value.license;
+
+    this.metadata.types = [];
+    Object.keys(IExerciseType).forEach(
+        typeName => {
+            if(value[typeName]) {
+                const eType = IExerciseType[typeName];
+                this.metadata.types.push(eType)
+            }
+        })
+  }
+}
diff --git a/src/main/webapp/app/shared/model/search/search-result-dto.model.ts b/src/main/webapp/app/shared/model/search/search-result-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c1659188f4007710e8b60521ffd7cb2c586a1afa
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/search-result-dto.model.ts
@@ -0,0 +1,19 @@
+import { ProjectDTO } from './project-dto.model';
+import { MetadataFileDTO } from './metadata-file-dto.model';
+import { UserProvidedMetadataDTO } from './user-provided-metadata-dto.model';
+
+export interface SearchResultDTO {
+  project: ProjectDTO;
+  file: MetadataFileDTO;
+  metadata: UserProvidedMetadataDTO;
+  ranking5: number;
+  supportedActions: PluginActionInfo[];
+  views: number;
+  downloads: number;
+}
+
+export interface PluginActionInfo {
+  plugin: string;
+  action: string;
+  commandName: string;
+}
diff --git a/src/main/webapp/app/shared/model/search/search-results-dto.model.ts b/src/main/webapp/app/shared/model/search/search-results-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..82aea4021ea6559f644212393f7790b7b27f5786
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/search-results-dto.model.ts
@@ -0,0 +1,12 @@
+
+import { SearchResultDTO } from './search-result-dto.model';
+
+export interface SearchResultsDTO {
+    hitCount: number;
+    pageStartIndex: number;
+    searchResult: Array<SearchResultDTO>;
+}
+
+
+
+
diff --git a/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts b/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4de576833031ac0308ffff99fa5acefcf92b3c6c
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts
@@ -0,0 +1,29 @@
+import { Person } from '../person.model';
+import { IExerciseType } from '../exercise.model';
+
+export interface UserProvidedMetadataDTO {
+  contributor: Array<Person>;
+  creator: Array<Person>;
+  deprecated: boolean;
+  description: string;
+  difficulty: string;
+  educationLevel: string;
+  format: Array<string>;
+  identifier: string;
+  image: string;
+  keyword: Array<string>;
+  language: Array<string>;
+  license: string;
+  metadataVersion: string;
+  programmingLanguage: Array<string>;
+  collectionContent: Array<string>;
+  publisher: Array<Person>;
+  requires: Array<string>;
+  source: Array<string>;
+  status: string;
+  structure: string;
+  timeRequired: string;
+  title: string;
+  type: IExerciseType;
+  version: string;
+}
diff --git a/src/main/webapp/app/shared/model/statistics.model.ts b/src/main/webapp/app/shared/model/statistics.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0683ac3ee5417da36d3bc45f40fa6bd78f8182b
--- /dev/null
+++ b/src/main/webapp/app/shared/model/statistics.model.ts
@@ -0,0 +1,10 @@
+export interface IStatistics {
+  id?: number;
+  views?: number;
+  downloads?: number;
+  exerciseID?: number;
+}
+
+export class Statistics implements IStatistics {
+  constructor(public id?: number, public views?: number, public downloads?: number, public exerciseID?: number) {}
+}
diff --git a/src/main/webapp/app/shared/service/plugin-service.ts b/src/main/webapp/app/shared/service/plugin-service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..23eaf8f539a22e663317473024c48f7464bdc947
--- /dev/null
+++ b/src/main/webapp/app/shared/service/plugin-service.ts
@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+import { SERVER_API_URL } from 'app/app.constants';
+import { ShoppingBasketInfo, ShoppingBasketRedirectInfoDTO } from 'app/shared/model/basket/shopping-basket-info.model';
+
+/**
+ provides infrastructure services for Plugins
+ */
+@Injectable({ providedIn: 'root' })
+export class PluginService {
+  public resourcePluginRedirectService = SERVER_API_URL + '/api/pluginIF/getPluginRedirectInfos';
+
+  constructor(protected http: HttpClient) {}
+
+  public getRedirectLink(basketInfo: ShoppingBasketInfo ): Observable<ShoppingBasketRedirectInfoDTO> {
+    return this.http.post<ShoppingBasketRedirectInfoDTO>(this.resourcePluginRedirectService, basketInfo);
+  }
+
+}
+
diff --git a/src/main/webapp/app/teaserContent/teaserContent.component.html b/src/main/webapp/app/teaserContent/teaserContent.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..bc3c3d65adf969c3ffe926fe752efdecc8554494
--- /dev/null
+++ b/src/main/webapp/app/teaserContent/teaserContent.component.html
@@ -0,0 +1,21 @@
+
+<div class="row" style="width: 80%;margin-left: auto; margin-right: auto;">
+	<div class="col-sm-3">
+	  <p style="padding-left: 30px;"><strong>Keywords</strong></p>
+	  <ul style="list-style-type: circle;">
+                <li *ngFor="let keyWord of keywords"><a (click)="clickKeyword(keyWord.target)"  style="cursor:pointer;">{{keyWord.target}} ({{keyWord.hitCount}})</a></li>
+      </ul>
+    </div>
+	<div class="col-sm-3">
+	  <p style="padding-left: 30px;"><strong>Programming Languages</strong></p>
+	  <ul style="list-style-type: circle;">
+                <li *ngFor="let programmingLanguage of programmingLanguages"><a (click)="clickLanguage(programmingLanguage.target)"  style="cursor:pointer;">{{programmingLanguage.target}} ({{programmingLanguage.hitCount}})</a></li>
+      </ul>
+    </div>
+	<div class="col-sm-3">
+	  <p style="padding-left: 30px;"><strong>Contributors</strong></p>
+	  <ul style="list-style-type: circle;">
+                <li *ngFor="let contributor of contributors"><a (click)="clickContributor(contributor.target)" style="cursor:pointer;">{{contributor.target}} ({{contributor.hitCount}})</a></li>
+      </ul>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/main/webapp/app/teaserContent/teaserContent.component.scss b/src/main/webapp/app/teaserContent/teaserContent.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/main/webapp/app/teaserContent/teaserContent.component.ts b/src/main/webapp/app/teaserContent/teaserContent.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a62c05b854c89c684def77c6d241e26230e9634e
--- /dev/null
+++ b/src/main/webapp/app/teaserContent/teaserContent.component.ts
@@ -0,0 +1,60 @@
+import { Component, OnInit } from '@angular/core';
+
+import { SearchService, AutoCompletionEntry } from 'app/search/service/search-service';
+import { SearchInputComponent } from 'app/search/search-input/search-input.component';
+
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'jhi-teaser-content',
+  templateUrl: './teaserContent.component.html',
+  styleUrls: ['./teaserContent.component.scss'],
+  providers: [SearchInputComponent],
+})
+export class TeaserContentComponent implements OnInit {
+  public keywords: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>();
+  public contributors: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>();
+  public programmingLanguages: Array<AutoCompletionEntry> = new Array<AutoCompletionEntry>();
+
+  constructor(private searchService: SearchService, private router: Router, private searchInputComponent: SearchInputComponent) {}
+
+  ngOnInit(): void {
+    this.searchService.getKeywordsAutoComplete('').subscribe(
+      (data: Array<AutoCompletionEntry>) => {
+        this.keywords = data;
+      },
+      () => {
+        alert('Initialization of keywords failed');
+      }
+    );
+
+    this.searchService.getProgrammingLanguageAutoComplete('').subscribe(
+      (data: Array<AutoCompletionEntry>) => {
+        this.programmingLanguages = data;
+      },
+      () => {
+        alert('Initialization of programming languages failed');
+      }
+    );
+    this.searchService.getContributorCreatorAutoComplete('').subscribe(
+      (data: Array<AutoCompletionEntry>) => {
+        this.contributors = data;
+      },
+      () => {
+        alert('Initialization of contributors failed');
+      }
+    );
+  }
+
+  clickLanguage(programmingLanguage: String): void {
+    this.router.navigate(['/search'], { queryParams: { pl: programmingLanguage } });
+  }
+
+  clickContributor(contributor: String): void {
+    this.router.navigate(['/search'], { queryParams: { a: contributor } });
+  }
+
+  clickKeyword(keyWord: String): void {
+    this.router.navigate(['/search'], { queryParams: { kw: keyWord } });
+  }
+}
diff --git a/src/main/webapp/content/img/OpenID_logo.svg.png b/src/main/webapp/content/img/OpenID_logo.svg.png
new file mode 100644
index 0000000000000000000000000000000000000000..3b1ecb7c450fdfa9f233091200e5b5c5da297e3d
Binary files /dev/null and b/src/main/webapp/content/img/OpenID_logo.svg.png differ
diff --git a/src/main/webapp/content/img/gitLab.png b/src/main/webapp/content/img/gitLab.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb21da4cdf4f1489f46bd64ab3cd6355f418627b
Binary files /dev/null and b/src/main/webapp/content/img/gitLab.png differ
diff --git a/src/main/webapp/content/img/java.png b/src/main/webapp/content/img/java.png
new file mode 100644
index 0000000000000000000000000000000000000000..72f8e5f3a5777b1af531765de605c9862aa10795
Binary files /dev/null and b/src/main/webapp/content/img/java.png differ
diff --git a/src/main/webapp/content/img/latex.png b/src/main/webapp/content/img/latex.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f049743d79f92422f8aee30dba738d1e03ba20a
Binary files /dev/null and b/src/main/webapp/content/img/latex.png differ
diff --git a/src/main/webapp/content/img/logo-footer.png b/src/main/webapp/content/img/logo-footer.png
new file mode 100644
index 0000000000000000000000000000000000000000..aa6d24f20a95cf4f45f530084e344f81e1ae1e6f
Binary files /dev/null and b/src/main/webapp/content/img/logo-footer.png differ
diff --git a/src/main/webapp/content/img/logo-top.png b/src/main/webapp/content/img/logo-top.png
new file mode 100644
index 0000000000000000000000000000000000000000..cc3e849dccdb70f0c0c940b6c364b7761ebe55d4
Binary files /dev/null and b/src/main/webapp/content/img/logo-top.png differ
diff --git a/src/main/webapp/content/img/python.png b/src/main/webapp/content/img/python.png
new file mode 100644
index 0000000000000000000000000000000000000000..738f6ed41f499d1f57fd4db356dc1d64769bf725
Binary files /dev/null and b/src/main/webapp/content/img/python.png differ
diff --git a/src/main/webapp/content/js/bootstrap4.5.2.min.js b/src/main/webapp/content/js/bootstrap4.5.2.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef4d9cbd6ab1bf3a1bd86127433eab330a429699
--- /dev/null
+++ b/src/main/webapp/content/js/bootstrap4.5.2.min.js
@@ -0,0 +1,7 @@
+/*!
+  * Bootstrap v4.5.2 (https://getbootstrap.com/)
+  * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+  */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function o(t,e,n){return e&&i(t.prototype,e),n&&i(t,n),t}function s(){return(s=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e,n=n&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n;function r(t){var n=this,i=!1;return e(this).one(a.TRANSITION_END,(function(){i=!0})),setTimeout((function(){i||a.triggerTransitionEnd(n)}),t),this}var a={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var n=e(t).css("transition-duration"),i=e(t).css("transition-delay"),o=parseFloat(n),s=parseFloat(i);return o||s?(n=n.split(",")[0],i=i.split(",")[0],1e3*(parseFloat(n)+parseFloat(i))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){e(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],s=e[i],r=s&&a.isElement(s)?"element":null===(l=s)||"undefined"==typeof l?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(r))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+r+'" but expected type "'+o+'".')}var l},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?a.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof e)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=e.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};a.jQueryDetection(),e.fn.emulateTransitionEnd=r,e.event.special[a.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(e(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var l="alert",c=e.fn[l],h=function(){function t(t){this._element=t}var n=t.prototype;return n.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},n.dispose=function(){e.removeData(this._element,"bs.alert"),this._element=null},n._getRootElement=function(t){var n=a.getSelectorFromElement(t),i=!1;return n&&(i=document.querySelector(n)),i||(i=e(t).closest(".alert")[0]),i},n._triggerCloseEvent=function(t){var n=e.Event("close.bs.alert");return e(t).trigger(n),n},n._removeElement=function(t){var n=this;if(e(t).removeClass("show"),e(t).hasClass("fade")){var i=a.getTransitionDurationFromElement(t);e(t).one(a.TRANSITION_END,(function(e){return n._destroyElement(t,e)})).emulateTransitionEnd(i)}else this._destroyElement(t)},n._destroyElement=function(t){e(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.alert");o||(o=new t(this),i.data("bs.alert",o)),"close"===n&&o[n](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}}]),t}();e(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',h._handleDismiss(new h)),e.fn[l]=h._jQueryInterface,e.fn[l].Constructor=h,e.fn[l].noConflict=function(){return e.fn[l]=c,h._jQueryInterface};var u=e.fn.button,d=function(){function t(t){this._element=t}var n=t.prototype;return n.toggle=function(){var t=!0,n=!0,i=e(this._element).closest('[data-toggle="buttons"]')[0];if(i){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var s=i.querySelector(".active");s&&e(s).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),e(o).trigger("change")),o.focus(),n=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(n&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&e(this._element).toggleClass("active"))},n.dispose=function(){e.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.button");i||(i=new t(this),e(this).data("bs.button",i)),"toggle"===n&&i[n]()}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}}]),t}();e(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=t.target,i=n;if(e(n).hasClass("btn")||(n=e(n).closest(".btn")[0]),!n||n.hasAttribute("disabled")||n.classList.contains("disabled"))t.preventDefault();else{var o=n.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();("LABEL"!==i.tagName||o&&"checkbox"!==o.type)&&d._jQueryInterface.call(e(n),"toggle")}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=e(t.target).closest(".btn")[0];e(n).toggleClass("focus",/^focus(in)?$/.test(t.type))})),e(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var s=0,r=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;s<r;s++){var a=t[s];"true"===a.getAttribute("aria-pressed")?a.classList.add("active"):a.classList.remove("active")}})),e.fn.button=d._jQueryInterface,e.fn.button.Constructor=d,e.fn.button.noConflict=function(){return e.fn.button=u,d._jQueryInterface};var f="carousel",g=".bs.carousel",m=e.fn[f],p={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},_={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},v={TOUCH:"touch",PEN:"pen"},b=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var n=t.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&e(this._element).is(":visible")&&"hidden"!==e(this._element).css("visibility")&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(a.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var n=this;this._activeElement=this._element.querySelector(".active.carousel-item");var i=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)e(this._element).one("slid.bs.carousel",(function(){return n.to(t)}));else{if(i===t)return this.pause(),void this.cycle();var o=t>i?"next":"prev";this._slide(o,this._items[t])}},n.dispose=function(){e(this._element).off(g),e.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=s({},p,t),a.typeCheckConfig(f,t,_),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&e(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&e(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var n=function(e){t._pointerEvent&&v[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},i=function(e){t._pointerEvent&&v[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};e(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(e(this._element).on("pointerdown.bs.carousel",(function(t){return n(t)})),e(this._element).on("pointerup.bs.carousel",(function(t){return i(t)})),this._element.classList.add("pointer-event")):(e(this._element).on("touchstart.bs.carousel",(function(t){return n(t)})),e(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),e(this._element).on("touchend.bs.carousel",(function(t){return i(t)})))}},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,n){var i=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),s=e.Event("slide.bs.carousel",{relatedTarget:t,direction:n,from:o,to:i});return e(this._element).trigger(s),s},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var n=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));e(n).removeClass("active");var i=this._indicatorsElement.children[this._getItemIndex(t)];i&&e(i).addClass("active")}},n._slide=function(t,n){var i,o,s,r=this,l=this._element.querySelector(".active.carousel-item"),c=this._getItemIndex(l),h=n||l&&this._getItemByDirection(t,l),u=this._getItemIndex(h),d=Boolean(this._interval);if("next"===t?(i="carousel-item-left",o="carousel-item-next",s="left"):(i="carousel-item-right",o="carousel-item-prev",s="right"),h&&e(h).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(h,s).isDefaultPrevented()&&l&&h){this._isSliding=!0,d&&this.pause(),this._setActiveIndicatorElement(h);var f=e.Event("slid.bs.carousel",{relatedTarget:h,direction:s,from:c,to:u});if(e(this._element).hasClass("slide")){e(h).addClass(o),a.reflow(h),e(l).addClass(i),e(h).addClass(i);var g=parseInt(h.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=a.getTransitionDurationFromElement(l);e(l).one(a.TRANSITION_END,(function(){e(h).removeClass(i+" "+o).addClass("active"),e(l).removeClass("active "+o+" "+i),r._isSliding=!1,setTimeout((function(){return e(r._element).trigger(f)}),0)})).emulateTransitionEnd(m)}else e(l).removeClass("active"),e(h).addClass("active"),this._isSliding=!1,e(this._element).trigger(f);d&&this.cycle()}},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.carousel"),o=s({},p,e(this).data());"object"==typeof n&&(o=s({},o,n));var r="string"==typeof n?n:o.slide;if(i||(i=new t(this,o),e(this).data("bs.carousel",i)),"number"==typeof n)i.to(n);else if("string"==typeof r){if("undefined"==typeof i[r])throw new TypeError('No method named "'+r+'"');i[r]()}else o.interval&&o.ride&&(i.pause(),i.cycle())}))},t._dataApiClickHandler=function(n){var i=a.getSelectorFromElement(this);if(i){var o=e(i)[0];if(o&&e(o).hasClass("carousel")){var r=s({},e(o).data(),e(this).data()),l=this.getAttribute("data-slide-to");l&&(r.interval=!1),t._jQueryInterface.call(e(o),r),l&&e(o).data("bs.carousel").to(l),n.preventDefault()}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return p}}]),t}();e(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",b._dataApiClickHandler),e(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),n=0,i=t.length;n<i;n++){var o=e(t[n]);b._jQueryInterface.call(o,o.data())}})),e.fn[f]=b._jQueryInterface,e.fn[f].Constructor=b,e.fn[f].noConflict=function(){return e.fn[f]=m,b._jQueryInterface};var y="collapse",E=e.fn[y],w={toggle:!0,parent:""},T={toggle:"boolean",parent:"(string|element)"},C=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var s=n[i],r=a.getSelectorFromElement(s),l=[].slice.call(document.querySelectorAll(r)).filter((function(e){return e===t}));null!==r&&l.length>0&&(this._selector=r,this._triggerArray.push(s))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var n=t.prototype;return n.toggle=function(){e(this._element).hasClass("show")?this.hide():this.show()},n.show=function(){var n,i,o=this;if(!this._isTransitioning&&!e(this._element).hasClass("show")&&(this._parent&&0===(n=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(n=null),!(n&&(i=e(n).not(this._selector).data("bs.collapse"))&&i._isTransitioning))){var s=e.Event("show.bs.collapse");if(e(this._element).trigger(s),!s.isDefaultPrevented()){n&&(t._jQueryInterface.call(e(n).not(this._selector),"hide"),i||e(n).data("bs.collapse",null));var r=this._getDimension();e(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[r]=0,this._triggerArray.length&&e(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(r[0].toUpperCase()+r.slice(1)),c=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(){e(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[r]="",o.setTransitioning(!1),e(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(c),this._element.style[r]=this._element[l]+"px"}}},n.hide=function(){var t=this;if(!this._isTransitioning&&e(this._element).hasClass("show")){var n=e.Event("hide.bs.collapse");if(e(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",a.reflow(this._element),e(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var s=0;s<o;s++){var r=this._triggerArray[s],l=a.getSelectorFromElement(r);if(null!==l)e([].slice.call(document.querySelectorAll(l))).hasClass("show")||e(r).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[i]="";var c=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(){t.setTransitioning(!1),e(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(c)}}},n.setTransitioning=function(t){this._isTransitioning=t},n.dispose=function(){e.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},n._getConfig=function(t){return(t=s({},w,t)).toggle=Boolean(t.toggle),a.typeCheckConfig(y,t,T),t},n._getDimension=function(){return e(this._element).hasClass("width")?"width":"height"},n._getParent=function(){var n,i=this;a.isElement(this._config.parent)?(n=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(n=this._config.parent[0])):n=document.querySelector(this._config.parent);var o='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',s=[].slice.call(n.querySelectorAll(o));return e(s).each((function(e,n){i._addAriaAndCollapsedClass(t._getTargetFromElement(n),[n])})),n},n._addAriaAndCollapsedClass=function(t,n){var i=e(t).hasClass("show");n.length&&e(n).toggleClass("collapsed",!i).attr("aria-expanded",i)},t._getTargetFromElement=function(t){var e=a.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.collapse"),r=s({},w,i.data(),"object"==typeof n&&n?n:{});if(!o&&r.toggle&&"string"==typeof n&&/show|hide/.test(n)&&(r.toggle=!1),o||(o=new t(this,r),i.data("bs.collapse",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return w}}]),t}();e(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var n=e(this),i=a.getSelectorFromElement(this),o=[].slice.call(document.querySelectorAll(i));e(o).each((function(){var t=e(this),i=t.data("bs.collapse")?"toggle":n.data();C._jQueryInterface.call(t,i)}))})),e.fn[y]=C._jQueryInterface,e.fn[y].Constructor=C,e.fn[y].noConflict=function(){return e.fn[y]=E,C._jQueryInterface};var S="dropdown",k=e.fn[S],D=new RegExp("38|40|27"),N={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},A={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},I=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var i=t.prototype;return i.toggle=function(){if(!this._element.disabled&&!e(this._element).hasClass("disabled")){var n=e(this._menu).hasClass("show");t._clearMenus(),n||this.show(!0)}},i.show=function(i){if(void 0===i&&(i=!1),!(this._element.disabled||e(this._element).hasClass("disabled")||e(this._menu).hasClass("show"))){var o={relatedTarget:this._element},s=e.Event("show.bs.dropdown",o),r=t._getParentFromElement(this._element);if(e(r).trigger(s),!s.isDefaultPrevented()){if(!this._inNavbar&&i){if("undefined"==typeof n)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var l=this._element;"parent"===this._config.reference?l=r:a.isElement(this._config.reference)&&(l=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(l=this._config.reference[0])),"scrollParent"!==this._config.boundary&&e(r).addClass("position-static"),this._popper=new n(l,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===e(r).closest(".navbar-nav").length&&e(document.body).children().on("mouseover",null,e.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),e(this._menu).toggleClass("show"),e(r).toggleClass("show").trigger(e.Event("shown.bs.dropdown",o))}}},i.hide=function(){if(!this._element.disabled&&!e(this._element).hasClass("disabled")&&e(this._menu).hasClass("show")){var n={relatedTarget:this._element},i=e.Event("hide.bs.dropdown",n),o=t._getParentFromElement(this._element);e(o).trigger(i),i.isDefaultPrevented()||(this._popper&&this._popper.destroy(),e(this._menu).toggleClass("show"),e(o).toggleClass("show").trigger(e.Event("hidden.bs.dropdown",n)))}},i.dispose=function(){e.removeData(this._element,"bs.dropdown"),e(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},i.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},i._addEventListeners=function(){var t=this;e(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},i._getConfig=function(t){return t=s({},this.constructor.Default,e(this._element).data(),t),a.typeCheckConfig(S,t,this.constructor.DefaultType),t},i._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},i._getPlacement=function(){var t=e(this._element.parentNode),n="bottom-start";return t.hasClass("dropup")?n=e(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?n="right-start":t.hasClass("dropleft")?n="left-start":e(this._menu).hasClass("dropdown-menu-right")&&(n="bottom-end"),n},i._detectNavbar=function(){return e(this._element).closest(".navbar").length>0},i._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=s({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},i._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),s({},t,this._config.popperConfig)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.dropdown");if(i||(i=new t(this,"object"==typeof n?n:null),e(this).data("bs.dropdown",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},t._clearMenus=function(n){if(!n||3!==n.which&&("keyup"!==n.type||9===n.which))for(var i=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,s=i.length;o<s;o++){var r=t._getParentFromElement(i[o]),a=e(i[o]).data("bs.dropdown"),l={relatedTarget:i[o]};if(n&&"click"===n.type&&(l.clickEvent=n),a){var c=a._menu;if(e(r).hasClass("show")&&!(n&&("click"===n.type&&/input|textarea/i.test(n.target.tagName)||"keyup"===n.type&&9===n.which)&&e.contains(r,n.target))){var h=e.Event("hide.bs.dropdown",l);e(r).trigger(h),h.isDefaultPrevented()||("ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),i[o].setAttribute("aria-expanded","false"),a._popper&&a._popper.destroy(),e(c).removeClass("show"),e(r).removeClass("show").trigger(e.Event("hidden.bs.dropdown",l)))}}}},t._getParentFromElement=function(t){var e,n=a.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(n){if(!(/input|textarea/i.test(n.target.tagName)?32===n.which||27!==n.which&&(40!==n.which&&38!==n.which||e(n.target).closest(".dropdown-menu").length):!D.test(n.which))&&!this.disabled&&!e(this).hasClass("disabled")){var i=t._getParentFromElement(this),o=e(i).hasClass("show");if(o||27!==n.which){if(n.preventDefault(),n.stopPropagation(),!o||o&&(27===n.which||32===n.which))return 27===n.which&&e(i.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void e(this).trigger("click");var s=[].slice.call(i.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return e(t).is(":visible")}));if(0!==s.length){var r=s.indexOf(n.target);38===n.which&&r>0&&r--,40===n.which&&r<s.length-1&&r++,r<0&&(r=0),s[r].focus()}}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return N}},{key:"DefaultType",get:function(){return A}}]),t}();e(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',I._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",I._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",I._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),I._jQueryInterface.call(e(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),e.fn[S]=I._jQueryInterface,e.fn[S].Constructor=I,e.fn[S].noConflict=function(){return e.fn[S]=k,I._jQueryInterface};var O=e.fn.modal,j={backdrop:!0,keyboard:!0,focus:!0,show:!0},x={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},P=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var n=t.prototype;return n.toggle=function(t){return this._isShown?this.hide():this.show(t)},n.show=function(t){var n=this;if(!this._isShown&&!this._isTransitioning){e(this._element).hasClass("fade")&&(this._isTransitioning=!0);var i=e.Event("show.bs.modal",{relatedTarget:t});e(this._element).trigger(i),this._isShown||i.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),e(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return n.hide(t)})),e(this._dialog).on("mousedown.dismiss.bs.modal",(function(){e(n._element).one("mouseup.dismiss.bs.modal",(function(t){e(t.target).is(n._element)&&(n._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return n._showElement(t)})))}},n.hide=function(t){var n=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var i=e.Event("hide.bs.modal");if(e(this._element).trigger(i),this._isShown&&!i.isDefaultPrevented()){this._isShown=!1;var o=e(this._element).hasClass("fade");if(o&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),e(document).off("focusin.bs.modal"),e(this._element).removeClass("show"),e(this._element).off("click.dismiss.bs.modal"),e(this._dialog).off("mousedown.dismiss.bs.modal"),o){var s=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(t){return n._hideModal(t)})).emulateTransitionEnd(s)}else this._hideModal()}}},n.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return e(t).off(".bs.modal")})),e(document).off("focusin.bs.modal"),e.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},n.handleUpdate=function(){this._adjustDialog()},n._getConfig=function(t){return t=s({},j,t),a.typeCheckConfig("modal",t,x),t},n._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var n=e.Event("hidePrevented.bs.modal");if(e(this._element).trigger(n),n.defaultPrevented)return;var i=this._element.scrollHeight>document.documentElement.clientHeight;i||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var o=a.getTransitionDurationFromElement(this._dialog);e(this._element).off(a.TRANSITION_END),e(this._element).one(a.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),i||e(t._element).one(a.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}else this.hide()},n._showElement=function(t){var n=this,i=e(this._element).hasClass("fade"),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),e(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,i&&a.reflow(this._element),e(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var s=e.Event("shown.bs.modal",{relatedTarget:t}),r=function(){n._config.focus&&n._element.focus(),n._isTransitioning=!1,e(n._element).trigger(s)};if(i){var l=a.getTransitionDurationFromElement(this._dialog);e(this._dialog).one(a.TRANSITION_END,r).emulateTransitionEnd(l)}else r()},n._enforceFocus=function(){var t=this;e(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(n){document!==n.target&&t._element!==n.target&&0===e(t._element).has(n.target).length&&t._element.focus()}))},n._setEscapeEvent=function(){var t=this;this._isShown?e(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||e(this._element).off("keydown.dismiss.bs.modal")},n._setResizeEvent=function(){var t=this;this._isShown?e(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):e(window).off("resize.bs.modal")},n._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){e(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),e(t._element).trigger("hidden.bs.modal")}))},n._removeBackdrop=function(){this._backdrop&&(e(this._backdrop).remove(),this._backdrop=null)},n._showBackdrop=function(t){var n=this,i=e(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",i&&this._backdrop.classList.add(i),e(this._backdrop).appendTo(document.body),e(this._element).on("click.dismiss.bs.modal",(function(t){n._ignoreBackdropClick?n._ignoreBackdropClick=!1:t.target===t.currentTarget&&n._triggerBackdropTransition()})),i&&a.reflow(this._backdrop),e(this._backdrop).addClass("show"),!t)return;if(!i)return void t();var o=a.getTransitionDurationFromElement(this._backdrop);e(this._backdrop).one(a.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){e(this._backdrop).removeClass("show");var s=function(){n._removeBackdrop(),t&&t()};if(e(this._element).hasClass("fade")){var r=a.getTransitionDurationFromElement(this._backdrop);e(this._backdrop).one(a.TRANSITION_END,s).emulateTransitionEnd(r)}else s()}else t&&t()},n._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},n._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var n=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),i=[].slice.call(document.querySelectorAll(".sticky-top"));e(n).each((function(n,i){var o=i.style.paddingRight,s=e(i).css("padding-right");e(i).data("padding-right",o).css("padding-right",parseFloat(s)+t._scrollbarWidth+"px")})),e(i).each((function(n,i){var o=i.style.marginRight,s=e(i).css("margin-right");e(i).data("margin-right",o).css("margin-right",parseFloat(s)-t._scrollbarWidth+"px")}));var o=document.body.style.paddingRight,s=e(document.body).css("padding-right");e(document.body).data("padding-right",o).css("padding-right",parseFloat(s)+this._scrollbarWidth+"px")}e(document.body).addClass("modal-open")},n._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));e(t).each((function(t,n){var i=e(n).data("padding-right");e(n).removeData("padding-right"),n.style.paddingRight=i||""}));var n=[].slice.call(document.querySelectorAll(".sticky-top"));e(n).each((function(t,n){var i=e(n).data("margin-right");"undefined"!=typeof i&&e(n).css("margin-right",i).removeData("margin-right")}));var i=e(document.body).data("padding-right");e(document.body).removeData("padding-right"),document.body.style.paddingRight=i||""},n._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(n,i){return this.each((function(){var o=e(this).data("bs.modal"),r=s({},j,e(this).data(),"object"==typeof n&&n?n:{});if(o||(o=new t(this,r),e(this).data("bs.modal",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](i)}else r.show&&o.show(i)}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return j}}]),t}();e(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var n,i=this,o=a.getSelectorFromElement(this);o&&(n=document.querySelector(o));var r=e(n).data("bs.modal")?"toggle":s({},e(n).data(),e(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var l=e(n).one("show.bs.modal",(function(t){t.isDefaultPrevented()||l.one("hidden.bs.modal",(function(){e(i).is(":visible")&&i.focus()}))}));P._jQueryInterface.call(e(n),r,this)})),e.fn.modal=P._jQueryInterface,e.fn.modal.Constructor=P,e.fn.modal.noConflict=function(){return e.fn.modal=O,P._jQueryInterface};var R=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],L={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},q=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,F=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function Q(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),s=[].slice.call(i.body.querySelectorAll("*")),r=function(t,n){var i=s[t],r=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var a=[].slice.call(i.attributes),l=[].concat(e["*"]||[],e[r]||[]);a.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===R.indexOf(n)||Boolean(t.nodeValue.match(q)||t.nodeValue.match(F));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,s=i.length;o<s;o++)if(n.match(i[o]))return!0;return!1})(t,l)||i.removeAttribute(t.nodeName)}))},a=0,l=s.length;a<l;a++)r(a);return i.body.innerHTML}var B="tooltip",H=e.fn[B],U=new RegExp("(^|\\s)bs-tooltip\\S+","g"),M=["sanitize","whiteList","sanitizeFn"],W={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},V={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},z={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:L,popperConfig:null},K={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},X=function(){function t(t,e){if("undefined"==typeof n)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var i=t.prototype;return i.enable=function(){this._isEnabled=!0},i.disable=function(){this._isEnabled=!1},i.toggleEnabled=function(){this._isEnabled=!this._isEnabled},i.toggle=function(t){if(this._isEnabled)if(t){var n=this.constructor.DATA_KEY,i=e(t.currentTarget).data(n);i||(i=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(e(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},i.dispose=function(){clearTimeout(this._timeout),e.removeData(this.element,this.constructor.DATA_KEY),e(this.element).off(this.constructor.EVENT_KEY),e(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&e(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},i.show=function(){var t=this;if("none"===e(this.element).css("display"))throw new Error("Please use show on visible elements");var i=e.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){e(this.element).trigger(i);var o=a.findShadowRoot(this.element),s=e.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(i.isDefaultPrevented()||!s)return;var r=this.getTipElement(),l=a.getUID(this.constructor.NAME);r.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&e(r).addClass("fade");var c="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,h=this._getAttachment(c);this.addAttachmentClass(h);var u=this._getContainer();e(r).data(this.constructor.DATA_KEY,this),e.contains(this.element.ownerDocument.documentElement,this.tip)||e(r).appendTo(u),e(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n(this.element,r,this._getPopperConfig(h)),e(r).addClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().on("mouseover",null,e.noop);var d=function(){t.config.animation&&t._fixTransition();var n=t._hoverState;t._hoverState=null,e(t.element).trigger(t.constructor.Event.SHOWN),"out"===n&&t._leave(null,t)};if(e(this.tip).hasClass("fade")){var f=a.getTransitionDurationFromElement(this.tip);e(this.tip).one(a.TRANSITION_END,d).emulateTransitionEnd(f)}else d()}},i.hide=function(t){var n=this,i=this.getTipElement(),o=e.Event(this.constructor.Event.HIDE),s=function(){"show"!==n._hoverState&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),e(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()};if(e(this.element).trigger(o),!o.isDefaultPrevented()){if(e(i).removeClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,e(this.tip).hasClass("fade")){var r=a.getTransitionDurationFromElement(i);e(i).one(a.TRANSITION_END,s).emulateTransitionEnd(r)}else s();this._hoverState=""}},i.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},i.isWithContent=function(){return Boolean(this.getTitle())},i.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-tooltip-"+t)},i.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},i.setContent=function(){var t=this.getTipElement();this.setElementContent(e(t.querySelectorAll(".tooltip-inner")),this.getTitle()),e(t).removeClass("fade show")},i.setElementContent=function(t,n){"object"!=typeof n||!n.nodeType&&!n.jquery?this.config.html?(this.config.sanitize&&(n=Q(n,this.config.whiteList,this.config.sanitizeFn)),t.html(n)):t.text(n):this.config.html?e(n).parent().is(t)||t.empty().append(n):t.text(e(n).text())},i.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},i._getPopperConfig=function(t){var e=this;return s({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},i._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=s({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},i._getContainer=function(){return!1===this.config.container?document.body:a.isElement(this.config.container)?e(this.config.container):e(document).find(this.config.container)},i._getAttachment=function(t){return V[t.toUpperCase()]},i._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(n){if("click"===n)e(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==n){var i="hover"===n?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===n?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;e(t.element).on(i,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},e(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=s({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},i._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},i._enter=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e(n.getTipElement()).hasClass("show")||"show"===n._hoverState?n._hoverState="show":(clearTimeout(n._timeout),n._hoverState="show",n.config.delay&&n.config.delay.show?n._timeout=setTimeout((function(){"show"===n._hoverState&&n.show()}),n.config.delay.show):n.show())},i._leave=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState="out",n.config.delay&&n.config.delay.hide?n._timeout=setTimeout((function(){"out"===n._hoverState&&n.hide()}),n.config.delay.hide):n.hide())},i._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},i._getConfig=function(t){var n=e(this.element).data();return Object.keys(n).forEach((function(t){-1!==M.indexOf(t)&&delete n[t]})),"number"==typeof(t=s({},this.constructor.Default,n,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a.typeCheckConfig(B,t,this.constructor.DefaultType),t.sanitize&&(t.template=Q(t.template,t.whiteList,t.sanitizeFn)),t},i._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},i._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(U);null!==n&&n.length&&t.removeClass(n.join(""))},i._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},i._fixTransition=function(){var t=this.getTipElement(),n=this.config.animation;null===t.getAttribute("x-placement")&&(e(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.tooltip"),o="object"==typeof n&&n;if((i||!/dispose|hide/.test(n))&&(i||(i=new t(this,o),e(this).data("bs.tooltip",i)),"string"==typeof n)){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return z}},{key:"NAME",get:function(){return B}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return K}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return W}}]),t}();e.fn[B]=X._jQueryInterface,e.fn[B].Constructor=X,e.fn[B].noConflict=function(){return e.fn[B]=H,X._jQueryInterface};var Y="popover",$=e.fn[Y],J=new RegExp("(^|\\s)bs-popover\\S+","g"),G=s({},X.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),Z=s({},X.DefaultType,{content:"(string|element|function)"}),tt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},et=function(t){var n,i;function s(){return t.apply(this,arguments)||this}i=t,(n=s).prototype=Object.create(i.prototype),n.prototype.constructor=n,n.__proto__=i;var r=s.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},r.setContent=function(){var t=e(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(t.find(".popover-body"),n),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(J);null!==n&&n.length>0&&t.removeClass(n.join(""))},s._jQueryInterface=function(t){return this.each((function(){var n=e(this).data("bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new s(this,i),e(this).data("bs.popover",n)),"string"==typeof t)){if("undefined"==typeof n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(s,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return G}},{key:"NAME",get:function(){return Y}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return tt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Z}}]),s}(X);e.fn[Y]=et._jQueryInterface,e.fn[Y].Constructor=et,e.fn[Y].noConflict=function(){return e.fn[Y]=$,et._jQueryInterface};var nt="scrollspy",it=e.fn[nt],ot={offset:10,method:"auto",target:""},st={offset:"number",method:"string",target:"(string|element)"},rt=function(){function t(t,n){var i=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(n),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,e(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return i._process(t)})),this.refresh(),this._process()}var n=t.prototype;return n.refresh=function(){var t=this,n=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?n:this._config.method,o="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var n,s=a.getSelectorFromElement(t);if(s&&(n=document.querySelector(s)),n){var r=n.getBoundingClientRect();if(r.width||r.height)return[e(n)[i]().top+o,s]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){e.removeData(this._element,"bs.scrollspy"),e(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=s({},ot,"object"==typeof t&&t?t:{})).target&&a.isElement(t.target)){var n=e(t.target).attr("id");n||(n=a.getUID(nt),e(t.target).attr("id",n)),t.target="#"+n}return a.typeCheckConfig(nt,t,st),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},n._activate=function(t){this._activeTarget=t,this._clear();var n=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),i=e([].slice.call(document.querySelectorAll(n.join(","))));i.hasClass("dropdown-item")?(i.closest(".dropdown").find(".dropdown-toggle").addClass("active"),i.addClass("active")):(i.addClass("active"),i.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),i.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),e(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},n._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.scrollspy");if(i||(i=new t(this,"object"==typeof n&&n),e(this).data("bs.scrollspy",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"Default",get:function(){return ot}}]),t}();e(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),n=t.length;n--;){var i=e(t[n]);rt._jQueryInterface.call(i,i.data())}})),e.fn[nt]=rt._jQueryInterface,e.fn[nt].Constructor=rt,e.fn[nt].noConflict=function(){return e.fn[nt]=it,rt._jQueryInterface};var at=e.fn.tab,lt=function(){function t(t){this._element=t}var n=t.prototype;return n.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&e(this._element).hasClass("active")||e(this._element).hasClass("disabled"))){var n,i,o=e(this._element).closest(".nav, .list-group")[0],s=a.getSelectorFromElement(this._element);if(o){var r="UL"===o.nodeName||"OL"===o.nodeName?"> li > .active":".active";i=(i=e.makeArray(e(o).find(r)))[i.length-1]}var l=e.Event("hide.bs.tab",{relatedTarget:this._element}),c=e.Event("show.bs.tab",{relatedTarget:i});if(i&&e(i).trigger(l),e(this._element).trigger(c),!c.isDefaultPrevented()&&!l.isDefaultPrevented()){s&&(n=document.querySelector(s)),this._activate(this._element,o);var h=function(){var n=e.Event("hidden.bs.tab",{relatedTarget:t._element}),o=e.Event("shown.bs.tab",{relatedTarget:i});e(i).trigger(n),e(t._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},n.dispose=function(){e.removeData(this._element,"bs.tab"),this._element=null},n._activate=function(t,n,i){var o=this,s=(!n||"UL"!==n.nodeName&&"OL"!==n.nodeName?e(n).children(".active"):e(n).find("> li > .active"))[0],r=i&&s&&e(s).hasClass("fade"),l=function(){return o._transitionComplete(t,s,i)};if(s&&r){var c=a.getTransitionDurationFromElement(s);e(s).removeClass("show").one(a.TRANSITION_END,l).emulateTransitionEnd(c)}else l()},n._transitionComplete=function(t,n,i){if(n){e(n).removeClass("active");var o=e(n.parentNode).find("> .dropdown-menu .active")[0];o&&e(o).removeClass("active"),"tab"===n.getAttribute("role")&&n.setAttribute("aria-selected",!1)}if(e(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),a.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&e(t.parentNode).hasClass("dropdown-menu")){var s=e(t).closest(".dropdown")[0];if(s){var r=[].slice.call(s.querySelectorAll(".dropdown-toggle"));e(r).addClass("active")}t.setAttribute("aria-expanded",!0)}i&&i()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.tab");if(o||(o=new t(this),i.data("bs.tab",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}}]),t}();e(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),lt._jQueryInterface.call(e(this),"show")})),e.fn.tab=lt._jQueryInterface,e.fn.tab.Constructor=lt,e.fn.tab.noConflict=function(){return e.fn.tab=at,lt._jQueryInterface};var ct=e.fn.toast,ht={animation:"boolean",autohide:"boolean",delay:"number"},ut={animation:!0,autohide:!0,delay:500},dt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var n=t.prototype;return n.show=function(){var t=this,n=e.Event("show.bs.toast");if(e(this._element).trigger(n),!n.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var i=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),e(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),a.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,i).emulateTransitionEnd(o)}else i()}},n.hide=function(){if(this._element.classList.contains("show")){var t=e.Event("hide.bs.toast");e(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},n.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),e(this._element).off("click.dismiss.bs.toast"),e.removeData(this._element,"bs.toast"),this._element=null,this._config=null},n._getConfig=function(t){return t=s({},ut,e(this._element).data(),"object"==typeof t&&t?t:{}),a.typeCheckConfig("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;e(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},n._close=function(){var t=this,n=function(){t._element.classList.add("hide"),e(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var i=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,n).emulateTransitionEnd(i)}else n()},n._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.toast");if(o||(o=new t(this,"object"==typeof n&&n),i.data("bs.toast",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](this)}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.2"}},{key:"DefaultType",get:function(){return ht}},{key:"Default",get:function(){return ut}}]),t}();e.fn.toast=dt._jQueryInterface,e.fn.toast.Constructor=dt,e.fn.toast.noConflict=function(){return e.fn.toast=ct,dt._jQueryInterface},t.Alert=h,t.Button=d,t.Carousel=b,t.Collapse=C,t.Dropdown=I,t.Modal=P,t.Popover=et,t.Scrollspy=rt,t.Tab=lt,t.Toast=dt,t.Tooltip=X,t.Util=a,Object.defineProperty(t,"__esModule",{value:!0})}));
+//# sourceMappingURL=bootstrap.min.js.map
\ No newline at end of file
diff --git a/src/main/webapp/content/js/jquery3.5.1.min.js b/src/main/webapp/content/js/jquery3.5.1.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..b0614034ad3a95e4ae9f53c2b015eeb3e8d68bde
--- /dev/null
+++ b/src/main/webapp/content/js/jquery3.5.1.min.js
@@ -0,0 +1,2 @@
+/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=v.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:y}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,v,s,c,y,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),D=function(e,t){return e===t&&(l=!0),0},j={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",B=new RegExp(M+"+","g"),$=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="<a id='"+S+"'></a><select id='"+S+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0<se(t,C,null,[e]).length},se.contains=function(e,t){return(e.ownerDocument||e)!=C&&T(e),y(e,t)},se.attr=function(e,t){(e.ownerDocument||e)!=C&&T(e);var n=b.attrHandle[t.toLowerCase()],r=n&&j.call(b.attrHandle,t.toLowerCase())?n(e,t,!E):void 0;return void 0!==r?r:d.attributes||!E?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},se.escape=function(e){return(e+"").replace(re,ie)},se.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},se.uniqueSort=function(e){var t,n=[],r=0,i=0;if(l=!d.detectDuplicates,u=!d.sortStable&&e.slice(0),e.sort(D),l){while(t=e[i++])t===e[i]&&(r=n.push(i));while(r--)e.splice(n[r],1)}return u=null,e},o=se.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else while(t=e[r++])n+=o(t);return n},(b=se.selectors={cacheLength:50,createPseudo:le,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1<t.indexOf(i):"$="===r?i&&t.slice(-i.length)===i:"~="===r?-1<(" "+t.replace(B," ")+" ").indexOf(i):"|="===r&&(t===i||t.slice(0,i.length+1)===i+"-"))}},CHILD:function(h,e,t,g,v){var y="nth"!==h.slice(0,3),m="last"!==h.slice(-4),x="of-type"===e;return 1===g&&0===v?function(e){return!!e.parentNode}:function(e,t,n){var r,i,o,a,s,u,l=y!==m?"nextSibling":"previousSibling",c=e.parentNode,f=x&&e.nodeName.toLowerCase(),p=!n&&!x,d=!1;if(c){if(y){while(l){a=e;while(a=a[l])if(x?a.nodeName.toLowerCase()===f:1===a.nodeType)return!1;u=l="only"===h&&!u&&"nextSibling"}return!0}if(u=[m?c.firstChild:c.lastChild],m&&p){d=(s=(r=(i=(o=(a=c)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1])&&r[2],a=s&&c.childNodes[s];while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if(1===a.nodeType&&++d&&a===e){i[h]=[k,s,d];break}}else if(p&&(d=s=(r=(i=(o=(a=e)[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]||[])[0]===k&&r[1]),!1===d)while(a=++s&&a&&a[l]||(d=s=0)||u.pop())if((x?a.nodeName.toLowerCase()===f:1===a.nodeType)&&++d&&(p&&((i=(o=a[S]||(a[S]={}))[a.uniqueID]||(o[a.uniqueID]={}))[h]=[k,d]),a===e))break;return(d-=v)===g||d%g==0&&0<=d/g}}},PSEUDO:function(e,o){var t,a=b.pseudos[e]||b.setFilters[e.toLowerCase()]||se.error("unsupported pseudo: "+e);return a[S]?a(o):1<a.length?(t=[e,e,"",o],b.setFilters.hasOwnProperty(e.toLowerCase())?le(function(e,t){var n,r=a(e,o),i=r.length;while(i--)e[n=P(e,r[i])]=!(t[n]=r[i])}):function(e){return a(e,0,t)}):a}},pseudos:{not:le(function(e){var r=[],i=[],s=f(e.replace($,"$1"));return s[S]?le(function(e,t,n,r){var i,o=s(e,null,r,[]),a=e.length;while(a--)(i=o[a])&&(e[a]=!(t[a]=i))}):function(e,t,n){return r[0]=e,s(r,null,n,i),r[0]=null,!i.pop()}}),has:le(function(t){return function(e){return 0<se(t,e).length}}),contains:le(function(t){return t=t.replace(te,ne),function(e){return-1<(e.textContent||o(e)).indexOf(t)}}),lang:le(function(n){return V.test(n||"")||se.error("unsupported lang: "+n),n=n.replace(te,ne).toLowerCase(),function(e){var t;do{if(t=E?e.lang:e.getAttribute("xml:lang")||e.getAttribute("lang"))return(t=t.toLowerCase())===n||0===t.indexOf(n+"-")}while((e=e.parentNode)&&1===e.nodeType);return!1}}),target:function(e){var t=n.location&&n.location.hash;return t&&t.slice(1)===e.id},root:function(e){return e===a},focus:function(e){return e===C.activeElement&&(!C.hasFocus||C.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:ge(!1),disabled:ge(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!b.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:ve(function(){return[0]}),last:ve(function(e,t){return[t-1]}),eq:ve(function(e,t,n){return[n<0?n+t:n]}),even:ve(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:ve(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:ve(function(e,t,n){for(var r=n<0?n+t:t<n?t:n;0<=--r;)e.push(r);return e}),gt:ve(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=b.pseudos.eq,{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})b.pseudos[e]=de(e);for(e in{submit:!0,reset:!0})b.pseudos[e]=he(e);function me(){}function xe(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function be(s,e,t){var u=e.dir,l=e.next,c=l||u,f=t&&"parentNode"===c,p=r++;return e.first?function(e,t,n){while(e=e[u])if(1===e.nodeType||f)return s(e,t,n);return!1}:function(e,t,n){var r,i,o,a=[k,p];if(n){while(e=e[u])if((1===e.nodeType||f)&&s(e,t,n))return!0}else while(e=e[u])if(1===e.nodeType||f)if(i=(o=e[S]||(e[S]={}))[e.uniqueID]||(o[e.uniqueID]={}),l&&l===e.nodeName.toLowerCase())e=e[u]||e;else{if((r=i[c])&&r[0]===k&&r[1]===p)return a[2]=r[2];if((i[c]=a)[2]=s(e,t,n))return!0}return!1}}function we(i){return 1<i.length?function(e,t,n){var r=i.length;while(r--)if(!i[r](e,t,n))return!1;return!0}:i[0]}function Te(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Ce(d,h,g,v,y,e){return v&&!v[S]&&(v=Ce(v)),y&&!y[S]&&(y=Ce(y,e)),le(function(e,t,n,r){var i,o,a,s=[],u=[],l=t.length,c=e||function(e,t,n){for(var r=0,i=t.length;r<i;r++)se(e,t[r],n);return n}(h||"*",n.nodeType?[n]:n,[]),f=!d||!e&&h?c:Te(c,s,d,n,r),p=g?y||(e?d:l||v)?[]:t:f;if(g&&g(f,p,n,r),v){i=Te(p,u),v(i,[],n,r),o=i.length;while(o--)(a=i[o])&&(p[u[o]]=!(f[u[o]]=a))}if(e){if(y||d){if(y){i=[],o=p.length;while(o--)(a=p[o])&&i.push(f[o]=a);y(null,p=[],i,r)}o=p.length;while(o--)(a=p[o])&&-1<(i=y?P(e,a):s[o])&&(e[i]=!(t[i]=a))}}else p=Te(p===t?p.splice(l,p.length):p),y?y(null,t,p,r):H.apply(t,p)})}function Ee(e){for(var i,t,n,r=e.length,o=b.relative[e[0].type],a=o||b.relative[" "],s=o?1:0,u=be(function(e){return e===i},a,!0),l=be(function(e){return-1<P(i,e)},a,!0),c=[function(e,t,n){var r=!o&&(n||t!==w)||((i=t).nodeType?u(e,t,n):l(e,t,n));return i=null,r}];s<r;s++)if(t=b.relative[e[s].type])c=[be(we(c),t)];else{if((t=b.filter[e[s].type].apply(null,e[s].matches))[S]){for(n=++s;n<r;n++)if(b.relative[e[n].type])break;return Ce(1<s&&we(c),1<s&&xe(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace($,"$1"),t,s<n&&Ee(e.slice(s,n)),n<r&&Ee(e=e.slice(n)),n<r&&xe(e))}c.push(t)}return we(c)}return me.prototype=b.filters=b.pseudos,b.setFilters=new me,h=se.tokenize=function(e,t){var n,r,i,o,a,s,u,l=x[e+" "];if(l)return t?0:l.slice(0);a=e,s=[],u=b.preFilter;while(a){for(o in n&&!(r=_.exec(a))||(r&&(a=a.slice(r[0].length)||a),s.push(i=[])),n=!1,(r=z.exec(a))&&(n=r.shift(),i.push({value:n,type:r[0].replace($," ")}),a=a.slice(n.length)),b.filter)!(r=G[o].exec(a))||u[o]&&!(r=u[o](r))||(n=r.shift(),i.push({value:n,type:o,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?se.error(e):x(e,s).slice(0)},f=se.compile=function(e,t){var n,v,y,m,x,r,i=[],o=[],a=A[e+" "];if(!a){t||(t=h(e)),n=t.length;while(n--)(a=Ee(t[n]))[S]?i.push(a):o.push(a);(a=A(e,(v=o,m=0<(y=i).length,x=0<v.length,r=function(e,t,n,r,i){var o,a,s,u=0,l="0",c=e&&[],f=[],p=w,d=e||x&&b.find.TAG("*",i),h=k+=null==p?1:Math.random()||.1,g=d.length;for(i&&(w=t==C||t||i);l!==g&&null!=(o=d[l]);l++){if(x&&o){a=0,t||o.ownerDocument==C||(T(o),n=!E);while(s=v[a++])if(s(o,t||C,n)){r.push(o);break}i&&(k=h)}m&&((o=!s&&o)&&u--,e&&c.push(o))}if(u+=l,m&&l!==u){a=0;while(s=y[a++])s(c,f,t,n);if(e){if(0<u)while(l--)c[l]||f[l]||(f[l]=q.call(r));f=Te(f)}H.apply(r,f),i&&!e&&0<f.length&&1<u+y.length&&se.uniqueSort(r)}return i&&(k=h,w=p),c},m?le(r):r))).selector=e}return a},g=se.select=function(e,t,n,r){var i,o,a,s,u,l="function"==typeof e&&e,c=!r&&h(e=l.selector||e);if(n=n||[],1===c.length){if(2<(o=c[0]=c[0].slice(0)).length&&"ID"===(a=o[0]).type&&9===t.nodeType&&E&&b.relative[o[1].type]){if(!(t=(b.find.ID(a.matches[0].replace(te,ne),t)||[])[0]))return n;l&&(t=t.parentNode),e=e.slice(o.shift().value.length)}i=G.needsContext.test(e)?0:o.length;while(i--){if(a=o[i],b.relative[s=a.type])break;if((u=b.find[s])&&(r=u(a.matches[0].replace(te,ne),ee.test(o[0].type)&&ye(t.parentNode)||t))){if(o.splice(i,1),!(e=r.length&&xe(o)))return H.apply(n,r),n;break}}}return(l||f(e,c))(r,t,!E,n,!t||ee.test(e)&&ye(t.parentNode)||t),n},d.sortStable=S.split("").sort(D).join("")===S,d.detectDuplicates=!!l,T(),d.sortDetached=ce(function(e){return 1&e.compareDocumentPosition(C.createElement("fieldset"))}),ce(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||fe("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||fe("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute("disabled")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[":"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1<i.call(n,e)!==r}):S.filter(n,e,r)}S.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?S.find.matchesSelector(r,e)?[r]:[]:S.find.matches(e,S.grep(t,function(e){return 1===e.nodeType}))},S.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(S(e).filter(function(){for(t=0;t<r;t++)if(S.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)S.find(e,i[t],n);return 1<r?S.uniqueSort(n):n},filter:function(e){return this.pushStack(D(this,e||[],!1))},not:function(e){return this.pushStack(D(this,e||[],!0))},is:function(e){return!!D(this,"string"==typeof e&&k.test(e)?S(e):e||[],!1).length}});var j,q=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(S.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&S(e);if(!k.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?-1<a.index(n):1===n.nodeType&&S.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(1<o.length?S.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?i.call(S(e),this[0]):i.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(S.uniqueSort(S.merge(this.get(),S(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),S.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return h(e,"parentNode")},parentsUntil:function(e,t,n){return h(e,"parentNode",n)},next:function(e){return O(e,"nextSibling")},prev:function(e){return O(e,"previousSibling")},nextAll:function(e){return h(e,"nextSibling")},prevAll:function(e){return h(e,"previousSibling")},nextUntil:function(e,t,n){return h(e,"nextSibling",n)},prevUntil:function(e,t,n){return h(e,"previousSibling",n)},siblings:function(e){return T((e.parentNode||{}).firstChild,e)},children:function(e){return T(e.firstChild)},contents:function(e){return null!=e.contentDocument&&r(e.contentDocument)?e.contentDocument:(A(e,"template")&&(e=e.content||e),S.merge([],e.childNodes))}},function(r,i){S.fn[r]=function(e,t){var n=S.map(this,i,e);return"Until"!==r.slice(-5)&&(t=e),t&&"string"==typeof t&&(n=S.filter(t,n)),1<this.length&&(H[r]||S.uniqueSort(n),L.test(r)&&n.reverse()),this.pushStack(n)}});var P=/[^\x20\t\r\n\f]+/g;function R(e){return e}function M(e){throw e}function I(e,t,n,r){var i;try{e&&m(i=e.promise)?i.call(e).done(t).fail(n):e&&m(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}S.Callbacks=function(r){var e,n;r="string"==typeof r?(e=r,n={},S.each(e.match(P)||[],function(e,t){n[t]=!0}),n):S.extend({},r);var i,t,o,a,s=[],u=[],l=-1,c=function(){for(a=a||r.once,o=i=!0;u.length;l=-1){t=u.shift();while(++l<s.length)!1===s[l].apply(t[0],t[1])&&r.stopOnFalse&&(l=s.length,t=!1)}r.memory||(t=!1),i=!1,a&&(s=t?[]:"")},f={add:function(){return s&&(t&&!i&&(l=s.length-1,u.push(t)),function n(e){S.each(e,function(e,t){m(t)?r.unique&&f.has(t)||s.push(t):t&&t.length&&"string"!==w(t)&&n(t)})}(arguments),t&&!i&&c()),this},remove:function(){return S.each(arguments,function(e,t){var n;while(-1<(n=S.inArray(t,s,n)))s.splice(n,1),n<=l&&l--}),this},has:function(e){return e?-1<S.inArray(e,s):0<s.length},empty:function(){return s&&(s=[]),this},disable:function(){return a=u=[],s=t="",this},disabled:function(){return!s},lock:function(){return a=u=[],t||i||(s=t=""),this},locked:function(){return!!a},fireWith:function(e,t){return a||(t=[e,(t=t||[]).slice?t.slice():t],u.push(t),i||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},S.extend({Deferred:function(e){var o=[["notify","progress",S.Callbacks("memory"),S.Callbacks("memory"),2],["resolve","done",S.Callbacks("once memory"),S.Callbacks("once memory"),0,"resolved"],["reject","fail",S.Callbacks("once memory"),S.Callbacks("once memory"),1,"rejected"]],i="pending",a={state:function(){return i},always:function(){return s.done(arguments).fail(arguments),this},"catch":function(e){return a.then(null,e)},pipe:function(){var i=arguments;return S.Deferred(function(r){S.each(o,function(e,t){var n=m(i[t[4]])&&i[t[4]];s[t[1]](function(){var e=n&&n.apply(this,arguments);e&&m(e.promise)?e.promise().progress(r.notify).done(r.resolve).fail(r.reject):r[t[0]+"With"](this,n?[e]:arguments)})}),i=null}).promise()},then:function(t,n,r){var u=0;function l(i,o,a,s){return function(){var n=this,r=arguments,e=function(){var e,t;if(!(i<u)){if((e=a.apply(n,r))===o.promise())throw new TypeError("Thenable self-resolution");t=e&&("object"==typeof e||"function"==typeof e)&&e.then,m(t)?s?t.call(e,l(u,o,R,s),l(u,o,M,s)):(u++,t.call(e,l(u,o,R,s),l(u,o,M,s),l(u,o,R,o.notifyWith))):(a!==R&&(n=void 0,r=[e]),(s||o.resolveWith)(n,r))}},t=s?e:function(){try{e()}catch(e){S.Deferred.exceptionHook&&S.Deferred.exceptionHook(e,t.stackTrace),u<=i+1&&(a!==M&&(n=void 0,r=[e]),o.rejectWith(n,r))}};i?t():(S.Deferred.getStackHook&&(t.stackTrace=S.Deferred.getStackHook()),C.setTimeout(t))}}return S.Deferred(function(e){o[0][3].add(l(0,e,m(r)?r:R,e.notifyWith)),o[1][3].add(l(0,e,m(t)?t:R)),o[2][3].add(l(0,e,m(n)?n:M))}).promise()},promise:function(e){return null!=e?S.extend(e,a):a}},s={};return S.each(o,function(e,t){var n=t[2],r=t[5];a[t[1]]=n.add,r&&n.add(function(){i=r},o[3-e][2].disable,o[3-e][3].disable,o[0][2].lock,o[0][3].lock),n.add(t[3].fire),s[t[0]]=function(){return s[t[0]+"With"](this===s?void 0:this,arguments),this},s[t[0]+"With"]=n.fireWith}),a.promise(s),e&&e.call(s,s),s},when:function(e){var n=arguments.length,t=n,r=Array(t),i=s.call(arguments),o=S.Deferred(),a=function(t){return function(e){r[t]=this,i[t]=1<arguments.length?s.call(arguments):e,--n||o.resolveWith(r,i)}};if(n<=1&&(I(e,o.done(a(t)).resolve,o.reject,!n),"pending"===o.state()||m(i[t]&&i[t].then)))return o.then();while(t--)I(i[t],a(t),o.reject);return o.promise()}});var W=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;S.Deferred.exceptionHook=function(e,t){C.console&&C.console.warn&&e&&W.test(e.name)&&C.console.warn("jQuery.Deferred exception: "+e.message,e.stack,t)},S.readyException=function(e){C.setTimeout(function(){throw e})};var F=S.Deferred();function B(){E.removeEventListener("DOMContentLoaded",B),C.removeEventListener("load",B),S.ready()}S.fn.ready=function(e){return F.then(e)["catch"](function(e){S.readyException(e)}),this},S.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--S.readyWait:S.isReady)||(S.isReady=!0)!==e&&0<--S.readyWait||F.resolveWith(E,[S])}}),S.ready.then=F.then,"complete"===E.readyState||"loading"!==E.readyState&&!E.documentElement.doScroll?C.setTimeout(S.ready):(E.addEventListener("DOMContentLoaded",B),C.addEventListener("load",B));var $=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===w(n))for(s in i=!0,n)$(e,t,s,n[s],!0,o,a);else if(void 0!==r&&(i=!0,m(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(S(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},_=/^-ms-/,z=/-([a-z])/g;function U(e,t){return t.toUpperCase()}function X(e){return e.replace(_,"ms-").replace(z,U)}var V=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function G(){this.expando=S.expando+G.uid++}G.uid=1,G.prototype={cache:function(e){var t=e[this.expando];return t||(t={},V(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[X(t)]=n;else for(r in t)i[X(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][X(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(X):(t=X(t))in r?[t]:t.match(P)||[]).length;while(n--)delete r[t[n]]}(void 0===t||S.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!S.isEmptyObject(t)}};var Y=new G,Q=new G,J=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,K=/[A-Z]/g;function Z(e,t,n){var r,i;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(K,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n="true"===(i=n)||"false"!==i&&("null"===i?null:i===+i+""?+i:J.test(i)?JSON.parse(i):i)}catch(e){}Q.set(e,t,n)}else n=void 0;return n}S.extend({hasData:function(e){return Q.hasData(e)||Y.hasData(e)},data:function(e,t,n){return Q.access(e,t,n)},removeData:function(e,t){Q.remove(e,t)},_data:function(e,t,n){return Y.access(e,t,n)},_removeData:function(e,t){Y.remove(e,t)}}),S.fn.extend({data:function(n,e){var t,r,i,o=this[0],a=o&&o.attributes;if(void 0===n){if(this.length&&(i=Q.get(o),1===o.nodeType&&!Y.get(o,"hasDataAttrs"))){t=a.length;while(t--)a[t]&&0===(r=a[t].name).indexOf("data-")&&(r=X(r.slice(5)),Z(o,r,i[r]));Y.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof n?this.each(function(){Q.set(this,n)}):$(this,function(e){var t;if(o&&void 0===e)return void 0!==(t=Q.get(o,n))?t:void 0!==(t=Z(o,n))?t:void 0;this.each(function(){Q.set(this,n,e)})},null,e,1<arguments.length,null,!0)},removeData:function(e){return this.each(function(){Q.remove(this,e)})}}),S.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=Y.get(e,t),n&&(!r||Array.isArray(n)?r=Y.access(e,t,S.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=S.queue(e,t),r=n.length,i=n.shift(),o=S._queueHooks(e,t);"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,function(){S.dequeue(e,t)},o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return Y.get(e,n)||Y.access(e,n,{empty:S.Callbacks("once memory").add(function(){Y.remove(e,[t+"queue",n])})})}}),S.fn.extend({queue:function(t,n){var e=2;return"string"!=typeof t&&(n=t,t="fx",e--),arguments.length<e?S.queue(this[0],t):void 0===n?this:this.each(function(){var e=S.queue(this,t,n);S._queueHooks(this,t),"fx"===t&&"inprogress"!==e[0]&&S.dequeue(this,t)})},dequeue:function(e){return this.each(function(){S.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=S.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=Y.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var ee=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,te=new RegExp("^(?:([+-])=|)("+ee+")([a-z%]*)$","i"),ne=["Top","Right","Bottom","Left"],re=E.documentElement,ie=function(e){return S.contains(e.ownerDocument,e)},oe={composed:!0};re.getRootNode&&(ie=function(e){return S.contains(e.ownerDocument,e)||e.getRootNode(oe)===e.ownerDocument});var ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&ie(e)&&"none"===S.css(e,"display")};function se(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return S.css(e,t,"")},u=s(),l=n&&n[3]||(S.cssNumber[t]?"":"px"),c=e.nodeType&&(S.cssNumber[t]||"px"!==l&&+u)&&te.exec(S.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)S.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,S.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var ue={};function le(e,t){for(var n,r,i,o,a,s,u,l=[],c=0,f=e.length;c<f;c++)(r=e[c]).style&&(n=r.style.display,t?("none"===n&&(l[c]=Y.get(r,"display")||null,l[c]||(r.style.display="")),""===r.style.display&&ae(r)&&(l[c]=(u=a=o=void 0,a=(i=r).ownerDocument,s=i.nodeName,(u=ue[s])||(o=a.body.appendChild(a.createElement(s)),u=S.css(o,"display"),o.parentNode.removeChild(o),"none"===u&&(u="block"),ue[s]=u)))):"none"!==n&&(l[c]="none",Y.set(r,"display",n)));for(c=0;c<f;c++)null!=l[c]&&(e[c].style.display=l[c]);return e}S.fn.extend({show:function(){return le(this,!0)},hide:function(){return le(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?S(this).show():S(this).hide()})}});var ce,fe,pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="<textarea>x</textarea>",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="<option></option>",y.option=!!ce.lastChild;var ge={thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n<r;n++)Y.set(e[n],"globalEval",!t||Y.get(t[n],"globalEval"))}ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td,y.option||(ge.optgroup=ge.option=[1,"<select multiple='multiple'>","</select>"]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===w(o))S.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+S.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;S.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&-1<S.inArray(o,r))i&&i.push(o);else if(l=ie(o),a=ve(f.appendChild(o),"script"),l&&ye(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}var be=/^key/,we=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Te=/^([^.]*)(?:\.(.+)|)/;function Ce(){return!0}function Ee(){return!1}function Se(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function ke(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)ke(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Ee;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return S().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=S.guid++)),e.each(function(){S.event.add(this,t,i,r,n)})}function Ae(e,i,o){o?(Y.set(e,i,!1),S.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Y.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(S.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Y.set(this,i,r),t=o(this,i),this[i](),r!==(n=Y.get(this,i))||t?Y.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Y.set(this,i,{value:S.event.trigger(S.extend(r[0],S.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Y.get(e,i)&&S.event.add(e,i,Ce)}S.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.get(t);if(V(t)){n.handler&&(n=(o=n).handler,i=o.selector),i&&S.find.matchesSelector(re,i),n.guid||(n.guid=S.guid++),(u=v.events)||(u=v.events=Object.create(null)),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof S&&S.event.triggered!==e.type?S.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(P)||[""]).length;while(l--)d=g=(s=Te.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=S.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=S.event.special[d]||{},c=S.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&S.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),S.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Y.hasData(e)&&Y.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(P)||[""]).length;while(l--)if(d=g=(s=Te.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=S.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||S.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)S.event.remove(e,d+t[l],n,r,!0);S.isEmptyObject(u)&&Y.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=new Array(arguments.length),u=S.event.fix(e),l=(Y.get(this,"events")||Object.create(null))[u.type]||[],c=S.event.special[u.type]||{};for(s[0]=u,t=1;t<arguments.length;t++)s[t]=arguments[t];if(u.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,u)){a=S.event.handlers.call(this,u,l),t=0;while((i=a[t++])&&!u.isPropagationStopped()){u.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!u.isImmediatePropagationStopped())u.rnamespace&&!1!==o.namespace&&!u.rnamespace.test(o.namespace)||(u.handleObj=o,u.data=o.data,void 0!==(r=((S.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,s))&&!1===(u.result=r)&&(u.preventDefault(),u.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,u),u.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&1<=e.button))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?-1<S(i,this).index(l):S.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(t,e){Object.defineProperty(S.Event.prototype,t,{enumerable:!0,configurable:!0,get:m(e)?function(){if(this.originalEvent)return e(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[t]},set:function(e){Object.defineProperty(this,t,{enumerable:!0,configurable:!0,writable:!0,value:e})}})},fix:function(e){return e[S.expando]?e:new S.Event(e)},special:{load:{noBubble:!0},click:{setup:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click",Ce),!1},trigger:function(e){var t=this||e;return pe.test(t.type)&&t.click&&A(t,"input")&&Ae(t,"click"),!0},_default:function(e){var t=e.target;return pe.test(t.type)&&t.click&&A(t,"input")&&Y.get(t,"click")||A(t,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},S.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},S.Event=function(e,t){if(!(this instanceof S.Event))return new S.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ce:Ee,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&S.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[S.expando]=!0},S.Event.prototype={constructor:S.Event,isDefaultPrevented:Ee,isPropagationStopped:Ee,isImmediatePropagationStopped:Ee,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ce,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ce,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ce,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},S.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,code:!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&be.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&we.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},S.event.addProp),S.each({focus:"focusin",blur:"focusout"},function(e,t){S.event.special[e]={setup:function(){return Ae(this,e,Se),!1},trigger:function(){return Ae(this,e),!0},delegateType:t}}),S.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,i){S.event.special[e]={delegateType:i,bindType:i,handle:function(e){var t,n=e.relatedTarget,r=e.handleObj;return n&&(n===this||S.contains(this,n))||(e.type=r.origType,t=r.handler.apply(this,arguments),e.type=i),t}}}),S.fn.extend({on:function(e,t,n,r){return ke(this,e,t,n,r)},one:function(e,t,n,r){return ke(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,S(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=Ee),this.each(function(){S.event.remove(this,e,n,t)})}});var Ne=/<script|<style|<link/i,De=/checked\s*(?:[^=]|=\s*.checked.)/i,je=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n<r;n++)S.event.add(t,i,s[i][n]);Q.hasData(e)&&(o=Q.access(e),a=S.extend({},o),Q.set(t,a))}}function Pe(n,r,i,o){r=g(r);var e,t,a,s,u,l,c=0,f=n.length,p=f-1,d=r[0],h=m(d);if(h||1<f&&"string"==typeof d&&!y.checkClone&&De.test(d))return n.each(function(e){var t=n.eq(e);h&&(r[0]=d.call(this,e,t.html())),Pe(t,r,i,o)});if(f&&(t=(e=xe(r,n[0].ownerDocument,!1,n,o)).firstChild,1===e.childNodes.length&&(e=t),t||o)){for(s=(a=S.map(ve(e,"script"),Le)).length;c<f;c++)u=e,c!==p&&(u=S.clone(u,!0,!0),s&&S.merge(a,ve(u,"script"))),i.call(n[c],u,c);if(s)for(l=a[a.length-1].ownerDocument,S.map(a,He),c=0;c<s;c++)u=a[c],he.test(u.type||"")&&!Y.access(u,"globalEval")&&S.contains(l,u)&&(u.src&&"module"!==(u.type||"").toLowerCase()?S._evalUrl&&!u.noModule&&S._evalUrl(u.src,{nonce:u.nonce||u.getAttribute("nonce")},l):b(u.textContent.replace(je,""),u,l))}return n}function Re(e,t,n){for(var r,i=t?S.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||S.cleanData(ve(r)),r.parentNode&&(n&&ie(r)&&ye(ve(r,"script")),r.parentNode.removeChild(r));return e}S.extend({htmlPrefilter:function(e){return e},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=ie(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||S.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r<i;r++)s=o[r],u=a[r],void 0,"input"===(l=u.nodeName.toLowerCase())&&pe.test(s.type)?u.checked=s.checked:"input"!==l&&"textarea"!==l||(u.defaultValue=s.defaultValue);if(t)if(n)for(o=o||ve(e),a=a||ve(c),r=0,i=o.length;r<i;r++)Oe(o[r],a[r]);else Oe(e,c);return 0<(a=ve(c,"script")).length&&ye(a,!f&&ve(e,"script")),c},cleanData:function(e){for(var t,n,r,i=S.event.special,o=0;void 0!==(n=e[o]);o++)if(V(n)){if(t=n[Y.expando]){if(t.events)for(r in t.events)i[r]?S.event.remove(n,r):S.removeEvent(n,r,t.handle);n[Y.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),S.fn.extend({detach:function(e){return Re(this,e,!0)},remove:function(e){return Re(this,e)},text:function(e){return $(this,function(e){return void 0===e?S.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Pe(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||qe(this,e).appendChild(e)})},prepend:function(){return Pe(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=qe(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Pe(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(S.cleanData(ve(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return S.clone(this,e,t)})},html:function(e){return $(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ne.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=S.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(S.cleanData(ve(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var n=[];return Pe(this,arguments,function(e){var t=this.parentNode;S.inArray(this,n)<0&&(S.cleanData(ve(this)),t&&t.replaceChild(e,this))},n)}}),S.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,a){S.fn[e]=function(e){for(var t,n=[],r=S(e),i=r.length-1,o=0;o<=i;o++)t=o===i?this:this.clone(!0),S(r[o])[a](t),u.apply(n,t.get());return this.pushStack(n)}});var Me=new RegExp("^("+ee+")(?!px)[a-z%]+$","i"),Ie=function(e){var t=e.ownerDocument.defaultView;return t&&t.opener||(t=C),t.getComputedStyle(e)},We=function(e,t,n){var r,i,o={};for(i in t)o[i]=e.style[i],e.style[i]=t[i];for(i in r=n.call(e),t)e.style[i]=o[i];return r},Fe=new RegExp(ne.join("|"),"i");function Be(e,t,n){var r,i,o,a,s=e.style;return(n=n||Ie(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||ie(e)||(a=S.style(e,t)),!y.pixelBoxStyles()&&Me.test(a)&&Fe.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function $e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!function(){function e(){if(l){u.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",l.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",re.appendChild(u).appendChild(l);var e=C.getComputedStyle(l);n="1%"!==e.top,s=12===t(e.marginLeft),l.style.right="60%",o=36===t(e.right),r=36===t(e.width),l.style.position="absolute",i=12===t(l.offsetWidth/3),re.removeChild(u),l=null}}function t(e){return Math.round(parseFloat(e))}var n,r,i,o,a,s,u=E.createElement("div"),l=E.createElement("div");l.style&&(l.style.backgroundClip="content-box",l.cloneNode(!0).style.backgroundClip="",y.clearCloneStyle="content-box"===l.style.backgroundClip,S.extend(y,{boxSizingReliable:function(){return e(),r},pixelBoxStyles:function(){return e(),o},pixelPosition:function(){return e(),n},reliableMarginLeft:function(){return e(),s},scrollboxSize:function(){return e(),i},reliableTrDimensions:function(){var e,t,n,r;return null==a&&(e=E.createElement("table"),t=E.createElement("tr"),n=E.createElement("div"),e.style.cssText="position:absolute;left:-11111px",t.style.height="1px",n.style.height="9px",re.appendChild(e).appendChild(t).appendChild(n),r=C.getComputedStyle(t),a=3<parseInt(r.height),re.removeChild(e)),a}}))}();var _e=["Webkit","Moz","ms"],ze=E.createElement("div").style,Ue={};function Xe(e){var t=S.cssProps[e]||Ue[e];return t||(e in ze?e:Ue[e]=function(e){var t=e[0].toUpperCase()+e.slice(1),n=_e.length;while(n--)if((e=_e[n]+t)in ze)return e}(e)||e)}var Ve=/^(none|table(?!-c[ea]).+)/,Ge=/^--/,Ye={position:"absolute",visibility:"hidden",display:"block"},Qe={letterSpacing:"0",fontWeight:"400"};function Je(e,t,n){var r=te.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ke(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=S.css(e,n+ne[a],!0,i)),r?("content"===n&&(u-=S.css(e,"padding"+ne[a],!0,i)),"margin"!==n&&(u-=S.css(e,"border"+ne[a]+"Width",!0,i))):(u+=S.css(e,"padding"+ne[a],!0,i),"padding"!==n?u+=S.css(e,"border"+ne[a]+"Width",!0,i):s+=S.css(e,"border"+ne[a]+"Width",!0,i));return!r&&0<=o&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))||0),u}function Ze(e,t,n){var r=Ie(e),i=(!y.boxSizingReliable()||n)&&"border-box"===S.css(e,"boxSizing",!1,r),o=i,a=Be(e,t,r),s="offset"+t[0].toUpperCase()+t.slice(1);if(Me.test(a)){if(!n)return a;a="auto"}return(!y.boxSizingReliable()&&i||!y.reliableTrDimensions()&&A(e,"tr")||"auto"===a||!parseFloat(a)&&"inline"===S.css(e,"display",!1,r))&&e.getClientRects().length&&(i="border-box"===S.css(e,"boxSizing",!1,r),(o=s in e)&&(a=e[s])),(a=parseFloat(a)||0)+Ke(e,t,n||(i?"border":"content"),o,r,a)+"px"}function et(e,t,n,r,i){return new et.prototype.init(e,t,n,r,i)}S.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Be(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=X(t),u=Ge.test(t),l=e.style;if(u||(t=Xe(s)),a=S.cssHooks[t]||S.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"===(o=typeof n)&&(i=te.exec(n))&&i[1]&&(n=se(e,t,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(S.cssNumber[s]?"":"px")),y.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=X(t);return Ge.test(t)||(t=Xe(s)),(a=S.cssHooks[t]||S.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Be(e,t,r)),"normal"===i&&t in Qe&&(i=Qe[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),S.each(["height","width"],function(e,u){S.cssHooks[u]={get:function(e,t,n){if(t)return!Ve.test(S.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?Ze(e,u,n):We(e,Ye,function(){return Ze(e,u,n)})},set:function(e,t,n){var r,i=Ie(e),o=!y.scrollboxSize()&&"absolute"===i.position,a=(o||n)&&"border-box"===S.css(e,"boxSizing",!1,i),s=n?Ke(e,u,n,a,i):0;return a&&o&&(s-=Math.ceil(e["offset"+u[0].toUpperCase()+u.slice(1)]-parseFloat(i[u])-Ke(e,u,"border",!1,i)-.5)),s&&(r=te.exec(t))&&"px"!==(r[3]||"px")&&(e.style[u]=t,t=S.css(e,u)),Je(0,t,s)}}}),S.cssHooks.marginLeft=$e(y.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Be(e,"marginLeft"))||e.getBoundingClientRect().left-We(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),S.each({margin:"",padding:"",border:"Width"},function(i,o){S.cssHooks[i+o]={expand:function(e){for(var t=0,n={},r="string"==typeof e?e.split(" "):[e];t<4;t++)n[i+ne[t]+o]=r[t]||r[t-2]||r[0];return n}},"margin"!==i&&(S.cssHooks[i+o].set=Je)}),S.fn.extend({css:function(e,t){return $(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=Ie(e),i=t.length;a<i;a++)o[t[a]]=S.css(e,t[a],!1,r);return o}return void 0!==n?S.style(e,t,n):S.css(e,t)},e,t,1<arguments.length)}}),((S.Tween=et).prototype={constructor:et,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||S.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(S.cssNumber[n]?"":"px")},cur:function(){var e=et.propHooks[this.prop];return e&&e.get?e.get(this):et.propHooks._default.get(this)},run:function(e){var t,n=et.propHooks[this.prop];return this.options.duration?this.pos=t=S.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):et.propHooks._default.set(this),this}}).init.prototype=et.prototype,(et.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=S.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){S.fx.step[e.prop]?S.fx.step[e.prop](e):1!==e.elem.nodeType||!S.cssHooks[e.prop]&&null==e.elem.style[Xe(e.prop)]?e.elem[e.prop]=e.now:S.style(e.elem,e.prop,e.now+e.unit)}}}).scrollTop=et.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},S.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},S.fx=et.prototype.init,S.fx.step={};var tt,nt,rt,it,ot=/^(?:toggle|show|hide)$/,at=/queueHooks$/;function st(){nt&&(!1===E.hidden&&C.requestAnimationFrame?C.requestAnimationFrame(st):C.setTimeout(st,S.fx.interval),S.fx.tick())}function ut(){return C.setTimeout(function(){tt=void 0}),tt=Date.now()}function lt(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=ne[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function ct(e,t,n){for(var r,i=(ft.tweeners[t]||[]).concat(ft.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ft(o,e,t){var n,a,r=0,i=ft.prefilters.length,s=S.Deferred().always(function(){delete u.elem}),u=function(){if(a)return!1;for(var e=tt||ut(),t=Math.max(0,l.startTime+l.duration-e),n=1-(t/l.duration||0),r=0,i=l.tweens.length;r<i;r++)l.tweens[r].run(n);return s.notifyWith(o,[l,n,t]),n<1&&i?t:(i||s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l]),!1)},l=s.promise({elem:o,props:S.extend({},e),opts:S.extend(!0,{specialEasing:{},easing:S.easing._default},t),originalProperties:e,originalOptions:t,startTime:tt||ut(),duration:t.duration,tweens:[],createTween:function(e,t){var n=S.Tween(o,l.opts,e,t,l.opts.specialEasing[e]||l.opts.easing);return l.tweens.push(n),n},stop:function(e){var t=0,n=e?l.tweens.length:0;if(a)return this;for(a=!0;t<n;t++)l.tweens[t].run(1);return e?(s.notifyWith(o,[l,1,0]),s.resolveWith(o,[l,e])):s.rejectWith(o,[l,e]),this}}),c=l.props;for(!function(e,t){var n,r,i,o,a;for(n in e)if(i=t[r=X(n)],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=S.cssHooks[r])&&"expand"in a)for(n in o=a.expand(o),delete e[r],o)n in e||(e[n]=o[n],t[n]=i);else t[r]=i}(c,l.opts.specialEasing);r<i;r++)if(n=ft.prefilters[r].call(l,o,c,l.opts))return m(n.stop)&&(S._queueHooks(l.elem,l.opts.queue).stop=n.stop.bind(n)),n;return S.map(c,ct,l),m(l.opts.start)&&l.opts.start.call(o,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),S.fx.timer(S.extend(u,{elem:o,anim:l,queue:l.opts.queue})),l}S.Animation=S.extend(ft,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return se(n.elem,e,te.exec(t),n),n}]},tweener:function(e,t){m(e)?(t=e,e=["*"]):e=e.match(P);for(var n,r=0,i=e.length;r<i;r++)n=e[r],ft.tweeners[n]=ft.tweeners[n]||[],ft.tweeners[n].unshift(t)},prefilters:[function(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),v=Y.get(e,"fxshow");for(r in n.queue||(null==(a=S._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,S.queue(e,"fx").length||a.empty.fire()})})),t)if(i=t[r],ot.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!v||void 0===v[r])continue;g=!0}d[r]=v&&v[r]||S.style(e,r)}if((u=!S.isEmptyObject(t))||!S.isEmptyObject(d))for(r in f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=v&&v.display)&&(l=Y.get(e,"display")),"none"===(c=S.css(e,"display"))&&(l?c=l:(le([e],!0),l=e.style.display||l,c=S.css(e,"display"),le([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===S.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1,d)u||(v?"hidden"in v&&(g=v.hidden):v=Y.access(e,"fxshow",{display:l}),o&&(v.hidden=!g),g&&le([e],!0),p.done(function(){for(r in g||le([e]),Y.remove(e,"fxshow"),d)S.style(e,r,d[r])})),u=ct(g?v[r]:0,r,p),r in v||(v[r]=u.start,g&&(u.end=u.start,u.start=0))}],prefilter:function(e,t){t?ft.prefilters.unshift(e):ft.prefilters.push(e)}}),S.speed=function(e,t,n){var r=e&&"object"==typeof e?S.extend({},e):{complete:n||!n&&t||m(e)&&e,duration:e,easing:n&&t||t&&!m(t)&&t};return S.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in S.fx.speeds?r.duration=S.fx.speeds[r.duration]:r.duration=S.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){m(r.old)&&r.old.call(this),r.queue&&S.dequeue(this,r.queue)},r},S.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(t,e,n,r){var i=S.isEmptyObject(t),o=S.speed(e,n,r),a=function(){var e=ft(this,S.extend({},t),o);(i||Y.get(this,"finish"))&&e.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(i,e,o){var a=function(e){var t=e.stop;delete e.stop,t(o)};return"string"!=typeof i&&(o=e,e=i,i=void 0),e&&this.queue(i||"fx",[]),this.each(function(){var e=!0,t=null!=i&&i+"queueHooks",n=S.timers,r=Y.get(this);if(t)r[t]&&r[t].stop&&a(r[t]);else for(t in r)r[t]&&r[t].stop&&at.test(t)&&a(r[t]);for(t=n.length;t--;)n[t].elem!==this||null!=i&&n[t].queue!==i||(n[t].anim.stop(o),e=!1,n.splice(t,1));!e&&o||S.dequeue(this,i)})},finish:function(a){return!1!==a&&(a=a||"fx"),this.each(function(){var e,t=Y.get(this),n=t[a+"queue"],r=t[a+"queueHooks"],i=S.timers,o=n?n.length:0;for(t.finish=!0,S.queue(this,a,[]),r&&r.stop&&r.stop.call(this,!0),e=i.length;e--;)i[e].elem===this&&i[e].queue===a&&(i[e].anim.stop(!0),i.splice(e,1));for(e=0;e<o;e++)n[e]&&n[e].finish&&n[e].finish.call(this);delete t.finish})}}),S.each(["toggle","show","hide"],function(e,r){var i=S.fn[r];S.fn[r]=function(e,t,n){return null==e||"boolean"==typeof e?i.apply(this,arguments):this.animate(lt(r,!0),e,t,n)}}),S.each({slideDown:lt("show"),slideUp:lt("hide"),slideToggle:lt("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,r){S.fn[e]=function(e,t,n){return this.animate(r,e,t,n)}}),S.timers=[],S.fx.tick=function(){var e,t=0,n=S.timers;for(tt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||S.fx.stop(),tt=void 0},S.fx.timer=function(e){S.timers.push(e),S.fx.start()},S.fx.interval=13,S.fx.start=function(){nt||(nt=!0,st())},S.fx.stop=function(){nt=null},S.fx.speeds={slow:600,fast:200,_default:400},S.fn.delay=function(r,e){return r=S.fx&&S.fx.speeds[r]||r,e=e||"fx",this.queue(e,function(e,t){var n=C.setTimeout(e,r);t.stop=function(){C.clearTimeout(n)}})},rt=E.createElement("input"),it=E.createElement("select").appendChild(E.createElement("option")),rt.type="checkbox",y.checkOn=""!==rt.value,y.optSelected=it.selected,(rt=E.createElement("input")).value="t",rt.type="radio",y.radioValue="t"===rt.value;var pt,dt=S.expr.attrHandle;S.fn.extend({attr:function(e,t){return $(this,S.attr,e,t,1<arguments.length)},removeAttr:function(e){return this.each(function(){S.removeAttr(this,e)})}}),S.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?S.prop(e,t,n):(1===o&&S.isXMLDoc(e)||(i=S.attrHooks[t.toLowerCase()]||(S.expr.match.bool.test(t)?pt:void 0)),void 0!==n?null===n?void S.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=S.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!y.radioValue&&"radio"===t&&A(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(P);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),pt={set:function(e,t,n){return!1===t?S.removeAttr(e,n):e.setAttribute(n,n),n}},S.each(S.expr.match.bool.source.match(/\w+/g),function(e,t){var a=dt[t]||S.find.attr;dt[t]=function(e,t,n){var r,i,o=t.toLowerCase();return n||(i=dt[o],dt[o]=r,r=null!=a(e,t,n)?o:null,dt[o]=i),r}});var ht=/^(?:input|select|textarea|button)$/i,gt=/^(?:a|area)$/i;function vt(e){return(e.match(P)||[]).join(" ")}function yt(e){return e.getAttribute&&e.getAttribute("class")||""}function mt(e){return Array.isArray(e)?e:"string"==typeof e&&e.match(P)||[]}S.fn.extend({prop:function(e,t){return $(this,S.prop,e,t,1<arguments.length)},removeProp:function(e){return this.each(function(){delete this[S.propFix[e]||e]})}}),S.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&S.isXMLDoc(e)||(t=S.propFix[t]||t,i=S.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=S.find.attr(e,"tabindex");return t?parseInt(t,10):ht.test(e.nodeName)||gt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),y.optSelected||(S.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),S.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){S.propFix[this.toLowerCase()]=this}),S.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).addClass(t.call(this,e,yt(this)))});if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(m(t))return this.each(function(e){S(this).removeClass(t.call(this,e,yt(this)))});if(!arguments.length)return this.attr("class","");if((e=mt(t)).length)while(n=this[u++])if(i=yt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=e[a++])while(-1<r.indexOf(" "+o+" "))r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(i,t){var o=typeof i,a="string"===o||Array.isArray(i);return"boolean"==typeof t&&a?t?this.addClass(i):this.removeClass(i):m(i)?this.each(function(e){S(this).toggleClass(i.call(this,e,yt(this),t),t)}):this.each(function(){var e,t,n,r;if(a){t=0,n=S(this),r=mt(i);while(e=r[t++])n.hasClass(e)?n.removeClass(e):n.addClass(e)}else void 0!==i&&"boolean"!==o||((e=yt(this))&&Y.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===i?"":Y.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&-1<(" "+vt(yt(n))+" ").indexOf(t))return!0;return!1}});var xt=/\r/g;S.fn.extend({val:function(n){var r,e,i,t=this[0];return arguments.length?(i=m(n),this.each(function(e){var t;1===this.nodeType&&(null==(t=i?n.call(this,e,S(this).val()):n)?t="":"number"==typeof t?t+="":Array.isArray(t)&&(t=S.map(t,function(e){return null==e?"":e+""})),(r=S.valHooks[this.type]||S.valHooks[this.nodeName.toLowerCase()])&&"set"in r&&void 0!==r.set(this,t,"value")||(this.value=t))})):t?(r=S.valHooks[t.type]||S.valHooks[t.nodeName.toLowerCase()])&&"get"in r&&void 0!==(e=r.get(t,"value"))?e:"string"==typeof(e=t.value)?e.replace(xt,""):null==e?"":e:void 0}}),S.extend({valHooks:{option:{get:function(e){var t=S.find.attr(e,"value");return null!=t?t:vt(S.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!A(n.parentNode,"optgroup"))){if(t=S(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=S.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=-1<S.inArray(S.valHooks.option.get(r),o))&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),S.each(["radio","checkbox"],function(){S.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=-1<S.inArray(S(e).val(),t)}},y.checkOn||(S.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),y.focusin="onfocusin"in C;var bt=/^(?:focusinfocus|focusoutblur)$/,wt=function(e){e.stopPropagation()};S.extend(S.event,{trigger:function(e,t,n,r){var i,o,a,s,u,l,c,f,p=[n||E],d=v.call(e,"type")?e.type:e,h=v.call(e,"namespace")?e.namespace.split("."):[];if(o=f=a=n=n||E,3!==n.nodeType&&8!==n.nodeType&&!bt.test(d+S.event.triggered)&&(-1<d.indexOf(".")&&(d=(h=d.split(".")).shift(),h.sort()),u=d.indexOf(":")<0&&"on"+d,(e=e[S.expando]?e:new S.Event(d,"object"==typeof e&&e)).isTrigger=r?2:3,e.namespace=h.join("."),e.rnamespace=e.namespace?new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,e.result=void 0,e.target||(e.target=n),t=null==t?[e]:S.makeArray(t,[e]),c=S.event.special[d]||{},r||!c.trigger||!1!==c.trigger.apply(n,t))){if(!r&&!c.noBubble&&!x(n)){for(s=c.delegateType||d,bt.test(s+d)||(o=o.parentNode);o;o=o.parentNode)p.push(o),a=o;a===(n.ownerDocument||E)&&p.push(a.defaultView||a.parentWindow||C)}i=0;while((o=p[i++])&&!e.isPropagationStopped())f=o,e.type=1<i?s:c.bindType||d,(l=(Y.get(o,"events")||Object.create(null))[e.type]&&Y.get(o,"handle"))&&l.apply(o,t),(l=u&&o[u])&&l.apply&&V(o)&&(e.result=l.apply(o,t),!1===e.result&&e.preventDefault());return e.type=d,r||e.isDefaultPrevented()||c._default&&!1!==c._default.apply(p.pop(),t)||!V(n)||u&&m(n[d])&&!x(n)&&((a=n[u])&&(n[u]=null),S.event.triggered=d,e.isPropagationStopped()&&f.addEventListener(d,wt),n[d](),e.isPropagationStopped()&&f.removeEventListener(d,wt),S.event.triggered=void 0,a&&(n[u]=a)),e.result}},simulate:function(e,t,n){var r=S.extend(new S.Event,n,{type:e,isSimulated:!0});S.event.trigger(r,null,t)}}),S.fn.extend({trigger:function(e,t){return this.each(function(){S.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return S.event.trigger(e,t,n,!0)}}),y.focusin||S.each({focus:"focusin",blur:"focusout"},function(n,r){var i=function(e){S.event.simulate(r,e.target,S.event.fix(e))};S.event.special[r]={setup:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r);t||e.addEventListener(n,i,!0),Y.access(e,r,(t||0)+1)},teardown:function(){var e=this.ownerDocument||this.document||this,t=Y.access(e,r)-1;t?Y.access(e,r,t):(e.removeEventListener(n,i,!0),Y.remove(e,r))}}});var Tt=C.location,Ct={guid:Date.now()},Et=/\?/;S.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new C.DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||S.error("Invalid XML: "+e),t};var St=/\[\]$/,kt=/\r?\n/g,At=/^(?:submit|button|image|reset|file)$/i,Nt=/^(?:input|select|textarea|keygen)/i;function Dt(n,e,r,i){var t;if(Array.isArray(e))S.each(e,function(e,t){r||St.test(n)?i(n,t):Dt(n+"["+("object"==typeof t&&null!=t?e:"")+"]",t,r,i)});else if(r||"object"!==w(e))i(n,e);else for(t in e)Dt(n+"["+t+"]",e[t],r,i)}S.param=function(e,t){var n,r=[],i=function(e,t){var n=m(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(null==e)return"";if(Array.isArray(e)||e.jquery&&!S.isPlainObject(e))S.each(e,function(){i(this.name,this.value)});else for(n in e)Dt(n,e[n],t,i);return r.join("&")},S.fn.extend({serialize:function(){return S.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=S.prop(this,"elements");return e?S.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!S(this).is(":disabled")&&Nt.test(this.nodeName)&&!At.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=S(this).val();return null==n?null:Array.isArray(n)?S.map(n,function(e){return{name:t.name,value:e.replace(kt,"\r\n")}}):{name:t.name,value:n.replace(kt,"\r\n")}}).get()}});var jt=/%20/g,qt=/#.*$/,Lt=/([?&])_=[^&]*/,Ht=/^(.*?):[ \t]*([^\r\n]*)$/gm,Ot=/^(?:GET|HEAD)$/,Pt=/^\/\//,Rt={},Mt={},It="*/".concat("*"),Wt=E.createElement("a");function Ft(o){return function(e,t){"string"!=typeof e&&(t=e,e="*");var n,r=0,i=e.toLowerCase().match(P)||[];if(m(t))while(n=i[r++])"+"===n[0]?(n=n.slice(1)||"*",(o[n]=o[n]||[]).unshift(t)):(o[n]=o[n]||[]).push(t)}}function Bt(t,i,o,a){var s={},u=t===Mt;function l(e){var r;return s[e]=!0,S.each(t[e]||[],function(e,t){var n=t(i,o,a);return"string"!=typeof n||u||s[n]?u?!(r=n):void 0:(i.dataTypes.unshift(n),l(n),!1)}),r}return l(i.dataTypes[0])||!s["*"]&&l("*")}function $t(e,t){var n,r,i=S.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&S.extend(!0,e,r),e}Wt.href=Tt.href,S.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Tt.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Tt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":It,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":S.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?$t($t(e,S.ajaxSettings),t):$t(S.ajaxSettings,e)},ajaxPrefilter:Ft(Rt),ajaxTransport:Ft(Mt),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var c,f,p,n,d,r,h,g,i,o,v=S.ajaxSetup({},t),y=v.context||v,m=v.context&&(y.nodeType||y.jquery)?S(y):S.event,x=S.Deferred(),b=S.Callbacks("once memory"),w=v.statusCode||{},a={},s={},u="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(h){if(!n){n={};while(t=Ht.exec(p))n[t[1].toLowerCase()+" "]=(n[t[1].toLowerCase()+" "]||[]).concat(t[2])}t=n[e.toLowerCase()+" "]}return null==t?null:t.join(", ")},getAllResponseHeaders:function(){return h?p:null},setRequestHeader:function(e,t){return null==h&&(e=s[e.toLowerCase()]=s[e.toLowerCase()]||e,a[e]=t),this},overrideMimeType:function(e){return null==h&&(v.mimeType=e),this},statusCode:function(e){var t;if(e)if(h)T.always(e[T.status]);else for(t in e)w[t]=[w[t],e[t]];return this},abort:function(e){var t=e||u;return c&&c.abort(t),l(0,t),this}};if(x.promise(T),v.url=((e||v.url||Tt.href)+"").replace(Pt,Tt.protocol+"//"),v.type=t.method||t.type||v.method||v.type,v.dataTypes=(v.dataType||"*").toLowerCase().match(P)||[""],null==v.crossDomain){r=E.createElement("a");try{r.href=v.url,r.href=r.href,v.crossDomain=Wt.protocol+"//"+Wt.host!=r.protocol+"//"+r.host}catch(e){v.crossDomain=!0}}if(v.data&&v.processData&&"string"!=typeof v.data&&(v.data=S.param(v.data,v.traditional)),Bt(Rt,v,t,T),h)return T;for(i in(g=S.event&&v.global)&&0==S.active++&&S.event.trigger("ajaxStart"),v.type=v.type.toUpperCase(),v.hasContent=!Ot.test(v.type),f=v.url.replace(qt,""),v.hasContent?v.data&&v.processData&&0===(v.contentType||"").indexOf("application/x-www-form-urlencoded")&&(v.data=v.data.replace(jt,"+")):(o=v.url.slice(f.length),v.data&&(v.processData||"string"==typeof v.data)&&(f+=(Et.test(f)?"&":"?")+v.data,delete v.data),!1===v.cache&&(f=f.replace(Lt,"$1"),o=(Et.test(f)?"&":"?")+"_="+Ct.guid+++o),v.url=f+o),v.ifModified&&(S.lastModified[f]&&T.setRequestHeader("If-Modified-Since",S.lastModified[f]),S.etag[f]&&T.setRequestHeader("If-None-Match",S.etag[f])),(v.data&&v.hasContent&&!1!==v.contentType||t.contentType)&&T.setRequestHeader("Content-Type",v.contentType),T.setRequestHeader("Accept",v.dataTypes[0]&&v.accepts[v.dataTypes[0]]?v.accepts[v.dataTypes[0]]+("*"!==v.dataTypes[0]?", "+It+"; q=0.01":""):v.accepts["*"]),v.headers)T.setRequestHeader(i,v.headers[i]);if(v.beforeSend&&(!1===v.beforeSend.call(y,T,v)||h))return T.abort();if(u="abort",b.add(v.complete),T.done(v.success),T.fail(v.error),c=Bt(Mt,v,t,T)){if(T.readyState=1,g&&m.trigger("ajaxSend",[T,v]),h)return T;v.async&&0<v.timeout&&(d=C.setTimeout(function(){T.abort("timeout")},v.timeout));try{h=!1,c.send(a,l)}catch(e){if(h)throw e;l(-1,e)}}else l(-1,"No Transport");function l(e,t,n,r){var i,o,a,s,u,l=t;h||(h=!0,d&&C.clearTimeout(d),c=void 0,p=r||"",T.readyState=0<e?4:0,i=200<=e&&e<300||304===e,n&&(s=function(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(v,T,n)),!i&&-1<S.inArray("script",v.dataTypes)&&(v.converters["text script"]=function(){}),s=function(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}(v,s,T,i),i?(v.ifModified&&((u=T.getResponseHeader("Last-Modified"))&&(S.lastModified[f]=u),(u=T.getResponseHeader("etag"))&&(S.etag[f]=u)),204===e||"HEAD"===v.type?l="nocontent":304===e?l="notmodified":(l=s.state,o=s.data,i=!(a=s.error))):(a=l,!e&&l||(l="error",e<0&&(e=0))),T.status=e,T.statusText=(t||l)+"",i?x.resolveWith(y,[o,l,T]):x.rejectWith(y,[T,l,a]),T.statusCode(w),w=void 0,g&&m.trigger(i?"ajaxSuccess":"ajaxError",[T,v,i?o:a]),b.fireWith(y,[T,l]),g&&(m.trigger("ajaxComplete",[T,v]),--S.active||S.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return S.get(e,t,n,"json")},getScript:function(e,t){return S.get(e,void 0,t,"script")}}),S.each(["get","post"],function(e,i){S[i]=function(e,t,n,r){return m(t)&&(r=r||n,n=t,t=void 0),S.ajax(S.extend({url:e,type:i,dataType:r,data:t,success:n},S.isPlainObject(e)&&e))}}),S.ajaxPrefilter(function(e){var t;for(t in e.headers)"content-type"===t.toLowerCase()&&(e.contentType=e.headers[t]||"")}),S._evalUrl=function(e,t,n){return S.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(e){S.globalEval(e,t,n)}})},S.fn.extend({wrapAll:function(e){var t;return this[0]&&(m(e)&&(e=e.call(this[0])),t=S(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(n){return m(n)?this.each(function(e){S(this).wrapInner(n.call(this,e))}):this.each(function(){var e=S(this),t=e.contents();t.length?t.wrapAll(n):e.append(n)})},wrap:function(t){var n=m(t);return this.each(function(e){S(this).wrapAll(n?t.call(this,e):t)})},unwrap:function(e){return this.parent(e).not("body").each(function(){S(this).replaceWith(this.childNodes)}),this}}),S.expr.pseudos.hidden=function(e){return!S.expr.pseudos.visible(e)},S.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},S.ajaxSettings.xhr=function(){try{return new C.XMLHttpRequest}catch(e){}};var _t={0:200,1223:204},zt=S.ajaxSettings.xhr();y.cors=!!zt&&"withCredentials"in zt,y.ajax=zt=!!zt,S.ajaxTransport(function(i){var o,a;if(y.cors||zt&&!i.crossDomain)return{send:function(e,t){var n,r=i.xhr();if(r.open(i.type,i.url,i.async,i.username,i.password),i.xhrFields)for(n in i.xhrFields)r[n]=i.xhrFields[n];for(n in i.mimeType&&r.overrideMimeType&&r.overrideMimeType(i.mimeType),i.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest"),e)r.setRequestHeader(n,e[n]);o=function(e){return function(){o&&(o=a=r.onload=r.onerror=r.onabort=r.ontimeout=r.onreadystatechange=null,"abort"===e?r.abort():"error"===e?"number"!=typeof r.status?t(0,"error"):t(r.status,r.statusText):t(_t[r.status]||r.status,r.statusText,"text"!==(r.responseType||"text")||"string"!=typeof r.responseText?{binary:r.response}:{text:r.responseText},r.getAllResponseHeaders()))}},r.onload=o(),a=r.onerror=r.ontimeout=o("error"),void 0!==r.onabort?r.onabort=a:r.onreadystatechange=function(){4===r.readyState&&C.setTimeout(function(){o&&a()})},o=o("abort");try{r.send(i.hasContent&&i.data||null)}catch(e){if(o)throw e}},abort:function(){o&&o()}}}),S.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),S.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return S.globalEval(e),e}}}),S.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),S.ajaxTransport("script",function(n){var r,i;if(n.crossDomain||n.scriptAttrs)return{send:function(e,t){r=S("<script>").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="<form></form><form></form>",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1<s&&(r=vt(e.slice(s)),e=e.slice(0,s)),m(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),0<a.length&&S.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?S("<div>").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0<arguments.length?this.on(n,null,e,t):this.trigger(n)}});var Gt=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;S.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),m(e))return r=s.call(arguments,2),(i=function(){return e.apply(t||this,r.concat(s.call(arguments)))}).guid=e.guid=e.guid||S.guid++,i},S.holdReady=function(e){e?S.readyWait++:S.ready(!0)},S.isArray=Array.isArray,S.parseJSON=JSON.parse,S.nodeName=A,S.isFunction=m,S.isWindow=x,S.camelCase=X,S.type=w,S.now=Date.now,S.isNumeric=function(e){var t=S.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},S.trim=function(e){return null==e?"":(e+"").replace(Gt,"")},"function"==typeof define&&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
diff --git a/src/main/webapp/content/js/popper1.16.0.min.js b/src/main/webapp/content/js/popper1.16.0.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a17212f7dba4ce8b961eff7efe8ef5ba8c60844
--- /dev/null
+++ b/src/main/webapp/content/js/popper1.16.0.min.js
@@ -0,0 +1,5 @@
+/*
+ Copyright (C) Federico Zivolo 2019
+ Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
+ */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function i(e){return e&&e.referenceNode?e.referenceNode:e}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',n=e.nodeName;if('BODY'===n||'HTML'===n){var i=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||i;return r[o]}return e[o]}function f(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],n=l(t,'top'),i=l(t,'left'),r=o?-1:1;return e.top+=n*r,e.bottom+=n*r,e.left+=i*r,e.right+=i*r,e}function m(e,t){var o='x'===t?'Left':'Top',n='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'],10)+parseFloat(e['border'+n+'Width'],10)}function h(e,t,o,n){return ee(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],r(10)?parseInt(o['offset'+e])+parseInt(n['margin'+('Height'===e?'Top':'Left')])+parseInt(n['margin'+('Height'===e?'Bottom':'Right')]):0)}function c(e){var t=e.body,o=e.documentElement,n=r(10)&&getComputedStyle(o);return{height:h('Height',t,o,n),width:h('Width',t,o,n)}}function g(e){return le({},e,{right:e.left+e.width,bottom:e.top+e.height})}function u(e){var o={};try{if(r(10)){o=e.getBoundingClientRect();var n=l(e,'top'),i=l(e,'left');o.top+=n,o.left+=i,o.bottom+=n,o.right+=i}else o=e.getBoundingClientRect()}catch(t){}var p={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},s='HTML'===e.nodeName?c(e.ownerDocument):{},d=s.width||e.clientWidth||p.width,a=s.height||e.clientHeight||p.height,f=e.offsetWidth-d,h=e.offsetHeight-a;if(f||h){var u=t(e);f-=m(u,'x'),h-=m(u,'y'),p.width-=f,p.height-=h}return g(p)}function b(e,o){var i=2<arguments.length&&void 0!==arguments[2]&&arguments[2],p=r(10),s='HTML'===o.nodeName,d=u(e),a=u(o),l=n(e),m=t(o),h=parseFloat(m.borderTopWidth,10),c=parseFloat(m.borderLeftWidth,10);i&&s&&(a.top=ee(a.top,0),a.left=ee(a.left,0));var b=g({top:d.top-a.top-h,left:d.left-a.left-c,width:d.width,height:d.height});if(b.marginTop=0,b.marginLeft=0,!p&&s){var w=parseFloat(m.marginTop,10),y=parseFloat(m.marginLeft,10);b.top-=h-w,b.bottom-=h-w,b.left-=c-y,b.right-=c-y,b.marginTop=w,b.marginLeft=y}return(p&&!i?o.contains(l):o===l&&'BODY'!==l.nodeName)&&(b=f(b,o)),b}function w(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=e.ownerDocument.documentElement,n=b(e,o),i=ee(o.clientWidth,window.innerWidth||0),r=ee(o.clientHeight,window.innerHeight||0),p=t?0:l(o),s=t?0:l(o,'left'),d={top:p-n.top+n.marginTop,left:s-n.left+n.marginLeft,width:i,height:r};return g(d)}function y(e){var n=e.nodeName;if('BODY'===n||'HTML'===n)return!1;if('fixed'===t(e,'position'))return!0;var i=o(e);return!!i&&y(i)}function E(e){if(!e||!e.parentElement||r())return document.documentElement;for(var o=e.parentElement;o&&'none'===t(o,'transform');)o=o.parentElement;return o||document.documentElement}function v(e,t,r,p){var s=4<arguments.length&&void 0!==arguments[4]&&arguments[4],d={top:0,left:0},l=s?E(e):a(e,i(t));if('viewport'===p)d=w(l,s);else{var f;'scrollParent'===p?(f=n(o(t)),'BODY'===f.nodeName&&(f=e.ownerDocument.documentElement)):'window'===p?f=e.ownerDocument.documentElement:f=p;var m=b(f,l,s);if('HTML'===f.nodeName&&!y(l)){var h=c(e.ownerDocument),g=h.height,u=h.width;d.top+=m.top-m.marginTop,d.bottom=g+m.top,d.left+=m.left-m.marginLeft,d.right=u+m.left}else d=m}r=r||0;var v='number'==typeof r;return d.left+=v?r:r.left||0,d.top+=v?r:r.top||0,d.right-=v?r:r.right||0,d.bottom-=v?r:r.bottom||0,d}function x(e){var t=e.width,o=e.height;return t*o}function O(e,t,o,n,i){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=v(o,n,r,i),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return le({key:e},s[e],{area:x(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,n=e.height;return t>=o.clientWidth&&n>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function L(e,t,o){var n=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null,r=n?E(t):a(t,i(o));return b(o,r,n)}function S(e){var t=e.ownerDocument.defaultView,o=t.getComputedStyle(e),n=parseFloat(o.marginTop||0)+parseFloat(o.marginBottom||0),i=parseFloat(o.marginLeft||0)+parseFloat(o.marginRight||0),r={width:e.offsetWidth+i,height:e.offsetHeight+n};return r}function T(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function C(e,t,o){o=o.split('-')[0];var n=S(e),i={width:n.width,height:n.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return i[p]=t[p]+t[d]/2-n[d]/2,i[s]=o===s?t[s]-n[a]:t[T(s)],i}function D(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function N(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var n=D(e,function(e){return e[t]===o});return e.indexOf(n)}function P(t,o,n){var i=void 0===n?t:t.slice(0,N(t,'name',n));return i.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var n=t['function']||t.fn;t.enabled&&e(n)&&(o.offsets.popper=g(o.offsets.popper),o.offsets.reference=g(o.offsets.reference),o=n(o,t))}),o}function k(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),e.placement=O(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.positionFixed=this.options.positionFixed,e.offsets.popper=C(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position=this.options.positionFixed?'fixed':'absolute',e=P(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function W(e,t){return e.some(function(e){var o=e.name,n=e.enabled;return n&&o===t})}function B(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function H(){return this.state.isDestroyed=!0,W(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.position='',this.popper.style.top='',this.popper.style.left='',this.popper.style.right='',this.popper.style.bottom='',this.popper.style.willChange='',this.popper.style[B('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function A(e){var t=e.ownerDocument;return t?t.defaultView:window}function M(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||M(n(p.parentNode),t,o,i),i.push(p)}function F(e,t,o,i){o.updateBound=i,A(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return M(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=F(this.reference,this.options,this.state,this.scheduleUpdate))}function R(e,t){return A(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function U(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=R(this.reference,this.state))}function Y(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function V(e,t){Object.keys(t).forEach(function(o){var n='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&Y(t[o])&&(n='px'),e.style[o]=t[o]+n})}function j(e,t){Object.keys(t).forEach(function(o){var n=t[o];!1===n?e.removeAttribute(o):e.setAttribute(o,t[o])})}function q(e,t){var o=e.offsets,n=o.popper,i=o.reference,r=$,p=function(e){return e},s=r(i.width),d=r(n.width),a=-1!==['left','right'].indexOf(e.placement),l=-1!==e.placement.indexOf('-'),f=t?a||l||s%2==d%2?r:Z:p,m=t?r:p;return{left:f(1==s%2&&1==d%2&&!l&&t?n.left-1:n.left),top:m(n.top),bottom:m(n.bottom),right:f(n.right)}}function K(e,t,o){var n=D(e,function(e){var o=e.name;return o===t}),i=!!n&&e.some(function(e){return e.name===o&&e.enabled&&e.order<n.order});if(!i){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return i}function z(e){return'end'===e?'start':'start'===e?'end':e}function G(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=he.indexOf(e),n=he.slice(o+1).concat(he.slice(0,o));return t?n.reverse():n}function _(e,t,o,n){var i=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+i[1],p=i[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=n;}var d=g(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?ee(document.documentElement.clientHeight,window.innerHeight||0):ee(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function X(e,t,o,n){var i=[0,0],r=-1!==['right','left'].indexOf(n),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(D(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,n){var i=(1===n?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return _(e,i,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,n){Y(o)&&(i[t]+=o*('-'===e[n-1]?-1:1))})}),i}function J(e,t){var o,n=t.offset,i=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=i.split('-')[0];return o=Y(+n)?[+n,0]:X(n,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}var Q=Math.min,Z=Math.floor,$=Math.round,ee=Math.max,te='undefined'!=typeof window&&'undefined'!=typeof document&&'undefined'!=typeof navigator,oe=function(){for(var e=['Edge','Trident','Firefox'],t=0;t<e.length;t+=1)if(te&&0<=navigator.userAgent.indexOf(e[t]))return 1;return 0}(),ne=te&&window.Promise,ie=ne?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},oe))}},re=te&&!!(window.MSInputMethodContext&&document.documentMode),pe=te&&/MSIE 10/.test(navigator.userAgent),se=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},de=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,n){return o&&e(t.prototype,o),n&&e(t,n),t}}(),ae=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},le=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var n in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},fe=te&&/Firefox/i.test(navigator.userAgent),me=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],he=me.slice(3),ce={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},ge=function(){function t(o,n){var i=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};se(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=ie(this.update.bind(this)),this.options=le({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(le({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){i.options.modifiers[e]=le({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return le({name:e},i.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return de(t,[{key:'update',value:function(){return k.call(this)}},{key:'destroy',value:function(){return H.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return U.call(this)}}]),t}();return ge.Utils=('undefined'==typeof window?global:window).PopperUtils,ge.placements=me,ge.Defaults={placement:'bottom',positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],n=t.split('-')[1];if(n){var i=e.offsets,r=i.reference,p=i.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:ae({},d,r[d]),end:ae({},d,r[d]+r[a]-p[a])};e.offsets.popper=le({},p,l[n])}return e}},offset:{order:200,enabled:!0,fn:J,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||p(e.instance.popper);e.instance.reference===o&&(o=p(o));var n=B('transform'),i=e.instance.popper.style,r=i.top,s=i.left,d=i[n];i.top='',i.left='',i[n]='';var a=v(e.instance.popper,e.instance.reference,t.padding,o,e.positionFixed);i.top=r,i.left=s,i[n]=d,t.boundaries=a;var l=t.priority,f=e.offsets.popper,m={primary:function(e){var o=f[e];return f[e]<a[e]&&!t.escapeWithReference&&(o=ee(f[e],a[e])),ae({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=f[o];return f[e]>a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(n[d])&&(e.offsets.popper[d]=r(n[d])-o[a]),o[d]>r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!K(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[c]-u)),d[m]+u>s[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=ee(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,$(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case ce.FLIP:p=[n,i];break;case ce.CLOCKWISE:p=G(n);break;case ce.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)<f(l.right)||'top'===n&&f(a.bottom)>f(l.top)||'bottom'===n&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,w=-1!==['top','bottom'].indexOf(n),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u),E=!!t.flipVariationsByContent&&(w&&'start'===r&&c||w&&'end'===r&&h||!w&&'start'===r&&u||!w&&'end'===r&&g),v=y||E;(m||b||v)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),v&&(r=z(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport',flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!K(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,n=t.y,i=e.offsets.popper,r=D(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==r&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===r?t.gpuAcceleration:r,l=p(e.instance.popper),f=u(l),m={position:i.position},h=q(e,2>window.devicePixelRatio||!fe),c='bottom'===o?'top':'bottom',g='right'===n?'left':'right',b=B('transform');if(d='bottom'==c?'HTML'===l.nodeName?-l.clientHeight+h.bottom:-f.height+h.bottom:h.top,s='right'==g?'HTML'===l.nodeName?-l.clientWidth+h.right:-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[g]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==g?-1:1;m[c]=d*w,m[g]=s*y,m.willChange=c+', '+g}var E={"x-placement":e.placement};return e.attributes=le({},E,e.attributes),e.styles=le({},m,e.styles),e.arrowStyles=le({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return V(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&V(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,n,i){var r=L(i,t,e,o.positionFixed),p=O(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),V(t,{position:o.positionFixed?'fixed':'absolute'}),o},gpuAcceleration:void 0}}},ge});
+//# sourceMappingURL=popper.min.js.map
diff --git a/src/main/webapp/content/scss/_bootstrap-variables.scss b/src/main/webapp/content/scss/_bootstrap-variables.scss
deleted file mode 100644
index a64158a6942a5ff6ab603f749e3f85cabc3ea4a4..0000000000000000000000000000000000000000
--- a/src/main/webapp/content/scss/_bootstrap-variables.scss
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-* Bootstrap overrides https://getbootstrap.com/docs/4.0/getting-started/theming/
-* All values defined in bootstrap source
-* https://github.com/twbs/bootstrap/blob/v4-dev/scss/_variables.scss can be overwritten here
-* Make sure not to add !default to values here
-*/
-
-// Colors:
-// Grayscale and brand colors for use across Bootstrap.
-
-$primary: #3e8acc;
-$success: #28a745;
-$info: #17a2b8;
-$warning: #ffc107;
-$danger: #dc3545;
-
-// Options:
-// Quickly modify global styling by enabling or disabling optional features.
-$enable-rounded: true;
-$enable-shadows: false;
-$enable-gradients: false;
-$enable-transitions: true;
-$enable-hover-media-query: false;
-$enable-grid-classes: true;
-$enable-print-styles: true;
-
-// Components:
-// Define common padding and border radius sizes and more.
-
-$border-radius: 0.15rem;
-$border-radius-lg: 0.125rem;
-$border-radius-sm: 0.1rem;
-
-// Body:
-// Settings for the `<body>` element.
-
-$body-bg: #e4e5e6;
-
-// Typography:
-// Font, line-height, and color for body text, headings, and more.
-
-$font-size-base: 1rem;
-
-$dropdown-link-hover-color: white;
-$dropdown-link-hover-bg: #343a40;
diff --git a/src/main/webapp/content/scss/bootstrap4.5.2.min.scss b/src/main/webapp/content/scss/bootstrap4.5.2.min.scss
new file mode 100644
index 0000000000000000000000000000000000000000..21d10bad3e294f129ea09b9dc4e4d2f319fb8de5
--- /dev/null
+++ b/src/main/webapp/content/scss/bootstrap4.5.2.min.scss
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v4.5.2 (https://getbootstrap.com/)
+ * Copyright 2011-2020 The Bootstrap Authors
+ * Copyright 2011-2020 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus:not(:focus-visible){outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{-webkit-appearance:none;-moz-appearance:none;appearance:none}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;font-size:1rem;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;left:0;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc(.75em + 2.3125rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;min-width:0;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;left:0;z-index:-1;width:1rem;height:1.25rem;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label,.custom-control-input[disabled]~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before,.custom-control-input[disabled]~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #495057}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label,.custom-file-input[disabled]~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:1.4rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-ms-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid,.navbar-expand-sm>.container-lg,.navbar-expand-sm>.container-md,.navbar-expand-sm>.container-sm,.navbar-expand-sm>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid,.navbar-expand-md>.container-lg,.navbar-expand-md>.container-md,.navbar-expand-md>.container-sm,.navbar-expand-md>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid,.navbar-expand-lg>.container-lg,.navbar-expand-lg>.container-md,.navbar-expand-lg>.container-sm,.navbar-expand-lg>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid,.navbar-expand-xl>.container-lg,.navbar-expand-xl>.container-md,.navbar-expand-xl>.container-sm,.navbar-expand-xl>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid,.navbar-expand>.container-lg,.navbar-expand>.container-md,.navbar-expand>.container-sm,.navbar-expand>.container-xl{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;flex-shrink:0;width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.accordion>.card{overflow:hidden}.accordion>.card:not(:last-of-type){border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:not(:first-of-type){border-top-left-radius:0;border-top-right-radius:0}.accordion>.card>.card-header{border-radius:0;margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;display:flex}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:3;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0}a.close.disabled{pointer-events:none}.toast{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem);height:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}
+/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/src/main/webapp/content/scss/global.scss b/src/main/webapp/content/scss/global.scss
index 543a62c1a8d47c6b637b0e68d654c38e45a5bb97..a7235b928817118b56c5d9931e2da00dead855be 100644
--- a/src/main/webapp/content/scss/global.scss
+++ b/src/main/webapp/content/scss/global.scss
@@ -1,195 +1,6 @@
-@import 'bootstrap-variables';
-@import '~bootstrap/scss/functions';
-@import '~bootstrap/scss/variables';
+@import 'bootstrap4.5.2.min';
+@import 'navbar';
 
-/* ==============================================================
-Bootstrap tweaks
-===============================================================*/
-
-body,
-h1,
-h2,
-h3,
-h4 {
-  font-weight: 300;
-}
-
-/* Increase contrast of links to get 100% on Lighthouse Accessability Audit. Override this color if you want to change the link color, or use a Bootswatch theme */
-a {
-  color: #533f03;
-  font-weight: bold;
-}
-
-a:hover {
-  color: #533f03;
-}
-
-/* override hover color for dropdown-item forced by bootstrap to all a:not([href]):not([tabindex]) elements in _reboot.scss */
-a:not([href]):not([tabindex]):hover.dropdown-item {
-  color: $dropdown-link-hover-color;
-}
-
-/* override .dropdown-item.active background-color on hover */
-.dropdown-item.active:hover {
-  background-color: mix($dropdown-link-hover-bg, $dropdown-link-active-bg, 50%);
-}
-
-a:hover {
-  /* make sure browsers use the pointer cursor for anchors, even with no href */
-  cursor: pointer;
-}
-
-.dropdown-item:hover {
-  color: $dropdown-link-hover-color;
-}
-
-/* ==========================================================================
-Browser Upgrade Prompt
-========================================================================== */
-.browserupgrade {
-  margin: 0.2em 0;
-  background: #ccc;
-  color: #000;
-  padding: 0.2em 0;
-}
-
-/* ==========================================================================
-Generic styles
-========================================================================== */
-
-/* Error highlight on input fields */
-.ng-valid[required],
-.ng-valid.required {
-  border-left: 5px solid green;
-}
-
-.ng-invalid:not(form) {
-  border-left: 5px solid red;
-}
-
-/* other generic styles */
-
-.jh-card {
-  padding: 1.5%;
-  margin-top: 20px;
-  border: none;
-}
-
-.error {
-  color: white;
-  background-color: red;
-}
-
-.pad {
-  padding: 10px;
-}
-
-.w-40 {
-  width: 40% !important;
-}
-
-.w-60 {
-  width: 60% !important;
-}
-
-.break {
-  white-space: normal;
-  word-break: break-all;
-}
-
-.readonly {
-  background-color: #eee;
-  opacity: 1;
-}
-
-.footer {
-  border-top: 1px solid rgba(0, 0, 0, 0.125);
-}
-
-.hand,
-[jhisortby] {
-  cursor: pointer;
+.dropdown-item.active, .dropdown-item:active {
+  background-color: #aaaaaa;
 }
-
-/* ==========================================================================
-Custom alerts for notification
-========================================================================== */
-.alerts {
-  .alert {
-    text-overflow: ellipsis;
-    pre {
-      background: none;
-      border: none;
-      font: inherit;
-      color: inherit;
-      padding: 0;
-      margin: 0;
-    }
-    .popover pre {
-      font-size: 10px;
-    }
-  }
-  .jhi-toast {
-    position: fixed;
-    width: 100%;
-    &.left {
-      left: 5px;
-    }
-    &.right {
-      right: 5px;
-    }
-    &.top {
-      top: 55px;
-    }
-    &.bottom {
-      bottom: 55px;
-    }
-  }
-}
-
-@media screen and (min-width: 480px) {
-  .alerts .jhi-toast {
-    width: 50%;
-  }
-}
-
-/* ==========================================================================
-entity detail page css
-========================================================================== */
-.row.jh-entity-details > {
-  dd {
-    margin-bottom: 15px;
-  }
-}
-
-@media screen and (min-width: 768px) {
-  .row.jh-entity-details > {
-    dt {
-      margin-bottom: 15px;
-    }
-    dd {
-      border-bottom: 1px solid #eee;
-      padding-left: 180px;
-      margin-left: 0;
-    }
-  }
-}
-
-/* ==========================================================================
-ui bootstrap tweaks
-========================================================================== */
-.nav,
-.pagination,
-.carousel,
-.panel-title a {
-  cursor: pointer;
-}
-
-.thread-dump-modal-lock {
-  max-width: 450px;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-}
-
-/* jhipster-needle-scss-add-main JHipster will add new css style */
diff --git a/src/main/webapp/content/scss/navbar.scss b/src/main/webapp/content/scss/navbar.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a0f6b299181038c980de1e0850d5293b28a02d16
--- /dev/null
+++ b/src/main/webapp/content/scss/navbar.scss
@@ -0,0 +1,256 @@
+* {
+  box-sizing: border-box;
+  font-family: Open Sans;
+}
+
+.pagewrapper {
+  max-width: 1920px;
+  margin-left: auto;
+  margin-right: auto;
+  margin-top: 20px;
+  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+}
+
+#top-space {
+  height: auto;
+  padding-top: 15px;
+  padding-bottom: 25px;
+  padding-left: 25px;
+}
+
+/* Vector graphics */
+svg {
+  overflow: hidden;
+  vertical-align: middle;
+  margin-bottom: 5px;
+  margin-right: 5px;
+}
+
+.myBar {
+  height: 25px;
+  background-color: lightblue;
+}
+
+.row {
+  display: -ms-flexbox;
+  display: flex;
+  -ms-flex-wrap: wrap;
+  flex-wrap: wrap;
+  margin-right: auto;
+  margin-left: auto;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 10rem;
+  padding: 0.5rem 0;
+  margin: 0.125rem 0 0;
+  font-size: 1rem;
+  color: #212529;
+  text-align: left;
+  list-style: none;
+  background-color: #fff;
+  background-clip: padding-box;
+  border: 1px solid rgba(0, 0, 0, 0.15);
+  border-radius: 0.25rem;
+}
+
+.dropdown-menu.show {
+  display: block;
+  background-color: rgb(255, 255, 255);
+  width: max-content;
+  min-width: 408px;
+  padding-top: 15px;
+  padding-right: 25px;
+  padding-bottom: 30px;
+  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+}
+
+.dropdown-item {
+  display: block;
+  width: 100%;
+  padding: 0.25rem 1.5rem;
+  padding-top: 0.35rem;
+  padding-right: 1.5rem;
+  padding-bottom: 0.3rem;
+  padding-left: 1.5rem;
+  clear: both;
+  font-weight: 400;
+  color: #212529;
+  text-align: inherit;
+  white-space: nowrap;
+  background-color: transparent;
+  border: 0;
+}
+
+.flex-item-left,
+.flex-item-right {
+  flex: 50%;
+}
+
+@media (max-width: 800px) {
+  .flex-container {
+    flex-direction: column;
+  }
+}
+
+.dropdown-divider {
+  height: 0;
+  margin: 0.5rem 0;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  border-top: 1px solid #e9ecef;
+}
+
+.col-md-6 {
+  position: relative;
+  float: left;
+  width: max-content;
+  max-width: 100%;
+}
+
+.spawn-submenu {
+  padding-left: 25px;
+  color: darkgray;
+  padding-top: 10px;
+}
+
+.search-container {
+  padding-top: 15px;
+  padding-left: 25px;
+}
+
+.form-group {
+  padding-top: 15px;
+}
+
+.headline {
+  float: left;
+  padding-left: 25px;
+  padding-bottom: 15px;
+  padding-top: 15px;
+  color: #737373;
+}
+
+.col-sm-3 {
+  margin-bottom: 25px;
+}
+
+.footer {
+  width: 100%;
+  min-height: 64px;
+  max-height: fit-content;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  background-color: #343a40;
+  text-align: center;
+}
+
+.div-footer {
+  width: auto;
+  margin-top: 45px;
+  padding-right: 50px;
+  color: white;
+  float: left;
+}
+
+.container-fluid {
+  color: #737373;
+}
+
+.dot {
+  height: 10px;
+  width: 10px;
+  background-color: #b3b3b3;
+  border-radius: 50%;
+  float: left;
+  margin-right: 15px;
+  margin-top: 7px;
+}
+
+.nav-item {
+  padding-right: 50px;
+}
+
+.logo-footer {
+  width: 256px;
+  height: auto;
+}
+
+.logo-container {
+  float: left;
+}
+
+a {
+  color: #b3b3b3;
+}
+
+a:hover {
+  color: #d8d8d8;
+}
+
+#university,
+#repository,
+#file-format,
+#status,
+#difficulty {
+  color: darkgray;
+}
+
+::placeholder {
+  color: darkgray;
+}
+
+/* Star rating */
+// The following classes are used to style a five star rating system.
+// This probably should not be in navbar.scss, but in a file for search results.
+// Currently, rating is not implemented.
+.rate {
+  float: right;
+  height: auto;
+  padding: 0 10px;
+  padding-top: 12px;
+}
+
+.rate:not(:checked) > input {
+  position: absolute;
+  top: -9999px;
+}
+
+.rate:not(:checked) > label {
+  float: right;
+  width: 1em;
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+  font-size: 18px;
+  color: #ccc;
+}
+
+.rate:not(:checked) > label:before {
+  content: '★ ';
+}
+
+.rate > input:checked ~ label {
+  color: #ffc700;
+}
+
+.rate:not(:checked) > label:hover,
+.rate:not(:checked) > label:hover ~ label {
+  color: #deb217;
+}
+
+.rate > input:checked + label:hover,
+.rate > input:checked + label:hover ~ label,
+.rate > input:checked ~ label:hover,
+.rate > input:checked ~ label:hover ~ label,
+.rate > label:hover ~ input:checked ~ label {
+  color: #c59b08;
+}
diff --git a/src/main/webapp/content/scss/vendor.scss b/src/main/webapp/content/scss/vendor.scss
index 010966322e7b2069f70dad2e6ad910c192315e10..e0ddc497662e4c19c3e99b7c1baa17042f35d550 100644
--- a/src/main/webapp/content/scss/vendor.scss
+++ b/src/main/webapp/content/scss/vendor.scss
@@ -5,9 +5,9 @@ put Sass variables here:
 eg $input-color: red;
 ****************************/
 // Override Bootstrap variables
-@import 'bootstrap-variables';
+//@import 'bootstrap-variables';
 // Import Bootstrap source files from node_modules
-@import '~bootstrap/scss/bootstrap';
+//@import '~bootstrap/scss/bootstrap';
 
 /* jhipster-needle-scss-add-vendor JHipster will add new css style */
 @import "~@ng-select/ng-select/themes/default.theme.css";
diff --git a/src/main/webapp/favicon_jhipster.ico b/src/main/webapp/favicon_jhipster.ico
deleted file mode 100755
index 4179874f53b3c3b0ba9e2a401412f814ab6296bd..0000000000000000000000000000000000000000
Binary files a/src/main/webapp/favicon_jhipster.ico and /dev/null differ
diff --git a/src/main/webapp/i18n/de/exercise.json b/src/main/webapp/i18n/de/exercise.json
new file mode 100644
index 0000000000000000000000000000000000000000..8e1ff5ee81203653c9588d9bbd762660d2307400
--- /dev/null
+++ b/src/main/webapp/i18n/de/exercise.json
@@ -0,0 +1,71 @@
+{
+  "exercise": {
+    "details": {
+      "details": "Details",
+      "rating": "Trefferqualität",
+      "bookmark": "Merken",
+      "hitDetails": "Suchergebnis Details",
+      "allExercises": "Alle Aufgaben für diesen Kurs anzeigen",
+      "git": "GitLab öffnen",
+      "views": "Anzahl der Aufrufe",
+      "downloads": "Anzahl der Downloads"
+    },
+    "metadata": {
+      "metadata": "Metadaten",
+      "programmingLanguageSingular": "Programmiersprache",
+      "programmingLanguagesPlural": "Programmiersprachen",
+      "languageSingular": "Sprache",
+      "languagesPlural": "Sprachen",
+      "creatorSingular": "Ersteller",
+      "creatorsPlural": "Ersteller",
+      "contributorsSingular": "Mitwirkender",
+      "contributorsPlural": "Mitwirkende",
+      "publisherSingular": "Publisher",
+      "publisherPlural": "Publisher",
+      "lastUpdate": "Letzte Aktualisierung",
+      "license": "Lizenz",
+      "timeRequired": "Benötigte Zeit",
+      "requires": "Voraussetzungen",
+      "audience": "Zielgruppe",
+      "collectionContent": "Samlungsinhalt",
+      "deprecated": "Veraltet",
+      "difficulty": "Schwierigkeit",
+      "simple": "Simple",
+      "medium": "Mittel",
+      "advanced": "Fortgeschritten",
+      "educationLevel": "Bildungsniveau",
+      "keywords": "Schlüsselwörter",
+      "format": "Format",
+      "status": "Status",
+      "type": "Typ",
+      "version": "Version",
+      "metadataVersion": "Metadata Version",
+      "collection": "Aufgabensammlung",
+      "exercise": "Aufgabe",
+      "programming exercise": "Programmieraufgabe",
+      "other": "Andere",
+      "COLLECTION": "Aufgabensammlung",
+      "EXERCISE": "Aufgabe",
+      "PROGRAMMING_EXERCISE": "Programmieraufgabe",
+      "OTHER": "Andere",
+      "structure": "Struktur",
+      "atomic": "Atomar",
+      "networked": "Vernetzt",
+      "hierarchical": "Hierarchisch",
+      "linear": "Linear",
+      "final": "Final",
+      "draft": "Entwurf",
+      "revised": "Ãœberarbeitet",
+      "unavailable": "Nicht verfügbar"
+    },
+    "export": {
+      "export": "Exportieren",
+      "artemis": "Artemis",
+      "latex": "LaTeX",
+      "download": "Herunterladen"
+    },
+    "open": "Aufgabe öffnen",
+    "more": "Mehr ...",
+    "close": "Schließen"
+  }
+}
diff --git a/src/main/webapp/i18n/de/global.json b/src/main/webapp/i18n/de/global.json
index 0593181d6cdef4c05164298b2e124ffc67fb0e13..a032e859e2e32ead9d490d0845e60017697201dd 100644
--- a/src/main/webapp/i18n/de/global.json
+++ b/src/main/webapp/i18n/de/global.json
@@ -1,21 +1,26 @@
 {
   "global": {
-    "title": "Sharing Plattform",
+    "title": "CodeAbility Austauschplattform",
     "browsehappy": "Sie benutzen einen <strong>veralteten</strong> Browser. Bitte <a href=\"http://browsehappy.com/?locale=de/\">aktualisieren Sie Ihren Browser</a>, um die Benutzerfreundlichkeit zu erhöhen.",
     "menu": {
       "home": "Startseite",
       "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)",
       "entities": {
         "main": "Entitäten",
+        "statistics": "Statistics",
         "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)"
       },
       "account": {
         "main": "Zugang",
         "settings": "Einstellungen",
+        "settingsDescription": "Ihre Nutzereinstellungen bearbeiten",
         "password": "Passwort",
+        "passwordDescription": "Passwort ändern",
         "sessions": "Sitzungen",
         "login": "Anmelden",
+        "loginViaGitLab": "Login mit GitLab",
         "logout": "Abmelden",
+        "logoutDescription": "Beenden der Sitzung",
         "register": "Registrierung"
       },
       "admin": {
@@ -46,6 +51,11 @@
       "email.label": "Email Adresse",
       "email.placeholder": "Ihre Email Adresse"
     },
+    "footer": {
+      "imprint": "Impressum",
+      "about": "Ãœber codeAbility",
+      "privacy": "Datenschutz"
+    },
     "messages": {
       "info": {
         "sharing.platform": "Die CodeAbility Sharing Plattform stellt eine Infrastruktur für den Austausch von Lerninhalten im Bereich Programmierung bereit. Die Inhalte umfassen Programmierlernaufgaben, Vorlesungsunterlagen und Linksammlungen zu allen Themen des Lernens von Programmiersprachen. Damit Nutzer und Nutzerinnen einfach und schnell für sie relevante Materialen finden können, wird diese Suchmaschine für die Suche von Unterlagen, welche in der Sharing Plattform bereitgestellt werden, angeboten. Durch diverse Filtermöglichkeiten wie beispielsweise Lizenzen, Programmiersprachen und Autoren können die Suchergebnisse weiter eingeschränkt werden.",
@@ -138,6 +148,5 @@
       "could.not.extract": "Could not extract file",
       "not.image": "File was expected to be an image but was found to be \"{{ fileType }}\""
     }
-  },
-  "footer": "Mehr Informationen finden Sie <a href=\"https://sharing-codeability.uibk.ac.at/static/SharingCodeAbility.html\">hier</a>!"
+  }
 }
diff --git a/src/main/webapp/i18n/de/home.json b/src/main/webapp/i18n/de/home.json
index 648c23dd77edba6c90bc838cec18c2bc591c4fc8..fcf28fe0653da371cff09cd9bb9317fea1e6b57a 100644
--- a/src/main/webapp/i18n/de/home.json
+++ b/src/main/webapp/i18n/de/home.json
@@ -1,7 +1,8 @@
 {
   "home": {
-    "title": "Sharing Plattform!",
-    "subtitle": "Dies ist Ihre Hauptseite",
+    "title": "<img src=\"/content/img/logo-top.png\" alt=\"codeAbility\" width=\"168px\"/>Gemeinsame Bereitstellung von Lernresourcen",
+    "subtitle": "Finde interessante Inhalte",
+    "teaser": "<p>Die CodeAbility Sharing Plattform stellt eine Infrastruktur f&uuml;r den Austausch von Lerninhalten im Bereich Programmierung bereit.</p>\n      <p>Die Inhalte umfassen Programierlernaufgaben, Vorlesungsunterlagen und Linksammlungen zu allen Themen des Lernens von Programmiersprachen.</p>",
     "logged": {
       "message": "Sie sind als Benutzer \"{{username}}\" angemeldet."
     },
diff --git a/src/main/webapp/i18n/de/oauth2.json b/src/main/webapp/i18n/de/oauth2.json
new file mode 100644
index 0000000000000000000000000000000000000000..67428aceb63ec3bf74af77f06ec6fd3bcc682a3a
--- /dev/null
+++ b/src/main/webapp/i18n/de/oauth2.json
@@ -0,0 +1,8 @@
+{
+  "oauth2": {
+    "gitlabOidc": {
+      "icon": "/content/img/gitLab.png",
+      "text": "Mit gitLab anmelden"
+    }
+  }
+}
diff --git a/src/main/webapp/i18n/de/search.json b/src/main/webapp/i18n/de/search.json
index d0f035ca728ce807d23929676e9471f8b4d125a6..692e3352f6356fce787d93dd42af098e10048e54 100644
--- a/src/main/webapp/i18n/de/search.json
+++ b/src/main/webapp/i18n/de/search.json
@@ -2,7 +2,19 @@
   "search": {
     "title": "Suche",
     "tabTitle": "Sharing Plattform - Suche",
-    "usage": "Die Suchmaske erlaubt es aufbauend auf diversen Suchkriterien (z.B. Volltextsuche, Programmiersprachen, Schlüsselwörter usw.) nach Volltexten in der Sharing Plattform zu suchen. In der Suchmaske für Volltexte können boolsche Operatoren verwendet werden. Abgesehen von der Volltextsuche sind keine weitern Felder angegeben werden.",
+    "usage": "Die Suchmaske erlaubt es aufbauend auf diversen Suchkriterien (z.B. Volltextsuche, Programmiersprachen, Schlüsselwörter usw.) nach Volltexten in der Sharing Plattform zu suchen. In der Suchmaske für Volltexte können boolsche Operatoren verwendet werden. Abgesehen von der Volltextsuche müssen keine weiteren Felder angegeben werden.",
+    "help": {
+    	"fulltext": "dieses Feld sucht im Titel, der Beschreibung und den Schlüsselwörtern (Präfixsuche)",
+    	"programmingLanguage": "Bitte geben die gewünschte Programmiersprache ein (Präfixsuche)",
+    	"keywords": "Suche nach relevanten Schlüsselwörtern (Präfixsuche)",
+    	"authorContritbutors": "Suche nach Autoren (und Beitragenden) (Präfixsuche)",
+    	"license": "Suche nach Lizenztyp (Präfixsuche)"
+    },
+    "noResults": "Keine Suchergebnisse gefunden.",
+    "noResultsAnonymous": "Sie finden vielleicht mehr Resultate, wenn Sie sich anmelden",
+    "numberResults": "Wir haben ungefähr {{ length }} Treffer gefunden.",
+    "showUsage": "Hilfe anzeigen",
+    "hideUsage": "Hilfe ausblenden",
     "metadata": {
       "filter": "Suchfilter",
       "clearLocalFilters": "Alle löschen",
@@ -23,7 +35,7 @@
       "programmingLanguage": "Programmiersprachen",
       "keywords": "Schlüsselwörter",
       "license": "Lizenz",
-      "author": "Autoren*Autorinnen"
+      "author": "AutorInnen"
     }
   }
 }
diff --git a/src/main/webapp/i18n/de/statistics.json b/src/main/webapp/i18n/de/statistics.json
new file mode 100644
index 0000000000000000000000000000000000000000..4493d47fdfecdca1676edd2025a317f50ad709a8
--- /dev/null
+++ b/src/main/webapp/i18n/de/statistics.json
@@ -0,0 +1,25 @@
+{
+  "gitsearchApp": {
+    "statistics": {
+      "home": {
+        "title": "Statistics",
+        "createLabel": "Statistics erstellen",
+        "createOrEditLabel": "Statistics erstellen oder bearbeiten",
+        "search": "Suche nach Statistics",
+        "notFound": "No Statistics found"
+      },
+      "created": "Statistics erstellt mit ID {{ param }}",
+      "updated": "Statistics aktualisiert mit ID {{ param }}",
+      "deleted": "Statistics gelöscht mit ID {{ param }}",
+      "delete": {
+        "question": "Soll Statistics {{ id }} wirklich dauerhaft gelöscht werden?"
+      },
+      "detail": {
+        "title": "Statistics"
+      },
+      "views": "Views",
+      "downloads": "Downloads",
+      "exerciseID": "Exercise ID"
+    }
+  }
+}
diff --git a/src/main/webapp/i18n/en/exercise.json b/src/main/webapp/i18n/en/exercise.json
new file mode 100644
index 0000000000000000000000000000000000000000..f424b6dc47809a11cbcdd00a99e73cd1adb7f701
--- /dev/null
+++ b/src/main/webapp/i18n/en/exercise.json
@@ -0,0 +1,71 @@
+{
+  "exercise": {
+    "details": {
+      "details": "Details",
+      "rating": "Hit quality",
+      "bookmark": "Bookmark",
+      "hitDetails": "Show hit details",
+      "allExercises": "Show all exercises for this course",
+      "git": "Open GitLab",
+      "views": "Number of views",
+      "downloads": "Number of downloads"
+    },
+    "metadata": {
+      "metadata": "Meta data",
+      "programmingLanguageSingular": "Programming Language",
+      "programmingLanguagesPlural": "Programming Languages",
+      "languageSingular": "Language",
+      "languagesPlural": "Languages",
+      "creatorSingular": "Creator",
+      "creatorsPlural": "Creators",
+      "contributorsSingular": "Contributor",
+      "contributorsPlural": "Contributors",
+      "publisherSingular": "Publisher",
+      "publisherPlural": "Publishers",
+      "lastUpdate": "Last update",
+      "license": "License",
+      "timeRequired": "Time required",
+      "requires": "Requirements",
+      "audience": "Audience",
+      "collectionContent": "Content",
+      "deprecated": "Deprecated",
+      "difficulty": "Difficulty",
+      "simple": "Simple",
+      "medium": "Medium",
+      "advanced": "Advanced",
+      "educationLevel": "Education level",
+      "keywords": "Keywords",
+      "format": "Format",
+      "status": "Status",
+      "type": "Type",
+      "version": "Version",
+      "metadataVersion": "Metadata Version",
+      "collection": "Collection",
+      "exercise": "Exercise",
+      "programming exercise": "Programming Exercise",
+      "other": "Other",
+      "COLLECTION": "Collection",
+      "EXERCISE": "Exercise",
+      "PROGRAMMING_EXERCISE": "Programming Exercise",
+      "OTHER": "Other",
+      "structure": "Structure",
+      "atomic": "Atomic",
+      "networked": "Networked",
+      "hierarchical": "Hierarchical",
+      "linear": "Linear",
+      "final": "Final",
+      "draft": "Draft",
+      "revised": "Revised",
+      "unavailable": "Unavailable"
+    },
+    "export": {
+      "export": "Export",
+      "artemis": "Artemis",
+      "latex": "LaTeX",
+      "download": "Download"
+    },
+    "open": "Open Exercise",
+    "more": "More  ...",
+    "close": "Close"
+  }
+}
diff --git a/src/main/webapp/i18n/en/global.json b/src/main/webapp/i18n/en/global.json
index 3f75d83060156bb62083f776d32a23d3a6e5c470..cc920d1e8defaa3ab3344ac9280187b3304aa21f 100644
--- a/src/main/webapp/i18n/en/global.json
+++ b/src/main/webapp/i18n/en/global.json
@@ -1,21 +1,26 @@
 {
   "global": {
-    "title": "Sharing Platform",
+    "title": "CodeAbility Sharing Platform",
     "browsehappy": "You are using an <strong>outdated</strong> browser. Please <a href=\"http://browsehappy.com/?locale=en\">upgrade your browser</a> to improve your experience.",
     "menu": {
       "home": "Home",
       "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)",
       "entities": {
         "main": "Entities",
+        "statistics": "Statistics",
         "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)"
       },
       "account": {
         "main": "Account",
         "settings": "Settings",
+        "settingsDescription": "Manage your user settings",
         "password": "Password",
+        "passwordDescription": "Change your password",
         "sessions": "Sessions",
         "login": "Sign in",
+        "loginViaGitLab": "Sign in via GitLab",
         "logout": "Sign out",
+        "logoutDescription": "Quit session.",
         "register": "Register"
       },
       "admin": {
@@ -46,6 +51,11 @@
       "email.label": "Email",
       "email.placeholder": "Your email"
     },
+    "footer": {
+      "imprint": "Imprint",
+      "about": "About codeAbility",
+      "privacy": "Privacy"
+    },
     "messages": {
       "info": {
         "sharing.platform": "The CodeAbility Sharing Platform provides an infrastructure that enables the exchange of learning content in the field of programming. The content includes programming exercises, lecture materials, and collections of links on all topics related to the acquisition of programming skills. This search engine provides a quick and easy way for users to search for content published on the sharing platform. The search results can be further restricted by various filter options such as licenses, programming languages, and authors.",
@@ -139,6 +149,5 @@
       "could.not.extract": "Could not extract file",
       "not.image": "File was expected to be an image but was found to be \"{{ fileType }}\""
     }
-  },
-  "footer": "More information can be found <a href=\"https://sharing-codeability.uibk.ac.at/static/SharingCodeAbility.html\">here</a>!"
+  }
 }
diff --git a/src/main/webapp/i18n/en/home.json b/src/main/webapp/i18n/en/home.json
index 7667d475809767e079d3fbc7be9c1cdf89a30cbb..c15f81ab25aadc7cbe618b0a2a9cbb3e1533a953 100644
--- a/src/main/webapp/i18n/en/home.json
+++ b/src/main/webapp/i18n/en/home.json
@@ -1,7 +1,8 @@
 {
   "home": {
-    "title": "Sharing Platform",
-    "subtitle": "This is your homepage",
+    "title": "<img src=\"/content/img/logo-top.png\" alt=\"codeAbility\" width=\"168px\"/>Joint Ressourcing of Teaching Material",
+    "subtitle": "The CodeAbility Sharing Plattform is ready",
+    "teaser": "<p>The  CodeAbility Sharing Plattform offers an infrastructure for the open exchange of programming teaching content.</p>\n      <p>This comprises programming exercises, lecture scripts and slides, and link collections to all aspects of programming education.</p>",
     "logged": {
       "message": "You are logged in as user \"{{username}}\"."
     },
diff --git a/src/main/webapp/i18n/en/oauth2.json b/src/main/webapp/i18n/en/oauth2.json
new file mode 100644
index 0000000000000000000000000000000000000000..2a6338b01f43e08fda9a59c84a9224d1a7182f52
--- /dev/null
+++ b/src/main/webapp/i18n/en/oauth2.json
@@ -0,0 +1,8 @@
+{
+  "oauth2": {
+    "gitlabOidc": {
+      "icon": "/content/img/gitLab.png",
+      "text": "Login with gitLab"
+    }
+  }
+}
diff --git a/src/main/webapp/i18n/en/search.json b/src/main/webapp/i18n/en/search.json
index d7e3fc2ce1a9ef402dbc479b88300e17d44a53a1..3ef46b39637cad4fb9866734d3223df683755156 100644
--- a/src/main/webapp/i18n/en/search.json
+++ b/src/main/webapp/i18n/en/search.json
@@ -3,6 +3,18 @@
     "title": "Search",
     "tabTitle": "Sharing Platform - Search",
     "usage": "The search mask allows you to search for full texts in the sharing platform based on various search criteria (e.g. full text search, programming languages, keywords, etc.). Boolean operators can be used in the search mask for full texts. Apart from the full text search, no further fields are to be specified.",
+    "help": {
+    	"fulltext": "this field searches in all the title, the decription, and the keywords (prefix search)",
+    	"programmingLanguage": "please enter the programming language (prefix search)",
+    	"keywords": "please enter relevant keywords (prefix search)",
+    	"authorContritbutors": "please enter authors or contributors (prefix search)",
+    	"license": "please enter a license type (prefix search)"
+    },
+    "noResults": "No results found.",
+    "noResultsAnonymous": "You may find more results, if logged in",
+    "numberResults": "We have found about {{ length }} hit(s).",
+    "showUsage": "Show help",
+    "hideUsage": "Hide help",
     "metadata": {
       "filter": "Search filter",
       "clearLocalFilters": "Clear all",
diff --git a/src/main/webapp/i18n/en/statistics.json b/src/main/webapp/i18n/en/statistics.json
new file mode 100644
index 0000000000000000000000000000000000000000..981c34551232aed29c80b0a2919ca6341c831bbc
--- /dev/null
+++ b/src/main/webapp/i18n/en/statistics.json
@@ -0,0 +1,25 @@
+{
+  "gitsearchApp": {
+    "statistics": {
+      "home": {
+        "title": "Statistics",
+        "createLabel": "Create a new Statistics",
+        "createOrEditLabel": "Create or edit a Statistics",
+        "search": "Search for Statistics",
+        "notFound": "No Statistics found"
+      },
+      "created": "A new Statistics is created with identifier {{ param }}",
+      "updated": "A Statistics is updated with identifier {{ param }}",
+      "deleted": "A Statistics is deleted with identifier {{ param }}",
+      "delete": {
+        "question": "Are you sure you want to delete Statistics {{ id }}?"
+      },
+      "detail": {
+        "title": "Statistics"
+      },
+      "views": "Views",
+      "downloads": "Downloads",
+      "exerciseID": "Exercise ID"
+    }
+  }
+}
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
index 33fdab565a298f95490f7acce8bd711c8284d652..69d1fe7c85f5b348a87310e3f64604494dda14ea 100644
--- a/src/main/webapp/index.html
+++ b/src/main/webapp/index.html
@@ -4,14 +4,26 @@
     <base href="/" />
     <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <title>GitSearchV2</title>
-    <meta name="description" content="Description for GitSearchV2">
+    <title>Codeability Search Plattform</title>
+    <meta name="description" content="Codeability Search Plattform">
     <meta name="google" content="notranslate">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta name="theme-color" content="#000000">
     <link rel="icon" href="favicon.ico" />
     <link rel="manifest" href="manifest.webapp" />
+    <!--  old -->
     <link rel="stylesheet" href="content/css/loading.css">
+    <!-- Custom CSS -->	
+    <!-- 
+	<link rel="stylesheet" href="content/css/navbar.css">
+	 -->
+	<!-- jQuery library -->
+	<script src="content/js/jquery3.5.1.min.js"></script>
+	<!-- Popper JS -->
+	<script src="content/js/popper1.16.0.min.js"></script>
+	<!-- Latest compiled JavaScript -->
+	<script src="content/js/bootstrap4.5.2.min.js"></script>
+    
     <!-- jhipster-needle-add-resources-to-root - JHipster will add new resources here -->
 </head>
 <body>
diff --git a/src/main/webapp/swagger-ui/index.html b/src/main/webapp/swagger-ui/index.html
index ca4e6a047ec6faa5373b797a6e78b6b97305773e..c2b2f19c637cbd0dc23915ef4bfe17b6180113af 100644
--- a/src/main/webapp/swagger-ui/index.html
+++ b/src/main/webapp/swagger-ui/index.html
@@ -9,7 +9,8 @@
 </head>
 
 <body>
-<div id="swagger-ui"></div>
+<div id="swagger-uiXXX"></div>
+
 
 <script src="./swagger-ui-bundle.js"></script>
 <script src="./swagger-ui-standalone-preset.js"></script>
@@ -20,7 +21,7 @@
     window.onload = function () {
 
 
-        var urls = [];
+        var urls = ["http://localhost:8080/v2/api-docs"];
         axios.get("/swagger-resources").then(function (response) {
             response.data.forEach(function (resource) {
                 urls.push({"name": resource.name, "url": resource.location});
@@ -38,6 +39,7 @@
                 deepLinking: true,
                 filter: true,
                 layout: "StandaloneLayout",
+                schemes: ["http"],
                 withCredentials: true,
                 presets: [
                     SwaggerUIBundle.presets.apis,
diff --git a/src/test/java/at/ac/uibk/gitsearch/config/ElasticsearchTestConfiguration.java b/src/test/java/at/ac/uibk/gitsearch/config/ElasticsearchTestConfiguration.java
index 5ac10d4445f90bd124044a3191d216b249f4c7e0..e41c9f3570065687992f4fb53fe3b644e4f36144 100644
--- a/src/test/java/at/ac/uibk/gitsearch/config/ElasticsearchTestConfiguration.java
+++ b/src/test/java/at/ac/uibk/gitsearch/config/ElasticsearchTestConfiguration.java
@@ -1,13 +1,11 @@
 package at.ac.uibk.gitsearch.config;
 
+import java.io.File;
+
 import org.assertj.core.util.Files;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties;
-import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-import java.io.File;
 
 @Configuration
 public class ElasticsearchTestConfiguration {
diff --git a/src/test/java/at/ac/uibk/gitsearch/config/WebConfigurerTest.java b/src/test/java/at/ac/uibk/gitsearch/config/WebConfigurerTest.java
index 05b5d51e51d50b8a15a3213f4233ba9e131256cc..bc853407e5293ab4cecda38637e7af5818853ca3 100644
--- a/src/test/java/at/ac/uibk/gitsearch/config/WebConfigurerTest.java
+++ b/src/test/java/at/ac/uibk/gitsearch/config/WebConfigurerTest.java
@@ -1,8 +1,30 @@
 package at.ac.uibk.gitsearch.config;
 
-import io.github.jhipster.config.JHipsterConstants;
-import io.github.jhipster.config.JHipsterProperties;
-import io.github.jhipster.web.filter.CachingHttpHeadersFilter;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+
 import org.h2.server.web.WebServlet;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -13,18 +35,8 @@ import org.springframework.mock.web.MockServletContext;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
-import javax.servlet.*;
-import java.io.File;
-import java.util.*;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.*;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import io.github.jhipster.config.JHipsterConstants;
+import io.github.jhipster.config.JHipsterProperties;
 
 /**
  * Unit tests for the {@link WebConfigurer} class.
diff --git a/src/test/java/at/ac/uibk/gitsearch/domain/StatisticsTest.java b/src/test/java/at/ac/uibk/gitsearch/domain/StatisticsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1aa9897f0b6bbbec493514b5663f9c6d9c095e0e
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/domain/StatisticsTest.java
@@ -0,0 +1,22 @@
+package at.ac.uibk.gitsearch.domain;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import at.ac.uibk.gitsearch.web.rest.TestUtil;
+
+public class StatisticsTest {
+
+    @Test
+    public void equalsVerifier() throws Exception {
+        TestUtil.equalsVerifier(Statistics.class);
+        Statistics statistics1 = new Statistics();
+        statistics1.setId(1L);
+        Statistics statistics2 = new Statistics();
+        statistics2.setId(statistics1.getId());
+        assertThat(statistics1).isEqualTo(statistics2);
+        statistics2.setId(2L);
+        assertThat(statistics1).isNotEqualTo(statistics2);
+        statistics1.setId(null);
+        assertThat(statistics1).isNotEqualTo(statistics2);
+    }
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java b/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..487c585b1ebc02063e776f9766765b37a294f39e
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/repository/search/MetaDataRepositoryTests.java
@@ -0,0 +1,65 @@
+package at.ac.uibk.gitsearch.repository.search;
+
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.hasProperty;
+import static org.hamcrest.Matchers.is;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.node.NodeValidationException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.repository.search.testESService.ElasticSearchTestServerConfiguration;
+import at.ac.uibk.gitsearch.service.dto.AutoCompleteEntry;
+
+@SpringBootTest(classes = GitsearchApp.class)
+
+public class MetaDataRepositoryTests {
+	
+	private static final Logger LOGGER = LogManager.getLogger(MetaDataRepositoryTests.class);
+
+	@Autowired
+	RestHighLevelClient elasticsearchTestClient;
+	
+	@BeforeAll
+	public static void setUpESServer() throws IOException, NodeValidationException {
+		ElasticSearchTestServerConfiguration.getTestInstance().startTestNode();
+	}
+	
+	
+	@BeforeEach
+	public void setUpIndex() {
+	}
+	@Autowired
+	private MetaDataRepository metaDataRepository;
+	
+	// @Test
+	// public void testKeywordAutocompletion() throws IOException {
+	// 	final List<AutoCompleteEntry> keywordsAutoComplete = metaDataRepository.getKeywordsAutoComplete("Jav");
+	// 	org.junit.Assert.assertThat(keywordsAutoComplete, contains(hasProperty("target",is("Java"))));
+	// }
+
+	// @Test
+	// public void testCreatorAutocompletion() throws IOException {
+	// 	final List<AutoCompleteEntry> creatorAutoComplete = metaDataRepository.getCreatorAutoComplete("Pod");
+	// 	org.junit.Assert.assertThat(creatorAutoComplete, contains(hasProperty("target",is("Stefan Podlipnig"))));
+	// 	final List<AutoCompleteEntry> creatorAutoComplete2 = metaDataRepository.getCreatorAutoComplete("Po");
+	// 	org.junit.Assert.assertThat(creatorAutoComplete2, contains(hasProperty("target",is("Stefan Podlipnig"))));
+	// }
+	
+	// @Test
+	// public void testContributorAutocompletion() throws IOException {
+	// 	final List<AutoCompleteEntry> contributorAutoComplete = metaDataRepository.getContributorAutoComplete("Bast");
+	// 	org.junit.Assert.assertThat(contributorAutoComplete, contains(hasProperty("target",is("Daniel Bastta"))));
+	// }
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepositoryMockConfiguration.java b/src/test/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepositoryMockConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..0be41bb6ec747d2b4726958c152a50e4237db509
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/repository/search/StatisticsSearchRepositoryMockConfiguration.java
@@ -0,0 +1,16 @@
+package at.ac.uibk.gitsearch.repository.search;
+
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Configure a Mock version of {@link StatisticsSearchRepository} to test the
+ * application without starting Elasticsearch.
+ */
+@Configuration
+public class StatisticsSearchRepositoryMockConfiguration {
+
+    @MockBean
+    private StatisticsSearchRepository mockStatisticsSearchRepository;
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestServerConfiguration.java b/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestServerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd16514800b3609dbfe29a110ff4c0e6aaa33bfa
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/repository/search/testESService/ElasticSearchTestServerConfiguration.java
@@ -0,0 +1,302 @@
+package at.ac.uibk.gitsearch.repository.search.testESService;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.http.HttpHost;
+import org.apache.http.util.EntityUtils;
+import org.apache.logging.log4j.LogManager;
+import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
+import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
+import org.elasticsearch.action.index.IndexRequest;
+import org.elasticsearch.action.search.SearchRequest;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.Response;
+import org.elasticsearch.client.RestClient;
+import org.elasticsearch.client.RestClientBuilder;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.node.InternalSettingsPreparer;
+import org.elasticsearch.node.Node;
+import org.elasticsearch.node.NodeValidationException;
+import org.elasticsearch.plugins.Plugin;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
+import org.elasticsearch.transport.Netty4Plugin;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.context.event.ApplicationStartingEvent;
+import org.springframework.context.event.ContextStartedEvent;
+import org.springframework.context.event.ContextStoppedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.data.elasticsearch.client.NodeClientFactoryBean;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+
+import at.ac.uibk.gitsearch.repository.search.SearchRepositoryConstants;
+
+public class ElasticSearchTestServerConfiguration {
+	
+	private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ElasticSearchTestServerConfiguration.class);
+    private TestESNode testNode;
+
+    private ElasticSearchTestServerConfiguration()  {
+    }
+    
+    private static ElasticSearchTestServerConfiguration instance = null;
+    
+    public static  ElasticSearchTestServerConfiguration getTestInstance()  {
+    	if (instance==null)
+    		instance = new ElasticSearchTestServerConfiguration();
+    	return instance;
+    	
+    }
+
+	public synchronized void startTestNode() throws IOException, NodeValidationException {
+		if(testNode==null) {
+			LOGGER.info("Starting ES Test Node");
+			try {
+			File f = File.createTempFile("tobedeleted", "txt");
+			Settings s =  Settings.builder().put("http.port", "29200")
+						 .put("transport.port", "29300")
+			             .put("node.name", "esSharingTest")
+	                     .put("transport.type", "netty4")
+		                 .put("http.type", "netty4")
+			             .put("path.home", f.getParent() + File.separator + "esTest").build();
+			f.delete();
+			
+			NodeClientFactoryBean fb = new NodeClientFactoryBean(true);
+			
+			testNode = new TestESNode(s, Collections.singleton(Netty4Plugin.class)) {
+				
+			}; 
+			testNode.start();
+			
+			setUpMetaDataIndex();
+			setUpContent();
+			LOGGER.info("Started ES Test Node");
+			} catch (Throwable t) {
+				LOGGER.info("ES Test Node crashed: ", t);
+			}
+		} else {
+			LOGGER.info("Node already started");
+		}
+	}
+	
+	private void setUpMetaDataIndex() throws IOException {
+		
+		RestTemplate restTemplate = new RestTemplate();
+		HttpHeaders headers = new HttpHeaders();
+	    headers.setContentType(MediaType.APPLICATION_JSON);
+
+	    try {
+	    	restTemplate.delete("http://localhost:29200/_all");
+		} catch (Throwable e) {
+			LOGGER.info("Deletion of previous index failed: {}!", e.getMessage());
+		}
+
+	    
+	    List<String> indices2 = getIndices();
+
+	    indices2.forEach((s) -> {
+	    	LOGGER.info("Found index {}", s);
+		    try {
+				restTemplate.delete("http://localhost:29200/" + s, String.class); // just in case, delete old index
+			} catch (RestClientException e) {
+				LOGGER.info("Deletion of previous index failed: {}!", e.getMessage());
+			}
+	    });
+	    
+	    
+	    
+	    
+//	    GetIndexRequest request2 = new GetIndexRequest();
+//	 
+//	    GetIndexResponse response = client.indices().get(request2, RequestOptions.DEFAULT);
+//	    String[] indices = response.getIndices();
+	    
+	    
+	    
+	    HttpEntity<String> request = 
+	    	      new HttpEntity<String>(getMetaDataConfigDefinition(), headers);
+	    
+	    
+	    
+	    restTemplate.put("http://localhost:29200/idx_metadata_1", request, String.class);
+	    
+	}
+
+	private List<String> getIndices() throws IOException, JsonProcessingException, JsonMappingException {
+		List<String> indices2 = null;
+	    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 29200)));
+
+		RestTemplate restTemplate = new RestTemplate();
+		HttpHeaders headers = new HttpHeaders();
+	    headers.setContentType(MediaType.APPLICATION_JSON);
+
+	    
+	    RestClient restClient = client.getLowLevelClient();
+	    Response response2 = null;
+	    try {
+	        response2 = restClient.performRequest("GET", "/_cat/indices?v&format=json");
+	    } catch (IOException e) {
+	        LOGGER.warn(e.toString(), e);
+	    }
+
+	    // parse the JSON response
+	    List<HashMap<String, String>> list = null;
+	    if (response2 != null) {
+	    	ObjectMapper mapper = new ObjectMapper();
+	        String rawBody = EntityUtils.toString(response2.getEntity());
+	        TypeReference<List<HashMap<String, String>>> typeRef = new TypeReference<List<HashMap<String, String>>>() {};
+	        list = mapper.readValue(rawBody, typeRef);
+	    }
+
+	    // get the index names
+	    if (list != null) {
+	        indices2 = list.stream()
+	            .map(x -> x.get("index"))
+	            .collect(Collectors.toList());
+	    }
+		return indices2;
+	}
+	
+	private void setUpContent() throws IOException {
+		
+
+		RestTemplate restTemplate = new RestTemplate();
+		HttpHeaders headers = new HttpHeaders();
+	    headers.setContentType(MediaType.APPLICATION_JSON);
+	    
+	    int contentCount = 0;
+	    
+	    while(true) {
+	    	contentCount++;
+		    final String content = getContent(contentCount);
+		    if(content==null) break;
+			HttpEntity<String> request = 
+		    	      new HttpEntity<String>(content, headers);
+		    restTemplate.put("http://localhost:29200/metadata/_doc/"+contentCount, request, String.class);
+	    }
+	    
+	    waitForCleanStartUp(contentCount-1);
+	}
+	
+	/**
+	 * we have to wait until correct startup of content indexing?
+	 * @param minExpectedHits
+	 */
+	private void waitForCleanStartUp(int minExpectedHits) {
+	    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 29200)));
+	    int tryCount = 0;
+	    while(tryCount < 10) {
+	    	tryCount++;
+			SearchRequest searchRequest = new SearchRequest(SearchRepositoryConstants.INDEX_METADATA);
+			BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
+			SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
+			sourceBuilder.query(queryBuilder);
+			SearchResponse searchResponse;
+			try {
+				searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
+				if(searchResponse.getHits().getTotalHits() >= minExpectedHits) {
+					LOGGER.info("Metadata index is up with {} hits after {} trials", searchResponse.getHits().getTotalHits(), tryCount);
+					return;
+				} else {
+					LOGGER.warn("Metadata index only {} hits up after {} trials", searchResponse.getHits().getTotalHits(), tryCount);
+				}
+			} catch (IOException e) {
+				LOGGER.warn("Metadata index startup exception", e);
+			}
+			try {
+				Thread.sleep(1000L);
+			} catch (InterruptedException e) {
+				LOGGER.debug("Never mind: {}", e.getMessage());
+			}
+	    }
+	}
+	
+	private String getMetaDataConfigDefinition() throws IOException {
+		InputStream metaDataStream = this.getClass().getResourceAsStream("../testData/es_metadata.schema.json");
+		ByteSource byteSource = new ByteSource() {
+			
+			@Override
+			public InputStream openStream() throws IOException {
+				return metaDataStream;
+			}
+		};
+		String text = byteSource.asCharSource(Charsets.UTF_8).read();
+		return text;
+	}
+	
+	private String getContent(int i) throws IOException {
+		InputStream metaDataStream = this.getClass().getResourceAsStream("../testData/content"+i+".json");
+		if(metaDataStream==null) return null;
+		ByteSource byteSource = new ByteSource() {
+			
+			@Override
+			public InputStream openStream() throws IOException {
+				return metaDataStream;
+			}
+		};
+		String text = byteSource.asCharSource(Charsets.UTF_8).read();
+		return text;
+	}
+
+    
+    @EventListener
+    public void onApplicationEvent(ApplicationStartingEvent event) {
+		try {
+			startTestNode();
+		} catch (IOException | NodeValidationException e) {
+			LOGGER.error("Cannot startup TestNode", e);
+		}
+    }
+
+	@EventListener
+    public void onApplicationEvent(ContextStartedEvent event) {
+		try {
+			startTestNode();
+		} catch (IOException | NodeValidationException e) {
+			LOGGER.error("Cannot startup TestNode", e);
+		}
+    }
+    
+    @EventListener
+    public void onApplicationEvent(ContextStoppedEvent event) {
+		try {
+			testNode.close();
+		} catch (IOException  e) {
+			LOGGER.error("Cannot startup TestNode", e);
+		}
+    }
+
+    public static class TestESNode extends Node {
+        public TestESNode(Settings preparedSettings, Collection<Class<? extends Plugin>> classpathPlugins) {
+            super(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), classpathPlugins, true);
+        }
+
+		@Override
+		protected void registerDerivedNodeNameWithLogger(String nodeName) {
+			LogManager.getLogger(nodeName);
+		}
+    }
+    
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/PluginManagerActionIT.java b/src/test/java/at/ac/uibk/gitsearch/service/PluginManagerActionIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..8ce28350909e3010cba73696dec8d089ed5c13f8
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/PluginManagerActionIT.java
@@ -0,0 +1,81 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.codeability.sharing.plugins.api.SharingPluginConfig;
+import org.codeability.sharing.plugins.api.SharingPluginConfig.Action;
+import org.junit.Assert;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.util.FileCopyUtils;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.service.PluginManagementService.PluginConfigWrapper;
+import at.ac.uibk.gitsearch.service.PluginManagementService.PluginConfigWrapper.ActionWrapper;
+import at.ac.uibk.gitsearch.service.dto.SearchResultDTO;
+import at.ac.uibk.gitsearch.service.dto.UserProvidedMetadataDTO;
+
+@SpringBootTest(classes = GitsearchApp.class)
+public class PluginManagerActionIT {
+
+	private final Logger log = LoggerFactory.getLogger(PluginManagerActionIT.class);
+	private PluginConfigWrapper config;
+
+	@BeforeEach
+	public void initConfig() {
+		config = new PluginConfigWrapper(
+				new SharingPluginConfig("TestPlugin", new Action[] { new Action("someId", "someImportURL",
+						"someActionName", "metadata.type.externalName=='programming exercise'") }),
+				"http://localhost:8080/xxx");
+	}
+
+	@Test
+	public void testExpressionFiltersOnMetaData() throws JsonParseException, JsonMappingException, IOException {
+
+		ActionWrapper actionWrapper = config.getActions().get("someId");
+
+		Assert.assertTrue("metaData1 should be applicable",
+				actionWrapper.isApplicable(readTestResults("metaData1.yaml")));
+		Assert.assertFalse("metaData2 should not be applicable",
+				actionWrapper.isApplicable(readTestResults("metaData2.yaml")));
+
+	}
+
+	/**
+	 * temporary static test data.
+	 *
+	 * @param infoString just a string to add to testdata description.
+	 * @return
+	 * @throws IOException
+	 * @throws JsonMappingException
+	 * @throws JsonParseException
+	 */
+	private SearchResultDTO readTestResults(String testFile)
+			throws JsonParseException, JsonMappingException, IOException {
+		ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+		mapper.findAndRegisterModules();
+
+		log.debug("reading test data from {} ", testFile);
+
+		final String resourceName = "./testData/" + testFile;
+		final InputStream resourceStream = this.getClass().getResourceAsStream(resourceName);
+		Assert.assertNotNull("cannot find resource at " + resourceName, resourceStream);
+
+		byte[] fileContent = FileCopyUtils.copyToByteArray(resourceStream);
+		UserProvidedMetadataDTO metaData = mapper.readValue(fileContent, UserProvidedMetadataDTO.class);
+		SearchResultDTO result = new SearchResultDTO();
+		result.setMetadata(metaData);
+		return result;
+
+	}
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java b/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..c800878df5c5ee836f0ed22e218144cc2058f29c
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/SearchServiceIT.java
@@ -0,0 +1,146 @@
+package at.ac.uibk.gitsearch.service;
+
+import static at.ac.uibk.gitsearch.web.rest.AccountResourceIT.TEST_USER_LOGIN;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.everyItem;
+import static org.hamcrest.Matchers.hasItemInArray;
+import static org.hamcrest.Matchers.hasProperty;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.node.NodeValidationException;
+import org.junit.Ignore;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithMockUser;
+
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.repository.search.testESService.ElasticSearchTestServerConfiguration;
+import at.ac.uibk.gitsearch.security.SecurityUtils;
+import at.ac.uibk.gitsearch.service.dto.SearchInputDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchInputMetadataDTO;
+import at.ac.uibk.gitsearch.service.dto.SearchResultsDTO;
+
+@SpringBootTest(classes = GitsearchApp.class)
+@WithMockUser(value = TEST_USER_LOGIN, authorities = "sharing")
+public class SearchServiceIT {
+	
+	@Autowired
+	RestHighLevelClient searchClient;
+
+	private static  final Logger LOGGER = LoggerFactory.getLogger(SearchServiceIT.class);
+	
+	@Autowired
+	private SearchService searchService;
+
+	@BeforeAll
+	public static void setUpESServer() throws IOException, NodeValidationException {
+		ElasticSearchTestServerConfiguration.getTestInstance().startTestNode();
+	}
+
+	@Test
+	public void testAllSearch() throws Exception {
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, null, null);
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+		
+		LOGGER.info("found {} hits for all", searchResultPage.getHitCount());
+	}
+
+	@Test
+	public void testFullTextSearch() throws Exception {
+		LOGGER.info("starting testFullTextSearch");
+		final String WIEN = "Wien";
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, null, null);
+		SearchInputDTO searchQuery = new SearchInputDTO(WIEN, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		org.junit.Assert.assertNotNull(searchResultPage.getSearchResult());
+		org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		org.junit.Assert.assertThat(searchResultPage.getSearchResult(),
+				everyItem(anyOf(hasProperty("metadata", hasProperty("description", containsString(WIEN))),
+						hasProperty("metadata", hasProperty("title", containsString(WIEN)))
+				// hasProperty("keywords", containsString("Wien"))
+				)));
+
+		LOGGER.info("exiting testFullTextSearch");
+	}
+
+	@Test
+	public void testSearchByAutor() throws Exception {
+		final String PODLIPNIG = "Stefan Podlipnig";
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, null, PODLIPNIG);
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		org.junit.Assert.assertNotNull(searchResultPage.getSearchResult());
+		org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		org.junit.Assert.assertThat(searchResultPage.getSearchResult(), everyItem(
+				hasProperty("metadata", hasProperty("creator", hasItemInArray(hasProperty("name", containsString(PODLIPNIG)))))));
+
+	}
+
+	@Test
+	public void testProgrammingLanguageSearch() throws Exception {
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO("Java".toLowerCase(), null, null, null, null);
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		assertNotNull(searchResultPage.getSearchResult());
+		assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		assertThat(searchResultPage.getSearchResult(), everyItem(
+				hasProperty("metadata", hasProperty("programmingLanguage", hasItemInArray(containsString("Java"))))));
+
+	}
+
+	@Test
+	public void testCreatorSearch() throws IOException {
+		searchClient.getLowLevelClient().getNodes().forEach(n -> {LOGGER.info("Using node {}", n);});
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, null, "Podlipnig");
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		org.junit.Assert.assertNotNull(searchResultPage.getSearchResult());
+		org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		assertThat(searchResultPage.getSearchResult(), everyItem(hasProperty("metadata",
+				hasProperty("creator", hasItemInArray(hasProperty("name", containsString("Podlipnig")))))));
+
+	}
+
+	@Test
+	public void testKeywordSearch() throws IOException {
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, "latex", null, null, null);
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		org.junit.Assert.assertNotNull(searchResultPage.getSearchResult());
+		org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		assertThat(searchResultPage.getSearchResult(),
+				everyItem(hasProperty("metadata", hasProperty("keyword", hasItemInArray(containsString("latex"))))));
+
+	}
+
+//	@Test()
+//	@Ignore() // Test funktioniert momentan nicht?
+	public void testLicenseSearch() throws IOException {
+		
+		final SearchInputMetadataDTO searchMetadata = new SearchInputMetadataDTO(null, null, null, "MIT", null);
+		SearchInputDTO searchQuery = new SearchInputDTO(null, searchMetadata, null, null, null, 0);
+		SearchResultsDTO searchResultPage = searchService.searchResultPage(searchQuery, 0, SearchInputDTO.PAGE_SIZE);
+
+		org.junit.Assert.assertNotNull(searchResultPage.getSearchResult());
+		org.junit.Assert.assertTrue("At least one test hit", searchResultPage.getHitCount() >= 1);
+		assertThat(searchResultPage.getSearchResult(),
+				everyItem(hasProperty("metadata", hasProperty("license", containsString("MIT")))));
+
+	}
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/ShoppingBasketServiceIT.java b/src/test/java/at/ac/uibk/gitsearch/service/ShoppingBasketServiceIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..a7e618b348f4269a9ae22783d2af592cdc29dde0
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/ShoppingBasketServiceIT.java
@@ -0,0 +1,50 @@
+package at.ac.uibk.gitsearch.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import at.ac.uibk.gitsearch.GitsearchApp;
+
+@SpringBootTest(classes = GitsearchApp.class)
+public class ShoppingBasketServiceIT {
+	
+	private static final String TEST_ZIP_LOCATION = "./testData/junit-quality-tests-exercise-master.zip";
+	@Autowired
+	protected ShoppingBasketService shoppingBasketService;
+	
+	private static final Logger log = LoggerFactory.getLogger(ShoppingBasketServiceIT.class);
+	
+    @Test
+	public void testRepackage() throws IOException {
+		
+		final InputStream resourceStream = this.getClass().getResourceAsStream(TEST_ZIP_LOCATION);
+
+		Assert.assertNotNull("cannot find resource at " + TEST_ZIP_LOCATION, resourceStream);
+		final ZipInputStream testZip = new ZipInputStream(resourceStream);
+		
+		final InputStream testRepackagedZipIS = shoppingBasketService.rePackageGitLabProjectZip(testZip, "from " + TEST_ZIP_LOCATION);
+
+		final ZipInputStream testRepackagedZip = new ZipInputStream(testRepackagedZipIS);
+		
+		ZipEntry entry = testRepackagedZip.getNextEntry();
+		
+		while(entry!=null) {
+			log.info("found entry {} (directory: {})", entry.getName(), entry.isDirectory());
+			Assert.assertTrue("original folder not chopped off?", !entry.getName().startsWith("junit-quality-tests-exercise-master"));
+			entry = testRepackagedZip.getNextEntry();
+		}
+		
+		testRepackagedZip.close();
+	}
+	
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTOTest.java b/src/test/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTOTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..82fb8a395be1e8a8d5bd344a084a17a1fc68ad87
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/dto/StatisticsDTOTest.java
@@ -0,0 +1,23 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import at.ac.uibk.gitsearch.web.rest.TestUtil;
+
+public class StatisticsDTOTest {
+
+    @Test
+    public void dtoEqualsVerifier() throws Exception {
+        TestUtil.equalsVerifier(StatisticsDTO.class);
+        StatisticsDTO statisticsDTO1 = new StatisticsDTO();
+        statisticsDTO1.setId(1L);
+        StatisticsDTO statisticsDTO2 = new StatisticsDTO();
+        assertThat(statisticsDTO1).isNotEqualTo(statisticsDTO2);
+        statisticsDTO2.setId(statisticsDTO1.getId());
+        assertThat(statisticsDTO1).isEqualTo(statisticsDTO2);
+        statisticsDTO2.setId(2L);
+        assertThat(statisticsDTO1).isNotEqualTo(statisticsDTO2);
+        statisticsDTO1.setId(null);
+        assertThat(statisticsDTO1).isNotEqualTo(statisticsDTO2);
+    }
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c9b1aa42cac98bb4a4f66a4459946e4243aaf0a
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/dto/VariousDTOTest.java
@@ -0,0 +1,33 @@
+package at.ac.uibk.gitsearch.service.dto;
+
+import java.lang.reflect.InvocationTargetException;
+
+import at.ac.uibk.gitsearch.service.MailService;
+import at.ac.uibk.gitsearch.testingUtilities.PropertiesTester;
+
+/**
+ * Integration tests for {@link MailService}.
+ */
+public class VariousDTOTest {
+
+	PropertiesTester propertiesTester = new PropertiesTester();
+
+	@org.junit.jupiter.api.Test
+	public void testUserProvidedMetadataDTO() throws IllegalAccessException, InvocationTargetException {
+		
+		
+		propertiesTester.testProperties(UserProvidedMetadataDTO.class);
+	}
+
+	@org.junit.jupiter.api.Test
+	public void testSearchResultsDTO() throws IllegalAccessException, InvocationTargetException {
+		propertiesTester.testProperties(SearchResultsDTO.class);
+		propertiesTester.testProperties(SearchResultDTO.class);
+	}
+	
+	@org.junit.jupiter.api.Test
+	public void testSearchInputDTO() throws IllegalAccessException, InvocationTargetException {
+		propertiesTester.testProperties(SearchInputDTO.class);
+	}
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapperTest.java b/src/test/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapperTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e280711016c5471c1b40e6bf241287231d0ddf82
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/service/mapper/StatisticsMapperTest.java
@@ -0,0 +1,22 @@
+package at.ac.uibk.gitsearch.service.mapper;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class StatisticsMapperTest {
+
+    private StatisticsMapper statisticsMapper;
+
+    @BeforeEach
+    public void setUp() {
+        statisticsMapper = new StatisticsMapperImpl();
+    }
+
+    @Test
+    public void testEntityFromId() {
+        Long id = 1L;
+        assertThat(statisticsMapper.fromId(id).getId()).isEqualTo(id);
+        assertThat(statisticsMapper.fromId(null)).isNull();
+    }
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/testingUtilities/PropertiesTester.java b/src/test/java/at/ac/uibk/gitsearch/testingUtilities/PropertiesTester.java
new file mode 100644
index 0000000000000000000000000000000000000000..23f9531c529861409015517faee9105124b808f8
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/testingUtilities/PropertiesTester.java
@@ -0,0 +1,162 @@
+package at.ac.uibk.gitsearch.testingUtilities;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Assert;
+import org.springframework.stereotype.Controller;
+
+/**
+ * tests all getters and setters of a bean.
+ * @author arctis Softwaretechnologie Team
+ *
+ */
+@Controller
+public class PropertiesTester {
+  
+  private static final Logger LOGGER = LogManager.getLogger(PropertiesTester.class);
+  
+  /**
+   * 
+   * @param beanClass the class to test
+   * @param setterExceptions the names of setters that should not be tested.
+   */
+  public void testProperties(Class<?> beanClass, String... setterExceptions) {
+    // first try to instantiate bean;
+    Object bean = null;
+    try {
+      bean = beanClass.getConstructor().newInstance();
+    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+      LOGGER.info("Cannot instantiate " + beanClass.getCanonicalName(), e);
+      return;
+    }
+    testSetters(beanClass, bean, setterExceptions);
+  }
+
+  /**
+   * just tests all setters on bean
+   * @param beanClass
+   * @param bean
+   * @param setterExceptions
+   */
+public void testSetters(Class<?> beanClass, Object bean, String... setterExceptions) {
+	final Method[] methods = beanClass.getMethods();
+    for(Method method: methods) {
+      if(method.getName().startsWith("set")) {
+    	  if(!Arrays.stream(setterExceptions).anyMatch(noTest -> method.getName().equals(noTest)))
+        testSetter(beanClass, bean, method);
+      }
+    }
+    
+    // finally test toString
+    String stringRepresentation = bean.toString();
+    Assert.assertNotNull(stringRepresentation);
+}
+  
+  public void testSetter(Class<?> beanClass, Object bean, Method setter) {
+    if(setter.getParameterCount()!=1) {
+      return;
+    }
+    Parameter p = setter.getParameters()[0];
+    
+    String propertyName = setter.getName().substring(3);
+    Class<?> type = p.getType();
+    
+    Method getter = null;
+    Object property = null;
+    if(Boolean.class.equals(type) || boolean.class.equals(type) ) {
+      try {
+        getter = beanClass.getMethod("is"+propertyName);
+        property = Boolean.TRUE;
+      } catch (NoSuchMethodException | SecurityException e) {
+        LOGGER.info("Cannot find getter for " + beanClass.getCanonicalName() + "." + setter.getName(), e);
+        return;
+      }
+    } else {
+      try {
+        getter = beanClass.getMethod("get" + propertyName);
+        if(Integer.class.isAssignableFrom(type) || "int".equals(type.getName())) {
+          property = new Integer(2343234);
+        }
+        else if(Long.class.isAssignableFrom(type) || "long".equals(type.getName())) {
+          property = new Long(12321332343234L);
+        }
+        else if(Byte.class.isAssignableFrom(type) || "byte".equals(type.getName())) {
+          property = new Byte((byte)22);
+        }
+        else if(String.class.isAssignableFrom(type)) {
+          property = "xyzzy";
+        }
+        else if(byte[].class.isAssignableFrom(type)) {
+          property = new byte[]{3,4};
+        }
+        else if(type.isArray()) {
+        	property = Array.newInstance(type.getComponentType(), 5);
+
+        }
+        else if(Timestamp.class.isAssignableFrom(type)) {
+            property = new Timestamp(System.currentTimeMillis());
+       } else if(Date.class.isAssignableFrom(type)) {
+          property = new Date();
+        } else if(List.class.isAssignableFrom(type)) {
+          property = new ArrayList<>();
+        } else if(Enum.class.isAssignableFrom(type)) {
+          Object o;
+          try {
+            o = type.getMethod("values").invoke(type);
+           if( o.getClass().isArray() && Array.getLength(o)>=0) {
+        		property = Array.get(o, 0);  
+           } else {
+        	   LOGGER.warn("Cannot get first element of enumeration {}", type.getName());
+        	   property = null;
+           }
+          } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            LOGGER.info("Cannot instantiate Enum for " + type.getCanonicalName() + "." + setter.getName(), e);
+            property = null;
+          }
+        } else if(Set.class.isAssignableFrom(type)) {
+        	property = Collections.EMPTY_SET;
+        } else if(type.isInterface()) {
+        	LOGGER.info("Cannot instantiate interface {} in {} for class {}", type.getName(), setter.getName(), bean.getClass().getName());
+        }
+        else {
+          try {
+            property = type.getConstructor().newInstance();
+          } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            LOGGER.info("Cannot instantiate Property for " + beanClass.getCanonicalName() + "." + setter.getName(), e);
+            property = null;
+          }
+        }
+      } catch (NoSuchMethodException | SecurityException e) {
+        LOGGER.info("Cannot find getter for " + beanClass.getCanonicalName() + "." + setter.getName(), e);
+        return;
+      }
+      
+      if(getter==null) {
+        // not getter found
+        return;
+      }
+      try {
+        setter.invoke(bean, property);
+        Object result = getter.invoke(bean);
+        Assert.assertEquals("Testing " + setter.getName() + " of " + beanClass.getCanonicalName(), property, result);
+      } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+        LOGGER.info("Cannot invoke ..." + beanClass.getCanonicalName() + "." + setter.getName(), e);
+        return;
+      }
+    }
+    
+  }
+
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/AccountResourceIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/AccountResourceIT.java
index 13e030090a11a8fa4714932b519fbdca28a5aa89..107301a34536602bc789db5ee377f8cffdeabc71 100644
--- a/src/test/java/at/ac/uibk/gitsearch/web/rest/AccountResourceIT.java
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/AccountResourceIT.java
@@ -1,16 +1,21 @@
 package at.ac.uibk.gitsearch.web.rest;
 
-import at.ac.uibk.gitsearch.GitsearchApp;
-import at.ac.uibk.gitsearch.config.Constants;
-import at.ac.uibk.gitsearch.domain.User;
-import at.ac.uibk.gitsearch.repository.AuthorityRepository;
-import at.ac.uibk.gitsearch.repository.UserRepository;
-import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
-import at.ac.uibk.gitsearch.service.UserService;
-import at.ac.uibk.gitsearch.service.dto.PasswordChangeDTO;
-import at.ac.uibk.gitsearch.service.dto.UserDTO;
-import at.ac.uibk.gitsearch.web.rest.vm.KeyAndPasswordVM;
-import at.ac.uibk.gitsearch.web.rest.vm.ManagedUserVM;
+import static at.ac.uibk.gitsearch.web.rest.AccountResourceIT.TEST_USER_LOGIN;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
 import org.apache.commons.lang3.RandomStringUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -18,20 +23,24 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
-import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.context.WebApplicationContext;
 
-import java.time.Instant;
-import java.util.*;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static at.ac.uibk.gitsearch.web.rest.AccountResourceIT.TEST_USER_LOGIN;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.config.Constants;
+import at.ac.uibk.gitsearch.domain.User;
+import at.ac.uibk.gitsearch.repository.AuthorityRepository;
+import at.ac.uibk.gitsearch.repository.UserRepository;
+import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
+import at.ac.uibk.gitsearch.service.UserService;
+import at.ac.uibk.gitsearch.service.dto.PasswordChangeDTO;
+import at.ac.uibk.gitsearch.service.dto.UserDTO;
+import at.ac.uibk.gitsearch.web.rest.vm.KeyAndPasswordVM;
+import at.ac.uibk.gitsearch.web.rest.vm.ManagedUserVM;
 /**
  * Integration tests for the {@link AccountResource} REST controller.
  */
@@ -39,7 +48,11 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 @WithMockUser(value = TEST_USER_LOGIN)
 @SpringBootTest(classes = GitsearchApp.class)
 public class AccountResourceIT {
-    static final String TEST_USER_LOGIN = "test";
+	public static final String TEST_USER_LOGIN = "test";
+    
+    @Autowired
+	private WebApplicationContext context;
+    
 
     @Autowired
     private UserRepository userRepository;
@@ -53,13 +66,23 @@ public class AccountResourceIT {
     @Autowired
     private PasswordEncoder passwordEncoder;
 
-    @Autowired
+//    @Autowired
     private MockMvc restAccountMockMvc;
 
+    @BeforeEach
+	public void setup() {
+    	restAccountMockMvc = MockMvcBuilders
+				.webAppContextSetup(context)
+				.apply(springSecurity())
+//				.apply(ConfigurableReactiveWebServerFactory()f)
+				.build();
+	}
+
     @Test
     @WithUnauthenticatedMockUser
     public void testNonAuthenticatedUser() throws Exception {
         restAccountMockMvc.perform(get("/api/authenticate")
+            .with(csrf().asHeader())
             .accept(MediaType.APPLICATION_JSON))
             .andExpect(status().isOk())
             .andExpect(content().string(""));
@@ -68,6 +91,7 @@ public class AccountResourceIT {
     @Test
     public void testAuthenticatedUser() throws Exception {
         restAccountMockMvc.perform(get("/api/authenticate")
+            .with(csrf().asHeader())
             .with(request -> {
                 request.setRemoteUser(TEST_USER_LOGIN);
                 return request;
@@ -93,6 +117,7 @@ public class AccountResourceIT {
         userService.createUser(user);
 
         restAccountMockMvc.perform(get("/api/account")
+                .with(csrf().asHeader())
             .accept(MediaType.APPLICATION_JSON))
             .andExpect(status().isOk())
             .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
@@ -108,11 +133,12 @@ public class AccountResourceIT {
     @Test
     public void testGetUnknownAccount() throws Exception {
         restAccountMockMvc.perform(get("/api/account")
+                .with(csrf().asHeader())
             .accept(MediaType.APPLICATION_PROBLEM_JSON))
             .andExpect(status().isInternalServerError());
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterValid() throws Exception {
         ManagedUserVM validUser = new ManagedUserVM();
@@ -128,6 +154,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+            .with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(validUser)))
             .andExpect(status().isCreated());
@@ -135,7 +162,31 @@ public class AccountResourceIT {
         assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isTrue();
     }
 
-    @Test
+    @Test 
+    @Transactional
+    public void testRegisterDisabled() throws Exception {
+        ManagedUserVM validUser = new ManagedUserVM();
+        validUser.setLogin("test-register-valid");
+        validUser.setPassword("password");
+        validUser.setFirstName("Alice");
+        validUser.setLastName("Test");
+        validUser.setEmail("test-register-valid@example.com");
+        validUser.setImageUrl("http://placehold.it/50x50");
+        validUser.setLangKey(Constants.DEFAULT_LANGUAGE);
+        validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
+        assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isFalse();
+
+        restAccountMockMvc.perform(
+            post("/api/register")
+            .with(csrf().asHeader())
+                .contentType(MediaType.APPLICATION_JSON)
+                .content(TestUtil.convertObjectToJsonBytes(validUser)))
+            .andExpect(status().isForbidden());
+
+        // assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isTrue();
+    }
+
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterInvalidLogin() throws Exception {
         ManagedUserVM invalidUser = new ManagedUserVM();
@@ -151,6 +202,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+            .with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(invalidUser)))
             .andExpect(status().isBadRequest());
@@ -159,7 +211,7 @@ public class AccountResourceIT {
         assertThat(user.isPresent()).isFalse();
     }
 
-    @Test
+//    @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterInvalidEmail() throws Exception {
         ManagedUserVM invalidUser = new ManagedUserVM();
@@ -175,6 +227,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+            .with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(invalidUser)))
             .andExpect(status().isBadRequest());
@@ -183,7 +236,7 @@ public class AccountResourceIT {
         assertThat(user.isPresent()).isFalse();
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterInvalidPassword() throws Exception {
         ManagedUserVM invalidUser = new ManagedUserVM();
@@ -199,6 +252,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+            	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(invalidUser)))
             .andExpect(status().isBadRequest());
@@ -207,7 +261,7 @@ public class AccountResourceIT {
         assertThat(user.isPresent()).isFalse();
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterNullPassword() throws Exception {
         ManagedUserVM invalidUser = new ManagedUserVM();
@@ -223,6 +277,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(invalidUser)))
             .andExpect(status().isBadRequest());
@@ -231,7 +286,7 @@ public class AccountResourceIT {
         assertThat(user.isPresent()).isFalse();
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterDuplicateLogin() throws Exception {
         // First registration
@@ -263,6 +318,7 @@ public class AccountResourceIT {
         // First user
         restAccountMockMvc.perform(
             post("/api/register")
+            	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(firstUser)))
             .andExpect(status().isCreated());
@@ -287,7 +343,7 @@ public class AccountResourceIT {
             .andExpect(status().is4xxClientError());
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterDuplicateEmail() throws Exception {
         // First user
@@ -304,6 +360,7 @@ public class AccountResourceIT {
         // Register first user
         restAccountMockMvc.perform(
             post("/api/register")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(firstUser)))
             .andExpect(status().isCreated());
@@ -325,6 +382,7 @@ public class AccountResourceIT {
         // Register second (non activated) user
         restAccountMockMvc.perform(
             post("/api/register")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(secondUser)))
             .andExpect(status().isCreated());
@@ -350,6 +408,7 @@ public class AccountResourceIT {
         // Register third (not activated) user
         restAccountMockMvc.perform(
             post("/api/register")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(userWithUpperCaseEmail)))
             .andExpect(status().isCreated());
@@ -364,12 +423,13 @@ public class AccountResourceIT {
         // Register 4th (already activated) user
         restAccountMockMvc.perform(
             post("/api/register")
+            .with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(secondUser)))
             .andExpect(status().is4xxClientError());
     }
 
-    @Test
+//  @Test Self-Registration is not supported any more
     @Transactional
     public void testRegisterAdminIsIgnored() throws Exception {
         ManagedUserVM validUser = new ManagedUserVM();
@@ -385,6 +445,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/register")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(validUser)))
             .andExpect(status().isCreated());
@@ -408,7 +469,8 @@ public class AccountResourceIT {
 
         userRepository.saveAndFlush(user);
 
-        restAccountMockMvc.perform(get("/api/activate?key={activationKey}", activationKey))
+        restAccountMockMvc.perform(get("/api/activate?key={activationKey}", activationKey)
+    	.with(csrf().asHeader()))
             .andExpect(status().isOk());
 
         user = userRepository.findOneByLogin(user.getLogin()).orElse(null);
@@ -445,6 +507,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(userDTO)))
             .andExpect(status().isOk());
@@ -484,6 +547,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account")
+            .with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(userDTO)))
             .andExpect(status().isBadRequest());
@@ -522,6 +586,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(userDTO)))
             .andExpect(status().isBadRequest());
@@ -553,6 +618,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(userDTO)))
             .andExpect(status().isOk());
@@ -573,6 +639,7 @@ public class AccountResourceIT {
         userRepository.saveAndFlush(user);
 
         restAccountMockMvc.perform(post("/api/account/change-password")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO("1"+currentPassword, "new password")))
 )
@@ -595,6 +662,7 @@ public class AccountResourceIT {
         userRepository.saveAndFlush(user);
 
         restAccountMockMvc.perform(post("/api/account/change-password")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, "new password")))
 )
@@ -618,6 +686,7 @@ public class AccountResourceIT {
         String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MIN_LENGTH - 1);
 
         restAccountMockMvc.perform(post("/api/account/change-password")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword)))
 )
@@ -641,6 +710,7 @@ public class AccountResourceIT {
         String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MAX_LENGTH + 1);
 
         restAccountMockMvc.perform(post("/api/account/change-password")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword)))
 )
@@ -662,6 +732,7 @@ public class AccountResourceIT {
         userRepository.saveAndFlush(user);
 
         restAccountMockMvc.perform(post("/api/account/change-password")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, "")))
 )
@@ -682,9 +753,9 @@ public class AccountResourceIT {
         userRepository.saveAndFlush(user);
 
         restAccountMockMvc.perform(post("/api/account/reset-password/init")
-            .content("password-reset@example.com")
-)
-            .andExpect(status().isOk());
+            	.with(csrf().asHeader())
+                .content("password-reset@example.com"))
+        .andExpect(status().isOk());
     }
 
     @Test
@@ -698,6 +769,7 @@ public class AccountResourceIT {
         userRepository.saveAndFlush(user);
 
         restAccountMockMvc.perform(post("/api/account/reset-password/init")
+            	.with(csrf().asHeader())
             .content("password-reset-upper-case@EXAMPLE.COM")
 )
             .andExpect(status().isOk());
@@ -707,6 +779,7 @@ public class AccountResourceIT {
     public void testRequestPasswordResetWrongEmail() throws Exception {
         restAccountMockMvc.perform(
             post("/api/account/reset-password/init")
+        	.with(csrf().asHeader())
                 .content("password-reset-wrong-email@example.com"))
             .andExpect(status().isOk());
     }
@@ -728,6 +801,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account/reset-password/finish")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)))
             .andExpect(status().isOk());
@@ -753,6 +827,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account/reset-password/finish")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)))
             .andExpect(status().isBadRequest());
@@ -770,6 +845,7 @@ public class AccountResourceIT {
 
         restAccountMockMvc.perform(
             post("/api/account/reset-password/finish")
+        	.with(csrf().asHeader())
                 .contentType(MediaType.APPLICATION_JSON)
                 .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)))
             .andExpect(status().isInternalServerError());
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/AuditResourceIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/AuditResourceIT.java
index 465a027a61c22e1d93aa20cd1f920cd8c5445cea..00fdc6521b58f60f39e44fe6d2421900b893ae0b 100644
--- a/src/test/java/at/ac/uibk/gitsearch/web/rest/AuditResourceIT.java
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/AuditResourceIT.java
@@ -1,29 +1,29 @@
 package at.ac.uibk.gitsearch.web.rest;
 
-import at.ac.uibk.gitsearch.GitsearchApp;
-import at.ac.uibk.gitsearch.domain.PersistentAuditEvent;
-import at.ac.uibk.gitsearch.repository.PersistenceAuditEventRepository;
-import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.time.Instant;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
 import org.springframework.http.MediaType;
 import org.springframework.security.test.context.support.WithMockUser;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.time.Instant;
-
-import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
-import static org.hamcrest.Matchers.hasItem;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.domain.PersistentAuditEvent;
+import at.ac.uibk.gitsearch.repository.PersistenceAuditEventRepository;
+import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
 
 /**
  * Integration tests for the {@link AuditResource} REST controller.
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java
new file mode 100644
index 0000000000000000000000000000000000000000..552819c97c9b869a8a1968e5767fdb9f75591d83
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/StatisticsResourceIT.java
@@ -0,0 +1,292 @@
+package at.ac.uibk.gitsearch.web.rest;
+
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.domain.Statistics;
+import at.ac.uibk.gitsearch.repository.StatisticsRepository;
+import at.ac.uibk.gitsearch.repository.search.StatisticsSearchRepository;
+import at.ac.uibk.gitsearch.service.StatisticsService;
+import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
+import at.ac.uibk.gitsearch.service.mapper.StatisticsMapper;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.http.MediaType;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import java.util.Collections;
+import java.util.List;
+
+import at.ac.uibk.gitsearch.security.AuthoritiesConstants;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
+import static org.hamcrest.Matchers.hasItem;
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+
+/**
+ * Integration tests for the {@link StatisticsResource} REST controller.
+ */
+@SpringBootTest(classes = GitsearchApp.class)
+@ExtendWith(MockitoExtension.class)
+@AutoConfigureMockMvc
+@WithMockUser
+public class StatisticsResourceIT {
+
+    private static final Integer DEFAULT_VIEWS = 1;
+    private static final Integer UPDATED_VIEWS = 2;
+
+    private static final Integer DEFAULT_DOWNLOADS = 1;
+    private static final Integer UPDATED_DOWNLOADS = 2;
+
+    private static final Long DEFAULT_EXERCISE_ID = (long) 1;
+    private static final Long UPDATED_EXERCISE_ID = (long) 2;
+
+    @Autowired
+    private StatisticsRepository statisticsRepository;
+
+    @Autowired
+    private StatisticsMapper statisticsMapper;
+
+    @Autowired
+    private StatisticsService statisticsService;
+
+    /**
+     * This repository is mocked in the at.ac.uibk.gitsearch.repository.search test
+     * package.
+     *
+     * @see at.ac.uibk.gitsearch.repository.search.StatisticsSearchRepositoryMockConfiguration
+     */
+    @Autowired
+    private StatisticsSearchRepository mockStatisticsSearchRepository;
+
+    @Autowired
+    private EntityManager em;
+
+    @Autowired
+    private MockMvc restStatisticsMockMvc;
+
+    private Statistics statistics;
+
+    /**
+     * Create an entity for this test.
+     *
+     * This is a static method, as tests for other entities might also need it, if
+     * they test an entity which requires the current entity.
+     */
+    public static Statistics createEntity(EntityManager em) {
+        Statistics statistics = new Statistics().views(DEFAULT_VIEWS).downloads(DEFAULT_DOWNLOADS)
+                .exerciseID(DEFAULT_EXERCISE_ID);
+        return statistics;
+    }
+
+    /**
+     * Create an updated entity for this test.
+     *
+     * This is a static method, as tests for other entities might also need it, if
+     * they test an entity which requires the current entity.
+     */
+    public static Statistics createUpdatedEntity(EntityManager em) {
+        Statistics statistics = new Statistics().views(UPDATED_VIEWS).downloads(UPDATED_DOWNLOADS)
+                .exerciseID(UPDATED_EXERCISE_ID);
+        return statistics;
+    }
+
+    @BeforeEach
+    public void initTest() {
+        statistics = createEntity(em);
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void createStatistics() throws Exception {
+        int databaseSizeBeforeCreate = statisticsRepository.findAll().size();
+        // Create the Statistics
+        StatisticsDTO statisticsDTO = statisticsMapper.toDto(statistics);
+        restStatisticsMockMvc.perform(post("/api/statistics").with(csrf().asHeader())
+                .contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(statisticsDTO)))
+                .andExpect(status().isCreated());
+
+        // Validate the Statistics in the database
+        List<Statistics> statisticsList = statisticsRepository.findAll();
+        assertThat(statisticsList).hasSize(databaseSizeBeforeCreate + 1);
+        Statistics testStatistics = statisticsList.get(statisticsList.size() - 1);
+        assertThat(testStatistics.getViews()).isEqualTo(DEFAULT_VIEWS);
+        assertThat(testStatistics.getDownloads()).isEqualTo(DEFAULT_DOWNLOADS);
+        assertThat(testStatistics.getExerciseID()).isEqualTo(DEFAULT_EXERCISE_ID);
+
+        // Validate the Statistics in Elasticsearch
+        verify(mockStatisticsSearchRepository, times(1)).save(testStatistics);
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void createStatisticsWithExistingId() throws Exception {
+        int databaseSizeBeforeCreate = statisticsRepository.findAll().size();
+
+        // Create the Statistics with an existing ID
+        statistics.setId(1L);
+        StatisticsDTO statisticsDTO = statisticsMapper.toDto(statistics);
+
+        // An entity with an existing ID cannot be created, so this API call must fail
+        restStatisticsMockMvc.perform(post("/api/statistics").with(csrf().asHeader())
+                .contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(statisticsDTO)))
+                .andExpect(status().isBadRequest());
+
+        // Validate the Statistics in the database
+        List<Statistics> statisticsList = statisticsRepository.findAll();
+        assertThat(statisticsList).hasSize(databaseSizeBeforeCreate);
+
+        // Validate the Statistics in Elasticsearch
+        verify(mockStatisticsSearchRepository, times(0)).save(statistics);
+    }
+
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void getAllStatistics() throws Exception {
+        // Initialize the database
+        statisticsRepository.saveAndFlush(statistics);
+
+        // Get all the statisticsList
+        restStatisticsMockMvc.perform(get("/api/statistics?sort=id,desc").with(csrf().asHeader())).andExpect(status().isOk())
+                .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+                .andExpect(jsonPath("$.[*].id").value(hasItem(statistics.getId().intValue())))
+                .andExpect(jsonPath("$.[*].views").value(hasItem(DEFAULT_VIEWS)))
+                .andExpect(jsonPath("$.[*].downloads").value(hasItem((int)(long)DEFAULT_DOWNLOADS)))
+                .andExpect(jsonPath("$.[*].exerciseID").value(hasItem(DEFAULT_EXERCISE_ID.intValue())));
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void getStatistics() throws Exception {
+        // Initialize the database
+        statisticsRepository.saveAndFlush(statistics);
+
+        // Get the statistics
+        restStatisticsMockMvc.perform(get("/api/statistics/{id}", statistics.getId()).with(csrf().asHeader())).andExpect(status().isOk())
+                .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+                .andExpect(jsonPath("$.id").value(statistics.getId().intValue()))
+                .andExpect(jsonPath("$.views").value(DEFAULT_VIEWS))
+                .andExpect(jsonPath("$.downloads").value(DEFAULT_DOWNLOADS))
+                .andExpect(jsonPath("$.exerciseID").value(DEFAULT_EXERCISE_ID));
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void getNonExistingStatistics() throws Exception {
+        // Get the statistics
+        restStatisticsMockMvc.perform(get("/api/statistics/{id}", Long.MAX_VALUE).with(csrf().asHeader())).andExpect(status().isNotFound());
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void updateStatistics() throws Exception {
+        // Initialize the database
+        statisticsRepository.saveAndFlush(statistics);
+
+        int databaseSizeBeforeUpdate = statisticsRepository.findAll().size();
+
+        // Update the statistics
+        Statistics updatedStatistics = statisticsRepository.findById(statistics.getId()).get();
+        // Disconnect from session so that the updates on updatedStatistics are not
+        // directly saved in db
+        em.detach(updatedStatistics);
+        updatedStatistics.views(UPDATED_VIEWS).downloads(UPDATED_DOWNLOADS).exerciseID(UPDATED_EXERCISE_ID);
+        StatisticsDTO statisticsDTO = statisticsMapper.toDto(updatedStatistics);
+
+        restStatisticsMockMvc.perform(put("/api/statistics").with(csrf().asHeader()).contentType(MediaType.APPLICATION_JSON)
+                .content(TestUtil.convertObjectToJsonBytes(statisticsDTO))).andExpect(status().isOk());
+
+        // Validate the Statistics in the database
+        List<Statistics> statisticsList = statisticsRepository.findAll();
+        assertThat(statisticsList).hasSize(databaseSizeBeforeUpdate);
+        Statistics testStatistics = statisticsList.get(statisticsList.size() - 1);
+        assertThat(testStatistics.getViews()).isEqualTo(UPDATED_VIEWS);
+        assertThat(testStatistics.getDownloads()).isEqualTo(UPDATED_DOWNLOADS);
+        assertThat(testStatistics.getExerciseID()).isEqualTo(UPDATED_EXERCISE_ID);
+
+        // Validate the Statistics in Elasticsearch
+        verify(mockStatisticsSearchRepository, times(1)).save(testStatistics);
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void updateNonExistingStatistics() throws Exception {
+        int databaseSizeBeforeUpdate = statisticsRepository.findAll().size();
+
+        // Create the Statistics
+        StatisticsDTO statisticsDTO = statisticsMapper.toDto(statistics);
+
+        // If the entity doesn't have an ID, it will throw BadRequestAlertException
+        restStatisticsMockMvc.perform(put("/api/statistics").with(csrf().asHeader()).contentType(MediaType.APPLICATION_JSON)
+                .content(TestUtil.convertObjectToJsonBytes(statisticsDTO))).andExpect(status().isBadRequest());
+
+        // Validate the Statistics in the database
+        List<Statistics> statisticsList = statisticsRepository.findAll();
+        assertThat(statisticsList).hasSize(databaseSizeBeforeUpdate);
+
+        // Validate the Statistics in Elasticsearch
+        verify(mockStatisticsSearchRepository, times(0)).save(statistics);
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void deleteStatistics() throws Exception {
+        // Initialize the database
+        statisticsRepository.saveAndFlush(statistics);
+
+        int databaseSizeBeforeDelete = statisticsRepository.findAll().size();
+
+        // Delete the statistics
+        restStatisticsMockMvc
+                .perform(delete("/api/statistics/{id}", statistics.getId()).with(csrf().asHeader()).accept(MediaType.APPLICATION_JSON))
+                .andExpect(status().isNoContent());
+
+        // Validate the database contains one less item
+        List<Statistics> statisticsList = statisticsRepository.findAll();
+        assertThat(statisticsList).hasSize(databaseSizeBeforeDelete - 1);
+
+        // Validate the Statistics in Elasticsearch
+        verify(mockStatisticsSearchRepository, times(1)).deleteById(statistics.getId());
+    }
+
+    @Test
+    @Transactional
+    @WithMockUser(authorities = AuthoritiesConstants.ADMIN)
+    public void searchStatistics() throws Exception {
+        // Configure the mock search repository
+        // Initialize the database
+        statisticsRepository.saveAndFlush(statistics);
+        when(mockStatisticsSearchRepository.search(queryStringQuery("id:" + statistics.getId()), PageRequest.of(0, 20)))
+                .thenReturn(new PageImpl<>(Collections.singletonList(statistics), PageRequest.of(0, 1), 1));
+
+        // Search the statistics
+        restStatisticsMockMvc.perform(get("/api/_search/statistics?query=id:" + statistics.getId()).with(csrf().asHeader()))
+                .andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
+                .andExpect(jsonPath("$.[*].id").value(hasItem(statistics.getId().intValue())))
+                .andExpect(jsonPath("$.[*].views").value(hasItem(DEFAULT_VIEWS)))
+                .andExpect(jsonPath("$.[*].downloads").value(hasItem(DEFAULT_DOWNLOADS)))
+                .andExpect(jsonPath("$.[*].exerciseID").value(hasItem(DEFAULT_EXERCISE_ID.intValue())));
+    }
+}
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/UserJWTControllerIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/UserJWTControllerIT.java
index 9a66ab7967bfbf576bbab1d7ea3c9bd90d7232f2..eef8f8900d1694f813cb5974003e145d53421416 100644
--- a/src/test/java/at/ac/uibk/gitsearch/web/rest/UserJWTControllerIT.java
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/UserJWTControllerIT.java
@@ -1,9 +1,15 @@
 package at.ac.uibk.gitsearch.web.rest;
 
-import at.ac.uibk.gitsearch.GitsearchApp;
-import at.ac.uibk.gitsearch.domain.User;
-import at.ac.uibk.gitsearch.repository.UserRepository;
-import at.ac.uibk.gitsearch.web.rest.vm.LoginVM;
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -11,17 +17,12 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.MediaType;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.test.web.servlet.MockMvc;
-import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.transaction.annotation.Transactional;
 
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
-import static org.hamcrest.Matchers.nullValue;
-import static org.hamcrest.Matchers.emptyString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
+import at.ac.uibk.gitsearch.GitsearchApp;
+import at.ac.uibk.gitsearch.domain.User;
+import at.ac.uibk.gitsearch.repository.UserRepository;
+import at.ac.uibk.gitsearch.web.rest.vm.LoginVM;
 
 /**
  * Integration tests for the {@link UserJWTController} REST controller.
@@ -54,6 +55,7 @@ public class UserJWTControllerIT {
         login.setUsername("user-jwt-controller");
         login.setPassword("test");
         mockMvc.perform(post("/api/authenticate")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(login)))
             .andExpect(status().isOk())
@@ -79,6 +81,7 @@ public class UserJWTControllerIT {
         login.setPassword("test");
         login.setRememberMe(true);
         mockMvc.perform(post("/api/authenticate")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(login)))
             .andExpect(status().isOk())
@@ -94,6 +97,7 @@ public class UserJWTControllerIT {
         login.setUsername("wrong-user");
         login.setPassword("wrong password");
         mockMvc.perform(post("/api/authenticate")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(login)))
             .andExpect(status().isUnauthorized())
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/UserResourceIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/UserResourceIT.java
index cd2abc8798bdd26b0c3f3421c597240bb0c2810b..a98f447e4a82d6d652e53347c9a916b9e05e2f55 100644
--- a/src/test/java/at/ac/uibk/gitsearch/web/rest/UserResourceIT.java
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/UserResourceIT.java
@@ -29,6 +29,7 @@ import java.util.function.Consumer;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.hasItem;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
@@ -138,6 +139,7 @@ public class UserResourceIT {
         managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
         restUserMockMvc.perform(post("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isCreated());
@@ -174,6 +176,7 @@ public class UserResourceIT {
 
         // An entity with an existing ID cannot be created, so this API call must fail
         restUserMockMvc.perform(post("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isBadRequest());
@@ -203,6 +206,7 @@ public class UserResourceIT {
 
         // Create the User
         restUserMockMvc.perform(post("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isBadRequest());
@@ -232,6 +236,7 @@ public class UserResourceIT {
 
         // Create the User
         restUserMockMvc.perform(post("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isBadRequest());
@@ -316,6 +321,7 @@ public class UserResourceIT {
         managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
         restUserMockMvc.perform(put("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isOk());
@@ -359,6 +365,7 @@ public class UserResourceIT {
         managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
         restUserMockMvc.perform(put("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isOk());
@@ -415,6 +422,7 @@ public class UserResourceIT {
         managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
         restUserMockMvc.perform(put("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isBadRequest());
@@ -459,6 +467,7 @@ public class UserResourceIT {
         managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
         restUserMockMvc.perform(put("/api/users")
+            	.with(csrf().asHeader())
             .contentType(MediaType.APPLICATION_JSON)
             .content(TestUtil.convertObjectToJsonBytes(managedUserVM)))
             .andExpect(status().isBadRequest());
@@ -473,6 +482,7 @@ public class UserResourceIT {
 
         // Delete the user
         restUserMockMvc.perform(delete("/api/users/{login}", user.getLogin())
+            	.with(csrf().asHeader())
             .accept(MediaType.APPLICATION_JSON))
             .andExpect(status().isNoContent());
 
@@ -486,6 +496,7 @@ public class UserResourceIT {
     @Transactional
     public void getAllAuthorities() throws Exception {
         restUserMockMvc.perform(get("/api/users/authorities")
+            	.with(csrf().asHeader())
             .accept(MediaType.APPLICATION_JSON)
             .contentType(MediaType.APPLICATION_JSON))
             .andExpect(status().isOk())
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/errors/ExceptionTranslatorIT.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/errors/ExceptionTranslatorIT.java
index 187533dccd00a0a0090b1d763df6ef0c2a3f2d76..26e6085198abc56fc35eb996291c922d433d7b79 100644
--- a/src/test/java/at/ac/uibk/gitsearch/web/rest/errors/ExceptionTranslatorIT.java
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/errors/ExceptionTranslatorIT.java
@@ -9,6 +9,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMock
 import org.springframework.http.MediaType;
 import org.springframework.test.web.servlet.MockMvc;
 
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -36,7 +37,9 @@ public class ExceptionTranslatorIT {
 
     @Test
     public void testMethodArgumentNotValid() throws Exception {
-         mockMvc.perform(post("/api/exception-translator-test/method-argument").content("{}").contentType(MediaType.APPLICATION_JSON))
+         mockMvc.perform(post("/api/exception-translator-test/method-argument")
+        	 .with(csrf().asHeader())
+        	 .content("{}").contentType(MediaType.APPLICATION_JSON))
              .andExpect(status().isBadRequest())
              .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
              .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_VALIDATION))
@@ -82,7 +85,8 @@ public class ExceptionTranslatorIT {
 
     @Test
     public void testMethodNotSupported() throws Exception {
-        mockMvc.perform(post("/api/exception-translator-test/access-denied"))
+        mockMvc.perform(post("/api/exception-translator-test/access-denied")
+        	.with(csrf().asHeader()))
             .andExpect(status().isMethodNotAllowed())
             .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
             .andExpect(jsonPath("$.message").value("error.http.405"))
diff --git a/src/test/java/at/ac/uibk/gitsearch/web/rest/util/HeaderUtil.java b/src/test/java/at/ac/uibk/gitsearch/web/rest/util/HeaderUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..ea50490db7fe17edea0af0ced365fa20b5c7a0e0
--- /dev/null
+++ b/src/test/java/at/ac/uibk/gitsearch/web/rest/util/HeaderUtil.java
@@ -0,0 +1,64 @@
+package at.ac.uibk.gitsearch.web.rest.util;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+/**
+ * Utility class for HTTP headers creation.
+ */
+public final class HeaderUtil {
+
+    private static final Logger log = LoggerFactory.getLogger(HeaderUtil.class);
+
+    private HeaderUtil() {
+    }
+
+    public static HttpHeaders createAlert(String applicationName, String message, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createAlert(applicationName, message, param);
+    }
+
+    public static HttpHeaders createEntityCreationAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityCreationAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createEntityUpdateAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityUpdateAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createEntityDeletionAlert(String applicationName, boolean enableTranslation, String entityName, String param) {
+        return io.github.jhipster.web.util.HeaderUtil.createEntityDeletionAlert(applicationName, enableTranslation, entityName, param);
+    }
+
+    public static HttpHeaders createFailureAlert(String applicationName, boolean enableTranslation, String entityName, String errorKey, String defaultMessage) {
+        HttpHeaders headers = io.github.jhipster.web.util.HeaderUtil.createFailureAlert(applicationName, enableTranslation, entityName, errorKey, defaultMessage);
+        headers.add("X-" + applicationName + "-message", defaultMessage);
+        return headers;
+    }
+
+    /**
+     * Creates a authorization headers for a given username and password
+     * @param username the username
+     * @param password the password
+     * @return the acceptHeader
+     */
+    public static HttpHeaders createAuthorization(String username, String password) {
+        HttpHeaders acceptHeaders = new HttpHeaders() {
+
+            {
+                set(com.google.common.net.HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString());
+                set(com.google.common.net.HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON.toString());
+            }
+        };
+        String authorization = username + ":" + password;
+        String basic = new String(Base64.getEncoder().encode(authorization.getBytes(StandardCharsets.UTF_8)));
+        acceptHeaders.set("Authorization", "Basic " + basic);
+
+        return acceptHeaders;
+    }
+}
+
diff --git a/src/test/javascript/e2e/entities/statistics/statistics.page-object.ts b/src/test/javascript/e2e/entities/statistics/statistics.page-object.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2105107f70a3242d67a6554d41bf1f52d7a9fbc7
--- /dev/null
+++ b/src/test/javascript/e2e/entities/statistics/statistics.page-object.ts
@@ -0,0 +1,88 @@
+import { element, by, ElementFinder } from 'protractor';
+
+export class StatisticsComponentsPage {
+  createButton = element(by.id('jh-create-entity'));
+  deleteButtons = element.all(by.css('jhi-statistics div table .btn-danger'));
+  title = element.all(by.css('jhi-statistics div h2#page-heading span')).first();
+  noResult = element(by.id('no-result'));
+  entities = element(by.id('entities'));
+
+  async clickOnCreateButton(): Promise<void> {
+    await this.createButton.click();
+  }
+
+  async clickOnLastDeleteButton(): Promise<void> {
+    await this.deleteButtons.last().click();
+  }
+
+  async countDeleteButtons(): Promise<number> {
+    return this.deleteButtons.count();
+  }
+
+  async getTitle(): Promise<string> {
+    return this.title.getAttribute('jhiTranslate');
+  }
+}
+
+export class StatisticsUpdatePage {
+  pageTitle = element(by.id('jhi-statistics-heading'));
+  saveButton = element(by.id('save-entity'));
+  cancelButton = element(by.id('cancel-save'));
+
+  viewsInput = element(by.id('field_views'));
+  downloadsInput = element(by.id('field_downloads'));
+  exerciseIDInput = element(by.id('field_exerciseID'));
+
+  async getPageTitle(): Promise<string> {
+    return this.pageTitle.getAttribute('jhiTranslate');
+  }
+
+  async setViewsInput(views: string): Promise<void> {
+    await this.viewsInput.sendKeys(views);
+  }
+
+  async getViewsInput(): Promise<string> {
+    return await this.viewsInput.getAttribute('value');
+  }
+
+  async setDownloadsInput(downloads: string): Promise<void> {
+    await this.downloadsInput.sendKeys(downloads);
+  }
+
+  async getDownloadsInput(): Promise<string> {
+    return await this.downloadsInput.getAttribute('value');
+  }
+
+  async setExerciseIDInput(exerciseID: string): Promise<void> {
+    await this.exerciseIDInput.sendKeys(exerciseID);
+  }
+
+  async getExerciseIDInput(): Promise<string> {
+    return await this.exerciseIDInput.getAttribute('value');
+  }
+
+  async save(): Promise<void> {
+    await this.saveButton.click();
+  }
+
+  async cancel(): Promise<void> {
+    await this.cancelButton.click();
+  }
+
+  getSaveButton(): ElementFinder {
+    return this.saveButton;
+  }
+}
+
+export class StatisticsDeleteDialog {
+  private dialogTitle = element(by.id('jhi-delete-statistics-heading'));
+  private confirmButton = element(by.id('jhi-confirm-delete-statistics'));
+
+  async getDialogTitle(): Promise<string> {
+    return this.dialogTitle.getAttribute('jhiTranslate');
+  }
+
+  async clickOnConfirmButton(): Promise<void> {
+    await this.confirmButton.click();
+  }
+}
diff --git a/src/test/javascript/e2e/entities/statistics/statistics.spec.ts b/src/test/javascript/e2e/entities/statistics/statistics.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1fa24ad73de2444d108dfb7f2e64dbe221754c3b
--- /dev/null
+++ b/src/test/javascript/e2e/entities/statistics/statistics.spec.ts
@@ -0,0 +1,73 @@
+import { browser, ExpectedConditions as ec, promise } from 'protractor';
+import { NavBarPage, SignInPage } from '../../page-objects/jhi-page-objects';
+
+import { StatisticsComponentsPage, StatisticsDeleteDialog, StatisticsUpdatePage } from './statistics.page-object';
+
+const expect = chai.expect;
+
+describe('Statistics e2e test', () => {
+  let navBarPage: NavBarPage;
+  let signInPage: SignInPage;
+  let statisticsComponentsPage: StatisticsComponentsPage;
+  let statisticsUpdatePage: StatisticsUpdatePage;
+  let statisticsDeleteDialog: StatisticsDeleteDialog;
+
+  before(async () => {
+    await browser.get('/');
+    navBarPage = new NavBarPage();
+    signInPage = await navBarPage.getSignInPage();
+    await signInPage.autoSignInUsing('admin', 'admin');
+    await browser.wait(ec.visibilityOf(navBarPage.entityMenu), 5000);
+  });
+
+  it('should load Statistics', async () => {
+    await navBarPage.goToEntity('statistics');
+    statisticsComponentsPage = new StatisticsComponentsPage();
+    await browser.wait(ec.visibilityOf(statisticsComponentsPage.title), 5000);
+    expect(await statisticsComponentsPage.getTitle()).to.eq('gitsearchApp.statistics.home.title');
+    await browser.wait(ec.or(ec.visibilityOf(statisticsComponentsPage.entities), ec.visibilityOf(statisticsComponentsPage.noResult)), 1000);
+  });
+
+  it('should load create Statistics page', async () => {
+    await statisticsComponentsPage.clickOnCreateButton();
+    statisticsUpdatePage = new StatisticsUpdatePage();
+    expect(await statisticsUpdatePage.getPageTitle()).to.eq('gitsearchApp.statistics.home.createOrEditLabel');
+    await statisticsUpdatePage.cancel();
+  });
+
+  it('should create and save Statistics', async () => {
+    const nbButtonsBeforeCreate = await statisticsComponentsPage.countDeleteButtons();
+
+    await statisticsComponentsPage.clickOnCreateButton();
+
+    await promise.all([
+      statisticsUpdatePage.setViewsInput('5'),
+      statisticsUpdatePage.setDownloadsInput('5'),
+      statisticsUpdatePage.setExerciseIDInput('5'),
+    ]);
+
+    expect(await statisticsUpdatePage.getViewsInput()).to.eq('5', 'Expected views value to be equals to 5');
+    expect(await statisticsUpdatePage.getDownloadsInput()).to.eq('5', 'Expected downloads value to be equals to 5');
+    expect(await statisticsUpdatePage.getExerciseIDInput()).to.eq('5', 'Expected exerciseID value to be equals to 5');
+
+    await statisticsUpdatePage.save();
+    expect(await statisticsUpdatePage.getSaveButton().isPresent(), 'Expected save button disappear').to.be.false;
+
+    expect(await statisticsComponentsPage.countDeleteButtons()).to.eq(nbButtonsBeforeCreate + 1, 'Expected one more entry in the table');
+  });
+
+  it('should delete last Statistics', async () => {
+    const nbButtonsBeforeDelete = await statisticsComponentsPage.countDeleteButtons();
+    await statisticsComponentsPage.clickOnLastDeleteButton();
+
+    statisticsDeleteDialog = new StatisticsDeleteDialog();
+    expect(await statisticsDeleteDialog.getDialogTitle()).to.eq('gitsearchApp.statistics.delete.question');
+    await statisticsDeleteDialog.clickOnConfirmButton();
+
+    expect(await statisticsComponentsPage.countDeleteButtons()).to.eq(nbButtonsBeforeDelete - 1);
+  });
+
+  after(async () => {
+    await navBarPage.autoSignOut();
+  });
+});
diff --git a/src/test/javascript/spec/app/entities/statistics/statistics-delete-dialog.component.spec.ts b/src/test/javascript/spec/app/entities/statistics/statistics-delete-dialog.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9addde2edc51571177a74cad4b32f4a20f746c50
--- /dev/null
+++ b/src/test/javascript/spec/app/entities/statistics/statistics-delete-dialog.component.spec.ts
@@ -0,0 +1,65 @@
+import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { of } from 'rxjs';
+import { JhiEventManager } from 'ng-jhipster';
+
+import { GitsearchTestModule } from '../../../test.module';
+import { MockEventManager } from '../../../helpers/mock-event-manager.service';
+import { MockActiveModal } from '../../../helpers/mock-active-modal.service';
+import { StatisticsDeleteDialogComponent } from 'app/entities/statistics/statistics-delete-dialog.component';
+import { StatisticsService } from 'app/entities/statistics/statistics.service';
+
+describe('Component Tests', () => {
+  describe('Statistics Management Delete Component', () => {
+    let comp: StatisticsDeleteDialogComponent;
+    let fixture: ComponentFixture<StatisticsDeleteDialogComponent>;
+    let service: StatisticsService;
+    let mockEventManager: MockEventManager;
+    let mockActiveModal: MockActiveModal;
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [GitsearchTestModule],
+        declarations: [StatisticsDeleteDialogComponent],
+      })
+        .overrideTemplate(StatisticsDeleteDialogComponent, '')
+        .compileComponents();
+      fixture = TestBed.createComponent(StatisticsDeleteDialogComponent);
+      comp = fixture.componentInstance;
+      service = fixture.debugElement.injector.get(StatisticsService);
+      mockEventManager = TestBed.get(JhiEventManager);
+      mockActiveModal = TestBed.get(NgbActiveModal);
+    });
+
+    describe('confirmDelete', () => {
+      it('Should call delete service on confirmDelete', inject(
+        [],
+        fakeAsync(() => {
+          // GIVEN
+          spyOn(service, 'delete').and.returnValue(of({}));
+
+          // WHEN
+          comp.confirmDelete(123);
+          tick();
+
+          // THEN
+          expect(service.delete).toHaveBeenCalledWith(123);
+          expect(mockActiveModal.closeSpy).toHaveBeenCalled();
+          expect(mockEventManager.broadcastSpy).toHaveBeenCalled();
+        })
+      ));
+
+      it('Should not call delete service on clear', () => {
+        // GIVEN
+        spyOn(service, 'delete');
+
+        // WHEN
+        comp.cancel();
+
+        // THEN
+        expect(service.delete).not.toHaveBeenCalled();
+        expect(mockActiveModal.dismissSpy).toHaveBeenCalled();
+      });
+    });
+  });
+});
diff --git a/src/test/javascript/spec/app/entities/statistics/statistics-detail.component.spec.ts b/src/test/javascript/spec/app/entities/statistics/statistics-detail.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..948b511d52ed3af775738e33d575e116cae4ea1c
--- /dev/null
+++ b/src/test/javascript/spec/app/entities/statistics/statistics-detail.component.spec.ts
@@ -0,0 +1,37 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ActivatedRoute } from '@angular/router';
+import { of } from 'rxjs';
+
+import { GitsearchTestModule } from '../../../test.module';
+import { StatisticsDetailComponent } from 'app/entities/statistics/statistics-detail.component';
+import { Statistics } from 'app/shared/model/statistics.model';
+
+describe('Component Tests', () => {
+  describe('Statistics Management Detail Component', () => {
+    let comp: StatisticsDetailComponent;
+    let fixture: ComponentFixture<StatisticsDetailComponent>;
+    const route = ({ data: of({ statistics: new Statistics(123) }) } as any) as ActivatedRoute;
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [GitsearchTestModule],
+        declarations: [StatisticsDetailComponent],
+        providers: [{ provide: ActivatedRoute, useValue: route }],
+      })
+        .overrideTemplate(StatisticsDetailComponent, '')
+        .compileComponents();
+      fixture = TestBed.createComponent(StatisticsDetailComponent);
+      comp = fixture.componentInstance;
+    });
+
+    describe('OnInit', () => {
+      it('Should load statistics on init', () => {
+        // WHEN
+        comp.ngOnInit();
+
+        // THEN
+        expect(comp.statistics).toEqual(jasmine.objectContaining({ id: 123 }));
+      });
+    });
+  });
+});
diff --git a/src/test/javascript/spec/app/entities/statistics/statistics-update.component.spec.ts b/src/test/javascript/spec/app/entities/statistics/statistics-update.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ea597398428d138f03ac287db512af85fb95495f
--- /dev/null
+++ b/src/test/javascript/spec/app/entities/statistics/statistics-update.component.spec.ts
@@ -0,0 +1,61 @@
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+import { HttpResponse } from '@angular/common/http';
+import { FormBuilder } from '@angular/forms';
+import { of } from 'rxjs';
+
+import { GitsearchTestModule } from '../../../test.module';
+import { StatisticsUpdateComponent } from 'app/entities/statistics/statistics-update.component';
+import { StatisticsService } from 'app/entities/statistics/statistics.service';
+import { Statistics } from 'app/shared/model/statistics.model';
+
+describe('Component Tests', () => {
+  describe('Statistics Management Update Component', () => {
+    let comp: StatisticsUpdateComponent;
+    let fixture: ComponentFixture<StatisticsUpdateComponent>;
+    let service: StatisticsService;
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [GitsearchTestModule],
+        declarations: [StatisticsUpdateComponent],
+        providers: [FormBuilder],
+      })
+        .overrideTemplate(StatisticsUpdateComponent, '')
+        .compileComponents();
+
+      fixture = TestBed.createComponent(StatisticsUpdateComponent);
+      comp = fixture.componentInstance;
+      service = fixture.debugElement.injector.get(StatisticsService);
+    });
+
+    describe('save', () => {
+      it('Should call update service on save for existing entity', fakeAsync(() => {
+        // GIVEN
+        const entity = new Statistics(123);
+        spyOn(service, 'update').and.returnValue(of(new HttpResponse({ body: entity })));
+        comp.updateForm(entity);
+        // WHEN
+        comp.save();
+        tick(); // simulate async
+
+        // THEN
+        expect(service.update).toHaveBeenCalledWith(entity);
+        expect(comp.isSaving).toEqual(false);
+      }));
+
+      it('Should call create service on save for new entity', fakeAsync(() => {
+        // GIVEN
+        const entity = new Statistics();
+        spyOn(service, 'create').and.returnValue(of(new HttpResponse({ body: entity })));
+        comp.updateForm(entity);
+        // WHEN
+        comp.save();
+        tick(); // simulate async
+
+        // THEN
+        expect(service.create).toHaveBeenCalledWith(entity);
+        expect(comp.isSaving).toEqual(false);
+      }));
+    });
+  });
+});
diff --git a/src/test/javascript/spec/app/entities/statistics/statistics.component.spec.ts b/src/test/javascript/spec/app/entities/statistics/statistics.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1984ede998fde3883cc031f4cc547c3d83839635
--- /dev/null
+++ b/src/test/javascript/spec/app/entities/statistics/statistics.component.spec.ts
@@ -0,0 +1,132 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { of } from 'rxjs';
+import { HttpHeaders, HttpResponse } from '@angular/common/http';
+import { ActivatedRoute, convertToParamMap } from '@angular/router';
+
+import { GitsearchTestModule } from '../../../test.module';
+import { StatisticsComponent } from 'app/entities/statistics/statistics.component';
+import { StatisticsService } from 'app/entities/statistics/statistics.service';
+import { Statistics } from 'app/shared/model/statistics.model';
+
+describe('Component Tests', () => {
+  describe('Statistics Management Component', () => {
+    let comp: StatisticsComponent;
+    let fixture: ComponentFixture<StatisticsComponent>;
+    let service: StatisticsService;
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [GitsearchTestModule],
+        declarations: [StatisticsComponent],
+        providers: [
+          {
+            provide: ActivatedRoute,
+            useValue: {
+              data: of({
+                defaultSort: 'id,asc',
+              }),
+              queryParamMap: of(
+                convertToParamMap({
+                  page: '1',
+                  size: '1',
+                  sort: 'id,desc',
+                })
+              ),
+            },
+          },
+        ],
+      })
+        .overrideTemplate(StatisticsComponent, '')
+        .compileComponents();
+
+      fixture = TestBed.createComponent(StatisticsComponent);
+      comp = fixture.componentInstance;
+      service = fixture.debugElement.injector.get(StatisticsService);
+    });
+
+    it('Should call load all on init', () => {
+      // GIVEN
+      const headers = new HttpHeaders().append('link', 'link;link');
+      spyOn(service, 'query').and.returnValue(
+        of(
+          new HttpResponse({
+            body: [new Statistics(123)],
+            headers,
+          })
+        )
+      );
+
+      // WHEN
+      comp.ngOnInit();
+
+      // THEN
+      expect(service.query).toHaveBeenCalled();
+      expect(comp.statistics && comp.statistics[0]).toEqual(jasmine.objectContaining({ id: 123 }));
+    });
+
+    it('should load a page', () => {
+      // GIVEN
+      const headers = new HttpHeaders().append('link', 'link;link');
+      spyOn(service, 'query').and.returnValue(
+        of(
+          new HttpResponse({
+            body: [new Statistics(123)],
+            headers,
+          })
+        )
+      );
+
+      // WHEN
+      comp.loadPage(1);
+
+      // THEN
+      expect(service.query).toHaveBeenCalled();
+      expect(comp.statistics && comp.statistics[0]).toEqual(jasmine.objectContaining({ id: 123 }));
+    });
+
+    it('should re-initialize the page', () => {
+      // GIVEN
+      const headers = new HttpHeaders().append('link', 'link;link');
+      spyOn(service, 'query').and.returnValue(
+        of(
+          new HttpResponse({
+            body: [new Statistics(123)],
+            headers,
+          })
+        )
+      );
+
+      // WHEN
+      comp.loadPage(1);
+      comp.reset();
+
+      // THEN
+      expect(comp.page).toEqual(0);
+      expect(service.query).toHaveBeenCalledTimes(2);
+      expect(comp.statistics && comp.statistics[0]).toEqual(jasmine.objectContaining({ id: 123 }));
+    });
+
+    it('should calculate the sort attribute for an id', () => {
+      // WHEN
+      comp.ngOnInit();
+      const result = comp.sort();
+
+      // THEN
+      expect(result).toEqual(['id,asc']);
+    });
+
+    it('should calculate the sort attribute for a non-id attribute', () => {
+      // INIT
+      comp.ngOnInit();
+
+      // GIVEN
+      comp.predicate = 'name';
+
+      // WHEN
+      const result = comp.sort();
+
+      // THEN
+      expect(result).toEqual(['name,asc', 'id']);
+    });
+  });
+});
diff --git a/src/test/javascript/spec/app/entities/statistics/statistics.service.spec.ts b/src/test/javascript/spec/app/entities/statistics/statistics.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d631e577e6666f2dff22e79e457cca8b9c0de1b
--- /dev/null
+++ b/src/test/javascript/spec/app/entities/statistics/statistics.service.spec.ts
@@ -0,0 +1,106 @@
+import { TestBed, getTestBed } from '@angular/core/testing';
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { StatisticsService } from 'app/entities/statistics/statistics.service';
+import { IStatistics, Statistics } from 'app/shared/model/statistics.model';
+
+describe('Service Tests', () => {
+  describe('Statistics Service', () => {
+    let injector: TestBed;
+    let service: StatisticsService;
+    let httpMock: HttpTestingController;
+    let elemDefault: IStatistics;
+    let expectedResult: IStatistics | IStatistics[] | boolean | null;
+
+    beforeEach(() => {
+      TestBed.configureTestingModule({
+        imports: [HttpClientTestingModule],
+      });
+      expectedResult = null;
+      injector = getTestBed();
+      service = injector.get(StatisticsService);
+      httpMock = injector.get(HttpTestingController);
+
+      elemDefault = new Statistics(0, 0, 0, 0);
+    });
+
+    describe('Service methods', () => {
+      it('should find an element', () => {
+        const returnedFromService = Object.assign({}, elemDefault);
+
+        service.find(123).subscribe(resp => (expectedResult = resp.body));
+
+        const req = httpMock.expectOne({ method: 'GET' });
+        req.flush(returnedFromService);
+        expect(expectedResult).toMatchObject(elemDefault);
+      });
+
+      it('should create a Statistics', () => {
+        const returnedFromService = Object.assign(
+          {
+            id: 0,
+          },
+          elemDefault
+        );
+
+        const expected = Object.assign({}, returnedFromService);
+
+        service.create(new Statistics()).subscribe(resp => (expectedResult = resp.body));
+
+        const req = httpMock.expectOne({ method: 'POST' });
+        req.flush(returnedFromService);
+        expect(expectedResult).toMatchObject(expected);
+      });
+
+      it('should update a Statistics', () => {
+        const returnedFromService = Object.assign(
+          {
+            views: 1,
+            downloads: 1,
+            exerciseID: 1,
+          },
+          elemDefault
+        );
+
+        const expected = Object.assign({}, returnedFromService);
+
+        service.update(expected).subscribe(resp => (expectedResult = resp.body));
+
+        const req = httpMock.expectOne({ method: 'PUT' });
+        req.flush(returnedFromService);
+        expect(expectedResult).toMatchObject(expected);
+      });
+
+      it('should return a list of Statistics', () => {
+        const returnedFromService = Object.assign(
+          {
+            views: 1,
+            downloads: 1,
+            exerciseID: 1,
+          },
+          elemDefault
+        );
+
+        const expected = Object.assign({}, returnedFromService);
+
+        service.query().subscribe(resp => (expectedResult = resp.body));
+
+        const req = httpMock.expectOne({ method: 'GET' });
+        req.flush([returnedFromService]);
+        httpMock.verify();
+        expect(expectedResult).toContainEqual(expected);
+      });
+
+      it('should delete a Statistics', () => {
+        service.delete(123).subscribe(resp => (expectedResult = resp.ok));
+
+        const req = httpMock.expectOne({ method: 'DELETE' });
+        req.flush({ status: 200 });
+        expect(expectedResult);
+      });
+    });
+
+    afterEach(() => {
+      httpMock.verify();
+    });
+  });
+});
diff --git a/src/test/javascript/spec/app/layouts/navbar/navbar.component.spec.ts b/src/test/javascript/spec/app/layouts/navbar/navbar.component.spec.ts
index eced34b47958a31c3c3b6866d204fe5cc9fb7087..1617dbe4add48b2e8c3a980862bdae1dc0dbfa92 100644
--- a/src/test/javascript/spec/app/layouts/navbar/navbar.component.spec.ts
+++ b/src/test/javascript/spec/app/layouts/navbar/navbar.component.spec.ts
@@ -6,6 +6,8 @@ import { ProfileInfo } from 'app/layouts/profiles/profile-info.model';
 import { NavbarComponent } from 'app/layouts/navbar/navbar.component';
 import { AccountService } from 'app/core/auth/account.service';
 import { ProfileService } from 'app/layouts/profiles/profile.service';
+import { SearchService } from 'app/search/service/search-service'
+import { FormBuilder } from '@angular/forms';
 
 describe('Component Tests', () => {
   describe('Navbar Component', () => {
@@ -13,11 +15,14 @@ describe('Component Tests', () => {
     let fixture: ComponentFixture<NavbarComponent>;
     let accountService: AccountService;
     let profileService: ProfileService;
-
+	let searchService: SearchService;
     beforeEach(async(() => {
       TestBed.configureTestingModule({
         imports: [GitsearchTestModule],
         declarations: [NavbarComponent],
+		providers: [
+          FormBuilder
+      	],
       })
         .overrideTemplate(NavbarComponent, '')
         .compileComponents();
@@ -28,6 +33,7 @@ describe('Component Tests', () => {
       comp = fixture.componentInstance;
       accountService = TestBed.get(AccountService);
       profileService = TestBed.get(ProfileService);
+	  searchService = TestBed.get(SearchService);
     });
 
     it('Should call profileService.getProfileInfo on init', () => {
@@ -48,5 +54,16 @@ describe('Component Tests', () => {
       // THEN
       expect(accountService.isAuthenticated).toHaveBeenCalled();
     });
+
+/* TODO later
+    it('Should call searchService.searchPageDetails on search changed', () => {
+      // WHEN
+      comp.searchChanged();
+
+      // THEN
+      expect(searchService.searchPageDetails).toHaveBeenCalled();
+    });
+*/
   });
+
 });
diff --git a/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content1.json b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content1.json
new file mode 100644
index 0000000000000000000000000000000000000000..c936ac6eaf94a176a00b83c593ef092b1ce83e01
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content1.json
@@ -0,0 +1,97 @@
+{
+          "project" : {
+            "project_id" : 143,
+            "project_name" : "apitest_76_1613574806194",
+            "namespace" : "sharing/health-check-tests/apitest_76_1613574806194",
+            "main_group" : "sharing",
+            "sub_group" : "health-check-tests",
+            "url" : "https://sharing.codeability-austria.uibk.ac.at/sharing/health-check-tests/apitest_76_1613574806194",
+            "visibility" : "private",
+            "archived" : false,
+            "star_count" : 0,
+            "open_issues_count" : 0,
+            "forks_count" : 0,
+            "last_activity_at" : "2021-02-17T15:13:27.733Z",
+            "description" : "Automatically generated testapi"
+          },
+          "file" : {
+            "filename" : "metadata.yml",
+            "path" : "metadata.yml",
+            "commit_id" : "679236d0e8722976c285caa3a5391b6cff74b397",
+            "indexing_date" : "2021-03-10T17:02:58.245815"
+          },
+          "metadata" : {
+            "metadataVersion" : "0.2",
+            "type" : "collection",
+            "collectionContent" : [
+              "src/if-001/if-001.yml",
+              "src/if-002/if-002.yml",
+              "src/if-003/if-003.yml"
+            ],
+            "identifier" : "javaCourseTUWienTest",
+            "structure" : "hierarchical",
+            "version" : "1.0",
+            "status" : "final",
+            "title" : "Meta Test Data 76",
+            "description" : "Dies sind Teile des Einführungskurses an der TU Wien. Momentan hier genutzt zum Testen der Metadateninfrastruktur. Version 1.xxx 3. Zeile",
+            "programmingLanguage" : [
+              "Java"
+            ],
+            "language" : [
+              "de"
+            ],
+            "educationLevel" : "Anfänger, (to be detailed)",
+            "audience" : "Anfaenger",
+            "keyword" : [
+              "Java",
+              "IOTest",
+              "latex",
+              "testing76"
+            ],
+            "license" : "MIT",
+            "creator" : [
+              {
+                "name" : "Stefan Podlipnig",
+                "affiliation" : "TU Wien",
+                "email" : "stefan.podlipnig@tuwien.ac.at"
+              }
+            ],
+            "contributor" : [
+              {
+                "name" : "Daniel Bastta",
+                "affiliation" : "TU Wien",
+                "email" : "daniel.bastta@tuwien.ac.at"
+              },
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              },
+              {
+                "name" : "Kerstin Limbeck",
+                "affiliation" : "TU Wien",
+                "email" : "kerstin.limbeck@tuwien.ac.at"
+              }
+            ],
+            "publisher" : [
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              }
+            ],
+            "format" : [
+              "latex"
+            ],
+            "deprecated" : false,
+            "difficulty" : "simple",
+            "valid" : {
+              "start" : "2020-01-01",
+              "end" : "2030-12-31"
+            },
+            "requires" : [
+              "Java14"
+            ],
+            "image" : "./courseIcon.png"
+          }
+        }
diff --git a/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content2.json b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content2.json
new file mode 100644
index 0000000000000000000000000000000000000000..a1eb515efe8dc405aa1726541ae4ed11ecdf5028
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content2.json
@@ -0,0 +1,97 @@
+{
+          "project" : {
+            "project_id" : 143,
+            "project_name" : "apitest_76_1613574806194",
+            "namespace" : "sharing/health-check-tests/apitest_76_1613574806194",
+            "main_group" : "sharing",
+            "sub_group" : "health-check-tests",
+            "url" : "https://sharing.codeability-austria.uibk.ac.at/sharing/health-check-tests/apitest_76_1613574806194",
+            "visibility" : "private",
+            "archived" : false,
+            "star_count" : 0,
+            "open_issues_count" : 0,
+            "forks_count" : 0,
+            "last_activity_at" : "2021-02-17T15:13:27.733Z",
+            "description" : "Automatically generated testapi"
+          },
+          "file" : {
+            "filename" : "metadata.yml",
+            "path" : "metadata.yml",
+            "commit_id" : "679236d0e8722976c285caa3a5391b6cff74b397",
+            "indexing_date" : "2021-03-10T17:02:58.245815"
+          },
+          "metadata" : {
+            "metadataVersion" : "0.2",
+            "type" : "collection",
+            "collectionContent" : [
+              "src/if-001/if-001.yml",
+              "src/if-002/if-002.yml",
+              "src/if-003/if-003.yml"
+            ],
+            "identifier" : "javaCourseTUWienTest",
+            "structure" : "hierarchical",
+            "version" : "1.0",
+            "status" : "final",
+            "title" : "Meta Test Data 76",
+            "description" : "Dies sind Teile des Einführungskurses an der TU Wien. Momentan hier genutzt zum Testen der Metadateninfrastruktur. Version 1.xxx 3. Zeile",
+            "programmingLanguage" : [
+              "Java"
+            ],
+            "language" : [
+              "de"
+            ],
+            "educationLevel" : "Anfänger, (to be detailed)",
+            "audience" : "Anfaenger",
+            "keyword" : [
+              "Java",
+              "IOTest",
+              "latex",
+              "testing76"
+            ],
+            "license" : "CC-SA-BY 4.0",
+            "creator" : [
+              {
+                "name" : "Stefan Podlipnig",
+                "affiliation" : "TU Wien",
+                "email" : "stefan.podlipnig@tuwien.ac.at"
+              }
+            ],
+            "contributor" : [
+              {
+                "name" : "Daniel Bastta",
+                "affiliation" : "TU Wien",
+                "email" : "daniel.bastta@tuwien.ac.at"
+              },
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              },
+              {
+                "name" : "Kerstin Limbeck",
+                "affiliation" : "TU Wien",
+                "email" : "kerstin.limbeck@tuwien.ac.at"
+              }
+            ],
+            "publisher" : [
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              }
+            ],
+            "format" : [
+              "latex"
+            ],
+            "deprecated" : false,
+            "difficulty" : "simple",
+            "valid" : {
+              "start" : "2020-01-01",
+              "end" : "2030-12-31"
+            },
+            "requires" : [
+              "Java14"
+            ],
+            "image" : "./courseIcon.png"
+          }
+        }
\ No newline at end of file
diff --git a/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content3.json b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content3.json
new file mode 100644
index 0000000000000000000000000000000000000000..719cb9b63bde7e531d35a830209c59ec020ecf1d
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/content3.json
@@ -0,0 +1,97 @@
+{
+          "project" : {
+            "project_id" : 143,
+            "project_name" : "apitest_76_1613574806194",
+            "namespace" : "sharing/health-check-tests/apitest_76_1613574806194",
+            "main_group" : "sharing",
+            "sub_group" : "health-check-tests",
+            "url" : "https://sharing.codeability-austria.uibk.ac.at/sharing/health-check-tests/apitest_76_1613574806194",
+            "visibility" : "public",
+            "archived" : false,
+            "star_count" : 0,
+            "open_issues_count" : 0,
+            "forks_count" : 0,
+            "last_activity_at" : "2021-02-17T15:13:27.733Z",
+            "description" : "Automatically generated testapi"
+          },
+          "file" : {
+            "filename" : "metadata.yml",
+            "path" : "metadata.yml",
+            "commit_id" : "679236d0e8722976c285caa3a5391b6cff74b397",
+            "indexing_date" : "2021-03-10T17:02:58.245815"
+          },
+          "metadata" : {
+            "metadataVersion" : "0.2",
+            "type" : "collection",
+            "collectionContent" : [
+              "src/if-001/if-001.yml",
+              "src/if-002/if-002.yml",
+              "src/if-003/if-003.yml"
+            ],
+            "identifier" : "javaCourseTUWienTest",
+            "structure" : "hierarchical",
+            "version" : "1.0",
+            "status" : "final",
+            "title" : "Meta Test Data 76",
+            "description" : "Dies sind Teile des Einführungskurses an der TU Wien. Momentan hier genutzt zum Testen der Metadateninfrastruktur. Version 1.xxx 3. Zeile",
+            "programmingLanguage" : [
+              "Java"
+            ],
+            "language" : [
+              "de"
+            ],
+            "educationLevel" : "Anfänger, (to be detailed)",
+            "audience" : "Anfaenger",
+            "keyword" : [
+              "Java",
+              "IOTest",
+              "latex",
+              "testing76"
+            ],
+            "license" : "MIT",
+            "creator" : [
+              {
+                "name" : "Stefan Podlipnig",
+                "affiliation" : "TU Wien",
+                "email" : "stefan.podlipnig@tuwien.ac.at"
+              }
+            ],
+            "contributor" : [
+              {
+                "name" : "Daniel Bastta",
+                "affiliation" : "TU Wien",
+                "email" : "daniel.bastta@tuwien.ac.at"
+              },
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              },
+              {
+                "name" : "Kerstin Limbeck",
+                "affiliation" : "TU Wien",
+                "email" : "kerstin.limbeck@tuwien.ac.at"
+              }
+            ],
+            "publisher" : [
+              {
+                "name" : "Andreas Merckel",
+                "affiliation" : "TU Wien",
+                "email" : "andreas.merkel@tuwien.ac.at"
+              }
+            ],
+            "format" : [
+              "latex"
+            ],
+            "deprecated" : false,
+            "difficulty" : "simple",
+            "valid" : {
+              "start" : "2020-01-01",
+              "end" : "2030-12-31"
+            },
+            "requires" : [
+              "Java14"
+            ],
+            "image" : "./courseIcon.png"
+          }
+        }
\ No newline at end of file
diff --git a/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/es_metadata.schema.json b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/es_metadata.schema.json
new file mode 100644
index 0000000000000000000000000000000000000000..6922d5b091ccc5ad8f7f3a88720bb657a7cda053
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/repository/search/testData/es_metadata.schema.json
@@ -0,0 +1,200 @@
+{
+  "idx_metadata_1" : {
+    "aliases" : {
+      "metadata" : {
+        "is_write_index" : true
+      }
+    },
+    "settings" : {
+      "index.mapping.total_fields.limit" : "2000",
+       "number_of_shards" : "1"
+      }
+    },
+    "mappings" : {
+      "_doc" : {
+
+        "properties" : {
+          "file" : {
+            "properties" : {
+              "commit_id" : {
+                "type" : "keyword"
+              },
+              "filename" : {
+                "type" : "keyword"
+              },
+              "indexing_date" : {
+                "type" : "date",
+                "format" : "dateOptionalTime"
+              },
+              "path" : {
+                "type" : "keyword"
+              }
+            }
+          },
+          "metadata" : {
+            "properties" : {
+              "audience" : {
+                "type" : "text"
+              },
+              "collectionContent" : {
+                "type" : "keyword"
+              },
+              "contributor" : {
+                "properties" : {
+                  "affiliation" : {
+                    "type" : "text"
+                  },
+                  "email" : {
+                    "type" : "text"
+                  },
+                  "name" : {
+                    "type" : "text"
+                  }
+                }
+              },
+              "creator" : {
+                "properties" : {
+                  "affiliation" : {
+                    "type" : "text"
+                  },
+                  "email" : {
+                    "type" : "text"
+                  },
+                  "name" : {
+                    "type" : "text"
+                  }
+                }
+              },
+              "deprecated" : {
+                "type" : "boolean"
+              },
+              "description" : {
+                "type" : "text"
+              },
+              "difficulty" : {
+                "type" : "keyword"
+              },
+              "educationLevel" : {
+                "type" : "text"
+              },
+              "format" : {
+                "type" : "keyword"
+              },
+              "identifier" : {
+                "type" : "keyword"
+              },
+              "image" : {
+                "type" : "keyword"
+              },
+              "keyword" : {
+                "type" : "keyword"
+              },
+              "language" : {
+                "type" : "keyword"
+              },
+              "license" : {
+                "type" : "keyword"
+              },
+              "metadataVersion" : {
+                "type" : "keyword"
+              },
+              "programmingLanguage" : {
+                "type" : "keyword"
+              },
+              "publisher" : {
+                "properties" : {
+                  "affiliation" : {
+                    "type" : "text"
+                  },
+                  "email" : {
+                    "type" : "text"
+                  },
+                  "name" : {
+                    "type" : "text"
+                  }
+                }
+              },
+              "requires" : {
+                "type" : "text"
+              },
+              "source" : {
+                "type" : "keyword"
+              },
+              "status" : {
+                "type" : "keyword"
+              },
+              "structure" : {
+                "type" : "keyword"
+              },
+              "timeRequired" : {
+                "type" : "keyword"
+              },
+              "title" : {
+                "type" : "text"
+              },
+              "type" : {
+                "type" : "keyword"
+              },
+              "valid" : {
+                "properties" : {
+                  "end" : {
+                    "type" : "keyword"
+                  },
+                  "start" : {
+                    "type" : "keyword"
+                  }
+                }
+              },
+              "version" : {
+                "type" : "keyword"
+              }
+            }
+          },
+          "project" : {
+            "properties" : {
+              "archived" : {
+                "type" : "boolean"
+              },
+              "description" : {
+                "type" : "text"
+              },
+              "forks_count" : {
+                "type" : "integer"
+              },
+              "last_activity_at" : {
+                "type" : "date",
+                "format" : "dateOptionalTime"
+              },
+              "main_group" : {
+                "type" : "keyword"
+              },
+              "namespace" : {
+                "type" : "keyword"
+              },
+              "open_issues_count" : {
+                "type" : "integer"
+              },
+              "project_id" : {
+                "type" : "long"
+              },
+              "project_name" : {
+                "type" : "keyword"
+              },
+              "star_count" : {
+                "type" : "integer"
+              },
+              "sub_group" : {
+                "type" : "keyword"
+              },
+              "url" : {
+                "type" : "keyword"
+              },
+              "visibility" : {
+                "type" : "keyword"
+              }
+            }
+          }
+        }
+      }
+    }
+    }
diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/junit-quality-tests-exercise-master.zip b/src/test/resources/at/ac/uibk/gitsearch/service/testData/junit-quality-tests-exercise-master.zip
new file mode 100644
index 0000000000000000000000000000000000000000..fb0ceb38304a5d24f0431ac09617eeb9930b4ce0
Binary files /dev/null and b/src/test/resources/at/ac/uibk/gitsearch/service/testData/junit-quality-tests-exercise-master.zip differ
diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData1.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData1.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c6fb5218190ea673e5ae740df484949147226e83
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData1.yaml
@@ -0,0 +1,31 @@
+metadataVersion: 0.2
+type: programming exercise
+# format (not used here)
+identifier: simpleIO
+structure: atomic # one from atomic, networked, hierarchical, linear
+version: "1.0" # just a version tag
+status: final # one fo draft, final, revised, unavailable
+title: Simple IO Test
+description: This is a programming exercise to demonstrate a simple IO testing framework.
+programmingLanguage: [JAVA]
+language: [de]
+educationLevel: "high school"  # just for demonstration
+"audience": {
+    "@type": "EducationalAudience",
+    "educationalRole": "student"
+  }
+timeRequired: 0:05:00 # [hh:mm:ss]
+keyword: [Java, IOTest, artemis]
+license: CC-SA-BY 4.0 # mandatory
+creator: 
+  - {name: "Breu Michael", affiliation: "University of Innsbruck", email: "c703257@uibk.ac.at"}
+publisher:
+  - {name: "Breu Michael", affiliation: "University of Innsbruck", email: "c703257@uibk.ac.at"}
+deprecated: false
+difficulty: simple
+source: 
+  - "cf. cunit"
+contributor:
+  - {name: "Manuel Seywald", affiliation: "University of Klagenfurt", email: "maseywald@edu.aau.at"}
+requires: []  # empty, t.b.d. later
+image: 'https://sharing-codeability.uibk.ac.at/static/CodeAbility%20Austria-Dateien/Partner_UIBK.png'
diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData2.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData2.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..de25e0cf20cbc351759e4d021f3f0b04e031b457
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData2.yaml
@@ -0,0 +1,29 @@
+metadataVersion: "0.2"
+type: collection
+identifier: javaCourseTUWienTest
+structure: hierarchical # one from atomic, networked, hierarchical, linear
+version: "1.0" # just a version tag
+status: final # one fo draft, final, revised, unavalable
+title: Java Einführungskurs der TU Wien (Test)
+description: Dies sind Teile des Einfhrungskurses an der TU Wien. Momentan hier genutzt zum Testen der Metadateninfrastruktur.
+programmingLanguage: 
+  - JAVA
+language: [de]
+educationLevel: "Anfänger, (to be detailed)"
+audience: "Anfänger"
+keyword: [Java, IOTest, latex]
+license: MIT # mandatory
+creator: 
+  - {name: "Stefan Podlipnig", affiliation: "TU Wien", email: "stefan.podlipnig@tuwien.ac.at"}
+contributor:
+  - {name: "Daniel Bastta", affiliation: "TU Wien", email: "daniel.bastta@tuwien.ac.at"}
+  - {name: "Andreas Merckel", affiliation: "TU Wien", email: "andreas.merkel@tuwien.ac.at"}
+  - {name: "Kerstin Limbeck", affiliation: "TU Wien", email: "kerstin.limbeck@tuwien.ac.at"}
+publisher:
+  - {name: "Andreas Merckel", affiliation: "TU Wien", email: "andreas.merkel@tuwien.ac.at"}
+
+format: [latex]
+deprecated: false
+difficulty: EASY
+requires: [Java14]
+image: 'https://sharing-codeability.uibk.ac.at/static/CodeAbility%20Austria-Dateien/Partner_UIBK.png'
diff --git a/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData3.yaml b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData3.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c3086b1db5ce9d8fd5619258dc5802f7dfac4f7d
--- /dev/null
+++ b/src/test/resources/at/ac/uibk/gitsearch/service/testData/metaData3.yaml
@@ -0,0 +1,31 @@
+metadataVersion: 0.2
+type: collection
+# format (not used here)
+identifier: collection1
+structure: atomic # one from atomic, networked, hierarchical, linear
+version: "1.0" # just a version tag
+status: final # one fo draft, final, revised, unavailable
+title: Simple IO Test
+description: This is a programming exercise to demonstrate a simple IO testing framework.
+programmingLanguage: [JAVA]
+language: [de]
+educationLevel: "high school"  # just for demonstration
+"audience": {
+    "@type": "EducationalAudience",
+    "educationalRole": "student"
+  }
+timeRequired: 0:05:00 # [hh:mm:ss]
+keyword: [Java, IOTest, artemis]
+license: CC-SA-BY 4.0 # mandatory
+creator: 
+  - {name: "Breu Michael", affiliation: "University of Innsbruck", email: "c703257@uibk.ac.at"}
+publisher:
+  - {name: "Breu Michael", affiliation: "University of Innsbruck", email: "c703257@uibk.ac.at"}
+deprecated: false
+difficulty: simple
+source: 
+  - "cf. cunit"
+contributor:
+  - {name: "Manuel Seywald", affiliation: "University of Klagenfurt", email: "maseywald@edu.aau.at"}
+requires: []  # empty, t.b.d. later
+image: 'https://sharing-codeability.uibk.ac.at/static/CodeAbility%20Austria-Dateien/Partner_UIBK.png'
diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml
index bc4c6f2764a9fd2c73bfe57099c59fa374b4178c..9f046f3ebb750d6e90b86b4dd19f65cd9e611d18 100644
--- a/src/test/resources/config/application.yml
+++ b/src/test/resources/config/application.yml
@@ -52,6 +52,9 @@ spring:
       properties:
         path:
           home: target/elasticsearch
+  elasticsearch:
+    rest:
+      uris: http://localhost:29200
   liquibase:
     contexts: test
   mail:
@@ -73,6 +76,16 @@ spring:
         size: 1
   thymeleaf:
     mode: HTML
+  security:
+    oauth2:
+      client:
+        provider:
+          gitlabOidc:
+            issuer-uri: https://sharing.codeability-austria.uibk.ac.at
+        registration:
+          gitlabOidc:
+            client-id: 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24
+            client-secret: 6f480635241f420a361581f4837594ea6f48f5ee6f515c1aa89f325dd922dbb0
 
 server:
   port: 10344
@@ -99,6 +112,10 @@ jhipster:
     from: test@localhost
     base-url: http://127.0.0.1:8080
   security:
+    oauth2:
+# TODO: audience seems not really relevant, could be omitted? It is identical with client-id above
+      audience:
+        - 149276ac11138d9ba72fb3cd12815e3fa2f372866df0eac0f7d1aae5fdffea24
     authentication:
       jwt:
         # This token must be encoded using Base64 (you can type `echo 'secret-key'|base64` on your command line)
@@ -120,3 +137,10 @@ jhipster:
 # ===================================================================
 
 # application:
+application:
+  search:
+    highlight-pre: <mark><strong>
+    highlight-post: </strong></mark>
+  gitlab:
+    url: http://dev-exchange.codeability.org
+    mainGroup: exchange
diff --git a/tslint.json b/tslint.json
index 52fb92500384158e56e3286ff140f5c1412302a9..d8124f7563bc12314344c9867172d5c9f44844e5 100644
--- a/tslint.json
+++ b/tslint.json
@@ -11,7 +11,6 @@
     "use-lifecycle-interface": true,
     "use-pipe-transform-interface": false,
     "component-class-suffix": true,
-    "directive-class-suffix": true,
-    "typedef": [true, "call-signature"]
+    "directive-class-suffix": true
   }
 }