diff --git a/app/Models/Key.php b/app/Models/Key.php
index 7748e1046cfc1de1fa3842435556a12c5bb2cf94..4dbc633abbe121595fb3a63f3028b969300d5c65 100644
--- a/app/Models/Key.php
+++ b/app/Models/Key.php
@@ -2,19 +2,23 @@
 
 namespace App\Models;
 
+use Illuminate\Support\Facades\Redis;
+use Request;
+
 class Key
 {
     public $key;
     public $status; # valid key = true, invalid key = false, unidentified key = null
-    private $keyserver = "https://key.metager.de/";
+    //private $keyserver = "https://key.metager.de/";
+    private $keyserver = "https://dev.key.metager.de/";
 
     public function __construct($key, $status = null)
     {
         $this->key = $key;
         $this->status = $status;
-        if (getenv("APP_ENV") !== "production") {
+        /*if (getenv("APP_ENV") !== "production") {
             $this->keyserver = "https://dev.key.metager.de/";
-        }
+        }*/
     }
 
     # always returns true or false
@@ -22,6 +26,29 @@ class Key
     {
         if ($this->key !== '' && $this->status === null) {
             $this->updateStatus();
+            if(empty($this->status)){
+                // The user provided an invalid key which we will log to fail2ban
+                $fail2banEnabled = config("metager.metager.fail2ban_enabled");
+                if(empty($fail2banEnabled) || !$fail2banEnabled || !env("fail2banurl", false) || !env("fail2banuser") || !env("fail2banpassword")){
+                    return false;
+                }
+
+                // Submit fetch job to worker
+                $mission = [
+                        "resulthash" => "captcha",
+                        "url" => env("fail2banurl") . "/mgkeytry/",
+                        "useragent" => "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0",
+                        "username" => env("fail2banuser"),
+                        "password" => env("fail2banpassword"),
+                        "headers" => [
+                            "ip" => Request::ip()
+                        ],
+                        "cacheDuration" => 0,
+                        "name" => "Captcha",
+                    ];
+                $mission = json_encode($mission);
+                Redis::rpush(\App\MetaGer::FETCHQUEUE_KEY, $mission);
+            }
         }
         if ($this->status === null || $this->status === false) {
             return false;
@@ -33,10 +60,23 @@ class Key
     
     public function updateStatus()
     {
+        $authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
+
+        $opts = array(
+            'http' => array(
+                'method' => 'GET',
+                'header' => 'Authorization: Basic ' . $authKey ,
+            ),
+        );
+        $context = stream_context_create($opts);
+
         try {
-            $link = $this->keyserver . urlencode($this->key) . "/request-permission/api-access";
-            $result = json_decode(file_get_contents($link));
-            if ($result->{'api-access'} == true) {
+            $link = $this->keyserver . "v2/key/". urlencode($this->key);
+            $result = json_decode(file_get_contents($link, false, $context));
+            if ($result->{'apiAccess'} == 'unlimited') {
+                $this->status = true;
+                return true;
+            } else if ($result->{'apiAccess'} == 'normal' && $result->{'adFreeSearches'} > 0){
                 $this->status = true;
                 return true;
             } else {
@@ -50,13 +90,17 @@ class Key
 
     public function requestPermission()
     {
+        $authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
         $postdata = http_build_query(array(
             'dummy' => 0,
         ));
         $opts = array(
             'http' => array(
                 'method' => 'POST',
-                'header' => 'Content-type: application/x-www-form-urlencoded',
+                'header' => [
+                    'Content-type: application/x-www-form-urlencoded',
+                    'Authorization: Basic ' . $authKey
+                ],
                 'content' => $postdata,
             ),
         );
@@ -64,9 +108,9 @@ class Key
         $context = stream_context_create($opts);
 
         try {
-            $link = $this->keyserver . urlencode($this->key) . "/request-permission/api-access";
+            $link = $this->keyserver . "v2/key/". urlencode($this->key) . "/request-permission";
             $result = json_decode(file_get_contents($link, false, $context));
-            if ($result->{'api-access'} == true) {
+            if ($result->{'apiAccess'} == true) {
                 return true;
             } else {
                 $this->status = false;
diff --git a/resources/js/donation.js b/resources/js/donation.js
index f6ad022a560959d2701fc652228fed19b748d187..9c649d0b1913578c145a6627ba5f7dd1e2e824eb 100644
--- a/resources/js/donation.js
+++ b/resources/js/donation.js
@@ -1,7 +1,9 @@
-$(document).ready(function () {
-    $(".amount-custom").click(function () {
-        setTimeout(function () {
-            $("#custom-amount").focus();
-        }, 100)
+document.addEventListener("DOMContentLoaded", (event) => {
+    document.querySelectorAll(".amount-custom").forEach(element => {
+        element.onclick = (e) => {
+            setTimeout(() => {
+                document.querySelector("#custom-amount").focus();
+            }, 100);
+        }
     });
 });
\ No newline at end of file
diff --git a/resources/js/keyboardNavigation.js b/resources/js/keyboardNavigation.js
index 70c2e476eb0e51f27fb449f984e11f0a98c0f1c0..d22490402f42c65e3f321094030b552d479f6367 100644
--- a/resources/js/keyboardNavigation.js
+++ b/resources/js/keyboardNavigation.js
@@ -2,27 +2,27 @@
  * Flag ctrlInfo is used for initial display of the navigation box
  */
 var ctrlInfo = false;
-
-$(document).ready(function () {
+document.addEventListener("DOMContentLoaded", (event) => {
     // Add entry point for tabbing to the first result
-    $('.result, .ad').first().attr("id", "results-entry");
+    document.querySelectorAll('.result, .ad')[0].setAttribute("id", "results-entry");
     // Initially focus the searchbar
 });
 
 /**
  * Simulate a click on enter keypress when focused on labels
  */
-$('label').on('keydown', function (e) {
-    if (e.keyCode == '13') {
-        $(this).click();
-        $('a', this)
+document.querySelectorAll('label').forEach((element) => {
+    element.onkeydown = (e) => {
+        if (e.keyCode == '13') {
+            e.srcElement.click();
+        }
     }
 });
 
 /**
  * Handles tab keypress and escape keypress
  */
-$(document).on('keydown', function (e) {
+document.onkeydown = (e) => {
     e = e || window.event;
     // On first tab keypress there is special behaviour and the ctrlInfo flag is set
     if (!ctrlInfo && e.keyCode == '9') {
@@ -32,14 +32,15 @@ $(document).on('keydown', function (e) {
     } else if (e.keyCode == '27') {
         escKeyPressed();
     }
-});
+};
+
 
 /**
  * Shows the navigation box and focuses the first <a> tag 
  */
 function focusNavBox() {
-    $('#keyboard-nav-info').show();
-    $('#keyboard-nav-info a').first().focus();
+    document.querySelector("#keyboard-nav-info").style.display = "inherit";
+    document.querySelectorAll("#keyboard-nav-info a")[0].focus();
 }
 
 /**
@@ -47,34 +48,36 @@ function focusNavBox() {
  */
 function escKeyPressed() {
     focusNavBox();
-    $('input[type="checkbox"]').removeAttr('checked');
+    document.querySelectorAll('input[type="checkbox"]').forEach((element) => {
+        element.checked = false;
+    });
 }
 
 /**
  * Focuses the first <a> tag of the first result 
  */
 function focusResults() {
-    $('#results-entry .result-title a').focus();
+    document.querySelector('#results-entry .result-title a').focus();
 }
 
 /**
  * Focuses the first <a> tag of the focus options
  */
 function focusFoki() {
-    $('#foki a').first().focus();
+    document.querySelector("#foki a").focus();
 }
 
 /**
  * Focuses the search settings
  */
 function focusSettings() {
-    $('#settings a').focus();
+    document.querySelector('#settings a').focus();
 }
 
 /**
  * Focuses the first <tag> of the sidebar
  */
 function focusNavigation() {
-    $('#sidebarToggle').prop('checked', true);
-    $('.sidebar-list a').first().focus();
+    document.querySelector("#sidebarToggle").checked = true;
+    document.querySelector(".sidebar-list a").focus();
 }
\ No newline at end of file
diff --git a/resources/js/result-saver.js b/resources/js/result-saver.js
index 7776f026fbf06928a7df376f786fad53002b1e4d..07703da5c63afe4c347808ff673692a14cd53169 100644
--- a/resources/js/result-saver.js
+++ b/resources/js/result-saver.js
@@ -1,16 +1,33 @@
+if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
+  // Yes, there's really no need for `Object.defineProperty` here
+  NodeList.prototype.forEach = Array.prototype.forEach;
+}
+
 /**
  * All results are stored in the global object 'results'
- */
+  */
 results = new Results();
 
-$(document).ready(function () {
+document.addEventListener("DOMContentLoaded", (event) => {
+  if(document.readyState == 'complete'){
+    initResultSaver();
+  }else{
+    document.addEventListener("readystatechange", e => {
+      if (document.readyState === 'complete') {
+        initResultSaver();
+      }
+    });
+  }
+});
+
+function initResultSaver() {
   // Add all saved results
   results.loadAllResults();
   // Sort all results
   results.sortResults();
   // Update the visualization
   results.updateResultPageInterface();
-});
+}
 
 /**
  * Load all saved results and sort them
@@ -99,7 +116,7 @@ Results.prototype.deleteAllResults = function () {
     }
   }
   // Remove all keys saved in the keys array from localstorage
-  $.each(keys, function (index, value) {
+  keys.forEach(value => {
     localStorage.removeItem(value);
   });
 };
@@ -110,22 +127,25 @@ Results.prototype.deleteAllResults = function () {
 Results.prototype.updateResultPageInterface = function () {
   if (this.results.length === 0) {
     // If there are no saved-results left, remove the savedFoki element
-    $('#savedFoki').remove();
+    document.querySelectorAll('#savedFoki').forEach(element => {
+      element.remove();
+    });
     return;
   }
-  if ($('#savedFoki').length === 0) {
+  if (document.querySelector('#savedFoki') == null || document.querySelector('#savedFoki').length === 0) {
     // If there is no savedFoki element yet, create it
-    var tabPanel = $('\
-        <div id="savedFoki">\
-          <h1>' + t('result-saver.title') + '</h1>\
-        </div>\
-        ');
-    $('#additions-container').prepend(tabPanel);
+    var template = document.createElement("div");
+    template.innerHTML = '<div id="savedFoki">\
+      <h1>' + t('result-saver.title') + '</h1>\
+    </div>';
+    var tabPanel = template.firstChild;
+    document.querySelector('#additions-container').prepend(tabPanel);
   } else {
     // If there already is a savedFoki element, get it
-    $('#savedFoki').html('');
-    var tabPanel = $('#savedFoki');
+    var tabPanel = document.querySelector("#savedFoki");
+    tabPanel.innerHTML = "";
   }
+
   // Add the full savedFoki element to the tabPanel
   this.addToContainer(tabPanel);
 };
@@ -138,49 +158,50 @@ Results.prototype.updateResultPageInterface = function () {
 Results.prototype.addToContainer = function (container) {
   // Create the saver-options element, which is a bar containing 
   // options for filtering, sorting and deleting all results
-  var options = $('\
-    <div id="saver-options">\
-      <div class="saver-option saver-option-filter">\
-        <input style="font-family: \'Font Awesome 5 Free\', sans-serif;" class="form-control" type="text" placeholder="&#xf0b0 ' + t('result-saver.filter') + '">\
-      </div>\
-      <div class="saver-option saver-option-sort">\
-        <select class="form-control" style="font-family: \'Font Awesome 5 Free\', sans-serif;">\
-          <option value="chronological" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf017 ' + t('result-saver.sort.chronological') + '</option>\
-          <option value="rank" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf162 ' + t('result-saver.sort.ranking') + '</option>\
-          <option value="alphabetical" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf15d ' + t('result-saver.sort.alphabetical') + '</option>\
-        </select>\
-      </div>\
-      <div class="saver-option saver-option-delete">\
-        <button class="btn btn-danger btn-md" id="saver-options-delete-btn">\
-          <i class="fa fa-trash-o" aria-hidden="true"></i>\
-          ' + t('result-saver.deleteAll') + '\
-        </button>\
-      </div>\
+  var template = document.createElement("div");
+
+  template.innerHTML = '<div id="saver-options">\
+    <div class="saver-option saver-option-filter">\
+      <input style="font-family: \'Font Awesome 5 Free\', sans-serif;" class="form-control" type="text" placeholder="&#xf0b0 ' + t('result-saver.filter') + '">\
+    </div>\
+    <div class="saver-option saver-option-sort">\
+      <select class="form-control" style="font-family: \'Font Awesome 5 Free\', sans-serif;">\
+        <option value="chronological" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf017 ' + t('result-saver.sort.chronological') + '</option>\
+        <option value="rank" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf162 ' + t('result-saver.sort.ranking') + '</option>\
+        <option value="alphabetical" style="font-family: \'Font Awesome 5 Free\', sans-serif;">&#xf15d ' + t('result-saver.sort.alphabetical') + '</option>\
+      </select>\
+    </div>\
+    <div class="saver-option saver-option-delete">\
+      <button class="btn btn-danger btn-md" id="saver-options-delete-btn">\
+        <i class="fa fa-trash-o" aria-hidden="true"></i>\
+        ' + t('result-saver.deleteAll') + '\
+      </button>\
     </div>\
-  ');
+  </div>';
+  var options = template.firstChild;
 
   // Set the initial value for the sorting select, based on this.sort
-  $(options).find('select').val(this.sort);
+  options.querySelector("select").value = this.sort;
 
   // Add the saver-options element to the given container
-  $(container).append(options);
+  container.append(options);
 
   /* ~~~ Filter ~~~ */
   // When the user is done typing into the filter input field,
   // Filter all results, only showing ones that contain the filer
-  $(options).find('input').keyup(function () {
+  options.querySelectorAll("input").forEach(element => {
     // Get the entered filter
-    var search = $(this).val();
+    var search = element.value;
     // Hide all results that do not contain the entered filter
-    $('#savedFoki .saved-result-content').each(function (index, value) {
+    document.querySelectorAll('#savedFoki .saved-result-content').forEach(value => {
       // check for filter in all of the elements html-content
-      var html = $(this).html();
+      var html = value.innerHTML;
       if (html.toLowerCase().indexOf(search.toLowerCase()) === -1) {
         // hide entire result block
-        $(value).parent().addClass('hidden');
+        value.parentNode.classList.add('hidden');
       } else {
         // show entire result block
-        $(value).parent().removeClass('hidden');
+        value.parentNode.classList.remove("hidden");
       }
     });
   });
@@ -188,23 +209,27 @@ Results.prototype.addToContainer = function (container) {
   /* ~~~ Sort ~~~ */
   // When the sorting select value is changed, 
   // Sort all results with the selected sorting function and update their appearance
-  $(options).find('select').change(function () {
-    var sort = $(this).val();
-    results.sort = sort;
-    results.sortResults(sort).updateResultPageInterface();
+  options.querySelectorAll("select").forEach(element => {
+    element.onchange = (e) => {
+      var sort = element.value;
+      results.sort = sort;
+      results.sortResults(sort).updateResultPageInterface();
+    };
   });
 
   /* ~~~ Delete ~~~ */
   // When the delete button is clicked,
   // Delete all results and update their appearance
-  $(options).find('#saver-options-delete-btn').click(function (event) {
-    results.deleteAllResults();
-    results.updateResultPageInterface();
+  options.querySelectorAll('#saver-options-delete-btn').forEach(element => {
+    element.onclick = (e) => {
+      results.deleteAllResults();
+      results.updateResultPageInterface();
+    }
   });
 
   // Append all results available
-  $.each(this.results, function (index, result) {
-    $(container).append(result.toHtml());
+  this.results.forEach(result => {
+    container.append(result.toHtml());
   });
 };
 
@@ -272,7 +297,7 @@ Result.prototype.load = function () {
   this.anonym = result.anonym;
   this.description = result.description;
   this.added = result.added;
-  this.index = -result.index;
+  this.index = result.index;
   this.rank = result.rank;
 
   return true;
@@ -351,46 +376,51 @@ Result.prototype.remove = function () {
  */
 Result.prototype.toHtml = function () {
   // Create the saved-result element
-  var result = $('\
-    <div class="saved-result result" data-count="' + this.index + '">\
-      <div class="saved-result-remover remover" title="' + t('result-saver.delete') + '">\
-        <i class="fa fa-trash"></i>\
-      </div>\
-      <div class="saved-result-content">\
-        <div class="result-header">\
-          <div class="result-headline">\
-            <h2 class="result-title">\
-              <a href="' + this.link + '" target="_blank" data-count="1" rel="noopener">\
-                ' + this.title + '\
-              </a>\
-            </h2>\
-            <a class="result-hoster" href="' + this.hosterLink + '" target="_blank" data-count="1" rel="noopener">\
-              ' + this.hosterName + '\
+  var template = document.createElement("div");
+  template.innerHTML = '<div class="saved-result result" data-count="' + this.index + '">\
+    <div class="saved-result-remover remover" title="' + t('result-saver.delete') + '">\
+      <i class="fa fa-trash"></i>\
+    </div>\
+    <div class="saved-result-content">\
+      <div class="result-header">\
+        <div class="result-headline">\
+          <h2 class="result-title">\
+            <a href="' + this.link + '" target="_blank" data-count="1" rel="noopener">\
+              ' + this.title + '\
             </a>\
-          </div>\
-          <a class="result-link" href="' + this.link + '" target="_blank" rel="noopener">\
-            ' + this.anzeigeLink + '\
-          </a>\
-        <div class="result-body">\
-          <div class="description">' + this.description + '</div>\
-        </div>\
-        <div class="result-footer">\
-          <a class="result-open" href="' + this.link + '" target="_self" rel="noopener">\
-            ' + t('result-saver.save.this') + '\
-          </a>\
-          <a class="result-open" href="' + this.link + '" target="_blank" rel="noopener">\
-            ' + t('result-saver.save.newtab') + '\
-          </a>\
-          <a class="result-open-proxy" onmouseover="$(this).popover(\'show\');" onmouseout="$(this).popover(\'hide\');" data-toggle="popover" data-placement="auto right" data-container="body" data-content="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übertragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt." href="' + this.anonym + '" target="_blank" rel="noopener" data-original-title="" title="">\
-            ' + t('result-saver.save.anonymous') + '\
+          </h2>\
+          <a class="result-hoster" href="' + this.hosterLink + '" target="_blank" data-count="1" rel="noopener">\
+            ' + this.hosterName + '\
           </a>\
         </div>\
+        <a class="result-link" href="' + this.link + '" target="_blank" rel="noopener">\
+          ' + this.anzeigeLink + '\
+        </a>\
+      <div class="result-body">\
+        <div class="description">' + this.description + '</div>\
+      </div>\
+      <div class="result-footer">\
+        <a class="result-open" href="' + this.link + '" target="_self" rel="noopener">\
+          ' + t('result-saver.save.this') + '\
+        </a>\
+        <a class="result-open" href="' + this.link + '" target="_blank" rel="noopener">\
+          ' + t('result-saver.save.newtab') + '\
+        </a>\
+        <a class="result-open-proxy" href="' + this.anonym + '" target="_blank" rel="noopener" title="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übertragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt.">\
+          ' + t('result-saver.save.anonymous') + '\
+        </a>\
       </div>\
-    </div>');
+    </div>\
+  </div>';
+
+  var result = template.firstChild;
 
   // Add a click listener to the remover, that removes the result on click
-  $(result).find('.remover').click({ caller: this }, function (event) {
-    event.data.caller.remove();
+  var caller = this;
+  result.querySelectorAll(".remover").forEach(element => {
+    element.onclick = (e) => {
+      caller.remove();
+    };
   });
 
   return result;
@@ -402,17 +432,17 @@ Result.prototype.toHtml = function () {
  */
 function resultSaver(index) {
   // Remember the original result element
-  var original = $('.result[data-count=' + index + ']');
+  var original = document.querySelector('.result[data-count="' + index + '"]');
 
   // Read the necessary data from the result html
-  var title = $('.result[data-count=' + index + '] .result-title a').html().trim();
-  var link = $('.result[data-count=' + index + '] .result-title a').attr('href').trim();
-  var hosterName = $('.result[data-count=' + index + '] .result-hoster').html().trim();
-  var hosterLink = $('.result[data-count=' + index + '] .result-hoster').attr('href');
-      hosterLink = hosterLink?hosterLink.trim():"#";
-  var anzeigeLink = $('.result[data-count=' + index + '] .result-link').html().trim();
-  var description = $('.result[data-count=' + index + '] .result-description').html().trim();
-  var anonym = $('.result[data-count=' + index + '] .result-open-proxy').attr('href').trim();
+  var title = document.querySelector('.result[data-count="' + index + '"] .result-title a').innerHTML.trim();
+  var link = document.querySelector('.result[data-count="' + index + '"] .result-title a').href.trim();
+  var hosterName = document.querySelector('.result[data-count="' + index + '"] .result-subheadline > a').title.trim();
+  var hosterLink = document.querySelector('.result[data-count="' + index + '"] .result-hoster').href;
+  hosterLink = hosterLink ? hosterLink.trim() : "#";
+  var anzeigeLink = document.querySelector('.result[data-count="' + index + '"] .result-link').innerHTML.trim();
+  var description = document.querySelector('.result[data-count="' + index + '"] .result-description').innerHTML.trim();
+  var anonym = document.querySelector('.result[data-count="' + index + '"] .result-open-proxy').href.trim();
 
   // Create the result object
   var result = new Result(title, link, hosterName, hosterLink, anzeigeLink, description, anonym, index, null);
@@ -427,8 +457,8 @@ function resultSaver(index) {
   results.updateResultPageInterface();
 
   // Animate the result transfer to the saved results
-  var transferTarget = $('.saved-result[data-count=' + index + ']');
-  if (original.length > 0 && transferTarget.length > 0) {
-    $(original).transfer({ to: transferTarget, duration: 1000 });
-  }
+  // var transferTarget = $('.saved-result[data-count=' + index + ']');
+  // if (original.length > 0 && transferTarget.length > 0) {
+  //   $(original).transfer({ to: transferTarget, duration: 1000 });
+  // }
 }
diff --git a/resources/js/scriptResultPage.js b/resources/js/scriptResultPage.js
index 82d24b3f85beeed5c31f4d4d37ef0954579b27ec..5ec468e5f142a632307a279e0c037b75781871d9 100644
--- a/resources/js/scriptResultPage.js
+++ b/resources/js/scriptResultPage.js
@@ -1,65 +1,84 @@
-$(document).ready(function () {
+document.addEventListener("DOMContentLoaded", (event) => {
+  if(document.readyState == 'complete'){
+    initialize();
+  }else{
+    document.addEventListener("readystatechange", e => {
+      if (document.readyState == 'complete') {
+        initialize();
+      }
+    });
+  }
+});
+
+function initialize(){
   botProtection();
   enableFormResetter();
   loadMoreResults();
-});
+}
+
+
+let link, newtab, top;
 
 function botProtection() {
-  $('.result').find('a').click(function (e) {
-    var link = $(this).attr('href');
-    var newtab = false;
-    var top = false;
-    if ($(this).attr('target') == '_blank' || e.ctrlKey || e.metaKey) {
-      newtab = true;
-    } else if ($(this).attr('target') == "_top") {
-      top = true;
-    }
+  document.querySelectorAll(".result a").forEach((element) => {
+    element.onclick = e => {
+      link = element.href;
+      newtab = false;
+      top = false;
+      if (element.target == '_blank' || e.ctrlKey || e.metaKey) {
+        newtab = true;
+      } else if (element.target == "_top") {
+        top = true;
+      }
 
-    $.ajax({
-      url: '/img/cat.jpg',
-      type: 'post',
-      data: {
-        mm: $('meta[name=mm]').attr('content')
-      },
-      timeout: 2000
-    })
-      .always(function () {
-        if (!newtab) {
-          if (top) {
-            window.top.location.href = link;
-          } else {
-            document.location.href = link;
+      fetch("/img/cat.png", {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/x-www-form-urlencoded",
+        },
+        body: "mm=" + document.querySelector('meta[name="mm"]').content
+      })
+        .then(response => {
+          if (!newtab) {
+            if (top) {
+              window.top.location.href = link;
+            } else {
+              document.location.href = link;
+            }
           }
-        }
-      });
-    return newtab;
+        });
+      return newtab;
+    };
   });
 }
 
 function enableFormResetter() {
-  var deleteButton = $("#search-delete-btn");
+  var deleteButton = document.querySelector("#search-delete-btn");
   var timeout = null;
-  $(deleteButton).click(function () {
+
+  deleteButton.onclick = (e) => {
     if (timeout != null) {
       clearTimeout(timeout);
       timeout = null;
     }
-    $("input[name=eingabe]").val("");
-    $("input[name=eingabe]").focus();
-  });
-  $("input[name=eingabe]").focusin(function () {
-    $(deleteButton).css("display", "initial");
+    document.querySelector("input[name=\"eingabe\"]").value = "";
+    document.querySelector("input[name=\"eingabe\"]").focus();
+  };
+
+  document.querySelector("input[name=\"eingabe\"]").addEventListener("focusin", (e) => {
+    deleteButton.style.display = "initial";
   });
-  $("input[name=eingabe]").focusout(function () {
+
+  document.querySelector("input[name=\"eingabe\"]").addEventListener("focusout", (e) => {
     timeout = window.setTimeout(function () {
-      $(deleteButton).css("display", "none");
+      deleteButton.style.display = "none";
       timeout = null;
     }, 500);
   });
 }
 
 function loadMoreResults() {
-  var searchKey = $("meta[name=searchkey]").attr("content");
+  var searchKey = document.querySelector("meta[name=searchkey]").content
   var updateUrl = document.location.href;
   updateUrl += "&loadMore=loader_" + searchKey + "&script=yes";
 
@@ -75,61 +94,80 @@ function loadMoreResults() {
         clearInterval(resultLoader);
       }
       currentlyLoading = true;
-      $.getJSON(updateUrl, function (data) {
-        // Check if we can clear the interval (once every searchengine has answered)
-        if (!data || data.finished) {
-          clearInterval(resultLoader);
-        }
-        // If there are new results we can add them
-        if (typeof data.newResults != "undefined") {
-          for (var key in data.newResults) {
-            var value = data.newResults[key];
-
-            // If there are more results than the given index we will prepend otherwise we will append the result
-            if (!data.imagesearch) {
-              var results = $(".result:not(.ad)");
-              if (key == 0) {
-                if ($(".result.ad").length > 0) {
-                  $(value).insertAfter($($(".result.ad")[$(".result.ad").length - 1]));
-                } else {
-                  $("#results").prepend(value);
-                }
-              } else if (typeof results[key] != "undefined") {
-                $(value).insertBefore($(results[key]));
-              } else if (typeof results[key - 1] != "undefined") {
-                $(value).insertAfter($(results[key - 1]));
-              }
-            } else {
-              var results = $(".image-container > .image");
-              if (key == 0) {
-                $(".image-container").prepend(value);
-              } else if (typeof results[key] != "undefined") {
-                $(value).insertBefore($(results[key]));
-              } else if (typeof results[key - 1] != "undefined") {
-                $(value).insertAfter($(results[key - 1]));
+      fetch(updateUrl)
+        .then(response => response.json())
+        .then(data => {
+          // Check if we can clear the interval (once every searchengine has answered)
+          if (!data || data.finished) {
+            clearInterval(resultLoader);
+          }
+
+          if (typeof data.changedResults != "undefined") {
+            for (var key in data.changedResults) {
+              var value = data.changedResults[key];
+              // If there are more results than the given index we will prepend otherwise we will append the result
+              if (!data.imagesearch) {
+                var results = document.querySelectorAll(".result:not(.ad)");
+                var replacement = document.createElement("div");
+                replacement.innerHTML = value.trim();
+                results[key].parentNode.replaceChild(replacement.firstChild, results[key]);
+              } else {
+                var results = document.querySelectorAll(".image-container > .image");
+                var replacement = document.createElement("div");
+                replacement.innerHTML = value.trim();
+                results[key].parentNode.replaceChild(replacement.firstChild, results[key]);
               }
             }
+            botProtection();
           }
-          if ($(".no-results-error").length > 0 && ($(".image-container > .image").length > 0) || $(".result:not(.ad)").length > 0) {
-            $(".no-results-error").remove();
-            if ($(".alert.alert-danger > ul").children().length == 0) {
-              $(".alert.alert-danger").remove();
+
+          // If there are new results we can add them
+          if (typeof data.newResults != "undefined") {
+            for (var key in data.newResults) {
+              var value = data.newResults[key];
+
+              // If there are more results than the given index we will prepend otherwise we will append the result
+              if (!data.imagesearch) {
+                var resultContainer = document.querySelector("#results");
+                var results = document.querySelectorAll(".result:not(.ad)");
+                var replacement = document.createElement("div");
+                replacement.innerHTML = value.trim();
+                if (key == 0) {
+                  resultContainer.insertBefore(replacement.firstChild, results[0]);
+                } else if (typeof results[key] != "undefined") {
+                  resultContainer.insertBefore(replacement.firstChild, results[key]);
+                } else if (typeof results[key - 1] != "undefined") {
+                  resultContainer.append(replacement.firstChild);
+                }
+              } else {
+                var resultContainer = document.querySelector("#results");
+                var results = document.querySelectorAll(".image-container > .image");
+                var replacement = document.createElement("div");
+                replacement.innerHTML = value.trim();
+                if (key == 0) {
+                  resultContainer.insertBefore(replacement.firstChild, results[0]);
+                } else if (typeof results[key] != "undefined") {
+                  resultContainer.insertBefore(replacement.firstChild, results[key]);
+                } else if (typeof results[key - 1] != "undefined") {
+                  resultContainer.append(replacement.firstChild);
+                }
+              }
             }
-          }
-        }
-        if (typeof data.changedResults != "undefined") {
-          for (var key in data.changedResults) {
-            var value = data.changedResults[key];
-            // If there are more results than the given index we will prepend otherwise we will append the result
-            if (!data.imagesearch) {
-              $($(".result:not(.ad)")[key]).replaceWith($(value));
-            } else {
-              $($(".image-container > .image")[key]).replaceWith($(value));
+            botProtection();
+            if (document.querySelectorAll(".no-results-error").length > 0 && (document.querySelectorAll(".image-container > .image").length > 0) || document.querySelectorAll(".result:not(.ad)").length > 0) {
+              document.querySelectorAll(".no-results-error").forEach(element => {
+                element.remove();
+              });
+              if (document.querySelector(".alert.alert-danger > ul") != null && document.querySelector(".alert.alert-danger > ul").children().length == 0) {
+                document.querySelectorAll(".alert.alert-danger").forEach(element => {
+                  element.remove();
+                });
+              }
             }
           }
-        }
-        currentlyLoading = false;
-      });
+
+          currentlyLoading = false;
+        });
     }
   }, 1000);
 }
diff --git a/resources/js/scriptSettings.js b/resources/js/scriptSettings.js
index 1c62f16c510d42f1a1467a94df4076b4de2e2356..14af01e836c143bf41c150486c8e5ec2f0c8b9b6 100644
--- a/resources/js/scriptSettings.js
+++ b/resources/js/scriptSettings.js
@@ -1,6 +1,7 @@
-$(document).ready(function () {
-    $("#filter-form, #setting-form").find("button[type=submit]").css("display", "none");
-    $("#filter-form, #setting-form").find("select").on("change", function () {
-        $(this).closest('form').submit();
+document.addEventListener("DOMContentLoaded", (event) => {
+    document.querySelectorAll("#setting-form select").forEach((element) => {
+        element.onchange = (e) => {
+            e.srcElement.form.submit();
+        };
     });
 });
\ No newline at end of file
diff --git a/resources/js/translations.js b/resources/js/translations.js
index 1d6ce9a98d6929f976c4e91d3f6d555ca5627e09..97058ffd885a45a142ec71ec461e6192b7aba1af 100644
--- a/resources/js/translations.js
+++ b/resources/js/translations.js
@@ -22,8 +22,8 @@ var translations = {
     'result-saver.save.this': 'ÖFFNEN',
     'result-saver.save.newtab': 'IN NEUEM TAB',
     'result-saver.save.anonymous': 'ANONYM ÖFFNEN',
-    'close-dropdowns' : 'Alle zuklappen',
-    'open-dropdowns'  : 'Alle aufklappen'
+    'close-dropdowns': 'Alle zuklappen',
+    'open-dropdowns': 'Alle aufklappen'
   },
 
   'en': {
@@ -42,20 +42,20 @@ var translations = {
     'result-saver.save.this': 'OPEN',
     'result-saver.save.newtab': 'IN NEW TAB',
     'result-saver.save.anonymous': 'OPEN ANONYMOUSLY',
-    'close-dropdowns' : 'Collapse all',
-    'open-dropdowns'  : 'Expand all'
+    'close-dropdowns': 'Collapse all',
+    'open-dropdowns': 'Expand all'
   },
 
   'es': {
     'select-engine': 'Por favor, seleccione al menos un motor de búsqueda.',
     'select-valid-name': 'Por favor, introduzca un nombre válido constituido por letras y números.',
     'confirm-overwrite-name': 'Nombre ya ha sido elegido.\n¿Substituirlo?',
-  // 'saved-settings': '',
-  // 'generated-plugin': ''
-  // 'result-saver.sort.chronological': 'Chronologisch',
-  // 'result-saver.sort.ranking': 'MetaGer-Ranking',
-  // 'result-saver.sort.alphabetical': 'Alphabetisch (Hostname)',
-  // 'result-saver.delete': 'Ergebnis aus dem Speicher löschen',
+    // 'saved-settings': '',
+    // 'generated-plugin': ''
+    // 'result-saver.sort.chronological': 'Chronologisch',
+    // 'result-saver.sort.ranking': 'MetaGer-Ranking',
+    // 'result-saver.sort.alphabetical': 'Alphabetisch (Hostname)',
+    // 'result-saver.delete': 'Ergebnis aus dem Speicher löschen',
   }
 };
 
@@ -65,9 +65,9 @@ var translations = {
  * @param {string} key Zu übersetzender Schlüssel
  * @param {string} lang Zu verwendende Sprache
  */
-function t (key, lang) {
+function t(key, lang) {
   if (arguments.length == 1) {
-    var lang = $('html').attr('lang');
+    var lang = document.querySelector("html").getAttribute("lang");
     return t(key, lang);
   } else if (arguments.length == 2 && translations[lang] && translations[lang][key]) {
     return translations[lang][key];
diff --git a/resources/js/utility.js b/resources/js/utility.js
index 4a94950271def55a3b887de3b0bfaee2a4a4dbd9..0b01f3ad738d6755ff6902543d5ba8bf636ef0c0 100644
--- a/resources/js/utility.js
+++ b/resources/js/utility.js
@@ -1,4 +1,4 @@
-$(document).ready(function () {
-  $('.js-only').removeClass('js-only');
-  $('.no-js').addClass('hide');
+document.addEventListener("DOMContentLoaded", (event) => {
+  document.querySelectorAll(".js-only").forEach(el => el.classList.remove("js-only"));
+  document.querySelectorAll(".no-js").forEach(el => el.classList.add("hide"));
 });
\ No newline at end of file
diff --git a/resources/js/widgets.js b/resources/js/widgets.js
index 9ae0330a22afd644dd4e3033956981e240f12a34..3a060292e888eaf631d22db9756dde8c9268ce89 100644
--- a/resources/js/widgets.js
+++ b/resources/js/widgets.js
@@ -1,19 +1,22 @@
-function copyCode () {
-  $('#codesnippet').select();
+function copyCode() {
+  document.querySelector("#codesnippet").select();
   try {
     var successful = document.execCommand('copy');
     if (successful) {
-      $('#copyButton').removeClass('btn-default');
-      $('#copyButton').addClass('btn-success');
+      document.querySelector("#copyButton").classList.remove("btn-default");
+      document.querySelector("#copyButton").classList.add("btn-success");
     } else {
-      $('#copyButton').removeClass('btn-default');
-      $('#copyButton').addClass('btn-danger');
+      document.querySelector("#copyButton").classList.remove("btn-default");
+      document.querySelector("#copyButton").classList.add("btn-danger");
     }
   } catch (err) {
-    $('#copyButton').removeClass('btn-default');
-    $('#copyButton').addClass('btn-danger');
+    document.querySelector("#copyButton").classList.remove("btn-default");
+    document.querySelector("#copyButton").classList.add("btn-danger");
   }
 }
 window.onload = function () {
-  $('#copyButton').bind('click', copyCode);
+  let copyButton = document.querySelector("#copyButton");
+  if (copyButton != null) {
+    copyButton.onclick = copyCode;
+  }
 };
diff --git a/resources/less/utility.less b/resources/less/utility.less
index bc6208bc2bbc82fc3f0eccba3eed75b5c809944f..e7989b70c5081a95e2f78fb3f8fdd12d02135dd0 100644
--- a/resources/less/utility.less
+++ b/resources/less/utility.less
@@ -4,6 +4,10 @@
   display: none !important;
 }
 
+.hide {
+  display: none !important;
+}
+
 /* Zweck: Beim klick auf den .dropdown-opener soll der .dropdown-content angezeigt werden.
  * Verwendung: <a class="dropdown-opener" href="javascript:void(0);">...</a>
  *             <div class="dropdown-content">...</div>
diff --git a/resources/views/layouts/resultpage/verificationHeader.blade.php b/resources/views/layouts/resultpage/verificationHeader.blade.php
index 85ba8d9a022cd048806aad020b94289184f01ad0..cbd8651ec9c0d7d15107321d6082463a22271682 100644
--- a/resources/views/layouts/resultpage/verificationHeader.blade.php
+++ b/resources/views/layouts/resultpage/verificationHeader.blade.php
@@ -2,5 +2,5 @@
 <html lang="{!! trans('staticPages.meta.language') !!}">
 <head>
     <meta charset="UTF-8">
-    <link rel="stylesheet" href="/index.css?id={{ $key }}" beforeLoad="console.log('test');">
+    <link rel="stylesheet" href="/index.css?id={{ $key }}">
     <script src="/index.js?id={{ $key }}"></script>
diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php
index cf82c1a47a4619958344af022f4033467e81dbcd..f7546d11ed4806aaaef0b5621d426b088fd1d017 100644
--- a/resources/views/settings/index.blade.php
+++ b/resources/views/settings/index.blade.php
@@ -153,7 +153,7 @@
                     </select>
                 </div>
                 @endif
-                <button type="submit" class="btn btn-default">@lang('settings.save')</button>
+                <button type="submit" class="btn btn-default no-js">@lang('settings.save')</button>
             </form>
         </div>
     <div class="card-light" id="actions">
diff --git a/resources/views/widget/sitesearch.blade.php b/resources/views/widget/sitesearch.blade.php
index 57a5ccaa9ef242363834ee58af87a70b5b7dc659..0685b949251aa0de86d8b8ae873bbf3afa5eda81 100644
--- a/resources/views/widget/sitesearch.blade.php
+++ b/resources/views/widget/sitesearch.blade.php
@@ -20,7 +20,7 @@
 		{!! $template !!}
 	</div>
 	<div class="card-medium">
-		<h2>{{ trans('sitesearch.generated.5') }} <button id="copyButton" class="btn btn-default" type="button"><i class="fa fa-paperclip" aria-hidden="true"></i> {{ trans('websearch.head.copy') }}</button></h2>
+		<h2>{{ trans('sitesearch.generated.5') }} <button id="copyButton" class="btn btn-default js-only" type="button"><i class="fa fa-paperclip" aria-hidden="true"></i> {{ trans('websearch.head.copy') }}</button></h2>
 		<textarea id="codesnippet" readonly style="width:100%;height:500px">
 			{!! $template !!}
 		</textarea>
diff --git a/routes/web.php b/routes/web.php
index befbe815ccad9acdecd554972f7f833ac4451c19..d8f4b4a17f2634684379d5841d33401d2f163628 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -224,7 +224,7 @@ Route::group(
         Route::match(['get', 'post'], 'meta/meta.ger3', 'MetaGerSearch@search')->middleware('browserverification', 'humanverification', 'useragentmaster')->name("resultpage");
 
         Route::get('meta/loadMore', 'MetaGerSearch@loadMore');
-        Route::post('img/cat.jpg', 'HumanVerification@remove');
+        Route::post('img/cat.png', 'HumanVerification@remove');
         Route::get('verify/metager/{id}/{uid}', ['as' => 'captcha', 'uses' => 'HumanVerification@captcha']);
         Route::get('r/metager/{mm}/{pw}/{url}', ['as' => 'humanverification', 'uses' => 'HumanVerification@removeGet']);
         Route::post('img/dog.jpg', 'HumanVerification@whitelist');
@@ -242,7 +242,7 @@ Route::group(
                 $redis->expire($key, 30);
             });
 
-            return response("", 200)->header("Content-Type", "application/js");
+            return response("", 200)->header("Content-Type", "application/javascript");
         });
 
         Route::get('meta/picture', 'Pictureproxy@get');
diff --git a/webpack.mix.js b/webpack.mix.js
index 5e4be685345876ffc0c3d04825d6a20f864e6a85..00fcc2ea56374a93c95da3978c6f590043c08057 100644
--- a/webpack.mix.js
+++ b/webpack.mix.js
@@ -58,9 +58,6 @@ mix
   // js
   .babel(
     [
-      "resources/js/lib/jquery.js",
-      "resources/js/lib/jquery-ui.min.js",
-      "resources/js/lib/bootstrap.js",
       "resources/js/lib/md5.js"
     ],
     "public/js/lib.js"