diff --git a/app/static/js/app/app.js b/app/static/js/app/app.js
index 92a5c5462d5a1c608f3e8ce36fed4de09b032ab7..fefabd1a80cd076820e760a509527cbf8a51d676 100644
--- a/app/static/js/app/app.js
+++ b/app/static/js/app/app.js
@@ -1,3 +1,8 @@
+// IDEA: Split the App logic into seperate units
+//       - App.Data
+//       - App.IO (name is WIP)
+//       - App.UI
+
 App.App = class App {
   constructor() {
     this.data = {
@@ -101,4 +106,94 @@ App.App = class App {
     // Apply Patch
     jsonpatch.applyPatch(this.data, filteredPatch);
   }
+
+  init() {
+    this.initUi();
+  }
+
+  initUi() {
+    /* Pre-Initialization fixes */
+    // #region
+
+    // Flask-WTF sets the standard HTML maxlength Attribute on input/textarea
+    // elements to specify their maximum length (in characters). Unfortunatly
+    // Materialize won't recognize the maxlength Attribute, instead it uses
+    // the data-length Attribute. It's conversion time :)
+    for (let elem of document.querySelectorAll('input[maxlength], textarea[maxlength]')) {
+      elem.dataset.length = elem.getAttribute('maxlength');
+      elem.removeAttribute('maxlength');
+    }
+
+    // To work around some limitations with the Form setup of Flask-WTF.
+    // HTML option elements with an empty value are considered as placeholder
+    // elements. The user should not be able to actively select these options.
+    // So they get the disabled attribute.
+    for (let optionElement of document.querySelectorAll('option[value=""]')) {
+      optionElement.disabled = true;
+    }
+
+    // TODO: Check why we are doing this.
+    for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) {
+      for (let c of optgroupElement.children) {
+        optgroupElement.parentElement.insertAdjacentElement('afterbegin', c);
+      }
+      optgroupElement.remove();
+    }
+
+    // #endregion
+
+    /* Initialize Materialize Components */
+    // #region
+  
+    // Automatically initialize Materialize Components that do not require
+    // additional configuration.
+    M.AutoInit();
+
+    // CharacterCounters
+    // Materialize didn't include the CharacterCounter plugin within the
+    // AutoInit method (maybe they forgot it?). Anyway... We do it here. :)
+    M.CharacterCounter.init(document.querySelectorAll('input[data-length]:not(.no-autoinit), textarea[data-length]:not(.no-autoinit)'));
+
+    // Header navigation "more" Dropdown.
+    M.Dropdown.init(
+      document.querySelector('#nav-more-dropdown-trigger'),
+      {
+        alignment: 'right',
+        constrainWidth: false,
+        coverTrigger: false
+      }
+    );
+
+    // Manual modal
+    M.Modal.init(
+      document.querySelector('#manual-modal'),
+      {
+        onOpenStart: (modalElement, modalTriggerElement) => {
+          if ('manualModalChapter' in modalTriggerElement.dataset) {
+            let manualModalTocElement = document.querySelector('#manual-modal-toc');
+            let manualModalToc = M.Tabs.getInstance(manualModalTocElement);
+            manualModalToc.select(modalTriggerElement.dataset.manualModalChapter);
+          }
+        }
+      }
+    );
+
+    // Terms of use modal
+    M.Modal.init(
+      document.querySelector('#terms-of-use-modal'),
+      {
+        dismissible: false,
+        onCloseEnd: (modalElement) => {
+          Requests.users.entity.acceptTermsOfUse();
+        }
+      }
+    );
+    // #endregion
+
+    // #region Nopaque Components
+    ResourceDisplays.AutoInit();
+    ResourceLists.AutoInit();
+    Forms.AutoInit();
+    // #endregion Nopaque Components
+  }
 };
diff --git a/app/static/js/forms/index.js b/app/static/js/forms/index.js
index 0290944846e0894efb2659f64aa3a5a6b4dcae08..a6d098ddf28d01701cc2e9e0f643dfc2066f39a6 100644
--- a/app/static/js/forms/index.js
+++ b/app/static/js/forms/index.js
@@ -1,6 +1,6 @@
 var Forms = {};
 
-Forms.autoInit = () => {
+Forms.AutoInit = () => {
   for (let propertyName in Forms) {
     let property = Forms[propertyName];
     // Call autoInit of all properties that are subclasses of Forms.BaseForm.
diff --git a/app/static/js/resource-displays/index.js b/app/static/js/resource-displays/index.js
index 8cc8809e9a247dca203db199e45dfdfebcafe1cd..a559189d21bc9950b546ca4f7556d37c9b882233 100644
--- a/app/static/js/resource-displays/index.js
+++ b/app/static/js/resource-displays/index.js
@@ -1,6 +1,6 @@
 var ResourceDisplays = {};
 
-ResourceDisplays.autoInit = () => {
+ResourceDisplays.AutoInit = () => {
   for (let propertyName in ResourceDisplays) {
     let property = ResourceDisplays[propertyName];
     // Call autoInit of all properties that are subclasses of `ResourceDisplays.ResourceDisplay`.
diff --git a/app/static/js/resource-lists/index.js b/app/static/js/resource-lists/index.js
index 513da46b90cee965b35b4fb45c77ca9464a660eb..1683a6a5a92e811490b2cab2bb04744084f4e269 100644
--- a/app/static/js/resource-lists/index.js
+++ b/app/static/js/resource-lists/index.js
@@ -1,6 +1,6 @@
 var ResourceLists = {};
 
-ResourceLists.autoInit = () => {
+ResourceLists.AutoInit = () => {
   for (let propertyName in ResourceLists) {
     let property = ResourceLists[propertyName];
     // Call autoInit of all properties that are subclasses of `ResourceLists.ResourceList`.
diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2
index 1fd32f7e377bb00ff905dd5f331f21739fb90d4f..78b7af601d75192d48d8682d75ba4d60002e4a91 100644
--- a/app/templates/_scripts.html.j2
+++ b/app/templates/_scripts.html.j2
@@ -119,76 +119,29 @@
 <script>
   // TODO: Implement an app.run method and use this for all of the following
   const app = new App.App();
+  app.init();
+
+  // Check if the current user is authenticated
   {%- if current_user.is_authenticated %}
+  // TODO: Set this as a property of the app object
   const currentUserId = {{ current_user.hashid|tojson }};
 
-  // Initialize components for current user
+  // Subscribe to the current user's data events
   app.subscribeUser(currentUserId)
     .catch((error) => {throw JSON.stringify(error);});
+
+  // Get the current user's data
   app.getUser(currentUserId, true, true)
     .catch((error) => {throw JSON.stringify(error);});
-  {%- endif %}
-
-  // Disable all option elements with no value
-  for (let optionElement of document.querySelectorAll('option[value=""]')) {
-    optionElement.disabled = true;
-  }
-  for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) {
-    for (let c of optgroupElement.children) {
-      optgroupElement.parentElement.insertAdjacentElement('afterbegin', c);
-    }
-    optgroupElement.remove();
-    
-  }
-  // Set the data-length attribute on textareas/inputs with the maxlength attribute
-  for (let inputElement of document.querySelectorAll('textarea[maxlength], input[maxlength]')) {
-    inputElement.dataset.length = inputElement.getAttribute('maxlength');
-  }
 
-  // Initialize components
-  M.AutoInit();
-  M.CharacterCounter.init(document.querySelectorAll('input[data-length], textarea[data-length]'));
-  M.Dropdown.init(
-    document.querySelectorAll('#nav-more-dropdown-trigger'),
-    {alignment: 'right', constrainWidth: false, coverTrigger: false}
-  );
-  ResourceDisplays.autoInit();
-  ResourceLists.autoInit();
-  Forms.autoInit();
+  // Check if the current user hasn't accepted the terms of use yet
+  {%- if not current_user.terms_of_use_accepted %}
+  M.Modal.getInstance(document.querySelector('#terms-of-use-modal')).open();
+  {%- endif %}
+  {%- endif %}
 
   // Display flashed messages
   for (let [category, message] of {{ get_flashed_messages(with_categories=True)|tojson }}) {
     app.flash(message, message);
   }
-
-  // Initialize manual modal
-  let manualModalTableOfContentsElement = document.querySelector('#manual-modal-table-of-contents');
-  let manualModalTableOfContents = M.Tabs.init(manualModalTableOfContentsElement);
-  let manualModalElement = document.querySelector('#manual-modal');
-  let manualModal = M.Modal.init(
-    manualModalElement,
-    {
-      onOpenStart: (manualModalElement, modalTriggerElement) => {
-        if ('manualModalChapter' in modalTriggerElement.dataset) {
-          manualModalTableOfContents.select(modalTriggerElement.dataset.manualModalChapter);
-        }
-      }
-    }
-  );
-
-  // Initialize terms of use modal
-  const termsOfUseModal = document.getElementById('terms-of-use-modal');
-  M.Modal.init(
-    termsOfUseModal, 
-    {
-      dismissible: false, 
-      onCloseEnd: () => {
-        requests.users.entity.acceptTermsOfUse();
-      }
-    }
-  );
-  {% if current_user.is_authenticated and not current_user.terms_of_use_accepted %}
-      termsOfUseModal.M_Modal.open();
-  {% endif %}
-
 </script>
diff --git a/app/templates/main/_manual_modal.html.j2 b/app/templates/main/_manual_modal.html.j2
index 3eb5d4a1c95ba1454192a820521489525a3ed31d..9233fb0d0567e8c4a00ff52f41cd98e01f000ef7 100644
--- a/app/templates/main/_manual_modal.html.j2
+++ b/app/templates/main/_manual_modal.html.j2
@@ -1,7 +1,7 @@
 <div id="manual-modal" class="modal no-autoinit">
   <div class="modal-content">
     <h2>Manual</h2>
-    <ul class="tabs no-autoinit" id="manual-modal-table-of-contents">
+    <ul class="tabs" id="manual-modal-toc">
       <li class="tab"><a href="#manual-modal-introduction">Introduction</a></li>
       <li class="tab"><a href="#manual-modal-registration-and-log-in">Registration and Log in</a></li>
       <li class="tab"><a href="#manual-modal-dashboard">Dashboard</a></li>