Browse Source

Frontend stuff

Roman Hergenreder 4 years ago
parent
commit
efe3ada470

+ 1 - 1
.htaccess

@@ -5,4 +5,4 @@ RedirectMatch 404 /\.git
 
 RewriteEngine On
 RewriteRule ^api/(.*)?$ index.php?api=$1&$2 [L,QSA]
-RewriteRule ^((?!((js|css|img|fonts|api)($|\/)))(.*)?)$ index.php?site=$1&$2 [L,QSA]
+RewriteRule ^((?!((js|css|img|fonts|api|docs)($|/)))(.*)?)$ index.php?site=$1&$2 [L,QSA]

+ 8 - 0
.idea/dictionaries/webbase.xml

@@ -0,0 +1,8 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="webbase">
+    <words>
+      <w>adminlte</w>
+      <w>navbar</w>
+    </words>
+  </dictionary>
+</component>

+ 1 - 1
core/Api/User/Fetch.class.php

@@ -9,7 +9,7 @@ class Fetch extends Request {
   public function __construct($user, $externalCall = false) {
     parent::__construct($user, $externalCall, array());
     $this->loginRequired = true;
-    // $this->requiredGroup = USER_GROUP_ADMIN;
+    $this->requiredGroup = USER_GROUP_ADMIN;
   }
 
   public function execute($values = array()) {

+ 6 - 27
core/Documents/Admin.class.php

@@ -2,25 +2,25 @@
 
 namespace Documents {
 
-  use Documents\Admin\AdminBody;
   use Documents\Admin\AdminHead;
   use Elements\Document;
+  use Objects\User;
+  use Views\AdminDashboard;
+  use Views\LoginBody;
 
   class Admin extends Document {
-    public function __construct($user) {
-      parent::__construct($user, AdminHead::class, AdminBody::class);
+    public function __construct(User $user) {
+      $body = $user->isLoggedIn() ? AdminDashboard::class : LoginBody::class;
+      parent::__construct($user, AdminHead::class, $body);
     }
   }
 }
 
 namespace Documents\Admin {
 
-  use Elements\Body;
   use Elements\Head;
   use Elements\Link;
   use Elements\Script;
-  use Views\Admin;
-  use Views\Login;
 
   class AdminHead extends Head {
 
@@ -30,7 +30,6 @@ namespace Documents\Admin {
 
     protected function initSources() {
       $this->loadJQuery();
-      $this->loadBootstrap();
       $this->loadFontawesome();
       $this->addJS(Script::CORE);
       $this->addCSS(Link::CORE);
@@ -56,24 +55,4 @@ namespace Documents\Admin {
       return "WebBase - Administration";
     }
   }
-
-  class AdminBody extends Body {
-
-    public function __construct($document) {
-      parent::__construct($document);
-    }
-
-    public function getCode() {
-      $html = parent::getCode();
-
-      $document = $this->getDocument();
-      if(!$document->getUser()->isLoggedIn()) {
-        $html .= new Login($document);
-      } else {
-        $html .= new Admin($document);
-      }
-
-      return $html;
-    }
-  }
 }

+ 1 - 1
core/Documents/Install.class.php

@@ -794,7 +794,7 @@ namespace Documents\Install {
             </div>
             <div class=\"col-md-8 order-md-1\">
               $progressMainview
-              <div class=\"alert$errorClass margin-top-m\" id=\"status\"$errorStyle>$this->errorString</div>
+              <div class=\"alert$errorClass mt-4\" id=\"status\"$errorStyle>$this->errorString</div>
             </div>
           </div>
         </div>

+ 1 - 1
core/Driver/SQL/SQL.class.php

@@ -161,7 +161,7 @@ abstract class SQL {
     $params = array();
 
     if (!$tables) {
-      return "SELECT $columns";
+      return $this->execute("SELECT $columns", $params, true);
     }
 
     $tables = $this->tableName($tables);

+ 5 - 0
core/Elements/Head.class.php

@@ -69,6 +69,11 @@ abstract class Head extends View {
     $this->addJS(Script::BOOTSTRAP);
   }
 
+  public function loadAdminlte() {
+    $this->addCSS(Link::ADMINLTE);
+    $this->addJS(Script::ADMINLTE);
+  }
+
   public function getCode() {
     $header = "<head>";
 

+ 1 - 0
core/Elements/Link.class.php

@@ -24,6 +24,7 @@ class Link extends View {
   // const REVEALJS                  = "/css/reveal.css";
   // const REVEALJS_THEME_MOON       = "/css/reveal_moon.css";
   // const REVEALJS_THEME_BLACK      = "/css/reveal_black.css";
+  const ADMINLTE = "/css/adminlte.min.css";
 
   private string $type;
   private string $rel;

+ 3 - 1
core/Elements/Script.class.php

@@ -20,7 +20,7 @@ class Script extends \View {
   // const SYNTAX_HIGHLIGHTER      = "/js/syntaxhighlighter.js";
   // const HIGHLIGHT               = "/js/highlight.pack.js";
   // const GOOGLE_CHARTS           = "/js/loader.js";
-  const BOOTSTRAP               = "/js/bootstrap.min.js";
+  // const BOOTSTRAP               = "/js/bootstrap.min.js";
   // const BOOTSTRAP_DATEPICKER_JS = "/js/bootstrap-datepicker.min.js";
   // const POPPER                  = "/js/popper.min.js";
   // const JSMPEG                  = "/js/jsmpeg.min.js";
@@ -29,8 +29,10 @@ class Script extends \View {
   // const REVEALJS                = "/js/reveal.js";
   // const REVEALJS_PLUGIN_NOTES   = "/js/reveal_notes.js";
   const INSTALL                 = "/js/install.js";
+  const BOOTSTRAP = "/js/bootstrap.bundle.min.js";
 
   const HIGHLIGHT_JS_LOADER = "\$(document).ready(function(){\$('code').each(function(i, block) { hljs.highlightBlock(block); }); })";
+  const ADMINLTE = "/js/adminlte.min.js";
 
   private string $type;
   private string $content;

+ 2 - 0
core/Elements/Style.class.php

@@ -2,6 +2,8 @@
 
 namespace Elements;
 
+use View;
+
 class Style extends View {
 
   private string $style;

+ 6 - 119
core/View.class.php

@@ -50,31 +50,6 @@ abstract class View {
   }
 
   // UI Functions
-
-  // TODO: do we need this in our general web-base?
-  public function createFileIcon($mimeType) {
-    $mimeType = htmlspecialchars($mimeType);
-    return "<img src=\"/img/icons/admin/getIcon.php?mimeType=$mimeType\" class=\"file-icon\" alt=\"[$mimeType icon]\">";
-  }
-
-  public function createParagraph($title, $id, $content) {
-    $id = replaceCssSelector($id);
-    $iconId = urlencode("$id-icon");
-    return "
-      <div class=\"row\">
-        <div class=\"col-12\">
-          <i class=\"fas fa-link\" style=\"display:none;position:absolute\" id=\"$iconId\"></i>
-          <h2 id=\"$id\" data-target=\"$iconId\" class=\"inlineLink\">$title</h2>
-          <div class=\"margin-bottom-xl\"><hr>$content</div>
-        </div>
-      </div>";
-  }
-
-  public function createSimpleParagraph($content, $class="") {
-    if($class) $class = " class=\"$class\"";
-    return "<p$class>$content</p>";
-  }
-
   private function createList($items, $tag) {
     if(count($items) === 0)
       return "<$tag></$tag>";
@@ -90,19 +65,6 @@ abstract class View {
     return $this->createList($items, "ul");
   }
 
-  public function createJumbotron($content, $lastModified=false) {
-    $lastModified = ($lastModified ? "<span class=\"float-right text-xxs margin-top-xxxl\">Last modified: $lastModified</span>" : "");
-    return "
-      <div class=\"row\">
-        <div class=\"col-12\">
-          <div class=\"jumbotron\">
-            $content
-            $lastModified
-          </div>
-        </div>
-      </div>";
-  }
-
   protected function createLink($link, $title=null) {
     if(is_null($title)) $title=$link;
     return "<a href=\"$link\">$title</a>";
@@ -113,91 +75,16 @@ abstract class View {
     return "<a href=\"$link\" target=\"_blank\" class=\"external\">$title</a>";
   }
 
-  protected function createCodeBlock($code, $lang="") {
-    if($lang) $lang = " class=\"$lang\"";
-    $html = "<pre><code$lang>";
-    $html .= intendCode($code);
-    $html .= "</code></pre>";
-    return $html;
-  }
-
-  protected function createIcon($icon, $margin = NULL) {
-    $marginStr = (is_null($margin) ? "" : " margin-$margin");
-    $iconClass = $this->getIconClass($icon);
-    return "<i class=\"$iconClass$marginStr\"></i>";
-  }
-
-  protected function getIconClass($icon) {
-
-    $mappings = array(
-      "sign-out" => "sign-out-alt",
-      "bank" => "university",
-      "line-chart" => "chart-line",
-      "circle-right" => "arrow-alt-circle-right",
-      "refresh" => "sync"
-    );
-
-    if(isset($mappings[$icon]))
-      $icon = $mappings[$icon];
+  protected function createIcon($icon, $type = "fas", $classes = "") {
+    $iconClass = "$type fa-$icon";
 
     if($icon === "spinner")
-      $icon .= " fa-spin";
+      $iconClass .= " fa-spin";
 
-    return "fas fa-$icon";
-  }
-
-  protected function createBootstrapTable($data) {
-    $code = "<div class=\"container\">";
-    foreach($data as $row) {
-      $code .= "<div class=\"row margin-top-xs margin-bottom-xs\">";
-      $columnCount = count($row);
-      if($columnCount > 0) {
-        $remainingSize = 12;
-        $columnSize = 12 / $columnCount;
-        foreach($row as $col) {
-          $size = ($columnSize <= $remainingSize ? $columnSize : $remainingSize);
-          $content = $col;
-          $class = "";
-          $code .= "<div";
-
-          if(is_array($col)) {
-            foreach($col as $key => $val) {
-              if(strcmp($key, "content") === 0) {
-                $content = $val;
-              } else if(strcmp($key, "class") === 0) {
-                $class = " " . $col["class"];
-              } else if(strcmp($key, "cols") === 0 && is_numeric($val)) {
-                $size = intval($val);
-              } else {
-                $code .= " $key=\"$val\"";
-              }
-            }
-
-            $content = (isset($col["content"]) ? $col["content"] : "");
-            if(isset($col["class"])) $class = " " . $col["class"];
-          }
-
-          if($size <= 6) $class .= " col-md-" . intval($size * 2);
-          $code .= " class=\"col-lg-$size$class\">$content</div>";
-          $remainingSize -= $size;
-        }
-      }
-      $code .= "</div>";
-    }
-
-    $code .= "</div>";
-    return $code;
-  }
+    if($classes)
+      $iconClass .= " $classes";
 
-  protected function createBash($command, $output="", $prefix="") {
-    $command = htmlspecialchars($command);
-    $output = htmlspecialchars($output);
-    $output = str_replace("\n", "<br>", $output);
-    return "<div class=\"bash\">
-              <span>$prefix$</span>&nbsp;
-              <span>$command</span><br>
-              <span>$output</span>
-            </div>";
+    return "<i class=\"$iconClass\"></i>";
   }
 
   protected function createErrorText($text, $id="", $hidden=false) {

+ 0 - 47
core/Views/Admin.class.php

@@ -1,47 +0,0 @@
-<?php
-
-namespace Views;
-
-// Source: https://adminlte.io/themes/v3/
-
-class Admin extends \View {
-  public function __construct($document) {
-    parent::__construct($document);
-  }
-
-  private function getMainHeader() {
-    $home = L("Home");
-    $search = L("Search");
-
-    $iconMenu = $this->createIcon("bars");
-    $iconSearch = $this->createIcon("search");
-    $iconNotifications = $this->createIcon("bell");
-    $header = "";
-
-    return $header;
-  }
-
-  private function getMainContent() {
-    return "";
-  }
-
-  private function getSideBar() {
-    return "";
-  }
-
-  public function getCode() {
-    $html = parent::getCode();
-
-    $html .= "<div class=\"main-wrapper\">";
-    $html .=    $this->getMainHeader();
-    $html .=    "<div id=\"content\">";
-    $html .=      $this->getSideBar();
-    $html .=      $this->getMainContent();
-    $html .=    "</div>
-             </div>";
-
-    return $html;
-  }
-}
-
-?>

+ 230 - 0
core/Views/AdminDashboard.class.php

@@ -0,0 +1,230 @@
+<?php
+
+namespace Views;
+
+// Source: https://adminlte.io/themes/v3/
+
+use Elements\Body;
+use Elements\Script;
+
+class AdminDashboard extends Body {
+
+  private array $errorMessages;
+
+  public function __construct($document) {
+    parent::__construct($document);
+    $this->errorMessages = array();
+  }
+
+  private function getNotifications() : array {
+    $req = new \Api\Notifications\Fetch($this->getDocument()->getUser());
+    if(!$req->execute()) {
+      $this->errorMessages[] = $req->getLastError();
+      return array();
+    } else {
+      return $req->getResult()['notifications'];
+    }
+  }
+
+  private function getUsers() : array {
+    $req = new \Api\User\Fetch($this->getDocument()->getUser());
+    if(!$req->execute()) {
+      $this->errorMessages[] = $req->getLastError();
+      return array();
+    } else {
+      return $req->getResult()['users'];
+    }
+  }
+
+  private function getHeader() {
+
+    // Locale
+    $home = L("Home");
+    $search = L("Search");
+
+    // Icons
+    $iconMenu = $this->createIcon("bars");
+    $iconNotification = $this->createIcon("bell", "far");
+    $iconSearch = $this->createIcon("search");
+    $iconMail = $this->createIcon("envelope", "fas");
+
+    // Notifications
+    $notifications = $this->getNotifications();
+    $numNotifications = count($notifications);
+    if ($numNotifications === 0) {
+      $notificationText = L("No new notifications");
+    } else if($numNotifications === 1) {
+      $notificationText = L("1 new notification");
+    } else {
+      $notificationText = sprintf(L("%d new notification"), $numNotifications);
+    }
+
+    $html =
+      "<nav class=\"main-header navbar navbar-expand navbar-white navbar-light\">
+
+        <!-- Left navbar links -->
+        <ul class=\"navbar-nav\">
+          <li class=\"nav-item\">
+            <a class=\"nav-link\" data-widget=\"pushmenu\" href=\"#\" role=\"button\">$iconMenu</a>
+          </li>
+          <li class=\"nav-item d-none d-sm-inline-block\">
+            <a href=\"/\" class=\"nav-link\">$home</a>
+          </li>
+        </ul>
+    
+        <!-- SEARCH FORM -->
+        <form class=\"form-inline ml-3\">
+          <div class=\"input-group input-group-sm\">
+            <input class=\"form-control form-control-navbar\" type=\"search\" placeholder=\"$search\" aria-label=\"$search\">
+            <div class=\"input-group-append\">
+              <button class=\"btn btn-navbar\" type=\"submit\">
+                $iconSearch
+              </button>
+            </div>
+          </div>
+        </form>
+    
+        <!-- Right navbar links -->
+        <ul class=\"navbar-nav ml-auto\">
+          <!-- Notifications Dropdown Menu -->
+          <li class=\"nav-item dropdown\">
+            <a class=\"nav-link\" data-toggle=\"dropdown\" href=\"#\">
+              $iconNotification
+              <span class=\"badge badge-warning navbar-badge\">$numNotifications</span>
+            </a>
+            <div class=\"dropdown-menu dropdown-menu-lg dropdown-menu-right\">
+              <span class=\"dropdown-item dropdown-header\">$notificationText</span>
+              <div class=\"dropdown-divider\"></div>";
+
+    // Notifications
+    $i = 0;
+    foreach($notifications as $notification) {
+
+      $title = $notification["title"];
+      $notificationId = $notification["uid"];
+      $createdAt = getPeriodString($notification["created_at"]);
+
+      if ($i > 0) {
+        $html .= "<div class=\"dropdown-divider\"></div>";
+      }
+
+      $html .=
+        "<a href=\"#\" class=\"dropdown-item\" data-id=\"$notificationId\">
+            $iconMail<span class=\"ml-2\">$title</span>
+            <span class=\"float-right text-muted text-sm\">$createdAt</span>
+        </a>";
+
+      $i++;
+      if ($i >= 5) {
+        break;
+      }
+    }
+
+    $html .= "<a href=\"#\" class=\"dropdown-item dropdown-footer\">See All Notifications</a>
+            </div>
+          </li>
+        </ul>
+      </nav>";
+
+    return $html;
+  }
+
+  private function getSidebar() {
+
+    $menuEntries = array(
+      "dashboard" => array(
+        "name" => "Dashboard",
+        "icon" => "tachometer-alt"
+      ),
+      "users" => array(
+        "name" => "Users",
+        "icon" => "users"
+      ),
+      "settings" => array(
+        "name" => "Settings",
+        "icon" => "tools"
+      ),
+      "help" => array(
+        "name" => "Help",
+        "icon" => "question-circle"
+      ),
+    );
+
+    $currentView = $_GET["view"] ?? "dashboard";
+
+    $html =
+      "<aside class=\"main-sidebar sidebar-dark-primary elevation-4\">
+        <!-- Brand Logo -->
+        <a href=\"index3.html\" class=\"brand-link\">
+          <img src=\"/img/web_base_logo.png\" alt=\"WebBase Logo\" class=\"brand-image img-circle elevation-3\"
+               style=\"opacity: .8\">
+          <span class=\"brand-text font-weight-light\">WebBase</span>
+        </a>
+    
+        <!-- Sidebar -->
+        <div class=\"sidebar\">
+     
+          <!-- Sidebar Menu -->
+          <nav class=\"mt-2\">
+            <ul class=\"nav nav-pills nav-sidebar flex-column\" data-widget=\"treeview\" role=\"menu\" data-accordion=\"false\">";
+
+    foreach($menuEntries as $view => $menuEntry) {
+      $name = L($menuEntry["name"]);
+      $icon = $this->createIcon($menuEntry["icon"], "fas", "nav-icon");
+      $active = ($currentView === $view) ? " active" : "";
+      $html .=
+              "<li class=\"nav-item\">
+                <a href=\"?view=$view\" class=\"nav-link$active\">
+                  $icon
+                  <p>$name </p>
+                </a>
+              </li>";
+    }
+
+    $html .=
+            "</ul>  
+          </nav>
+        </div>
+      </aside>";
+
+    return $html;
+  }
+
+  private function getContent() {
+
+    $this->getUsers();
+
+    $html = "<div class=\"content-wrapper p-2\">";
+
+    foreach($this->errorMessages as $errorMessage) {
+      $html .= $this->createErrorText($errorMessage);
+    }
+
+    $html .= "</div>";
+
+    return $html;
+  }
+
+  public function getCode() {
+
+    $head = $this->getDocument()->getHead();
+    $head->addJS(Script::BOOTSTRAP);
+    $head->loadAdminlte();
+
+    $header = $this->getHeader();
+    $sidebar = $this->getSidebar();
+    $content = $this->getContent();
+
+    $html =
+      "<!-- LICENSE: /docs/LICENSE_ADMINLTE -->
+      <body class=\"hold-transition sidebar-mini layout-fixed\">
+          <div class=\"wrapper\">
+            $header
+            $sidebar
+            $content
+          </div>
+      </body>";
+
+    return $html;
+  }
+}

+ 4 - 2
core/Views/LanguageFlags.class.php

@@ -2,6 +2,8 @@
 
 namespace Views;
 
+use Api\GetLanguages;
+
 class LanguageFlags extends \View {
 
   public function __construct($document) {
@@ -14,7 +16,7 @@ class LanguageFlags extends \View {
     $queryString = $_SERVER['QUERY_STRING'];
 
     $flags = array();
-    $request = new \Api\GetLanguages($this->getDocument()->getUser());
+    $request = new GetLanguages($this->getDocument()->getUser());
     $params = explode("&", $queryString);
     $query = array();
     foreach($params as $param) {
@@ -40,7 +42,7 @@ class LanguageFlags extends \View {
 
         $flags[] = $this->createLink(
           "$url$queryString",
-          "<img src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
+          "<img class=\"p-1\" src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
         );
       }
     } else {

+ 24 - 12
core/Views/Login.class.php → core/Views/LoginBody.class.php

@@ -2,53 +2,65 @@
 
 namespace Views;
 
-use View;
+use Elements\Body;
+
+class LoginBody extends Body {
 
-class Login extends View {
   public function __construct($document) {
     parent::__construct($document);
   }
 
   public function getCode() {
-    $html = parent::getCode();
+
+    $this->getDocument()->getHead()->loadBootstrap();
 
     $username = L("Username");
     $password = L("Password");
-    $rememberMe = L("Remember me");
     $login = L("Login");
     $backToStartPage = L("Back to Start Page");
+    $stayLoggedIn = L("Stay logged in");
+
     $flags = new LanguageFlags($this->getDocument());
-    $iconBack = $this->createIcon("arrow-circle-left", "right");
+    $iconBack = $this->createIcon("arrow-circle-left");
     $domain = $_SERVER['HTTP_HOST'];
     $protocol = getProtocol();
 
+    $html = "<body>";
+
     $accountCreated = "";
     if(isset($_GET["accountCreated"])) {
-      $accountCreated .= '
-        <div class="alert alert-success margin-top-xs" id="accountCreated">
+      $accountCreated =
+        '<div class="alert alert-success mt-3" id="accountCreated">
           Your account was successfully created, you may now login with your credentials
         </div>';
     }
 
     $html .= "
-      <div class=\"container margin-top-xxl\">
+      <div class=\"container mt-4\">
         <div class=\"title text-center\">
           <h2>Admin Control Panel</h2>
         </div>
-        <div class=\"loginContainer margin-center\">
+        <div class=\"loginContainer m-auto\">
           <form class=\"loginForm\">
             <label for=\"username\">$username</label>
             <input type=\"text\" class=\"form-control\" name=\"username\" id=\"username\" placeholder=\"$username\" required autofocus />
             <label for=\"password\">$password</label>
             <input type=\"password\" class=\"form-control\" name=\"password\" id=\"password\" placeholder=\"$password\" required />
+            <div class=\"form-check\">
+              <input type=\"checkbox\" class=\"form-check-input\" id=\"stayLoggedIn\" name=\"stayLoggedIn\">
+              <label class=\"form-check-label\" for=\"stayLoggedIn\">$stayLoggedIn</label>
+            </div>
             <button class=\"btn btn-lg btn-primary btn-block\" id=\"btnLogin\" type=\"button\">$login</button>
             <div class=\"alert alert-danger hidden\" role=\"alert\" id=\"loginError\"></div>
+            <span class=\"flags position-absolute\">$flags</span>
           </form>
-          <span class=\"subtitle flags-container\"><span class=\"flags\">$flags</span></span>
-          <span class=\"subtitle\"><a class=\"link\" href=\"$protocol://$domain\">$iconBack&nbsp;$backToStartPage</a></span>
+          <div class=\"p-1\">
+            <a href=\"$protocol://$domain\">$iconBack&nbsp;$backToStartPage</a>
+          </div>
           $accountCreated
         </div>
-      </div>";
+      </div>
+     </body>";
 
     return $html;
   }

+ 64 - 4
css/admin.css

@@ -10,6 +10,7 @@
   border-radius: 5px;
   background-color: #bbb;
   color: black;
+  position: relative;
 }
 
 .loginForm input {
@@ -27,8 +28,38 @@
   vertical-align: bottom;
 }
 
+.flags {
+  background-color: #999;
+  padding: 6px 3px 3px 3px;
+  border-radius: 4px;
+  bottom: -27px;
+  right: 5px;
+  z-index: -99;
+}
+
 .main-header {
+  transition: all 0.3s;
+  margin-left: 75px;
   border-bottom: 1px solid #dee2e6;
+  padding: 0.7rem;
+}
+
+.navbar-badge {
+  font-size: .6rem;
+  font-weight: 300;
+  padding: 2px 4px;
+  position: absolute;
+  right: 3px;
+  top: 7px;
+}
+
+.navbar-white {
+  background-color: #fff;
+}
+
+.main-wrapper:not(.sidebar-collapsed) .main-header {
+  transition: all 0.3s;
+  margin-left: 250px;
 }
 
 .main-sidebar {
@@ -43,11 +74,40 @@
   transition: all 0.3s;
 }
 
-.content-wrapper {
-  background: #f4f6f9;
+.main-sidebar.collapsed {
+  margin-left: 0;
+  width: 4.6rem;
+}
+
+.hide-collapsed {
+  transition: all 0.2s linear;
+  opacity: 1;
+}
+
+.main-sidebar.collapsed .hide-collapsed {
+  opacity: 0;
+  font-size: 0;
+  margin-left: 0;
+}
+
+.main-content {
+  /* background-color: red; */
+  height: 100%;
 }
 
 .main-wrapper {
-  display: flex;
-  width: 100%;
+  height: 100%;
+}
+
+.dropdown-menu-lg {
+  max-width: 300px;
+  min-width: 280px;
+  padding: 0;
+}
+
+.brand-link {
+  display: block;
+  font-size: 1.5rem;
+  line-height: 2;
+  padding: 1rem;
 }

File diff suppressed because it is too large
+ 10 - 0
css/adminlte.min.css


+ 2 - 211
css/style.css

@@ -2,138 +2,11 @@ html, body {
   height: 100%;
   margin: 0;
   padding: 0;
-  font-family: "Verdana";
+  font-family: "Verdana", serif;
   color: #555;
 }
 
-.syntaxhighlighter { margin-left: -25px; }
-
-.no-underline:hover { text-decoration: none; }
-.background { background-color: #fefefe; }
-.background-gray { background-color: gray; }
-.background-lightgray { background-color: lightgray; }
-.background-light-blue { background-color: rgb(88, 160, 224); }
-.background-jumbotron { background-color: #e9ecef; }
-
-/* TEXT COLOR */
-.text-white, .text-white:hover { color: white }
-.text-black, .text-black:hover { color: black; }
-.text-gray, .text-gray:hover { color: gray; }
-.text-default, .text-default:hover { color: #555; }
-.text-title, .text-title:hover { color: #333; text-decoration: underline; font-weight: bold; font-size: 1.7em; }
-.text-red, .text-red:hover { color: red; }
-.text-inherit, .text-inherit:hover { color: inherit; }
-.text-green-attr { color: #50a14f; }
-.text-cyan { color: cyan; }
-.highlight { background-color: yellow; }
-
-.code-box { background-color: #e0e0e0;
-  color: rgba(0,0,0,.87);
-  border-radius: 1px;
-  padding: 2px;
-  border: 1px solid #ddd;
-  word-break: break-all;
-  overflow-wrap: anywhere;
-}
-
-
-/* TEXT TRANSFORM */
-.underline { text-decoration: underline; }
-.italic { font-style: italic; }
-.bold { font-weight: bold; }
-.text-super { vertical-align : super; }
-
-/* TEXT SIZE */
-.text-xxs { font-size: 13px; }
-.text-xs { font-size: 15px; }
-.text-m { font-size: 20px; }
-.text-xl { font-size: 22px; }
-.text-xxl { font-size: 25px; }
-.text-xxxl { font-size: 30px; }
-
-/* MARGINS */
-.margin-xs { margin: 5px; }
-.margin-m { margin: 10px; }
-.margin-xl { margin: 15px; }
-.margin-top-xxs { margin-top: 10px; }
-.margin-top-xs { margin-top: 15px; }
-.margin-top-m { margin-top: 20px; }
-.margin-top-xl { margin-top: 25px; }
-.margin-top-xxl { margin-top: 30px; }
-.margin-top-xxxl { margin-top: 40px;  }
-.margin-bottom-xxs { margin-bottom: 10px; }
-.margin-bottom-xs { margin-bottom: 15px; }
-.margin-bottom-m { margin-bottom: 20px; }
-.margin-bottom-xl { margin-bottom: 25px; }
-.margin-bottom-xxl { margin-bottom: 30px; }
-.margin-bottom-xxxl { margin-bottom: 40px;  }
-.margin-left-xxs { margin-left: 10px; }
-.margin-left-xs { margin-left: 15px; }
-.margin-left-m { margin-left: 20px; }
-.margin-left-xl { margin-left: 25px; }
-.margin-left-xxl { margin-left: 30px; }
-.margin-center { margin-left: auto; margin-right: auto; }
-.margin-none { margin: 0 !important; }
-
-/* PADDINGS */
-.padding-xs { padding: 5px; }
-.padding-m { padding: 10px; }
-.padding-xl { padding: 15px; }
-.padding-xxl { padding: 20px; }
-.padding-xxxl { padding: 25px; }
-.padding-top-m { padding-top: 10px; }
-.padding-top-xl { padding-top: 15px; }
-.padding-top-xxl { padding-top: 20px; }
-.padding-bottom-m { padding-bottom: 10px; }
-.padding-bottom-xxl { padding-bottom: 20px; }
-.padding-left-xs { padding-left: 5px; }
-.padding-left-xl { padding-left: 15px; }
-.padding-none { padding: 0; }
-
-/* BORDER */
-.round { border-radius: 50%; }
-.border-white { border-color: white; }
-.border-grey { border-color: grey; }
-.border-xxs { border: 1px solid; }
-.border-xs { border: 2px solid; }
-.border-m { border: 3px solid; }
-.border-bottom-xss { border-bottom: 1px solid; }
-.round-border-xs { border-radius: 2px; }
-.round-border { border-radius: 5px; }
-.round-border-xl { border-radius: 10px; }
-.self-center { align-self: center; }
-
-/* LAYOUT */
-.fullheight { height: 100%; }
-.fullwidth { width: 100%; }
-.max-full-width { max-width: 100%; }
-.restwidth { width: auto; }
-.width-75  { width: 75%; }
-.relative { position: relative; }
-.absolute { position: absolute; }
-
-.bottom { bottom: 0; }
-.hide-overflow { overflow: hidden; }
 .hidden { display: none; }
-.clickable { cursor: pointer; }
-
-/* SCROLLBARS */
-.vertical-scroll { overflow-y: auto; }
-
-.mainContent {
-  margin-top: 40px;
-  margin-bottom: 100px;
-}
-
-.sidebar-toggle {
-  position: absolute;
-  top: 15px;
-  left: 15px;
-}
-
-.sidebar-toggle:hover {
-  background-color: rgb(69, 150, 240);
-}
 
 .external::after {
   font-family: "Font Awesome 5 Free";
@@ -141,86 +14,4 @@ html, body {
   content: "  \f35d";
   font-size: 10px;
   vertical-align: super;
-}
-
-.navigation {
-  padding-left: 15px;
-}
-
-.fullscreen-container {
-  margin: auto;
-  position: fixed;
-  top: 0; left: 0; bottom: 0; right: 0;
-  z-index: 9999;
-  overflow-y: auto;
-  overflow-x: hidden;
-  background-color: transparent;
-  background-color: #000;
-  background-color: rgba(0, 0, 0, 0.6);
-}
-
-.thumbnail {
-  border: 3px solid gray;
-}
-
-.fullscreen-container > img {
-  position: fixed;
-  z-index: 1000;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  max-width: 95%;
-  max-height: 95%;
-  height: auto;
-  border: 3px solid gray;
-}
-
-.closeButton {
-  position: fixed;
-  top: 30px;
-  right: 30px;
-  padding: 5px 9px 5px 9px;
-  background-color: #ddd;
-  border: 1px solid black;
-  font-size: 20px;
-  font-weight: bold;
-  cursor: pointer;
-}
-
-.flags {
-  padding: 3px;
-}
-
-.flags > a {
-  margin: 5px;
-}
-
-span > .hljs {
-  display: inline;
-  padding: .2em;
-}
-
-.inlineLink:hover {
-  text-decoration: underline;
-  cursor: pointer;
-}
-
-.bash {
-  background-color: #2E3436;
-  padding: 3px 5px 3px 5px;
-}
-
-.bash > span {
-  color: #D3D7CF;
-  overflow-wrap: anywhere;
-  word-break: break-all;
-}
-
-.bash > span:first-child {
-  display: inline;
-  color: #4E9A06;
-}
-
-.file-icon {
-  padding-bottom: 5px;
-}
+}

+ 20 - 0
docs/LICENSE_ADMINLTE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2018 almasaeed2010
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 12 - 6
js/admin.js

@@ -2,16 +2,17 @@ $(document).ready(function() {
   $("#username").keypress(function(e) { if(e.which == 13) $("#password").focus(); });
   $("#password").keypress(function(e) { if(e.which == 13) $("#btnLogin").click(); });
   $("#btnLogin").click(function() {
-    var username = $("#username").val();
-    var password = $("#password").val();
-    var errorDiv = $("#loginError");
-    var createdDiv = $("#accountCreated");
-    var btn      = $(this);
+    const username = $("#username").val();
+    const password = $("#password").val();
+    const errorDiv = $("#loginError");
+    const createdDiv = $("#accountCreated");
+    const stayLoggedIn = $("#stayLoggedIn").is(":checked");
+    const btn = $(this);
 
     errorDiv.hide();
     btn.prop("disabled", true);
     btn.html("Logging in… <i class=\"fa fa-spin fa-circle-notch\"></i>");
-    jsCore.apiCall("user/login", {"username": username, "password": password}, function(data) {
+    jsCore.apiCall("/user/login", {"username": username, "password": password, "stayLoggedIn": stayLoggedIn }, function(data) {
       window.location.reload();
     }, function(err) {
       btn.html("Login");
@@ -22,4 +23,9 @@ $(document).ready(function() {
       errorDiv.show();
     });
   });
+
+  $("#toggleSidebar").click(function() {
+    $(".main-wrapper").toggleClass("sidebar-collapsed");
+    $(".main-sidebar").toggleClass("collapsed");
+  });
 });

File diff suppressed because it is too large
+ 5 - 0
js/adminlte.min.js


File diff suppressed because it is too large
+ 5 - 0
js/bootstrap.bundle.min.js


+ 98 - 217
js/script.js

@@ -1,108 +1,79 @@
-var Core = function() {
+let Core = function () {
 
-  this.__construct = function() {
+  this.__construct = function () {
     this.url = document.location.href;
     this.parseParameters();
     this.langEntries = {};
   };
 
-  this.apiCall = function(func, aParams, callback, onerror) {
-    aParams  = typeof aParams  !== 'undefined' ? aParams  : { };
-    callback = typeof callback !== 'undefined' ? callback : function(data) { };
-    onerror  = typeof onerror  !== 'undefined' ? onerror  : function(msg) { bootbox.alert("Ein Fehler ist aufgetreten: " + msg); };
+  this.apiCall = function (func, aParams, callback, onerror) {
+    aParams = typeof aParams !== 'undefined' ? aParams : {};
+    callback = typeof callback !== 'undefined' ? callback : function (data) {};
 
-    $.post('/api/' + func, aParams, function(data) {
+    onerror = typeof onerror !== 'undefined' ? onerror : function (msg) {
+      bootbox.alert("An error occurred: " + msg);
+    };
+
+    const path = '/api' + (func.startsWith('/') ? '' : '/') + func;
+    $.post(path, aParams, function (data) {
       console.log(func + "(): success=" + data.success + " msg=" + data.msg);
-      if(data.hasOwnProperty('logoutIn') && $("#logoutTimer").length > 0) {
+      if (data.hasOwnProperty('logoutIn') && $("#logoutTimer").length > 0) {
         $("#logoutTimer").attr("data-time", data.logoutIn);
       }
 
-      if(!data.success) {
+      if (!data.success) {
         onerror.call(this, data.msg);
       } else {
         callback.call(this, data);
       }
-    }, "json").fail(function(jqXHR, textStatus, errorThrown) {
+    }, "json").fail(function (jqXHR, textStatus, errorThrown) {
       console.log("API-Function Error: " + func + " Status: " + textStatus + " error thrown: " + errorThrown);
       onerror.call(this, "An error occurred. API-Function: " + func + " Status: " + textStatus + " - " + errorThrown);
     });
   };
 
-  this.getCookie = function(cname) {
+  this.getCookie = function (cname) {
     var name = cname + "=";
     var decodedCookie = decodeURIComponent(document.cookie);
     var ca = decodedCookie.split(";");
-    for(var i = 0; i < ca.length; i++) {
+    for (var i = 0; i < ca.length; i++) {
       var c = ca[i];
-      while (c.charAt(0) == ' ') {
+      while (c.charAt(0) === ' ') {
         c = c.substring(1);
       }
-      if (c.indexOf(name) == 0) {
+      if (c.indexOf(name) === 0) {
         return c.substring(name.length, c.length);
       }
     }
     return "";
   };
 
-  this.requestData = function(api, dest, src, callback) {
-    if(typeof dest != "undefined" && Object.keys(dest).length > 0)
-      return true;
-
-    callback = typeof callback !== 'undefined' ? callback : function() { };
-    var core = this;
-
-    var dialog = bootbox.dialog({
-      message: 'Loading ' + src + '... <i class="fa fa-spin fa-spinner"></i>',
-      closeButton: false
-    }).bind('shown.bs.modal',function(){
-      core.apiCall(api, { }, function(data) {
-        for(var id in data[src])
-          dest[id] = data[src][id];
-
-        dialog.modal('hide');
-        callback.call(this);
-      }, function(msg) {
-        bootbox.alert("Ein Fehler ist aufgetreten: " + msg);
-        dialog.modal('hide');
-      });
-    });
-
-    return false;
+  this.addLangEntry = function (key, val) {
+    this.langEntries[key] = val;
   };
 
-  this.requestTags = function(callback) {
-    this.aTags = typeof this.aTags == "undefined" ? { } : this.aTags;
-    return this.requestData('tags/getTags', this.aTags, 'tags', callback);
-  };
-
-  this.requestAccounts = function(callback) {
-    this.aAccounts = typeof this.aAccounts == "undefined" ? { } : this.aAccounts;
-    return this.requestData('accounts/getAccounts', this.aAccounts, 'accounts', callback);
-  };
-
-  this.requestPartners = function(callback) {
-    this.aPartners = typeof this.aPartners == "undefined" ? { } : this.aPartners;
-    return this.requestData('accounts/getPartners', this.aPartners, 'partners', callback);
-  };
-
-  this.addLangEntry = function(key, val) { this.langEntries[key] = val; };
-  this.getLangEntry = function(key) {
-    if(typeof this.langEntries[key] !== 'undefined' && this.langEntries.hasOwnProperty(key)) {
+  this.getLangEntry = function (key) {
+    if (typeof this.langEntries[key] !== 'undefined' && this.langEntries.hasOwnProperty(key)) {
       return this.langEntries[key];
     }
 
     return key;
   };
 
-  this.getUrl = function() { return this.url; };
-  this.getParameters = function() { return this.aParameters; };
+  this.getUrl = function () {
+    return this.url;
+  };
+
+  this.getParameters = function () {
+    return this.aParameters;
+  };
 
-  this.setTitle = function(title) {
+  this.setTitle = function (title) {
     document.title = title;
   };
 
-  this.changeURL = function(history) {
-    if(history) {
+  this.changeURL = function (history) {
+    if (history) {
       window.history.pushState({
         "html": document.getElementsByTagName("body")[0].innerHTML,
         "pageTitle": document.title
@@ -115,37 +86,37 @@ var Core = function() {
     }
   };
 
-  this.redirect = function() {
+  this.redirect = function () {
     window.location = this.url;
   };
 
-  this.reload = function() {
+  this.reload = function () {
     window.location.reload();
   };
 
-  this.removeParameter = function(param) {
+  this.removeParameter = function (param) {
     if (typeof this.aParameters[param] !== 'undefined' && this.aParameters.hasOwnProperty(param)) {
       delete this.aParameters[param];
     }
     this.updateUrl();
   };
 
-  this.getParameter = function(param) {
+  this.getParameter = function (param) {
     if (typeof this.aParameters[param] !== 'undefined' && this.aParameters.hasOwnProperty(param))
       return this.aParameters[param];
     else
       return null;
   };
 
-  this.setParameter = function(param, newvalue) {
+  this.setParameter = function (param, newvalue) {
     newvalue = typeof newvalue !== 'undefined' ? newvalue : '';
     this.aParameters[param] = newvalue;
     this.updateUrl();
   };
 
-  this.parseParameters = function() {
+  this.parseParameters = function () {
     this.aParameters = [];
-    if(this.url.indexOf('?') === -1)
+    if (this.url.indexOf('?') === -1)
       return;
 
     var paramString = this.url.substring(this.url.indexOf('?') + 1);
@@ -153,7 +124,7 @@ var Core = function() {
     for (var i = 0; i < split.length; i++) {
       var param = split[i];
       var index = param.indexOf('=');
-      if(index !== -1) {
+      if (index !== -1) {
         var key = param.substr(0, index);
         var val = param.substr(index + 1);
         this.aParameters[key] = val;
@@ -162,9 +133,9 @@ var Core = function() {
     }
   };
 
-  this.updateUrl = function() {
+  this.updateUrl = function () {
     this.clearUrl();
-    var i = 0;
+    let i = 0;
     for (var parameter in this.aParameters) {
       this.url += (i === 0 ? "?" : "&") + parameter;
       if (this.aParameters.hasOwnProperty(parameter) && this.aParameters[parameter].toString().length > 0) {
@@ -174,107 +145,109 @@ var Core = function() {
     }
   };
 
-  this.clearParameters = function() {
+  this.clearParameters = function () {
     this.aParameters = [];
     this.updateUrl();
   };
 
-  this.clearUrl = function() {
-    if(this.url.indexOf('?') !== -1)
+  this.clearUrl = function () {
+    if (this.url.indexOf('?') !== -1)
       this.url = this.url.substring(0, this.url.indexOf('?'));
   };
 
-  this.logout = function() {
+  this.logout = function () {
     this.apiCall('user/logout');
   };
 
-  this.getJsonDateTime = function(date) {
+  this.getJsonDateTime = function (date) {
     return date.getFullYear() + "-" +
-      ((date.getMonth() + 1 < 10) ? "0" : "")  + (date.getMonth() + 1) + "-" +
-      (date.getDate() < 10 ? "0" : "")    + date.getDate() + " " +
-      (date.getHours() < 10 ? "0" : "")   + date.getHours() + "-" +
-      (date.getMinutes() < 10 ? "0" : "") + date.getMinutes() + "-" +
-      (date.getSeconds() < 10 ? "0" : "") + date.getSeconds();
+        ((date.getMonth() + 1 < 10) ? "0" : "") + (date.getMonth() + 1) + "-" +
+        (date.getDate() < 10 ? "0" : "") + date.getDate() + " " +
+        (date.getHours() < 10 ? "0" : "") + date.getHours() + "-" +
+        (date.getMinutes() < 10 ? "0" : "") + date.getMinutes() + "-" +
+        (date.getSeconds() < 10 ? "0" : "") + date.getSeconds();
   };
 
-  this.getJsonDate = function(date) {
+  this.getJsonDate = function (date) {
     return this.getJsonDateTime(date).split(' ')[0];
   };
 
-  this.getJsonTime = function(date) {
+  this.getJsonTime = function (date) {
     return this.getJsonDateTime(date).split(' ')[1];
   };
 
-  this.showInputDialog = function(title, aInputs, callback, element, onCreated) {
-    title     = typeof title     !== "undefined" ? title     : "";
-    aInputs   = typeof aInputs   !== "undefined" ? aInputs   : {};
-    callback  = typeof callback  !== "undefined" ? callback  : function(aResult, element) {};
-    onCreated = typeof onCreated !== "undefined" ? onCreated : function() {};
+  this.showInputDialog = function (title, aInputs, callback, element, onCreated) {
+    title = typeof title !== "undefined" ? title : "";
+    aInputs = typeof aInputs !== "undefined" ? aInputs : {};
+    callback = typeof callback !== "undefined" ? callback : function (aResult, element) {
+    };
+    onCreated = typeof onCreated !== "undefined" ? onCreated : function () {
+    };
 
     var html = '<div class="modal-header"><h4 class="modal-title">' + title + '</h4></div>' +
-               '<form class="bootbox-form">';
+        '<form class="bootbox-form">';
 
     for (var i in aInputs) {
       var input = aInputs[i];
 
-      if(input.type !== "hidden" && input.type !== "checkbox")
-        html += '<label for="' + input.name  + '">' + input.name  + ':</label>';
+      if (input.type !== "hidden" && input.type !== "checkbox")
+        html += '<label for="' + input.name + '">' + input.name + ':</label>';
 
-      if(input.type === "select") {
+      if (input.type === "select") {
         html += '<select id="' + input.id + '" class="bootbox-input bootbox-input-select form-control">';
 
         var aValues = (input.hasOwnProperty("aValues") && typeof input.aValues !== "undefined") ? input.aValues : {};
         for (var value in aValues) {
           var name = aValues[value];
-          var selected = (input.value == value) ? " selected" : "";
+          var selected = (input.value === value) ? " selected" : "";
           html += '<option value="' + value + '"' + selected + '>' + name + '</option>';
         }
 
         html += '</select>';
         input.type = "select";
-      } else if(input.type === "checkbox") {
+      } else if (input.type === "checkbox") {
         html += '<div class="checkbox">' +
-                  '<label><table><tr>' +
-                    '<td style="vertical-align:top;padding-top:3px;">' +
-                      '<input class="bootbox-input bootbox-input-checkbox" id="' + input.id + '" value="1" type="checkbox"'+ (input.value ? " checked" : "") + '>' +
-                    '</td>' +
-                    '<td style="padding-left: 5px;">' + input.text + '</td>' +
-                  '</tr></table></label>' +
-                '</div>';
-      } else if(input.type === "date") {
+            '<label><table><tr>' +
+            '<td style="vertical-align:top;padding-top:3px;">' +
+            '<input class="bootbox-input bootbox-input-checkbox" id="' + input.id + '" value="1" type="checkbox"' + (input.value ? " checked" : "") + '>' +
+            '</td>' +
+            '<td style="padding-left: 5px;">' + input.text + '</td>' +
+            '</tr></table></label>' +
+            '</div>';
+      } else if (input.type === "date") {
         html += '<input class="bootbox-input form-control customDatePicker" autocomplete="off" ' +
-                  'type="text" ' +
-                  'name="' + input.name + '" ' +
-                  'id="' + input.id + '" ' +
-                  'value="' + (input.value ? input.value : "") + '"' + (input.readonly ? " readonly" : "") +
-                  (input.maxlength ? ' maxlength="' + input.maxlength + '"' : '') + '>';
-      } else if(input.type === "time") {
+            'type="text" ' +
+            'name="' + input.name + '" ' +
+            'id="' + input.id + '" ' +
+            'value="' + (input.value ? input.value : "") + '"' + (input.readonly ? " readonly" : "") +
+            (input.maxlength ? ' maxlength="' + input.maxlength + '"' : '') + '>';
+      } else if (input.type === "time") {
         html += '<div class="input-group">' +
-                '<input class="bootbox-input" autocomplete="off" value="0" pattern="[0-9][0-9]" type="number" name="' + input.name + '" id="' + input.id + 'Hour" style="width:60px;text-align: center">' +
-                '<span>:</span>' +
-                '<input class="bootbox-input" autocomplete="off" type="number" name="' + input.name + '" id="' + input.id + 'Minute" value="00" style="width:60px;text-align: center">' +
-                '</div>';
+            '<input class="bootbox-input" autocomplete="off" value="0" pattern="[0-9][0-9]" type="number" name="' + input.name + '" id="' + input.id + 'Hour" style="width:60px;text-align: center">' +
+            '<span>:</span>' +
+            '<input class="bootbox-input" autocomplete="off" type="number" name="' + input.name + '" id="' + input.id + 'Minute" value="00" style="width:60px;text-align: center">' +
+            '</div>';
       } else {
         html += '<input class="bootbox-input form-control" autocomplete="off" ' +
-                  'type="' + input.type + '" ' +
-                  'name="' + input.name + '" ' +
-                  'id="' + input.id + '" ' +
-                  'value="' + (input.value ? input.value : "") + '"' + (input.readonly ? " readonly" : "") +
-                  (input.maxlength ? ' maxlength="' + input.maxlength + '"' : '') + '>';
+            'type="' + input.type + '" ' +
+            'name="' + input.name + '" ' +
+            'id="' + input.id + '" ' +
+            'value="' + (input.value ? input.value : "") + '"' + (input.readonly ? " readonly" : "") +
+            (input.maxlength ? ' maxlength="' + input.maxlength + '"' : '') + '>';
       }
     }
 
     html += '</form>';
-    var dialog = bootbox.confirm(html, function(result) {
-      if(result) {
+    var dialog = bootbox.confirm(html, function (result) {
+      if (result) {
         var aResult = [];
         for (var i in aInputs) {
           var input = aInputs[i];
           var value = $("#" + input.id).val();
 
-          if(input.type === "select")
+          if (input.type === "select")
             value = $("#" + input.id).find(":selected").val();
-          else if(input.type === "checkbox")
+          else if (input.type === "checkbox")
             value = $("#" + input.id).prop("checked");
 
           aResult[input.id] = value;
@@ -283,9 +256,9 @@ var Core = function() {
       }
     });
 
-    dialog.init(function() {
-      $(".modal-body").on("keypress", "input", function(e) {
-        if(e.keyCode == 13) {
+    dialog.init(function () {
+      $(".modal-body").on("keypress", "input", function (e) {
+        if (e.keyCode === 13) {
           e.preventDefault();
           $(".modal-footer .btn-primary").click();
         }
@@ -297,99 +270,7 @@ var Core = function() {
   this.__construct();
 };
 
-
-function findGetParameter(parameterName) {
-  var result = null, tmp = [];
-  location.search.substr(1).split("&").forEach(function (item) {
-    tmp = item.split("=");
-    if (tmp[0] === parameterName) {
-      result = decodeURIComponent(tmp[1]);
-    }
-  });
-  return result;
-}
-
-var jsCore = new Core();
+let jsCore = new Core();
 $(document).ready(function() {
 
-  $(".nav-toggle-menu").click(function(e) {
-    e.preventDefault();
-    var ul = $(this).parents('li').find('ul');
-    if(ul.hasClass('closed')) {
-      $(this).removeClass('fa-caret-down');
-      $(this).addClass('fa-caret-up');
-
-      ul.animate({
-        "max-height": (ul.find('li').length * 38) + "px"
-      }, {
-        duration: 350,
-        easing: "swing",
-        complete: function() { ul.removeClass('closed'); }
-      });
-    } else {
-      $(this).removeClass('fa-caret-up');
-      $(this).addClass('fa-caret-down');
-
-      ul.animate({
-        "max-height": "0"
-      }, {
-        duration: 350,
-        easing: "swing",
-        complete: function() { ul.addClass('closed'); }
-      });
-    }
-  });
-
-  $(".copy").click(function() {
-    var text = $(this).text();
-    if(navigator.clipboard) {
-      navigator.clipboard.writeText(text).then(function() { }, function(err) {
-        console.error('Async: Could not copy text: ', err);
-      });
-    }
-  });
-
-  function toggleLinkIcon(icon, show, parent) {
-    if(show) {
-      icon.show();
-      if(parent) {
-       icon.position({
-          my: "right-5",
-          at: "left",
-          of: parent
-        });
-      }
-    } else {
-      icon.hide();
-    }
-  }
-
-  $(".inlineLink").mouseenter(function() {
-    var target = $(this).data("target");
-    if(target) {
-      var icon = $("#" + target);
-      toggleLinkIcon(icon, true, $(this));
-    }
-  });
-
-  $(".inlineLink").mouseleave(function() {
-    var target = $(this).data("target");
-    if(target) {
-      var icon = $("#" + target);
-      toggleLinkIcon(icon, false);
-    }
-  });
-
-  $(".inlineLink").click(function() {
-    var id = $(this).attr('id');
-    if(id) {
-      var url = window.location.href;
-      var index = url.indexOf("#");
-      if(index !== -1) {
-        url = url.substring(0, index);
-      }
-
-      window.location.href = url + "#" + id;
-    }
-  });
 });

Some files were not shown because too many files changed in this diff