diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 63339e38..00000000
--- a/.editorconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-# EditorConfig is awesome: http://EditorConfig.org
-
-# top-most EditorConfig file
-root = true
-
-# Unix-style newlines with a newline ending every file
-[*]
-charset = utf-8
-indent_style = space
-indent_size = 2
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-# 4 space indentation
-[*.go]
-indent_style = tab
-indent_size = 4
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d287cd3e..00000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-debug
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index e6077a69..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "_embed/public/ace"]
-	path = _embed/public/ace
-	url = https://github.com/ajaxorg/ace-builds
diff --git a/.jsbeautifyrc b/.jsbeautifyrc
deleted file mode 100644
index 71f718e7..00000000
--- a/.jsbeautifyrc
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "html": {
-    "brace_style": "collapse",
-    "indent_scripts": "normal",
-    "max_preserve_newlines": 1,
-    "preserve_newlines": true,
-    "unformatted": ["a", "sub", "sup", "b", "i", "u"],
-    "wrap_line_length": 0
-  },
-  "css": {
-    "end_with_newline": false,
-    "newline_between_rules": true,
-    "selector_separator": " ",
-    "selector_separator_newline": true
-  },
-  "js": {
-    "indent_with_tabs": false,
-    "preserve_newlines": true,
-    "max_preserve_newlines": 2,
-    "jslint_happy": true
-  }
-}
diff --git a/_embed/public/ace b/_embed/public/ace
deleted file mode 160000
index 784ffa86..00000000
--- a/_embed/public/ace
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 784ffa862c5351e0d300370f61471b1eb95ebcf1
diff --git a/_embed/public/css/fonts.css b/_embed/public/css/fonts.css
deleted file mode 100644
index 1911d377..00000000
--- a/_embed/public/css/fonts.css
+++ /dev/null
@@ -1,137 +0,0 @@
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-cyrillic-ext.woff2) format('woff2');
-  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-cyrillic.woff2) format('woff2');
-  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-greek-ext.woff2) format('woff2');
-  unicode-range: U+1F00-1FFF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-greek.woff2) format('woff2');
-  unicode-range: U+0370-03FF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-vietnamese.woff2) format('woff2');
-  unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-latin-ext.woff2) format('woff2');
-  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Roboto'), local('Roboto-Regular'), url(roboto/normal-latin.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-cyrillic-ext.woff2) format('woff2');
-  unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-cyrillic.woff2) format('woff2');
-  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-greek-ext.woff2) format('woff2');
-  unicode-range: U+1F00-1FFF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-greek.woff2) format('woff2');
-  unicode-range: U+0370-03FF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-vietnamese.woff2) format('woff2');
-  unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-latin-ext.woff2) format('woff2');
-  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
-}
-
-@font-face {
-  font-family: 'Roboto';
-  font-style: normal;
-  font-weight: 500;
-  src: local('Roboto Medium'), local('Roboto-Medium'), url(roboto/medium-latin.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
-}
-
-@font-face {
-  font-family: 'Material Icons';
-  font-style: normal;
-  font-weight: 400;
-  src: local('Material Icons'), local('MaterialIcons-Regular'), url(material/icons.woff2) format('woff2');
-}
-
-.prompt .file-list ul li:before,
-.material-icons {
-  font-family: 'Material Icons';
-  font-weight: normal;
-  font-style: normal;
-  font-size: 24px;
-  line-height: 1;
-  letter-spacing: normal;
-  text-transform: none;
-  display: inline-block;
-  white-space: nowrap;
-  word-wrap: normal;
-  direction: ltr;
-  -webkit-font-smoothing: antialiased;
-  text-rendering: optimizeLegibility;
-  -moz-osx-font-smoothing: grayscale;
-  font-feature-settings: 'liga';
-}
diff --git a/_embed/public/css/material/icons.woff2 b/_embed/public/css/material/icons.woff2
deleted file mode 100644
index 9fa21125..00000000
Binary files a/_embed/public/css/material/icons.woff2 and /dev/null differ
diff --git a/_embed/public/css/normalize.css b/_embed/public/css/normalize.css
deleted file mode 100644
index 9b77e0eb..00000000
--- a/_embed/public/css/normalize.css
+++ /dev/null
@@ -1,461 +0,0 @@
-/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
-
-/**
- * 1. Change the default font family in all browsers (opinionated).
- * 2. Correct the line height in all browsers.
- * 3. Prevent adjustments of font size after orientation changes in
- *    IE on Windows Phone and in iOS.
- */
-
-/* Document
-   ========================================================================== */
-
-html {
-  font-family: sans-serif; /* 1 */
-  line-height: 1.15; /* 2 */
-  -ms-text-size-adjust: 100%; /* 3 */
-  -webkit-text-size-adjust: 100%; /* 3 */
-}
-
-/* Sections
-   ========================================================================== */
-
-/**
- * Remove the margin in all browsers (opinionated).
- */
-
-body {
-  margin: 0;
-}
-
-/**
- * Add the correct display in IE 9-.
- */
-
-article,
-aside,
-footer,
-header,
-nav,
-section {
-  display: block;
-}
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-
-h1 {
-  font-size: 2em;
-  margin: 0.67em 0;
-}
-
-/* Grouping content
-   ========================================================================== */
-
-/**
- * Add the correct display in IE 9-.
- * 1. Add the correct display in IE.
- */
-
-figcaption,
-figure,
-main { /* 1 */
-  display: block;
-}
-
-/**
- * Add the correct margin in IE 8.
- */
-
-figure {
-  margin: 1em 40px;
-}
-
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-
-hr {
-  box-sizing: content-box; /* 1 */
-  height: 0; /* 1 */
-  overflow: visible; /* 2 */
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-pre {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
-}
-
-/* Text-level semantics
-   ========================================================================== */
-
-/**
- * 1. Remove the gray background on active links in IE 10.
- * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
- */
-
-a {
-  background-color: transparent; /* 1 */
-  -webkit-text-decoration-skip: objects; /* 2 */
-}
-
-/**
- * Remove the outline on focused links when they are also active or hovered
- * in all browsers (opinionated).
- */
-
-a:active,
-a:hover {
-  outline-width: 0;
-}
-
-/**
- * 1. Remove the bottom border in Firefox 39-.
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-
-abbr[title] {
-  border-bottom: none; /* 1 */
-  text-decoration: underline; /* 2 */
-  text-decoration: underline dotted; /* 2 */
-}
-
-/**
- * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
- */
-
-b,
-strong {
-  font-weight: inherit;
-}
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-
-b,
-strong {
-  font-weight: bolder;
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-code,
-kbd,
-samp {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
-}
-
-/**
- * Add the correct font style in Android 4.3-.
- */
-
-dfn {
-  font-style: italic;
-}
-
-/**
- * Add the correct background and color in IE 9-.
- */
-
-mark {
-  background-color: #ff0;
-  color: #000;
-}
-
-/**
- * Add the correct font size in all browsers.
- */
-
-small {
-  font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-
-sub,
-sup {
-  font-size: 75%;
-  line-height: 0;
-  position: relative;
-  vertical-align: baseline;
-}
-
-sub {
-  bottom: -0.25em;
-}
-
-sup {
-  top: -0.5em;
-}
-
-/* Embedded content
-   ========================================================================== */
-
-/**
- * Add the correct display in IE 9-.
- */
-
-audio,
-video {
-  display: inline-block;
-}
-
-/**
- * Add the correct display in iOS 4-7.
- */
-
-audio:not([controls]) {
-  display: none;
-  height: 0;
-}
-
-/**
- * Remove the border on images inside links in IE 10-.
- */
-
-img {
-  border-style: none;
-}
-
-/**
- * Hide the overflow in IE.
- */
-
-svg:not(:root) {
-  overflow: hidden;
-}
-
-/* Forms
-   ========================================================================== */
-
-/**
- * 1. Change the font styles in all browsers (opinionated).
- * 2. Remove the margin in Firefox and Safari.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
-  font-family: sans-serif; /* 1 */
-  font-size: 100%; /* 1 */
-  line-height: 1.15; /* 1 */
-  margin: 0; /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-
-button,
-input { /* 1 */
-  overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-
-button,
-select { /* 1 */
-  text-transform: none;
-}
-
-/**
- * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
- *    controls in Android 4.
- * 2. Correct the inability to style clickable types in iOS and Safari.
- */
-
-button,
-html [type="button"], /* 1 */
-[type="reset"],
-[type="submit"] {
-  -webkit-appearance: button; /* 2 */
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-
-button::-moz-focus-inner,
-[type="button"]::-moz-focus-inner,
-[type="reset"]::-moz-focus-inner,
-[type="submit"]::-moz-focus-inner {
-  border-style: none;
-  padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-
-button:-moz-focusring,
-[type="button"]:-moz-focusring,
-[type="reset"]:-moz-focusring,
-[type="submit"]:-moz-focusring {
-  outline: 1px dotted ButtonText;
-}
-
-/**
- * Change the border, margin, and padding in all browsers (opinionated).
- */
-
-fieldset {
-  border: 1px solid #c0c0c0;
-  margin: 0 2px;
-  padding: 0.35em 0.625em 0.75em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- *    `fieldset` elements in all browsers.
- */
-
-legend {
-  box-sizing: border-box; /* 1 */
-  color: inherit; /* 2 */
-  display: table; /* 1 */
-  max-width: 100%; /* 1 */
-  padding: 0; /* 3 */
-  white-space: normal; /* 1 */
-}
-
-/**
- * 1. Add the correct display in IE 9-.
- * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-
-progress {
-  display: inline-block; /* 1 */
-  vertical-align: baseline; /* 2 */
-}
-
-/**
- * Remove the default vertical scrollbar in IE.
- */
-
-textarea {
-  overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10-.
- * 2. Remove the padding in IE 10-.
- */
-
-[type="checkbox"],
-[type="radio"] {
-  box-sizing: border-box; /* 1 */
-  padding: 0; /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-
-[type="number"]::-webkit-inner-spin-button,
-[type="number"]::-webkit-outer-spin-button {
-  height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-
-[type="search"] {
-  -webkit-appearance: textfield; /* 1 */
-  outline-offset: -2px; /* 2 */
-}
-
-/**
- * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
- */
-
-[type="search"]::-webkit-search-cancel-button,
-[type="search"]::-webkit-search-decoration {
-  -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-
-::-webkit-file-upload-button {
-  -webkit-appearance: button; /* 1 */
-  font: inherit; /* 2 */
-}
-
-/* Interactive
-   ========================================================================== */
-
-/*
- * Add the correct display in IE 9-.
- * 1. Add the correct display in Edge, IE, and Firefox.
- */
-
-details, /* 1 */
-menu {
-  display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-
-summary {
-  display: list-item;
-}
-
-/* Scripting
-   ========================================================================== */
-
-/**
- * Add the correct display in IE 9-.
- */
-
-canvas {
-  display: inline-block;
-}
-
-/**
- * Add the correct display in IE.
- */
-
-template {
-  display: none;
-}
-
-/* Hidden
-   ========================================================================== */
-
-/**
- * Add the correct display in IE 10-.
- */
-
-[hidden] {
-  display: none;
-}
diff --git a/_embed/public/css/roboto/medium-cyrillic-ext.woff2 b/_embed/public/css/roboto/medium-cyrillic-ext.woff2
deleted file mode 100644
index f63bc9a1..00000000
Binary files a/_embed/public/css/roboto/medium-cyrillic-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-cyrillic.woff2 b/_embed/public/css/roboto/medium-cyrillic.woff2
deleted file mode 100644
index b3ca824d..00000000
Binary files a/_embed/public/css/roboto/medium-cyrillic.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-greek-ext.woff2 b/_embed/public/css/roboto/medium-greek-ext.woff2
deleted file mode 100644
index 7e1a8078..00000000
Binary files a/_embed/public/css/roboto/medium-greek-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-greek.woff2 b/_embed/public/css/roboto/medium-greek.woff2
deleted file mode 100644
index 314cf3f8..00000000
Binary files a/_embed/public/css/roboto/medium-greek.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-latin-ext.woff2 b/_embed/public/css/roboto/medium-latin-ext.woff2
deleted file mode 100644
index 604b8935..00000000
Binary files a/_embed/public/css/roboto/medium-latin-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-latin.woff2 b/_embed/public/css/roboto/medium-latin.woff2
deleted file mode 100644
index 5f96609d..00000000
Binary files a/_embed/public/css/roboto/medium-latin.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/medium-vietnamese.woff2 b/_embed/public/css/roboto/medium-vietnamese.woff2
deleted file mode 100644
index d92b7125..00000000
Binary files a/_embed/public/css/roboto/medium-vietnamese.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-cyrillic-ext.woff2 b/_embed/public/css/roboto/normal-cyrillic-ext.woff2
deleted file mode 100644
index e4546e49..00000000
Binary files a/_embed/public/css/roboto/normal-cyrillic-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-cyrillic.woff2 b/_embed/public/css/roboto/normal-cyrillic.woff2
deleted file mode 100644
index d08397f7..00000000
Binary files a/_embed/public/css/roboto/normal-cyrillic.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-greek-ext.woff2 b/_embed/public/css/roboto/normal-greek-ext.woff2
deleted file mode 100644
index ed0b13ca..00000000
Binary files a/_embed/public/css/roboto/normal-greek-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-greek.woff2 b/_embed/public/css/roboto/normal-greek.woff2
deleted file mode 100644
index f630772d..00000000
Binary files a/_embed/public/css/roboto/normal-greek.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-latin-ext.woff2 b/_embed/public/css/roboto/normal-latin-ext.woff2
deleted file mode 100644
index 0c7aec28..00000000
Binary files a/_embed/public/css/roboto/normal-latin-ext.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-latin.woff2 b/_embed/public/css/roboto/normal-latin.woff2
deleted file mode 100644
index 120796bb..00000000
Binary files a/_embed/public/css/roboto/normal-latin.woff2 and /dev/null differ
diff --git a/_embed/public/css/roboto/normal-vietnamese.woff2 b/_embed/public/css/roboto/normal-vietnamese.woff2
deleted file mode 100644
index 7936b665..00000000
Binary files a/_embed/public/css/roboto/normal-vietnamese.woff2 and /dev/null differ
diff --git a/_embed/public/css/styles.css b/_embed/public/css/styles.css
deleted file mode 100644
index eb9d8a5b..00000000
--- a/_embed/public/css/styles.css
+++ /dev/null
@@ -1,1207 +0,0 @@
-body {
-  font-family: 'Roboto', sans-serif;
-  padding-top: 7.8em;
-  background-color: #f8f8f8;
-}
-
-* {
-  box-sizing: border-box;
-}
-
-*,
-*:hover,
-*:active,
-*:focus {
-  outline: 0
-}
-
-a {
-  text-decoration: none;
-}
-
-img {
-  max-width: 100%;
-}
-
-audio,
-video {
-  width: 100%;
-}
-
-pre {
-  padding: 1em;
-  border: 1px solid #e6e6e6;
-  border-radius: 0.5em;
-  background-color: #f5f5f5;
-  white-space: pre-wrap;
-  white-space: -moz-pre-wrap;
-  white-space: -pre-wrap;
-  white-space: -o-pre-wrap;
-  word-wrap: break-word;
-}
-
-button {
-  border: 0;
-  padding: .5em 1em;
-  margin-left: .5em;
-  border-radius: .1em;
-  cursor: pointer;
-  background: #2196f3;
-  color: #fff;
-  border: 1px solid rgba(0, 0, 0, 0.05);
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
-  transition: .1s ease all;
-}
-
-button:hover {
-  background-color: #1E88E5;
-}
-
-.mobile-only {
-  display: none !important;
-}
-
-.container {
-  width: 95%;
-  max-width: 960px;
-  margin: 1em auto 0;
-}
-
-i.spin {
-  animation: 1s spin linear infinite;
-}
-
-.pdf {
-  width: 100%;
-  height: calc(100vh - 13em);
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            EDITOR           *
- * * * * * * * * * * * * * * * */
-
-#editor .source {
-  display: none;
-}
-
-#editor .content {
-  background: #fff;
-  padding: 1em 0;
-}
-
-#editor #ace,
-#editor h2,
-#editor .frontmatter {
-  width: 95%;
-  max-width: 960px;
-  margin: 1em auto 0;
-}
-
-#editor h2 {
-  margin: 1.5em auto 1em;
-  color: rgba(0, 0, 0, 0.3);
-  font-weight: 500;
-}
-
-#editor .ace_gutter {
-  background-color: #fff;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *       EDITOR - MARKDOWN     *
- * * * * * * * * * * * * * * * */
-
-.frontmatter {
-  column-count: 3;
-  column-gap: 1em;
-  column-fill: balance;
-  /* display: flex; */
-  /* flex-wrap: wrap; */
-  /* justify-content: space-between; */
-  /* flex-grow: 1; */
-}
-
-.frontmatter label {
-  display: block;
-  width: calc(100% - 1em);
-  text-overflow: ellipsis;
-  overflow: hidden;
-  white-space: nowrap;
-}
-
-.frontmatter label,
-.frontmatter h3 {
-  font-weight: 500;
-  margin: 0 0;
-  color: rgba(0, 0, 0, 0.6);
-}
-
-.frontmatter input,
-.frontmatter textarea {
-  display: block;
-  width: 100%;
-  border: 0;
-  margin-top: .5em;
-  padding: 0;
-  line-height: 1;
-}
-
-.frontmatter .block,
-.frontmatter fieldset[data-type="array"],
-.button {
-  position: relative;
-  background: #fff;
-  border-radius: .2em;
-  border: 1px solid rgba(0, 0, 0, 0.075);
-  padding: .5em;
-  break-inside: avoid;
-  margin: 0 0 1em;
-  width: 100%;
-  display: inline-block;
-}
-
-.frontmatter fieldset[data-type="object"] {
-  position: relative;
-  margin: 0;
-}
-
-.frontmatter .button {
-  background-color: #2196f3;
-  color: #fff;
-  cursor: pointer;
-  text-align: center;
-}
-
-[data-type="array-item"] {
-  position: relative;
-}
-
-[data-type="array-item"] .action {
-  top: 0;
-  right: 0;
-}
-
-.frontmatter textarea {
-  resize: none;
-}
-
-[data-type="array-item"] input {
-  width: calc(100% - 1em);
-}
-
-.block .action,
-fieldset .action {
-  position: absolute;
-  top: .5em;
-  right: .5em;
-}
-
-.block>.action,
-fieldset>.action {
-  opacity: 0;
-}
-
-.block:hover>.action,
-fieldset:hover>.action {
-  opacity: 1;
-}
-
-.block .action.add,
-fieldset .action.add {
-  right: 1.5em;
-}
-
-.frontmatter .action i {
-  padding: 0;
-  font-size: 1em;
-}
-
-fieldset {
-  border: 0;
-  padding: 0;
-}
-
-.frontmatter>fieldset h3,
-.frontmatter>.group h3 {
-  font-size: 1.5em;
-  margin-bottom: .5em;
-}
-
-fieldset h3,
-.group h3 {
-  font-size: 0.9em;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            ACTION           *
- * * * * * * * * * * * * * * * */
-
-.action {
-  display: inline-block;
-  cursor: pointer;
-  -webkit-transition: 0.2s ease all;
-  transition: 0.2s ease all;
-  border: 0;
-  margin: 0;
-  color: #546E7A;
-  border-radius: 50%;
-}
-
-.action.disabled {
-  opacity: 0.2;
-  cursor: not-allowed;
-}
-
-.action i {
-  padding: 0.4em;
-  -webkit-transition: 0.2s ease-in-out all;
-  transition: 0.2s ease-in-out all;
-  border-radius: 50%;
-}
-
-.action:hover i {
-  background-color: rgba(0, 0, 0, .1);
-}
-
-.action ul {
-  position: absolute;
-  top: 0;
-  color: #7d7d7d;
-  list-style: none;
-  margin: 0;
-  padding: 0;
-  flex-direction: column;
-  display: flex;
-}
-
-.action ul li {
-  line-height: 1;
-  padding: .7em;
-  transition: .1s ease background-color;
-}
-
-.action ul li:hover {
-  background-color: rgba(0, 0, 0, 0.04);
-}
-
-
-/* * * * * * * * * * * * * * * *
- *         NEW FILE/DIR        *
- * * * * * * * * * * * * * * * */
-
-.floating {
-  position: fixed;
-  bottom: 1em;
-  right: 1em;
-}
-
-.floating .action {
-  background-color: #2196f3 !important;
-  color: #fff;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
-}
-
-#newdir {
-  position: fixed;
-  bottom: 1.3em;
-  right: 5em;
-  transition: .2s ease all;
-  opacity: 0;
-  border: 0;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24);
-  padding: .5em;
-  width: 22em;
-  border-radius: .2em;
-}
-
-#newdir.enabled {
-  opacity: 1;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            HEADER           *
- * * * * * * * * * * * * * * * */
-
-header {
-  z-index: 1000;
-  background-color: #fff;
-  border-bottom: 1px solid rgba(0, 0, 0, 0.075);
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  padding: 0;
-}
-
-header a,
-header a:hover {
-  color: inherit;
-}
-
-header p i {
-  font-size: 1em !important;
-  color: rgba(255, 255, 255, .31);
-}
-
-header>div {
-  display: flex;
-  width: 100%;
-  padding: 0.5em 0.5em 0.5em 1em;
-  align-items: center;
-}
-
-header p {
-  display: inline-block;
-  margin: 0;
-  vertical-align: middle;
-}
-
-header p a,
-header p a:hover {
-  color: inherit;
-}
-
-header .action span {
-  display: none;
-}
-
-header>div div {
-  vertical-align: middle;
-  position: relative;
-}
-
-#logout {
-  border-radius: 0;
-  margin-left: auto;
-  padding: .15em;
-}
-
-#click-overlay {
-  display: none;
-  position: fixed;
-  cursor: pointer;
-  top: 0;
-  left: 0;
-  height: 100%;
-  width: 100%;
-}
-
-#click-overlay.active {
-  display: block;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            TOP BAR          *
- * * * * * * * * * * * * * * * */
-
-#top-bar {
-  height: 4em;
-}
-
-#top-bar>div:nth-child(1) {
-  margin-right: 1em;
-  font-weight: 500;
-  font-size: 1.5em;
-  line-height: 2;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *          SEARCH BAR         *
- * * * * * * * * * * * * * * * */
-
-#search {
-  position: relative;
-  display: flex;
-  height: 100%;
-  padding: 0.75em;
-  vertical-align: middle;
-  border-radius: 0.3em;
-  background-color: #f5f5f5;
-  transition: .1s ease all;
-  width: 100%;
-  max-width: 25em;
-}
-
-#search.active {
-  background-color: #fff;
-  border-bottom-left-radius: 0;
-  border-bottom-right-radius: 0;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
-}
-
-#search.active i,
-#search.active input {
-  color: #212121;
-}
-
-#search i,
-#search input {
-  vertical-align: middle;
-}
-
-#search i {
-  margin-right: 0.3em;
-  user-select: none;
-}
-
-#search input {
-  width: 100%;
-  border: 0;
-  outline: 0;
-  background-color: transparent;
-}
-
-#search.active div {
-  visibility: visible;
-  opacity: 1;
-  top: 100%;
-}
-
-#search ul {
-  padding: 0;
-  margin: 0;
-  list-style: none;
-}
-
-#search li {
-  margin-bottom: .5em;
-}
-
-#search>div {
-  position: absolute;
-  top: 0;
-  width: 100%;
-  left: 0;
-  z-index: 999999;
-  background-color: #fff;
-  text-align: left;
-  color: #ccc;
-  box-shadow: 0 2px 3px rgba(0, 0, 0, .06), 0 2px 2px rgba(0, 0, 0, .12);
-  padding: .5em;
-  border-bottom-left-radius: .3em;
-  border-bottom-right-radius: .3em;
-  transition: .1s ease all;
-  visibility: hidden;
-  opacity: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-  max-height: 50vh;
-}
-
-#search>div div {
-  white-space: pre-wrap;
-  white-space: -moz-pre-wrap;
-  white-space: -pre-wrap;
-  white-space: -o-pre-wrap;
-  word-wrap: break-word;
-}
-
-#search>div p {
-  width: 100%;
-  text-align: center;
-  display: none;
-  margin: 0;
-  max-width: none;
-}
-
-#search.ongoing p {
-  display: block;
-}
-
-#search.active div i,
-#sidebar #search.active div i {
-  color: #ccc;
-  text-align: center;
-  margin: 0 auto;
-  display: table;
-}
-
-#search::-webkit-input-placeholder {
-  color: rgba(255, 255, 255, .5);
-}
-
-#search:-moz-placeholder {
-  opacity: 1;
-  color: rgba(255, 255, 255, .5);
-}
-
-#search::-moz-placeholder {
-  opacity: 1;
-  color: rgba(255, 255, 255, .5);
-}
-
-#search:-ms-input-placeholder {
-  color: rgba(255, 255, 255, .5);
-}
-
-
-/* * * * * * * * * * * * * * * *
- *          BOTTOM BAR         *
- * * * * * * * * * * * * * * * */
-
-#bottom-bar {
-  background-color: #fafafa;
-  border-top: 1px solid rgba(0, 0, 0, 0.075);
-  border-bottom: 1px solid rgba(0, 0, 0, 0.075);
-  height: 3.8em;
-}
-
-#bottom-bar>div:first-child>* {
-  display: inline-block;
-  vertical-align: middle;
-}
-
-#bottom-bar>div:first-child>i {
-  margin-right: .3em;
-}
-
-#bottom-bar>*:first-child {
-  margin-right: auto;
-  max-width: calc(100% - 25em);
-  width: 100%;
-}
-
-#bottom-bar p {
-  text-overflow: ellipsis;
-  overflow: hidden;
-  width: calc(100% - 3em);
-  white-space: nowrap;
-}
-
-#more {
-  display: none;
-}
-
-#file-only {
-  display: inline-block;
-  border-right: 1px solid rgba(0, 0, 0, 0.075);
-  padding-right: .3em;
-  margin-right: .3em;
-  transition: .2s ease opacity, visibility;
-  visibility: visible;
-}
-
-#file-only.disabled {
-  opacity: 0;
-  visibility: hidden;
-}
-
-#download ul.active {
-  top: 0;
-  right: 0;
-}
-
-#more ul.active {
-  right: .5em;
-  top: 4.5em;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            DROPDOWN         *
- * * * * * * * * * * * * * * * */
-
-.dropdown {
-  position: fixed;
-  top: -100%;
-  right: -100%;
-  visibility: hidden;
-  display: flex;
-  flex-direction: column;
-  border-radius: .1em;
-  border-top-left-radius: 0;
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
-  background: #fff;
-  z-index: 9999999;
-}
-
-.dropdown.active {
-  visibility: visible;
-}
-
-.dropdown .action {
-  padding: .7em;
-}
-
-.dropdown i {
-  padding: 0;
-  vertical-align: middle;
-}
-
-.dropdown span {
-  display: inline-block;
-  margin-left: .5em;
-  font-size: .9em;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *          BREADCRUMBS        *
- * * * * * * * * * * * * * * * */
-
-#previous {
-  margin-left: -.5em;
-}
-
-#breadcrumbs {
-  min-width: 7em;
-}
-
-#breadcrumbs.active {
-  top: 0;
-  left: 0;
-  right: auto;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            LISTING          *
- * * * * * * * * * * * * * * * */
-
-#listing {
-  max-width: calc(100% - 1.2em);
-  width: 100%;
-}
-
-#listing h2 {
-  margin: 0 0 0 0.5em;
-  font-size: 1em;
-  color: rgba(0, 0, 0, 0.2);
-  font-weight: 500;
-}
-
-#listing .item div:last-of-type * {
-  text-overflow: ellipsis;
-  overflow: hidden;
-}
-
-#listing>div {
-  display: flex;
-  padding: 0;
-  flex-wrap: wrap;
-  justify-content: flex-start;
-  position: relative;
-}
-
-#listing .item {
-  background-color: #fff;
-  position: relative;
-  display: flex;
-  flex-wrap: nowrap;
-  color: #6f6f6f;
-  transition: .1s ease all;
-  align-items: center;
-  cursor: pointer;
-}
-
-#listing .item div:last-of-type {
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  overflow: hidden;
-}
-
-#listing .item p {
-  margin: 0;
-}
-
-#listing .item .size,
-#listing .item .modified {
-  font-size: 0.9em;
-}
-
-#listing .item .name {
-  font-weight: bold;
-}
-
-#listing .item i {
-  font-size: 4em;
-  margin-right: 0.1em;
-  vertical-align: bottom;
-}
-
-#listing h2.message,
-.message {
-  text-align: center;
-  font-size: 3em;
-  margin: 1em auto;
-  display: block !important;
-  width: 95%;
-  color: rgba(0, 0, 0, 0.2);
-  font-weight: 500;
-}
-
-.message i {
-  font-size: inherit;
-  vertical-align: middle;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *        LISTING - MOSAIC     *
- * * * * * * * * * * * * * * * */
-
-#listing.mosaic {
-  margin-top: 1em;
-}
-
-#listing.mosaic .item {
-  width: calc(33% - 1em);
-  margin: .5em;
-  padding: 0.5em;
-  border-radius: 0.2em;
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .06), 0 1px 2px rgba(0, 0, 0, .12);
-}
-
-#listing.mosaic .item:hover {
-  box-shadow: 0 1px 3px rgba(0, 0, 0, .12), 0 1px 2px rgba(0, 0, 0, .24) !important;
-}
-
-#listing.mosaic .header {
-  display: none;
-}
-
-#listing.mosaic .item div:first-of-type {
-  width: 5em;
-}
-
-#listing.mosaic .item div:last-of-type {
-  width: calc(100% - 5vw);
-}
-
-
-/* * * * * * * * * * * * * * * *
-  *        LISTING - DETAIL     *
-  * * * * * * * * * * * * * * * */
-
-#listing.list {
-  flex-direction: column;
-  padding-top: 3.25em;
-  width: 100%;
-  max-width: 100%;
-  margin: 0;
-}
-
-#listing.list .item {
-  width: 100%;
-  margin: 0;
-  border: 0;
-  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-  padding: 1em;
-}
-
-#listing.list h2 {
-  display: none;
-}
-
-#listing .item[aria-selected=true] {
-  background: #2196f3 !important;
-  color: #fff !important;
-}
-
-#listing.list .item div:first-of-type {
-  width: 3em;
-}
-
-#listing.list .item div:first-of-type i {
-  font-size: 2em;
-}
-
-#listing.list .item div:last-of-type {
-  width: calc(100% - 3em);
-  display: flex;
-  align-items: center;
-}
-
-#listing.list .item .name {
-  width: 50%;
-}
-
-#listing.list .item .size {
-  width: 25%;
-}
-
-#listing .item.header {
-  display: none !important;
-  background-color: #ccc;
-}
-
-#listing.list .header i {
-  font-size: 1.5em;
-  vertical-align: middle;
-  margin-left: .2em;
-}
-
-#listing.list .item.header {
-  display: flex !important;
-  background: #fafafa;
-  position: fixed;
-  width: 100%;
-  top: 7.8em;
-  left: 0;
-  z-index: 999;
-  padding: .85em;
-}
-
-#listing.list .item.header>div:first-child {
-  width: 0;
-}
-
-#listing.list .item.header .name {
-  margin-right: 3em;
-}
-
-#listing.list .header {
-  display: flex;
-  background: #fafafa;
-  position: fixed;
-  width: 100%;
-  top: 7.8em;
-  left: 0;
-  z-index: 999;
-}
-
-#listing.list .header a {
-  color: inherit;
-}
-
-#listing.list .item.header>div:first-child {
-  width: 0;
-}
-
-#listing.list .name {
-  font-weight: normal;
-}
-
-#listing.list .item.header .name {
-  margin-right: 3em;
-}
-
-#listing.list .header span {
-  vertical-align: middle;
-}
-
-#listing.list .header i {
-  opacity: 0;
-  transition: .1s ease all;
-}
-
-#listing.list .header p:hover i,
-#listing.list .header .active i {
-  opacity: 1;
-}
-
-#listing.list .item.header .active {
-  font-weight: bold;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *  MULTIPLE SELECTION DIALOG  *
- * * * * * * * * * * * * * * * */
-
-#multiple-selection {
-  position: fixed;
-  bottom: -4em;
-  left: 0;
-  z-index: 99999999;
-  width: 100%;
-  background-color: #2196f3;
-  height: 4em;
-  display: flex !important;
-  padding: 0.5em 0.5em 0.5em 1em;
-  justify-content: space-between;
-  align-items: center;
-  transition: .2s ease all;
-}
-
-#multiple-selection.active {
-  bottom: 0;
-}
-
-#multiple-selection * {
-  margin: 0;
-}
-
-#multiple-selection p,
-#multiple-selection i {
-  color: #fff;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            PROMPT           *
- * * * * * * * * * * * * * * * */
-
-.overlay,
-.prompt,
-.help {
-  opacity: 0;
-  z-index: -1;
-  transition: .1s ease opacity, z-index;
-}
-
-.overlay.active,
-.prompt.active,
-.help.active {
-  z-index: 9999999;
-  opacity: 1;
-}
-
-.overlay {
-  background-color: rgba(0, 0, 0, 0.5);
-  position: fixed;
-  top: 0;
-  left: 0;
-  height: 0;
-  width: 0;
-}
-
-.overlay.active {
-  height: 100%;
-  width: 100%;
-}
-
-.prompt,
-.help {
-  position: fixed;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  z-index: 99999999;
-  background: #fff;
-  border: 1px solid rgba(0, 0, 0, 0.075);
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
-  padding: 2em;
-  max-width: 25em;
-  width: 90%;
-  max-height: 95%;
-}
-
-.prompt h3,
-.help h3 {
-  margin: 0;
-  font-weight: 500;
-  font-size: 1.5em;
-}
-
-.prompt p,
-.help p {
-  font-size: .9em;
-  color: rgba(0, 0, 0, 0.8);
-  margin: .5em 0 1em;
-}
-
-.prompt input {
-  width: 100%;
-  border: 1px solid #dadada;
-  line-height: 1;
-  padding: .3em;
-}
-
-.prompt code {
-  word-wrap: break-word;
-}
-
-.prompt div,
-.help div {
-  margin-top: 1em;
-  display: flex;
-  justify-content: flex-start;
-  flex-direction: row-reverse;
-}
-
-.prompt .cancel {
-  background-color: #ECEFF1;
-  color: #37474F;
-}
-
-.prompt .cancel:hover {
-  background-color: #e9eaeb;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *        PROMPT - MOVE        *
- * * * * * * * * * * * * * * * */
-
-.prompt .file-list {
-  flex-direction: initial;
-  max-height: 50vh;
-  overflow: auto;
-}
-
-.prompt .file-list ul {
-  list-style: none;
-  margin: 0;
-  padding: 0;
-  width: 100%;
-}
-
-.prompt .file-list ul li {
-  width: 100%;
-  user-select: none;
-}
-
-.prompt .file-list ul li[aria-selected=true] {
-  background: #2196f3 !important;
-  color: #fff !important;
-  transition: .1s ease all;
-}
-
-.prompt .file-list ul li:hover {
-  background-color: #e9eaeb;
-  cursor: pointer;
-}
-
-.prompt .file-list ul li:before {
-  content: "folder";
-  color: #6f6f6f;
-  vertical-align: middle;
-  padding: 0 .25em;
-  line-height: 2em;
-}
-
-.prompt .file-list ul li[aria-selected=true]:before {
-  color: white;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *             HELP            *
- * * * * * * * * * * * * * * * */
-
-.help {
-  max-width: 24em;
-  visibility: hidden;
-  top: -100%;
-  left: -100%;
-}
-
-.help.active {
-  visibility: visible;
-  top: 50%;
-  left: 50%;
-}
-
-.help ul {
-  padding: 0;
-  margin: 1em 0;
-  list-style: none;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *            FOOTER           *
- * * * * * * * * * * * * * * * */
-
-footer {
-  font-size: 0.6em;
-  margin: 2em 0 2em;
-  text-align: center;
-  color: grey;
-}
-
-footer a,
-footer a:hover {
-  color: inherit;
-}
-
-
-/* * * * * * * * * * * * * * * *
- *        MEDIA QUERIES        *
- * * * * * * * * * * * * * * * */
-
-@media screen and (max-width: 850px) {
-  .frontmatter {
-    column-count: 2;
-  }
-}
-
-@media screen and (max-width: 650px) {
-  body {
-    transition: .2s ease padding;
-  }
-  .mobile-only {
-    display: inherit !important;
-  }
-  #top-bar>div:nth-child(1) {
-    display: none;
-  }
-  #bottom-bar>*:first-child {
-    max-width: calc(100% - 16em) !important;
-  }
-  #main-actions {
-    position: fixed;
-    top: -100%;
-    right: -100%;
-    visibility: hidden;
-    display: flex;
-    flex-direction: column;
-    border-radius: .1em;
-    border-top-left-radius: 0;
-    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
-    background: #fff;
-    z-index: 9999999;
-  }
-  #main-actions.active {
-    right: .5em;
-    top: 4.5em;
-    visibility: visible;
-  }
-  #main-actions .action {
-    padding: .7em;
-    border-radius: 0;
-    align-items: center;
-  }
-  #main-actions .action:hover {
-    background-color: rgba(0, 0, 0, 0.04);
-  }
-  #main-actions i {
-    padding: 0;
-    vertical-align: middle;
-  }
-  #main-actions .action:hover i {
-    padding: 0;
-    background-color: transparent;
-  }
-  #main-actions span {
-    display: inline-block;
-    margin-left: .5em;
-    font-size: .9em;
-  }
-  #listing.list .item .size,
-  #listing.list .item .modified {
-    display: none;
-  }
-  #listing.list .item .name {
-    width: 100%;
-  }
-  .frontmatter {
-    column-count: 1;
-  }
-}
-
-@media screen and (max-width: 450px) {
-  #bottom-bar p {
-    display: none !important;
-  }
-}
-
-
-/* * * * * * * * * * * * * * * *
- *          ANIMATIONS         *
- * * * * * * * * * * * * * * * */
-
-@keyframes spin {
-  100% {
-    -webkit-transform: rotate(-360deg);
-    transform: rotate(-360deg);
-  }
-}
diff --git a/_embed/public/js/common.js b/_embed/public/js/common.js
deleted file mode 100644
index 76cae2a3..00000000
--- a/_embed/public/js/common.js
+++ /dev/null
@@ -1,685 +0,0 @@
-'use strict'
-
-var tempID = '_fm_internal_temporary_id'
-var ssl = (window.location.protocol === 'https:')
-var templates = {}
-var selectedItems = []
-var overlay
-var clickOverlay
-
-// Removes an element, if exists, from an array
-Array.prototype.removeElement = function (element) {
-  var i = this.indexOf(element)
-  if (i !== -1) {
-    this.splice(i, 1)
-  }
-}
-
-// Replaces an element inside an array by another
-Array.prototype.replaceElement = function (oldElement, newElement) {
-  var i = this.indexOf(oldElement)
-  if (i !== -1) {
-    this[i] = newElement
-  }
-}
-
-// Sends a costum event to itself
-Document.prototype.sendCostumEvent = function (text) {
-  this.dispatchEvent(new window.CustomEvent(text))
-}
-
-// Gets the content of a cookie
-Document.prototype.getCookie = function (name) {
-  var re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$')
-  return document.cookie.replace(re, '$1')
-}
-
-// Remove the last directory of an url
-var removeLastDirectoryPartOf = function (url) {
-  var arr = url.split('/')
-  if (arr.pop() === '') {
-    arr.pop()
-  }
-  return (arr.join('/'))
-}
-
-function getCSSRule (rules) {
-  for (let i = 0; i < rules.length; i++) {
-    rules[i] = rules[i].toLowerCase()
-  }
-
-  let result = null
-  let find = Array.prototype.find
-
-  find.call(document.styleSheets, styleSheet => {
-    result = find.call(styleSheet.cssRules, cssRule => {
-      let found = false
-
-      if (cssRule instanceof CSSStyleRule) {
-        for (let i = 0; i < rules.length; i++) {
-          if (cssRule.selectorText.toLowerCase() === rules[i]) {
-            found = true
-          }
-        }
-      }
-
-      return found
-    })
-
-    return result != null
-  })
-
-  return result
-}
-
-/* * * * * * * * * * * * * * * *
- *                             *
- *            BUTTONS          *
- *                             *
- * * * * * * * * * * * * * * * */
-var buttons = {
-  previousState: {}
-}
-
-buttons.setLoading = function (name) {
-  if (typeof this[name] === 'undefined') return
-  let i = this[name].querySelector('i')
-
-  this.previousState[name] = i.innerHTML
-  i.style.opacity = 0
-
-  setTimeout(function () {
-    i.classList.add('spin')
-    i.innerHTML = 'autorenew'
-    i.style.opacity = 1
-  }, 200)
-}
-
-// Changes an element to done animation
-buttons.setDone = function (name, success = true) {
-  let i = this[name].querySelector('i')
-
-  i.style.opacity = 0
-
-  let thirdStep = () => {
-    i.innerHTML = this.previousState[name]
-    i.style.opacity = null
-
-    if (selectedItems.length === 0 && document.getElementById('listing')) {
-      document.sendCostumEvent('changed-selected')
-    }
-  }
-
-  let secondStep = () => {
-    i.style.opacity = 0
-    setTimeout(thirdStep, 200)
-  }
-
-  let firstStep = () => {
-    i.classList.remove('spin')
-    i.innerHTML = success
-      ? 'done'
-      : 'close'
-    i.style.opacity = 1
-    setTimeout(secondStep, 1000)
-  }
-
-  setTimeout(firstStep, 200)
-  return false
-}
-
-/* * * * * * * * * * * * * * * *
- *                             *
- *            WEBDAV           *
- *                             *
- * * * * * * * * * * * * * * * */
-var webdav = {}
-
-webdav.convertURL = function (url) {
-  return window.location.origin + url.replace(baseURL + '/', webdavURL + '/')
-}
-
-webdav.move = function (oldLink, newLink) {
-  return new Promise((resolve, reject) => {
-    let request = new window.XMLHttpRequest()
-    let destination = newLink.replace(baseURL + '/', webdavURL + '/')
-
-    destination = window.location.origin + destination.substring(prefixURL.length)
-
-    request.open('MOVE', webdav.convertURL(oldLink), true)
-    request.setRequestHeader('Destination', destination)
-    request.onload = () => {
-      if (request.status === 201 || request.status === 204) {
-        resolve()
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send()
-  })
-}
-
-webdav.put = function (link, body, headers = {}) {
-  return new Promise((resolve, reject) => {
-    let request = new window.XMLHttpRequest()
-    request.open('PUT', webdav.convertURL(link), true)
-
-    for (let key in headers) {
-      request.setRequestHeader(key, headers[key])
-    }
-
-    request.onload = () => {
-      if (request.status == 201) {
-        resolve()
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send(body)
-  })
-}
-
-webdav.propfind = function (link, body, headers = {}) {
-  return new Promise((resolve, reject) => {
-    let request = new window.XMLHttpRequest()
-    request.open('PROPFIND', webdav.convertURL(link), true)
-
-    for (let key in headers) {
-      request.setRequestHeader(key, headers[key])
-    }
-
-    request.onload = () => {
-      if (request.status < 300) {
-        resolve(request.responseText)
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send(body)
-  })
-}
-
-webdav.delete = function (link) {
-  return new Promise((resolve, reject) => {
-    let request = new window.XMLHttpRequest()
-    request.open('DELETE', webdav.convertURL(link), true)
-    request.onload = () => {
-      if (request.status === 204) {
-        resolve()
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send()
-  })
-}
-
-webdav.new = function (link) {
-  return new Promise((resolve, reject) => {
-    let request = new window.XMLHttpRequest()
-    request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), webdav.convertURL(link), true)
-    request.onload = () => {
-      if (request.status === 201) {
-        resolve()
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send()
-  })
-}
-
-/* * * * * * * * * * * * * * * *
- *                             *
- *            EVENTS           *
- *                             *
- * * * * * * * * * * * * * * * */
-function closePrompt (event) {
-  let prompt = document.querySelector('.prompt')
-
-  if (!prompt) return
-
-  if (typeof event !== 'undefined') {
-    event.preventDefault()
-  }
-
-  document.querySelector('.overlay').classList.remove('active')
-  prompt.classList.remove('active')
-
-  setTimeout(() => {
-    prompt.remove()
-  }, 100)
-}
-
-function notImplemented (event) {
-  event.preventDefault()
-  clickOverlay.click()
-
-  let clone = document.importNode(templates.message.content, true)
-  clone.querySelector('h3').innerHTML = 'Not implemented'
-  clone.querySelector('p').innerHTML = "Sorry, but this feature wasn't implemented yet."
-
-  document.querySelector('body').appendChild(clone)
-  document.querySelector('.overlay').classList.add('active')
-  document.querySelector('.prompt').classList.add('active')
-}
-
-// Prevent Default event
-var preventDefault = function (event) {
-  event.preventDefault()
-}
-
-function logoutEvent (event) {
-  let request = new window.XMLHttpRequest()
-  request.open('GET', window.location.pathname, true, 'username', 'password')
-  request.send()
-  request.onreadystatechange = function () {
-    if (request.readyState === 4) {
-      window.location = '/'
-    }
-  }
-}
-
-function openEvent (event) {
-  if (event.currentTarget.classList.contains('disabled')) {
-    return false
-  }
-
-  let link = '?raw=true'
-
-  if (selectedItems.length) {
-    link = document.getElementById(selectedItems[0]).dataset.url + link
-  } else {
-    link = window.location.pathname + link
-  }
-
-  window.open(link)
-  return false
-}
-
-function getHash (event, hash) {
-  event.preventDefault()
-
-  let request = new window.XMLHttpRequest()
-  let link
-
-  if (selectedItems.length) {
-    link = document.getElementById(selectedItems[0]).dataset.url
-  } else {
-    link = window.location.pathname
-  }
-
-  request.open('GET', `${link}?checksum=${hash}`, true)
-
-  request.onload = () => {
-    if (request.status >= 300) {
-      console.log(request.statusText)
-      return
-    }
-    event.target.parentElement.innerHTML = request.responseText
-  }
-  request.onerror = (e) => console.log(e)
-  request.send()
-}
-
-function infoEvent (event) {
-  event.preventDefault()
-  if (event.currentTarget.classList.contains('disabled')) {
-    return
-  }
-
-  let dir = false
-  let link
-
-  if (selectedItems.length) {
-    link = document.getElementById(selectedItems[0]).dataset.url
-    dir = document.getElementById(selectedItems[0]).dataset.dir
-  } else {
-    if (document.getElementById('listing') !== null) {
-      dir = true
-    }
-
-    link = window.location.pathname
-  }
-
-  buttons.setLoading('info', false)
-
-  webdav.propfind(link)
-    .then((text) => {
-      let parser = new window.DOMParser()
-      let xml = parser.parseFromString(text, 'text/xml')
-      let clone = document.importNode(templates.info.content, true)
-
-      let value = xml.getElementsByTagName('displayname')
-      if (value.length > 0) {
-        clone.getElementById('display_name').innerHTML = value[0].innerHTML
-      } else {
-        clone.getElementById('display_name').innerHTML = xml.getElementsByTagName('D:displayname')[0].innerHTML
-      }
-
-      value = xml.getElementsByTagName('getcontentlength')
-      if (value.length > 0) {
-        clone.getElementById('content_length').innerHTML = value[0].innerHTML
-      } else {
-        clone.getElementById('content_length').innerHTML = xml.getElementsByTagName('D:getcontentlength')[0].innerHTML
-      }
-
-      value = xml.getElementsByTagName('getlastmodified')
-      if (value.length > 0) {
-        clone.getElementById('last_modified').innerHTML = value[0].innerHTML
-      } else {
-        clone.getElementById('last_modified').innerHTML = xml.getElementsByTagName('D:getlastmodified')[0].innerHTML
-      }
-
-      if (dir === true || dir === 'true') {
-        clone.querySelector('.file-only').style.display = 'none'
-      }
-
-      document.querySelector('body').appendChild(clone)
-      document.querySelector('.overlay').classList.add('active')
-      document.querySelector('.prompt').classList.add('active')
-      buttons.setDone('info', true)
-    })
-    .catch(e => {
-      buttons.setDone('info', false)
-      console.log(e)
-    })
-}
-
-function deleteOnSingleFile () {
-  closePrompt()
-  buttons.setLoading('delete')
-
-  webdav.delete(window.location.pathname)
-    .then(() => {
-      window.location.pathname = removeLastDirectoryPartOf(window.location.pathname)
-    })
-    .catch(e => {
-      buttons.setDone('delete', false)
-      console.log(e)
-    })
-}
-
-function deleteOnListing () {
-  closePrompt()
-  buttons.setLoading('delete')
-
-  let promises = []
-
-  for (let id of selectedItems) {
-    promises.push(webdav.delete(document.getElementById(id).dataset.url))
-  }
-
-  Promise.all(promises)
-    .then(() => {
-      listing.reload()
-      buttons.setDone('delete')
-    })
-    .catch(e => {
-      console.log(e)
-      buttons.setDone('delete', false)
-    })
-}
-
-// Handles the delete button event
-function deleteEvent (event) {
-  let single = false
-
-  if (!selectedItems.length) {
-    selectedItems = ['placeholder']
-    single = true
-  }
-
-  let clone = document.importNode(templates.question.content, true)
-  clone.querySelector('h3').innerHTML = 'Delete files'
-
-  if (single) {
-    clone.querySelector('form').addEventListener('submit', deleteOnSingleFile)
-    clone.querySelector('p').innerHTML = `Are you sure you want to delete this file/folder?`
-  } else {
-    clone.querySelector('form').addEventListener('submit', deleteOnListing)
-    clone.querySelector('p').innerHTML = `Are you sure you want to delete ${selectedItems.length} file(s)?`
-  }
-
-  clone.querySelector('input').remove()
-  clone.querySelector('.ok').innerHTML = 'Delete'
-
-  document.body.appendChild(clone)
-  document.querySelector('.overlay').classList.add('active')
-  document.querySelector('.prompt').classList.add('active')
-
-  return false
-}
-
-function resetSearchText () {
-  let box = document.querySelector('#search > div div')
-
-  if (user.AllowCommands) {
-    box.innerHTML = `Search or use one of your supported commands: ${user.Commands.join(", ")}.`
-  } else {
-    box.innerHTML = 'Type and press enter to search.'
-  }
-}
-
-function searchEvent (event) {
-  if (this.value.length === 0) {
-    resetSearchText()
-    return
-  }
-
-  let value = this.value,
-    search = document.getElementById('search'),
-    scrollable = document.querySelector('#search > div'),
-    box = document.querySelector('#search > div div'),
-    pieces = value.split(' '),
-    supported = false
-
-  user.Commands.forEach(function (cmd) {
-    if (cmd == pieces[0]) {
-      supported = true
-    }
-  })
-
-  if (!supported || !user.AllowCommands) {
-    box.innerHTML = 'Press enter to search.'
-  } else {
-    box.innerHTML = 'Press enter to execute.'
-  }
-
-  if (event.keyCode === 13) {
-    box.innerHTML = ''
-    search.classList.add('ongoing')
-
-    let url = window.location.host + window.location.pathname
-
-    if (document.getElementById('editor')) {
-      url = removeLastDirectoryPartOf(url)
-    }
-
-    let protocol = ssl ? 'wss:' : 'ws:'
-
-    if (supported && user.AllowCommands) {
-      let conn = new window.WebSocket(`${protocol}//${url}?command=true`)
-
-      conn.onopen = function () {
-        conn.send(value)
-      }
-
-      conn.onmessage = function (event) {
-        box.innerHTML = event.data
-        scrollable.scrollTop = scrollable.scrollHeight
-      }
-
-      conn.onclose = function (event) {
-        search.classList.remove('ongoing')
-        listing.reload()
-      }
-
-      return
-    }
-
-    box.innerHTML = '<ul></ul>'
-
-    let ul = box.querySelector('ul')
-    let conn = new window.WebSocket(`${protocol}//${url}?search=true`)
-
-    conn.onopen = function () {
-      conn.send(value)
-    }
-
-    conn.onmessage = function (event) {
-      ul.innerHTML += '<li><a href="' + event.data + '">' + event.data + '</a></li>'
-      scrollable.scrollTop = scrollable.scrollHeight
-    }
-
-    conn.onclose = function (event) {
-      search.classList.remove('ongoing')
-    }
-  }
-}
-
-function setupSearch () {
-  let search = document.getElementById('search')
-  let searchInput = search.querySelector('input')
-  let searchDiv = search.querySelector('div')
-  let hover = false
-  let focus = false
-
-  resetSearchText()
-
-  searchInput.addEventListener('focus', event => {
-    focus = true
-    search.classList.add('active')
-  })
-
-  searchDiv.addEventListener('mouseover', event => {
-    hover = true
-    search.classList.add('active')
-  })
-
-  searchInput.addEventListener('blur', event => {
-    focus = false
-    if (hover) return
-    search.classList.remove('active')
-  })
-
-  search.addEventListener('mouseleave', event => {
-    hover = false
-    if (focus) return
-    search.classList.remove('active')
-  })
-
-  search.addEventListener('click', event => {
-    search.classList.add('active')
-    search.querySelector('input').focus()
-  })
-
-  searchInput.addEventListener('keyup', searchEvent)
-}
-
-function closeHelp (event) {
-  event.preventDefault()
-
-  document.querySelector('.help').classList.remove('active')
-  document.querySelector('.overlay').classList.remove('active')
-}
-
-function openHelp (event) {
-  closePrompt(event)
-
-  document.querySelector('.help').classList.add('active')
-  document.querySelector('.overlay').classList.add('active')
-}
-
-window.addEventListener('keydown', (event) => {
-  if (event.keyCode === 27) {
-    if (document.querySelector('.help.active')) {
-      closeHelp(event)
-    }
-  }
-
-  if (event.keyCode === 46) {
-    deleteEvent(event)
-  }
-
-  if (event.keyCode === 112) {
-    event.preventDefault()
-    openHelp(event)
-  }
-})
-
-/* * * * * * * * * * * * * * * *
- *                             *
- *           BOOTSTRAP         *
- *                             *
- * * * * * * * * * * * * * * * */
-
-document.addEventListener('DOMContentLoaded', function (event) {
-  overlay = document.querySelector('.overlay')
-  clickOverlay = document.querySelector('#click-overlay')
-
-  buttons.logout = document.getElementById('logout')
-  buttons.open = document.getElementById('open')
-  buttons.delete = document.getElementById('delete')
-  buttons.previous = document.getElementById('previous')
-  buttons.info = document.getElementById('info')
-
-  // Attach event listeners
-  buttons.logout.addEventListener('click', logoutEvent)
-  buttons.open.addEventListener('click', openEvent)
-  buttons.info.addEventListener('click', infoEvent)
-
-  templates.question = document.querySelector('#question-template')
-  templates.info = document.querySelector('#info-template')
-  templates.message = document.querySelector('#message-template')
-  templates.move = document.querySelector('#move-template')
-
-  if (user.AllowEdit) {
-    buttons.delete.addEventListener('click', deleteEvent)
-  }
-
-  let dropdownButtons = document.querySelectorAll('.action[data-dropdown]')
-  Array.from(dropdownButtons).forEach(button => {
-    button.addEventListener('click', event => {
-      button.querySelector('ul').classList.toggle('active')
-      clickOverlay.classList.add('active')
-
-      clickOverlay.addEventListener('click', event => {
-        button.querySelector('ul').classList.remove('active')
-        clickOverlay.classList.remove('active')
-      })
-    })
-  })
-
-  overlay.addEventListener('click', event => {
-    if (document.querySelector('.help.active')) {
-      closeHelp(event)
-      return
-    }
-
-    closePrompt(event)
-  })
-
-  let mainActions = document.getElementById('main-actions')
-
-  document.getElementById('more').addEventListener('click', event => {
-    event.preventDefault()
-    event.stopPropagation()
-
-    clickOverlay.classList.add('active')
-    mainActions.classList.add('active')
-
-    clickOverlay.addEventListener('click', event => {
-      mainActions.classList.remove('active')
-      clickOverlay.classList.remove('active')
-    })
-  })
-
-  setupSearch()
-  return false
-})
diff --git a/_embed/public/js/editor.js b/_embed/public/js/editor.js
deleted file mode 100644
index 38ff40c7..00000000
--- a/_embed/public/js/editor.js
+++ /dev/null
@@ -1,278 +0,0 @@
-'use strict'
-
-var editor = {}
-
-editor.textareaAutoGrow = function () {
-  let autogrow = function () {
-    console.log(this.style.height)
-    this.style.height = 'auto'
-    this.style.height = (this.scrollHeight) + 'px'
-  }
-
-  let textareas = document.getElementsByTagName('textarea')
-
-  let addAutoGrow = () => {
-    Array.from(textareas).forEach(textarea => {
-      autogrow.bind(textarea)()
-      textarea.addEventListener('keyup', autogrow)
-    })
-  }
-
-  addAutoGrow()
-  window.addEventListener('resize', addAutoGrow)
-}
-
-editor.toggleSourceEditor = function (event) {
-  event.preventDefault()
-
-  if (document.querySelector('[data-kind="content-only"]')) {
-    window.location = window.location.pathname + '?visual=true'
-    return
-  }
-
-  window.location = window.location.pathname + '?visual=false'
-}
-
-function deleteFrontMatterItem (event) {
-  event.preventDefault()
-  document.getElementById(this.dataset.delete).remove()
-}
-
-function makeFromBaseTemplate (id, type, name, parent) {
-  let clone = document.importNode(templates.base.content, true)
-  clone.querySelector('fieldset').id = id
-  clone.querySelector('fieldset').dataset.type = type
-  clone.querySelector('h3').innerHTML = name
-  clone.querySelector('.delete').dataset.delete = id
-  clone.querySelector('.delete').addEventListener('click', deleteFrontMatterItem)
-  clone.querySelector('.add').addEventListener('click', addFrontMatterItem)
-
-  if (parent.classList.contains('frontmatter')) {
-    parent.insertBefore(clone, document.querySelector('div.button.add'))
-    return
-  }
-
-  parent.appendChild(clone)
-}
-
-function makeFromArrayItemTemplate (id, number, parent) {
-  let clone = document.importNode(templates.arrayItem.content, true)
-  clone.querySelector('[data-type="array-item"]').id = `${id}-${number}`
-  clone.querySelector('input').name = id
-  clone.querySelector('input').id = id
-  clone.querySelector('div.action').dataset.delete = `${id}-${number}`
-  clone.querySelector('div.action').addEventListener('click', deleteFrontMatterItem)
-  parent.querySelector('.group').appendChild(clone)
-  document.getElementById(`${id}-${number}`).querySelector('input').focus()
-}
-
-function makeFromObjectItemTemplate (id, name, parent) {
-  let clone = document.importNode(templates.objectItem.content, true)
-  clone.querySelector('.block').id = `block-${id}`
-  clone.querySelector('.block').dataset.content = id
-  clone.querySelector('label').for = id
-  clone.querySelector('label').innerHTML = name
-  clone.querySelector('input').name = id
-  clone.querySelector('input').id = id
-  clone.querySelector('.action').dataset.delete = `block-${id}`
-  clone.querySelector('.action').addEventListener('click', deleteFrontMatterItem)
-
-  parent.appendChild(clone)
-  document.getElementById(id).focus()
-}
-
-function addFrontMatterItemPrompt (parent) {
-  return function (event) {
-    event.preventDefault()
-
-    let value = event.currentTarget.querySelector('input').value
-    if (value === '') {
-      return true
-    }
-
-    closePrompt(event)
-
-    let name = value.substring(0, value.lastIndexOf(':')),
-      type = value.substring(value.lastIndexOf(':') + 1, value.length)
-
-    if (type !== '' && type !== 'array' && type !== 'object') {
-      name = value
-    }
-
-    name = name.replace(' ', '_')
-
-    let id = name
-
-    if (parent.id != '') {
-      id = parent.id + '.' + id
-    }
-
-    if (type == 'array' || type == 'object') {
-      if (parent.dataset.type == 'parent') {
-        makeFromBaseTemplate(id, type, name, document.querySelector('.frontmatter'))
-        return
-      }
-
-      makeFromBaseTemplate(id, type, name, block)
-      return
-    }
-
-    let group = parent.querySelector('.group')
-
-    if (group == null) {
-      parent.insertAdjacentHTML('afterbegin', '<div class="group"></div>')
-      group = parent.querySelector('.group')
-    }
-
-    makeFromObjectItemTemplate(id, name, group)
-  }
-}
-
-function addFrontMatterItem (event) {
-  event.preventDefault()
-
-  let parent = event.currentTarget.parentNode,
-    type = parent.dataset.type
-
-  // If the block is an array
-  if (type === 'array') {
-    let id = parent.id + '[]',
-      count = parent.querySelectorAll('.group > div').length,
-      fieldsets = parent.getElementsByTagName('fieldset')
-
-    if (fieldsets.length > 0) {
-      let itemType = fieldsets[0].dataset.type,
-        itemID = parent.id + '[' + fieldsets.length + ']',
-        itemName = fieldsets.length
-
-      makeFromBaseTemplate(itemID, itemType, itemName, parent)
-    } else {
-      makeFromArrayItemTemplate(id, count, parent)
-    }
-
-    return
-  }
-
-  if (type == 'object' || type == 'parent') {
-    let clone = document.importNode(templates.question.content, true)
-    clone.querySelector('form').id = tempID
-    clone.querySelector('h3').innerHTML = 'New field'
-    clone.querySelector('p').innerHTML = 'Write the field name and then press enter. If you want to create an array or an object, end the name with <code>:array</code> or <code>:object.</code>'
-    clone.querySelector('.ok').innerHTML = 'Create'
-    clone.querySelector('form').addEventListener('submit', addFrontMatterItemPrompt(parent))
-    clone.querySelector('form').classList.add('active')
-    document.querySelector('body').appendChild(clone)
-
-    document.querySelector('.overlay').classList.add('active')
-    document.getElementById(tempID).classList.add('active')
-  }
-
-  return false
-}
-
-document.addEventListener('DOMContentLoaded', (event) => {
-  if (!document.getElementById('editor')) return
-
-  editor.textareaAutoGrow()
-
-  templates.arrayItem = document.getElementById('array-item-template')
-  templates.base = document.getElementById('base-template')
-  templates.objectItem = document.getElementById('object-item-template')
-  templates.temporary = document.getElementById('temporary-template')
-
-  buttons.save = document.querySelector('#save')
-  buttons.editSource = document.querySelector('#edit-source')
-
-  if (buttons.editSource) {
-    buttons.editSource.addEventListener('click', editor.toggleSourceEditor)
-  }
-
-  let container = document.getElementById('editor'),
-    kind = container.dataset.kind,
-    rune = container.dataset.rune
-
-  if (kind != 'frontmatter-only') {
-    let editor = document.querySelector('.content #ace'),
-      mode = editor.dataset.mode,
-      textarea = document.querySelector('textarea[name="content"]'),
-      aceEditor = ace.edit('ace'),
-      options = {
-        wrap: true,
-        maxLines: Infinity,
-        theme: 'ace/theme/github',
-        showPrintMargin: false,
-        fontSize: '1em',
-        minLines: 20
-    }
-
-    aceEditor.getSession().setMode('ace/mode/' + mode)
-    aceEditor.getSession().setValue(textarea.value)
-    aceEditor.getSession().on('change', function () {
-      textarea.value = aceEditor.getSession().getValue()
-    })
-
-    if (mode == 'markdown') options.showGutter = false
-    aceEditor.setOptions(options)
-  }
-
-  let deleteFrontMatterItemButtons = document.getElementsByClassName('delete')
-  Array.from(deleteFrontMatterItemButtons).forEach(button => {
-    button.addEventListener('click', deleteFrontMatterItem)
-  })
-
-  let addFrontMatterItemButtons = document.getElementsByClassName('add')
-  Array.from(addFrontMatterItemButtons).forEach(button => {
-    button.addEventListener('click', addFrontMatterItem)
-  })
-
-  let saveContent = function () {
-    let data = form2js(document.querySelector('form'))
-
-    if (typeof data.content === 'undefined' && kind !== 'frontmatter-only') {
-      data.content = ''
-    }
-
-    if (typeof data.content === 'number') {
-      data.content = data.content.toString()
-    }
-
-    let request = new XMLHttpRequest()
-
-    buttons.setLoading('save')
-
-    webdav.put(window.location.pathname, JSON.stringify(data), {
-      'Kind': kind,
-      'Rune': rune
-    })
-      .then(() => {
-        buttons.setDone('save')
-      })
-      .catch(e => {
-        console.log(e)
-        buttons.setDone('save', false)
-      })
-  }
-
-  document.querySelector('#save').addEventListener('click', event => {
-    event.preventDefault()
-    saveContent()
-  })
-
-  document.querySelector('form').addEventListener('submit', (event) => {
-    event.preventDefault()
-    saveContent()
-  })
-
-  window.addEventListener('keydown', (event) => {
-    if (event.ctrlKey || event.metaKey) {
-      switch (String.fromCharCode(event.which).toLowerCase()) {
-        case 's':
-          event.preventDefault()
-          saveContent()
-          break
-      }
-    }
-  })
-
-  return false
-})
diff --git a/_embed/public/js/listing.js b/_embed/public/js/listing.js
deleted file mode 100644
index baef101d..00000000
--- a/_embed/public/js/listing.js
+++ /dev/null
@@ -1,580 +0,0 @@
-'use strict'
-
-var listing = {
-  selectMultiple: false
-}
-
-listing.reload = function (callback) {
-  let request = new XMLHttpRequest()
-
-  request.open('GET', window.location)
-  request.setRequestHeader('Minimal', 'true')
-  request.send()
-  request.onreadystatechange = function () {
-    if (request.readyState === 4) {
-      if (request.status === 200) {
-        document.querySelector('body main').innerHTML = request.responseText
-        listing.addDoubleTapEvent()
-
-        if (typeof callback === 'function') {
-          callback()
-        }
-      }
-    }
-  }
-}
-
-listing.itemDragStart = function (event) {
-  let el = event.target
-
-  for (let i = 0; i < 5; i++) {
-    if (!el.classList.contains('item')) {
-      el = el.parentElement
-    }
-  }
-
-  event.dataTransfer.setData('id', el.id)
-  event.dataTransfer.setData('name', el.querySelector('.name').innerHTML)
-}
-
-listing.itemDragOver = function (event) {
-  event.preventDefault()
-  let el = event.target
-
-  for (let i = 0; i < 5; i++) {
-    if (!el.classList.contains('item')) {
-      el = el.parentElement
-    }
-  }
-
-  el.style.opacity = 1
-}
-
-listing.itemDrop = function (e) {
-  e.preventDefault()
-
-  let el = e.target,
-    id = e.dataTransfer.getData('id'),
-    name = e.dataTransfer.getData('name')
-
-  if (id == '' || name == '') return
-
-  for (let i = 0; i < 5; i++) {
-    if (!el.classList.contains('item')) {
-      el = el.parentElement
-    }
-  }
-
-  if (el.id === id) return
-
-  let oldLink = document.getElementById(id).dataset.url,
-    newLink = el.dataset.url + name
-
-  webdav.move(oldLink, newLink)
-    .then(() => listing.reload())
-    .catch(e => console.log(e))
-}
-
-listing.documentDrop = function (event) {
-  event.preventDefault()
-  let dt = event.dataTransfer,
-    files = dt.files,
-    el = event.target,
-    items = document.getElementsByClassName('item')
-
-  for (let i = 0; i < 5; i++) {
-    if (el != null && !el.classList.contains('item')) {
-      el = el.parentElement
-    }
-  }
-
-  if (files.length > 0) {
-    if (el != null && el.classList.contains('item') && el.dataset.dir == 'true') {
-      listing.handleFiles(files, el.querySelector('.name').innerHTML + '/')
-      return
-    }
-
-    listing.handleFiles(files, '')
-  } else {
-    Array.from(items).forEach(file => {
-      file.style.opacity = 1
-    })
-  }
-}
-
-listing.rename = function (event) {
-  if (!selectedItems.length || selectedItems.length > 1) {
-    return false
-  }
-
-  let item = document.getElementById(selectedItems[0])
-
-  if (item.classList.contains('disabled')) {
-    return false
-  }
-
-  let link = item.dataset.url,
-    field = item.querySelector('.name'),
-    name = field.innerHTML
-
-  let submit = (event) => {
-    event.preventDefault()
-
-    let newName = event.currentTarget.querySelector('input').value,
-      newLink = removeLastDirectoryPartOf(link) + '/' + newName
-
-    closePrompt(event)
-    buttons.setLoading('rename')
-
-    webdav.move(link, newLink).then(() => {
-      listing.reload(() => {
-        newName = btoa(newName)
-        selectedItems = [newName]
-        document.getElementById(newName).setAttribute('aria-selected', true)
-        listing.handleSelectionChange()
-      })
-
-      buttons.setDone('rename')
-    }).catch(error => {
-      field.innerHTML = name
-      buttons.setDone('rename', false)
-      console.log(error)
-    })
-
-    return false
-  }
-
-  let clone = document.importNode(templates.question.content, true)
-  clone.querySelector('h3').innerHTML = 'Rename'
-  clone.querySelector('input').value = name
-  clone.querySelector('.ok').innerHTML = 'Rename'
-  clone.querySelector('form').addEventListener('submit', submit)
-
-  document.querySelector('body').appendChild(clone)
-  document.querySelector('.overlay').classList.add('active')
-  document.querySelector('.prompt').classList.add('active')
-
-  return false
-}
-
-listing.handleFiles = function (files, base) {
-  buttons.setLoading('upload')
-
-  let promises = []
-
-  for (let file of files) {
-    promises.push(webdav.put(window.location.pathname + base + file.name, file))
-  }
-
-  Promise.all(promises)
-    .then(() => {
-      listing.reload()
-      buttons.setDone('upload')
-    })
-    .catch(e => {
-      console.log(e)
-      buttons.setDone('upload', false)
-    })
-
-  return false
-}
-
-listing.unselectAll = function () {
-  let items = document.getElementsByClassName('item')
-  Array.from(items).forEach(link => {
-    link.setAttribute('aria-selected', false)
-  })
-
-  selectedItems = []
-
-  listing.handleSelectionChange()
-  return false
-}
-
-listing.handleSelectionChange = function (event) {
-  listing.redefineDownloadURLs()
-
-  let selectedNumber = selectedItems.length,
-    fileAction = document.getElementById('file-only')
-
-  if (selectedNumber) {
-    fileAction.classList.remove('disabled')
-
-    if (selectedNumber > 1) {
-      buttons.open.classList.add('disabled')
-      buttons.rename.classList.add('disabled')
-      buttons.info.classList.add('disabled')
-    }
-
-    if (selectedNumber == 1) {
-      if (document.getElementById(selectedItems[0]).dataset.dir == 'true') {
-        buttons.open.classList.add('disabled')
-      } else {
-        buttons.open.classList.remove('disabled')
-      }
-
-      buttons.info.classList.remove('disabled')
-      buttons.rename.classList.remove('disabled')
-    }
-
-    return false
-  }
-
-  buttons.info.classList.remove('disabled')
-  fileAction.classList.add('disabled')
-  return false
-}
-
-listing.redefineDownloadURLs = function () {
-  let files = ''
-
-  for (let i = 0; i < selectedItems.length; i++) {
-    let url = document.getElementById(selectedItems[i]).dataset.url
-    files += url.replace(window.location.pathname, '') + ','
-  }
-
-  files = files.substring(0, files.length - 1)
-  files = encodeURIComponent(files)
-
-  let links = document.querySelectorAll('#download ul a')
-  Array.from(links).forEach(link => {
-    link.href = '?download=' + link.dataset.format + '&files=' + files
-  })
-}
-
-listing.openItem = function (event) {
-  window.location = event.currentTarget.dataset.url
-}
-
-listing.selectItem = function (event) {
-  let el = event.currentTarget
-
-  if (selectedItems.length != 0) event.preventDefault()
-  if (selectedItems.indexOf(el.id) == -1) {
-    if (!event.ctrlKey && !listing.selectMultiple) listing.unselectAll()
-
-    el.setAttribute('aria-selected', true)
-    selectedItems.push(el.id)
-  } else {
-    el.setAttribute('aria-selected', false)
-    selectedItems.removeElement(el.id)
-  }
-
-  listing.handleSelectionChange()
-  return false
-}
-
-listing.newFileButton = function (event) {
-  event.preventDefault()
-
-  let clone = document.importNode(templates.question.content, true)
-  clone.querySelector('h3').innerHTML = 'New file'
-  clone.querySelector('p').innerHTML = 'End with a trailing slash to create a dir.'
-  clone.querySelector('.ok').innerHTML = 'Create'
-  clone.querySelector('form').addEventListener('submit', listing.newFilePrompt)
-
-  document.querySelector('body').appendChild(clone)
-  document.querySelector('.overlay').classList.add('active')
-  document.querySelector('.prompt').classList.add('active')
-}
-
-listing.newFilePrompt = function (event) {
-  event.preventDefault()
-  buttons.setLoading('new')
-
-  let name = event.currentTarget.querySelector('input').value
-
-  webdav.new(window.location.pathname + name)
-    .then(() => {
-      buttons.setDone('new')
-      listing.reload()
-    })
-    .catch(e => {
-      console.log(e)
-      buttons.setDone('new', false)
-    })
-
-  closePrompt(event)
-  return false
-}
-
-listing.updateColumns = function (event) {
-  let columns = Math.floor(document.getElementById('listing').offsetWidth / 300),
-    items = getCSSRule(['#listing.mosaic .item', '.mosaic#listing .item'])
-
-  items.style.width = `calc(${100/columns}% - 1em)`
-}
-
-listing.addDoubleTapEvent = function () {
-  let items = document.getElementsByClassName('item'),
-    touches = {
-      id: '',
-      count: 0
-  }
-
-  Array.from(items).forEach(file => {
-    file.addEventListener('touchstart', event => {
-      if (touches.id != file.id) {
-        touches.id = file.id
-        touches.count = 1
-
-        setTimeout(() => {
-          touches.count = 0
-        }, 300)
-
-        return
-      }
-
-      touches.count++
-
-      if (touches.count > 1) {
-        window.location = file.dataset.url
-      }
-    })
-  })
-}
-
-// Keydown events
-window.addEventListener('keydown', (event) => {
-  if (event.keyCode == 27) {
-    listing.unselectAll()
-
-    if (document.querySelectorAll('.prompt').length) {
-      closePrompt(event)
-    }
-  }
-
-  if (event.keyCode == 113) {
-    listing.rename()
-  }
-
-  if (event.ctrlKey || event.metaKey) {
-    switch (String.fromCharCode(event.which).toLowerCase()) {
-      case 's':
-        event.preventDefault()
-        window.location = '?download=true'
-    }
-  }
-})
-
-window.addEventListener('resize', () => {
-  listing.updateColumns()
-})
-
-listing.selectMoveFolder = function (event) {
-  if (event.target.getAttribute('aria-selected') === 'true') {
-    event.target.setAttribute('aria-selected', false)
-    return
-  } else {
-    if (document.querySelector('.file-list li[aria-selected=true]')) {
-      document.querySelector('.file-list li[aria-selected=true]').setAttribute('aria-selected', false)
-    }
-    event.target.setAttribute('aria-selected', true)
-    return
-  }
-}
-
-listing.getJSON = function (link) {
-  return new Promise((resolve, reject) => {
-    let request = new XMLHttpRequest()
-    request.open('GET', link)
-    request.setRequestHeader('Accept', 'application/json')
-    request.onload = () => {
-      if (request.status == 200) {
-        resolve(request.responseText)
-      } else {
-        reject(request.statusText)
-      }
-    }
-    request.onerror = () => reject(request.statusText)
-    request.send()
-  })
-}
-
-listing.moveMakeItem = function (url, name) {
-  let node = document.createElement('li'),
-    count = 0
-
-  node.dataset.url = url
-  node.innerHTML = name
-  node.setAttribute('aria-selected', false)
-
-  node.addEventListener('dblclick', listing.moveDialogNext)
-  node.addEventListener('click', listing.selectMoveFolder)
-  node.addEventListener('touchstart', event => {
-    count++
-
-    setTimeout(() => {
-      count = 0
-    }, 300)
-
-    if (count > 1) {
-      listing.moveDialogNext(event)
-    }
-  })
-
-  return node
-}
-
-listing.moveDialogNext = function (event) {
-  let request = new XMLHttpRequest(),
-    prompt = document.querySelector('form.prompt.active'),
-    list = prompt.querySelector('div.file-list ul')
-
-  prompt.addEventListener('submit', listing.moveSelected)
-
-  listing.getJSON(event.target.dataset.url)
-    .then((data) => {
-      let dirs = 0
-
-      prompt.querySelector('ul').innerHTML = ''
-      prompt.querySelector('code').innerHTML = event.target.dataset.url
-
-      if (event.target.dataset.url != baseURL + '/') {
-        let node = listing.moveMakeItem(removeLastDirectoryPartOf(event.target.dataset.url) + '/', '..')
-        list.appendChild(node)
-      }
-
-      if (JSON.parse(data) == null) {
-        prompt.querySelector('p').innerHTML = `There aren't any folders in this directory.`
-        return
-      }
-
-      for (let f of JSON.parse(data)) {
-        if (f.IsDir === true) {
-          dirs++
-          list.appendChild(listing.moveMakeItem(f.URL, f.Name))
-        }
-      }
-
-      if (dirs === 0)
-        prompt.querySelector('p').innerHTML = `There aren't any folders in this directory.`
-    })
-    .catch(e => console.log(e))
-}
-
-listing.moveSelected = function (event) {
-  event.preventDefault()
-
-  let promises = []
-  buttons.setLoading('move')
-
-  for (let file of selectedItems) {
-    let fileElement = document.getElementById(file),
-      destFolder = event.target.querySelector('p code').innerHTML
-
-    if (event.currentTarget.querySelector('li[aria-selected=true]') != null) {
-      destFolder = event.currentTarget.querySelector('li[aria-selected=true]').dataset.url
-    }
-
-    let destPath = '/' + destFolder + '/' + fileElement.querySelector('.name').innerHTML
-    destPath = destPath.replace('//', '/')
-
-    promises.push(webdav.move(fileElement.dataset.url, destPath))
-  }
-
-  Promise.all(promises)
-    .then(() => {
-      closePrompt(event)
-      buttons.setDone('move')
-      listing.reload()
-    })
-    .catch(e => {
-      console.log(e)
-    })
-}
-
-listing.moveEvent = function (event) {
-  if (event.currentTarget.classList.contains('disabled'))
-    return
-
-  listing.getJSON(window.location.pathname)
-    .then((data) => {
-      let prompt = document.importNode(templates.move.content, true),
-        list = prompt.querySelector('div.file-list ul'),
-        dirs = 0
-
-      prompt.querySelector('form').addEventListener('submit', listing.moveSelected)
-      prompt.querySelector('code').innerHTML = window.location.pathname
-
-      if (window.location.pathname !== baseURL + '/') {
-        list.appendChild(listing.moveMakeItem(removeLastDirectoryPartOf(window.location.pathname) + '/', '..'))
-      }
-
-      for (let f of JSON.parse(data)) {
-        if (f.IsDir === true) {
-          dirs++
-          list.appendChild(listing.moveMakeItem(f.URL, f.Name))
-        }
-      }
-
-      if (dirs === 0) {
-        prompt.querySelector('p').innerHTML = `There aren't any folders in this directory.`
-      }
-
-      document.body.appendChild(prompt)
-      document.querySelector('.overlay').classList.add('active')
-      document.querySelector('.prompt').classList.add('active')
-    })
-    .catch(e => console.log(e))
-}
-
-document.addEventListener('DOMContentLoaded', event => {
-  listing.updateColumns()
-  listing.addDoubleTapEvent()
-
-  buttons.rename = document.getElementById('rename')
-  buttons.upload = document.getElementById('upload')
-  buttons.new = document.getElementById('new')
-  buttons.download = document.getElementById('download')
-  buttons.move = document.getElementById('move')
-
-  document.getElementById('multiple-selection-activate').addEventListener('click', event => {
-    listing.selectMultiple = true
-    clickOverlay.click()
-
-    document.getElementById('multiple-selection').classList.add('active')
-    document.querySelector('body').style.paddingBottom = '4em'
-  })
-
-  document.getElementById('multiple-selection-cancel').addEventListener('click', event => {
-    listing.selectMultiple = false
-
-    document.querySelector('body').style.paddingBottom = '0'
-    document.getElementById('multiple-selection').classList.remove('active')
-  })
-
-  if (user.AllowEdit) {
-    buttons.move.addEventListener('click', listing.moveEvent)
-    buttons.rename.addEventListener('click', listing.rename)
-  }
-
-  let items = document.getElementsByClassName('item')
-
-  if (user.AllowNew) {
-    buttons.upload.addEventListener('click', (event) => {
-      document.getElementById('upload-input').click()
-    })
-
-    buttons.new.addEventListener('click', listing.newFileButton)
-
-    // Drag and Drop
-    document.addEventListener('dragover', function (event) {
-      event.preventDefault()
-    }, false)
-
-    document.addEventListener('dragenter', (event) => {
-      Array.from(items).forEach(file => {
-        file.style.opacity = 0.5
-      })
-    }, false)
-
-    document.addEventListener('dragend', (event) => {
-      Array.from(items).forEach(file => {
-        file.style.opacity = 1
-      })
-    }, false)
-
-    document.addEventListener('drop', listing.documentDrop, false)
-  }
-})
diff --git a/_embed/public/js/vendor/form2js.js b/_embed/public/js/vendor/form2js.js
deleted file mode 100644
index 2614c194..00000000
--- a/_embed/public/js/vendor/form2js.js
+++ /dev/null
@@ -1,356 +0,0 @@
-/**
- * Copyright (c) 2010 Maxim Vasiliev
- *
- * 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.
- *
- * @author Maxim Vasiliev
- * Date: 09.09.2010
- * Time: 19:02:33
- */
-
-
-(function (root, factory)
-{
-	if (typeof exports !== 'undefined' && typeof module !== 'undefined' && module.exports) {
-		// NodeJS
-		module.exports = factory();
-	}
-	else if (typeof define === 'function' && define.amd)
-	{
-		// AMD. Register as an anonymous module.
-		define(factory);
-	}
-	else
-	{
-		// Browser globals
-		root.form2js = factory();
-	}
-}(this, function ()
-{
-	"use strict";
-
-	/**
-	 * Returns form values represented as Javascript object
-	 * "name" attribute defines structure of resulting object
-	 *
-	 * @param rootNode {Element|String} root form element (or it's id) or array of root elements
-	 * @param delimiter {String} structure parts delimiter defaults to '.'
-	 * @param skipEmpty {Boolean} should skip empty text values, defaults to true
-	 * @param nodeCallback {Function} custom function to get node value
-	 * @param useIdIfEmptyName {Boolean} if true value of id attribute of field will be used if name of field is empty
-	 */
-	function form2js(rootNode, delimiter, skipEmpty, nodeCallback, useIdIfEmptyName, getDisabled)
-	{
-		getDisabled = getDisabled ? true : false;
-		if (typeof skipEmpty == 'undefined' || skipEmpty == null) skipEmpty = true;
-		if (typeof delimiter == 'undefined' || delimiter == null) delimiter = '.';
-		if (arguments.length < 5) useIdIfEmptyName = false;
-
-		rootNode = typeof rootNode == 'string' ? document.getElementById(rootNode) : rootNode;
-
-		var formValues = [],
-			currNode,
-			i = 0;
-
-		/* If rootNode is array - combine values */
-		if (rootNode.constructor == Array || (typeof NodeList != "undefined" && rootNode.constructor == NodeList))
-		{
-			while(currNode = rootNode[i++])
-			{
-				formValues = formValues.concat(getFormValues(currNode, nodeCallback, useIdIfEmptyName, getDisabled));
-			}
-		}
-		else
-		{
-			formValues = getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
-		}
-
-		return processNameValues(formValues, skipEmpty, delimiter);
-	}
-
-	/**
-	 * Processes collection of { name: 'name', value: 'value' } objects.
-	 * @param nameValues
-	 * @param skipEmpty if true skips elements with value == '' or value == null
-	 * @param delimiter
-	 */
-	function processNameValues(nameValues, skipEmpty, delimiter)
-	{
-		var result = {},
-			arrays = {},
-			i, j, k, l,
-			value,
-			nameParts,
-			currResult,
-			arrNameFull,
-			arrName,
-			arrIdx,
-			namePart,
-			name,
-			_nameParts;
-
-		for (i = 0; i < nameValues.length; i++)
-		{
-			value = nameValues[i].value;
-
-			if (skipEmpty && (value === '' || value === null)) continue;
-
-			name = nameValues[i].name;
-			_nameParts = name.split(delimiter);
-			nameParts = [];
-			currResult = result;
-			arrNameFull = '';
-
-			for(j = 0; j < _nameParts.length; j++)
-			{
-				namePart = _nameParts[j].split('][');
-				if (namePart.length > 1)
-				{
-					for(k = 0; k < namePart.length; k++)
-					{
-						if (k == 0)
-						{
-							namePart[k] = namePart[k] + ']';
-						}
-						else if (k == namePart.length - 1)
-						{
-							namePart[k] = '[' + namePart[k];
-						}
-						else
-						{
-							namePart[k] = '[' + namePart[k] + ']';
-						}
-
-						arrIdx = namePart[k].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);
-						if (arrIdx)
-						{
-							for(l = 1; l < arrIdx.length; l++)
-							{
-								if (arrIdx[l]) nameParts.push(arrIdx[l]);
-							}
-						}
-						else{
-							nameParts.push(namePart[k]);
-						}
-					}
-				}
-				else
-					nameParts = nameParts.concat(namePart);
-			}
-
-			for (j = 0; j < nameParts.length; j++)
-			{
-				namePart = nameParts[j];
-
-				if (namePart.indexOf('[]') > -1 && j == nameParts.length - 1)
-				{
-					arrName = namePart.substr(0, namePart.indexOf('['));
-					arrNameFull += arrName;
-
-					if (!currResult[arrName]) currResult[arrName] = [];
-					currResult[arrName].push(value);
-				}
-				else if (namePart.indexOf('[') > -1)
-				{
-					arrName = namePart.substr(0, namePart.indexOf('['));
-					arrIdx = namePart.replace(/(^([a-z_]+)?\[)|(\]$)/gi, '');
-
-					/* Unique array name */
-					arrNameFull += '_' + arrName + '_' + arrIdx;
-
-					/*
-					 * Because arrIdx in field name can be not zero-based and step can be
-					 * other than 1, we can't use them in target array directly.
-					 * Instead we're making a hash where key is arrIdx and value is a reference to
-					 * added array element
-					 */
-
-					if (!arrays[arrNameFull]) arrays[arrNameFull] = {};
-					if (arrName != '' && !currResult[arrName]) currResult[arrName] = [];
-
-					if (j == nameParts.length - 1)
-					{
-						if (arrName == '')
-						{
-							currResult.push(value);
-							arrays[arrNameFull][arrIdx] = convertValue(currResult[currResult.length - 1]);
-						}
-						else
-						{
-							currResult[arrName].push(value);
-							arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
-						}
-					}
-					else
-					{
-						if (!arrays[arrNameFull][arrIdx])
-						{
-							if ((/^[0-9a-z_]+\[?/i).test(nameParts[j+1])) currResult[arrName].push({});
-							else currResult[arrName].push([]);
-
-							arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
-						}
-					}
-
-					currResult = convertValue(arrays[arrNameFull][arrIdx]);
-				}
-				else
-				{
-					arrNameFull += namePart;
-
-					if (j < nameParts.length - 1) /* Not the last part of name - means object */
-					{
-						if (!currResult[namePart]) currResult[namePart] = {};
-						currResult = convertValue(currResult[namePart]);
-					}
-					else
-					{
-						currResult[namePart] = convertValue(value);
-					}
-				}
-			}
-		}
-
-		return result;
-	}
-
-    function convertValue(value) {
-        if (value == "true") return true;
-        if (value == "false") return false;
-        if (!isNaN(value)) return parseInt(value);
-        return value;
-    }
-
-    function getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
-    {
-        var result = extractNodeValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
-        return result.length > 0 ? result : getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
-    }
-
-    function getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
-	{
-		var result = [],
-			currentNode = rootNode.firstChild;
-
-		while (currentNode)
-		{
-			result = result.concat(extractNodeValues(currentNode, nodeCallback, useIdIfEmptyName, getDisabled));
-			currentNode = currentNode.nextSibling;
-		}
-
-		return result;
-	}
-
-    function extractNodeValues(node, nodeCallback, useIdIfEmptyName, getDisabled) {
-        if (node.disabled && !getDisabled) return [];
-
-        var callbackResult, fieldValue, result, fieldName = getFieldName(node, useIdIfEmptyName);
-
-        callbackResult = nodeCallback && nodeCallback(node);
-
-        if (callbackResult && callbackResult.name) {
-            result = [callbackResult];
-        }
-        else if (fieldName != '' && node.nodeName.match(/INPUT|TEXTAREA/i)) {
-            fieldValue = getFieldValue(node, getDisabled);
-            if (null === fieldValue) {
-                result = [];
-            } else {
-                result = [ { name: fieldName, value: fieldValue} ];
-            }
-        }
-        else if (fieldName != '' && node.nodeName.match(/SELECT/i)) {
-	        fieldValue = getFieldValue(node, getDisabled);
-	        result = [ { name: fieldName.replace(/\[\]$/, ''), value: fieldValue } ];
-        }
-        else {
-            result = getSubFormValues(node, nodeCallback, useIdIfEmptyName, getDisabled);
-        }
-
-        return result;
-    }
-
-	function getFieldName(node, useIdIfEmptyName)
-	{
-		if (node.name && node.name != '') return node.name;
-		else if (useIdIfEmptyName && node.id && node.id != '') return node.id;
-		else return '';
-	}
-
-
-	function getFieldValue(fieldNode, getDisabled)
-	{
-		if (fieldNode.disabled && !getDisabled) return null;
-
-		switch (fieldNode.nodeName) {
-			case 'INPUT':
-			case 'TEXTAREA':
-				switch (fieldNode.type.toLowerCase()) {
-					case 'radio':
-			if (fieldNode.checked && fieldNode.value === "false") return false;
-					case 'checkbox':
-                        if (fieldNode.checked && fieldNode.value === "true") return true;
-                        if (!fieldNode.checked && fieldNode.value === "true") return false;
-			if (fieldNode.checked) return fieldNode.value;
-						break;
-
-					case 'button':
-					case 'reset':
-					case 'submit':
-					case 'image':
-						return '';
-						break;
-
-					default:
-						return fieldNode.value;
-						break;
-				}
-				break;
-
-			case 'SELECT':
-				return getSelectedOptionValue(fieldNode);
-				break;
-
-			default:
-				break;
-		}
-
-		return null;
-	}
-
-	function getSelectedOptionValue(selectNode)
-	{
-		var multiple = selectNode.multiple,
-			result = [],
-			options,
-			i, l;
-
-		if (!multiple) return selectNode.value;
-
-		for (options = selectNode.getElementsByTagName("option"), i = 0, l = options.length; i < l; i++)
-		{
-			if (options[i].selected) result.push(options[i].value);
-		}
-
-		return result;
-	}
-
-	return form2js;
-
-}));
diff --git a/_embed/templates/base.tmpl b/_embed/templates/base.tmpl
deleted file mode 100644
index 38fbeaf5..00000000
--- a/_embed/templates/base.tmpl
+++ /dev/null
@@ -1,292 +0,0 @@
-<!DOCTYPE html>
-<html>
-{{ $absURL := .Config.AbsoluteURL }}
-<head>
-    <title>{{.Name}}</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
-    <meta charset="utf-8">
-    <link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/normalize.css">
-    <link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/fonts.css">
-    <link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/styles.css">
-    {{- if ne .User.StyleSheet "" -}}
-    <style>{{ CSS .User.StyleSheet }}</style>
-    {{- end -}}
-
-    <script>
-    var user = JSON.parse('{{ Marshal .User }}'),
-        webdavURL = "{{.Config.AbsoluteWebdavURL }}",
-        baseURL = "{{.Config.AbsoluteURL}}",
-        prefixURL = "{{ .Config.PrefixURL }}";
-    </script>
-    <script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/common.js" defer></script>
-    {{- if .IsDir }}
-    <script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/listing.js" defer></script>
-    {{- else }}
-    <script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/ace/src-min/ace.js" defer></script>
-    <script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/vendor/form2js.js" defer></script>
-    <script src="{{ .Config.AbsoluteURL }}/_filemanagerinternal/js/editor.js" defer></script>
-    {{- end }}
-    {{- if .Config.HugoEnabled }}
-    <script src="{{ .Config.AbsoluteURL }}/_hugointernal/js/application.js" defer></script>
-    {{- end }}
-</head>
-<body>
-    <header>
-        <div id="top-bar">
-            <div><p>File Manager</p></div>
-            <div id="search">
-                <i class="material-icons" title="Search">search</i>
-                <input type="text" aria-label="Write here to search" placeholder="Search or execute a command...">
-                <div>
-                    <div>Loading...</div>
-                    <p><i class="material-icons spin">autorenew</i></p>
-                </div>
-            </div>
-
-            <div class="action" id="logout" tabindex="0" role="button" aria-label="Log out">
-                <i class="material-icons" title="Logout">exit_to_app</i>
-            </div>
-        </div>
-
-        <div id="bottom-bar">
-            <div>
-                {{- if ne .Name "/"}}
-                <div data-dropdown tabindex="0" aria-label="Previous" role="button" class="action" id="previous">
-                    <i class="material-icons" title="Previous">subdirectory_arrow_left</i>
-                    <ul class="dropdown" id="breadcrumbs">
-                    {{- range $item := .BreadcrumbMap }}
-                        <a tabindex="0" href="{{ $absURL }}{{ $item.URL }}"><li>{{ $item.Name }}</li></a>
-                    {{- end }}
-                    </ul>
-                </div>
-                {{- end }}
-
-                {{ if ne .Name "/"}}<p id="current-file">{{ .Name }}</p>{{ end }}
-            </div>
-
-            <div class="actions{{ if .IsDir }} disabled{{ end }}" id="file-only">
-                {{- if and (not .IsDir) (.User.AllowEdit) }}
-                {{- if .Editor}}
-
-                {{- if eq .Data.Mode "markdown" }}
-                <div tabindex="0" role="button" aria-label="Preview" class="action" id="preview" onclick="notImplemented(event);">
-                    <i class="material-icons" title="Preview">remove_red_eye</i>
-                </div>
-                {{- end }}
-
-                {{- if eq .Data.Visual true }}
-                <div tabindex="0" role="button" aria-label="Toggle edit source" class="action" id="edit-source">
-                    <i class="material-icons" title="Toggle edit source">code</i>
-                </div>
-                {{- end }}
-                {{- end }}
-
-                <div tabindex="0" role="button" aria-label="Save" class="action" id="save">
-                    <i class="material-icons" title="Save">save</i>
-                </div>
-                {{- end }}
-
-                {{- if .IsDir }}
-                <div tabindex="0" role="button" aria-label="See raw" class="action" id="open">
-                    <i class="material-icons" title="See raw">open_in_new</i>
-                    <span>See raw</span>
-                </div>
-                {{- end }}
-
-                {{- if and (.User.AllowEdit) (.IsDir) }}
-                <div tabindex="0" role="button" aria-label="Move" class="action" id="move">
-                    <i class="material-icons" title="Move">forward</i>
-                    <span>Move file</span>
-                </div>
-                {{- end }}
-
-                {{- if and .IsDir .User.AllowEdit }}
-                <div tabindex="0" role="button" aria-label="Edit" class="action" id="rename">
-                    <i class="material-icons" title="Edit">mode_edit</i>
-                </div>
-                {{- end }}
-
-                {{- if and .User.AllowEdit .IsDir }}
-                <div tabindex="0" role="button" aria-label="Delete" class="action" id="delete">
-                    <i class="material-icons" title="Delete">delete</i><span>Delete</span>
-                </div>
-                {{- end }}
-            </div>
-
-            <div tabindex="0" role="button" aria-label="Moew" class="action mobile-only" id="more">
-                <i class="material-icons">more_vert</i>
-            </div>
-
-            <div class="actions" id="main-actions">
-                {{- if .IsDir }}
-                <div role="button" class="action" id="view">
-                    {{- if eq .Display "mosaic" }}
-                        <a tabindex="0" aria-label="Switch to list" title="Switch View" href="?display=list">
-                            <i class="material-icons">view_list</i><span>Switch view</span>
-                        </a>
-                    {{- else }}
-                        <a tabindex="0" aria-label="Switch to Mosaic" title="Switch View" href="?display=mosaic">
-                            <i class="material-icons">view_module</i><span>Switch view</span>
-                        </a>
-                    {{- end }}
-                </div>
-
-                <div tabindex="0" role="button" aria-label="Select multiple" class="action mobile-only" id="multiple-selection-activate">
-                    <i class="material-icons">check_circle</i><span>Select</span>
-                </div>
-                {{- end }}
-
-                {{- if and (.User.AllowNew) (.IsDir) }}
-                <div tabindex="0" aria-label="Upload" role="button" class="action" id="upload">
-                    <i class="material-icons" title="Upload">file_upload</i><span>Upload</span>
-                </div>
-                {{- end }}
-
-                {{- if not .IsDir }}
-                <div tabindex="0" role="button" aria-label="See raw" class="action" id="open">
-                    <i class="material-icons" title="See raw">open_in_new</i>
-                    <span>See raw</span>
-                </div>
-                {{- end }}
-
-                {{- if and .User.AllowEdit (not .IsDir) }}
-                <div tabindex="0" role="button" aria-label="Delete" class="action" id="delete">
-                    <i class="material-icons" title="Delete">delete</i><span>Delete</span>
-                </div>
-                {{- end }}
-
-                <div {{ if .IsDir }}data-dropdown{{ end }} tabindex="0" role="button" aria-label="Download" class="action" id="download">
-                    {{- if not .IsDir}}<a href="?download=true">{{ end }}
-                    <i class="material-icons" title="Download">file_download</i><span>Download</span>
-                    {{- if not .IsDir}}</a>{{ end }}
-
-                    {{- if .IsDir }}
-                    <ul class="dropdown" id="download-drop">
-                        <a tabindex="0" aria-label="Download as Zip" data-format="zip" href="?download=zip"><li>zip</li></a>
-                        <a tabindex="0" aria-label="Download as Tar" data-format="tar" href="?download=tar"><li>tar</li></a>
-                        <a tabindex="0" aria-label="Download as TarGz" data-format="targz" href="?download=targz"><li>tar.gz</li></a>
-                        <a tabindex="0" aria-label="Download as TarBz2" data-format="tarbz2" href="?download=tarbz2"><li>tar.bz2</li></a>
-                        <a tabindex="0" aria-label="Download as TarXz" data-format="tarbz2" href="?download=tarxz"><li>tar.xz</li></a>
-                    </ul>
-                    {{- end }}
-                </div>
-
-                <div tabindex="0" role="button" aria-label="Info" class="action" id="info">
-                    <i class="material-icons" title="Info">info</i><span>Info</span>
-                </div>
-            </div>
-        </div>
-
-        <div id="click-overlay"></div>
-    </header>
-
-    <div id="multiple-selection" class="mobile-only">
-        <p>Multiple selection enabled</p>
-        <div tabindex="0" role="button" class="action" id="multiple-selection-cancel">
-            <i class="material-icons" title="Clear">clear</i>
-        </div>
-    </div>
-
-    <main>
-        {{- template "content" . }}
-    </main>
-
-    <div class="overlay"></div>
-
-    {{- if and (.User.AllowNew) (.IsDir) }}
-    <div class="floating">
-        <div tabindex="0" role="button" class="action" id="new">
-            <i class="material-icons" title="New file or directory">add</i>
-        </div>
-    </div>
-    {{- end }}
-
-    <template id="question-template">
-        <form class="prompt">
-            <h3></h3>
-            <p></p>
-            <input autofocus type="text">
-            <div>
-                <button type="submit" autofocus class="ok">OK</button>
-                <button class="cancel" onclick="closePrompt(event);">Cancel</button>
-            </div>
-        </form>
-    </template>
-
-    <template id="info-template">
-        <div class="prompt">
-            <h3>File Information</h3>
-            <p><strong>Display Name:</strong> <span id="display_name"></span></p>
-            <p><strong>Content Length:</strong> <span id="content_length"></span> Bytes</p>
-            <p><strong>Last Modified:</strong> <span id="last_modified"></span></p>
-
-            <section class="file-only">
-                <p><strong>MD5:</strong> <code id="md5"><a href="#" onclick="getHash(event, 'md5')">show</a></code></p>
-                <p><strong>SHA1:</strong> <code id="sha1"><a href="#" onclick="getHash(event, 'sha1')">show</a></code></p>
-                <p><strong>SHA256:</strong> <code id="sha256"><a href="#" onclick="getHash(event, 'sha256')">show</a></code></p>
-                <p><strong>SHA512:</strong> <code id="sha512"><a href="#" onclick="getHash(event, 'sha512')">show</a></code></p>
-            </section>
-
-            <div>
-                <button type="submit" onclick="closePrompt(event);" class="ok">OK</button>
-            </div>
-        </div>
-    </template>
-
-    <template id="message-template">
-        <div class="prompt">
-            <h3></h3>
-            <p></p>
-            <div>
-                <button type="submit" onclick="closePrompt(event);" class="ok">OK</button>
-            </div>
-        </div>
-    </template>
-
-    <template id="move-template">
-        <form class="prompt">
-            <h3>Move</h3>
-            <p>Choose new house for your file(s)/folder(s):</p>
-
-            <div class="file-list">
-                <ul>
-                </ul>
-            </div>
-
-            <p>Currently navigating on: <code></code>.</p>
-
-            <div>
-                <button type="submit" autofocus class="ok">Move</button>
-                <button class="cancel" onclick="closePrompt(event);">Cancel</button>
-            </div>
-        </form>
-    </template>
-
-    <div class="help">
-        <h3>Help</h3>
-
-        <ul>
-            <li><strong>F1</strong> - this information</li>
-            <li><strong>F2</strong> - rename file</li>
-            <li><strong>DEL</strong> - delete selected items</li>
-            <li><strong>ESC</strong> - clear selection and/or close the prompt</li>
-            <li><strong>CTRL + S</strong> - save a file or download the directory where you are</li>
-            <li><strong>CTRL + Click</strong> - select multiple files or directories</li>
-            <li><strong>Double click</strong> - open a file or directory</li>
-            <li><strong>Click</strong> - select file or directory</li>
-        </ul>
-
-        <p>Not available yet</p>
-
-        <ul>
-            <li><strong>Alt + Click</strong> - select a group of files</li>
-        </ul>
-
-        <div>
-            <button type="submit" onclick="closeHelp(event);" class="ok">OK</button>
-        </div>
-    </div>
-
-    <footer>Served with <a rel="noopener noreferrer" href="https://caddyserver.com">Caddy</a> and <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
-</body>
-</html>
diff --git a/_embed/templates/editor.tmpl b/_embed/templates/editor.tmpl
deleted file mode 100644
index d02238d8..00000000
--- a/_embed/templates/editor.tmpl
+++ /dev/null
@@ -1,57 +0,0 @@
-{{ define "content" }}
-{{- with .Data }}
-<form id="editor" {{ if eq .Mode "markdown" }}class="markdown"{{ end }} data-kind="{{ .Class }}" data-rune="{{ if eq .Class "complete" }}{{ .FrontMatter.Rune }}{{ end }}">
-    {{- if or (eq .Class "frontmatter-only") (eq .Class "complete") }}
-    {{- if (eq .Class "complete")}}
-    <h2>Metadata</h2>
-    {{- end }}
-    <div class="frontmatter" data-type="parent">
-        {{- template "blocks" .FrontMatter.Content }}
-        <div class="button add">Add field</div>
-    </div>
-    {{- end }}
-
-    {{ if or (eq .Class "content-only") (eq .Class "complete") }}
-        {{ if (eq .Class "complete")}}
-        <h2>Body</h2>
-        {{ end }}
-        <div class="content">
-            <div id="ace" data-mode="{{ .Mode }}"></div>
-            <textarea class="source" name="content">{{ .Content }}</textarea>
-        </div>
-    {{ end }}
-</form>
-{{- end }}
-
-<template id="base-template">
-    <fieldset id="" data-type="">
-        <h3></h3>
-        <div class="action add">
-            <i class="material-icons">add</i>
-        </div>
-        <div class="action delete" data-delete="">
-            <i class="material-icons">close</i>
-        </div>
-        <div class="group"></div>
-    </fieldset>
-</template>
-
-<template id="object-item-template">
-    <div class="block" id="block-${bid}" data-content="${bid}">
-        <label for="${bid}">${name}</label>
-        <input name="${bid}" id="${bid}" type="text" data-parent-type="object"></input>
-        <div class="action delete" data-delete="block-${bid}">
-            <i class="material-icons">close</i>
-        </div>
-    </div>
-</template>
-
-<template id="array-item-template">
-    <div id="" data-type="array-item">
-        <input name="" id="" type="text" data-parent-type="array"></input>
-        <div class="action delete" data-delete="">
-            <i class="material-icons">close</i>
-        </div>
-    </div>
-</template>
-{{ end }}
diff --git a/_embed/templates/frontmatter.tmpl b/_embed/templates/frontmatter.tmpl
deleted file mode 100644
index 3389da90..00000000
--- a/_embed/templates/frontmatter.tmpl
+++ /dev/null
@@ -1,56 +0,0 @@
-{{ define "blocks" }}
-{{ if .Fields }}<div class="group">{{ end }}
-{{- range $key, $value := .Fields }}
-    {{- if eq $value.Parent.Type "array" }}
-        <div id="{{ $value.Name }}-{{ $key }}" data-type="array-item">
-            {{- template "value" $value }}
-            <div class="action delete" data-delete="{{ $value.Name }}-{{ $key }}">
-                <i class="material-icons" title="Close">close</i>
-            </div>
-        </div>
-    {{- else }}
-        <div class="block" id="block-{{ $value.Name }}" data-content="{{ $value.Name }}">
-            <label for="{{ $value.Name }}">{{ $value.Title }}</label>
-            {{ template "value" $value }}
-            <div class="action delete" data-delete="block-{{ $value.Name }}">
-                <i class="material-icons" title="Close">close</i>
-            </div>
-        </div>
-    {{- end }}
-{{- end }}
-{{- if .Fields }}</div>{{ end }}
-
-{{- range $key, $value := .Arrays }}
-{{- template "fielset" $value }}
-{{- end }}
-
-{{- range $key, $value := .Objects }}
-{{- template "fielset" $value }}
-{{- end }}
-
-{{ end }}
-
-{{ define "value" }}
-{{- if eq .HTMLType "textarea" }}
-    <textarea class="scroll" name="{{ .Name }}" id="{{.Name }}" data-parent-type="{{ .Parent.Type }}">{{ .Content.Other }}</textarea>
-{{- else if eq .HTMLType "datetime" }}
-    <input name="{{ .Name }}" id="{{ .Name }}" value="{{ .Content.Other.Format "2006-01-02T15:04" }}" type="datetime-local" data-parent-type="{{ .Parent.Type }}"></input>
-{{- else }}
-    <input name="{{ .Name }}" id="{{ .Name }}" value="{{ .Content.Other }}" type="{{ .HTMLType }}" data-parent-type="{{ .Parent.Type }}"></input>
-{{- end }}
-{{ end }}
-
-{{ define "fielset" }}
-<fieldset id="{{ .Name }}" data-type="{{ .Type }}">
-    {{- if not (eq .Title "") }}
-        <h3>{{ .Name }}</h3>
-    {{- end }}
-    <div class="action add">
-        <i class="material-icons" title="Add">add</i>
-    </div>
-    <div class="action delete" data-delete="{{ .Name }}">
-        <i class="material-icons" title="Close">close</i>
-    </div>
-    {{- template "blocks" .Content }}
-</fieldset>
-{{ end }}
diff --git a/_embed/templates/listing.tmpl b/_embed/templates/listing.tmpl
deleted file mode 100644
index da94b962..00000000
--- a/_embed/templates/listing.tmpl
+++ /dev/null
@@ -1,103 +0,0 @@
-{{ define "content" }}
-<div class="container {{ .Display }}" id="listing">
-{{- with .Data -}}
-    <div>
-        <div class="item header">
-            <div></div>
-            <div>
-                <p class="name{{ if eq .Sort "name" }} active{{ end }}"><span>Name</span>
-                    {{- if eq .Sort "name" -}}
-                    {{- if eq .Order "asc" -}}
-                    <a href="?sort=name&order=desc"><i class="material-icons">arrow_downward</i></a>
-                    {{- else -}}
-                    <a href="?sort=name&order=asc"><i class="material-icons">arrow_upward</i></a>
-                    {{- end -}}
-                    {{- else -}}
-                    <a href="?sort=name&order=desc"><i class="material-icons">arrow_downward</i></a>
-                    {{- end -}}
-                </p>
-                <p class="size{{ if eq .Sort "size" }} active{{ end }}"><span>File Size</span>
-                    {{- if eq .Sort "size" -}}
-                    {{- if eq .Order "asc" -}}
-                    <a href="?sort=size&order=desc"><i class="material-icons">arrow_downward</i></a>
-                    {{- else -}}
-                    <a href="?sort=size&order=asc"><i class="material-icons">arrow_upward</i></a>
-                    {{- end -}}
-                    {{- else -}}
-                    <a href="?sort=size&order=desc"><i class="material-icons">arrow_downward</i></a>
-                    {{- end -}}
-                </p>
-                <p class="modified">Last modified</p>
-            </div>
-        </div>
-    </div>
-    
-    {{ if and (eq .NumDirs 0) (eq .NumFiles 0) }}
-    <h2 class="message">It feels lonely here :'(</h2>
-    {{ end }}
-    
-    {{- if not (eq .NumDirs 0)}}
-    <h2>Folders</h2>
-    <div>
-    {{- range .Items }}
-        {{- if (.IsDir) }}
-        {{ template "item" .}}
-        {{- end }}
-    {{- end }}
-    </div>
-    {{- end }}
-        
-    {{- if not (eq .NumFiles 0)}}
-    <h2>Files</h2>
-    <div>
-    {{- range .Items }}
-        {{- if (not .IsDir) }}
-        {{ template "item" .}}
-        {{- end }}
-    {{- end }}
-    </div>
-    {{- end }}
-</div>
-
-<input style="display:none" type="file" id="upload-input" onchange="listing.handleFiles(this.files, '')" value="Upload" multiple>
-{{- end -}}
-{{- end -}}
-
-{{ define "item" }}
-<div ondragstart="listing.itemDragStart(event)" 
-    {{ if .IsDir}}ondragover="listing.itemDragOver(event)" ondrop="listing.itemDrop(event)"{{ end }} 
-    draggable="true" 
-    class="item" 
-    onclick="listing.selectItem(event)"
-    ondblclick="listing.openItem(event)"
-    data-dir="{{ .IsDir }}" 
-    data-url="{{ .URL }}"
-    id="{{ EncodeBase64 .Name }}">
-    <div>
-        {{- if .IsDir}}
-        <i class="material-icons">folder</i>
-        {{- else}}
-        {{ if eq .Type "image" }}
-        <i class="material-icons">insert_photo</i>
-        {{ else if eq .Type "audio" }}
-        <i class="material-icons">volume_up</i>
-        {{ else if eq .Type "video" }}
-        <i class="material-icons">movie</i>
-        {{ else }}
-        <i class="material-icons">insert_drive_file</i>
-        {{ end }}
-        {{- end}}
-    </div>
-    <div>
-        <p class="name">{{.Name}}</p>
-        {{- if .IsDir}}
-        <p class="size" data-order="-1">&mdash;</p>
-        {{- else}}
-        <p class="size" data-order="{{.Size}}">{{.HumanSize}}</p>
-        {{- end}}
-        <p class="modified">
-        <time datetime="{{.HumanModTime "2006-01-02T15:04:05Z"}}">{{.HumanModTime "2 Jan 2006 03:04 PM"}}</time>
-        </p>
-    </div>
-</div>
-{{ end }}
diff --git a/_embed/templates/minimal.tmpl b/_embed/templates/minimal.tmpl
deleted file mode 100644
index 66e0068d..00000000
--- a/_embed/templates/minimal.tmpl
+++ /dev/null
@@ -1 +0,0 @@
-{{ template "content" . }}
diff --git a/_embed/templates/single.tmpl b/_embed/templates/single.tmpl
deleted file mode 100644
index c85ab5a4..00000000
--- a/_embed/templates/single.tmpl
+++ /dev/null
@@ -1,23 +0,0 @@
-{{ define "content" }}
-{{ with .Data}}
-<main class="container">
-  {{ if eq .Type "image" }}
-  <center><img src="{{ .URL }}?raw=true"></center>
-  {{ else if eq .Type "audio" }}
-  <audio src="{{ .URL }}?raw=true" controls></audio>
-  {{ else if eq .Type "video" }}
-  <video src="{{ .URL }}?raw=true" controls>
-    Sorry, your browser doesn't support embedded videos,
-    but don't worry, you can <a href="?download=true">download it</a>
-    and watch it with your favorite video player!
-  </video>
-  {{ else if eq .Extension ".pdf" }}
-  <object class="pdf" data="{{ .URL }}?raw=true"></object>
-  {{ else if eq .Type "blob" }}
-  <a href="?download=true"><h2 class="message">Download <i class="material-icons">file_download</i></h2></a>
-  {{ else}}
-  <pre>{{ .StringifyContent }}</pre>
-  {{ end }}
-</main>
-{{ end }}
-{{ end }}
diff --git a/assets/assets.go b/assets/assets.go
deleted file mode 100644
index 2c4744c2..00000000
--- a/assets/assets.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package assets
-
-import (
-	"mime"
-	"net/http"
-	"path/filepath"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-)
-
-// BaseURL is the url of the assets
-const BaseURL = "/_filemanagerinternal"
-
-// Serve provides the needed assets for the front-end
-func Serve(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) {
-	// gets the filename to be used with Assets function
-	filename := strings.Replace(r.URL.Path, c.BaseURL+BaseURL, "public", 1)
-	file, err := Asset(filename)
-	if err != nil {
-		return http.StatusNotFound, nil
-	}
-
-	// Get the file extension and its mimetype
-	extension := filepath.Ext(filename)
-	mediatype := mime.TypeByExtension(extension)
-
-	// Write the header with the Content-Type and write the file
-	// content to the buffer
-	w.Header().Set("Content-Type", mediatype)
-	w.Write(file)
-	return 200, nil
-}
diff --git a/assets/binary.go.REMOVED.git-id b/assets/binary.go.REMOVED.git-id
deleted file mode 100644
index 8fc30cca..00000000
--- a/assets/binary.go.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-4c30378a214b5b33410a74961df51cbc21bd6122
\ No newline at end of file
diff --git a/config/commands.go b/config/commands.go
deleted file mode 100644
index 60c3b881..00000000
--- a/config/commands.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package config
-
-import (
-	"log"
-	"net/http"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-
-	"github.com/mholt/caddy"
-)
-
-// CommandFunc ...
-type CommandFunc func(r *http.Request, c *Config, u *User) error
-
-// CommandRunner ...
-func CommandRunner(c *caddy.Controller) (CommandFunc, error) {
-	fn := func(r *http.Request, c *Config, u *User) error { return nil }
-
-	args := c.RemainingArgs()
-	if len(args) == 0 {
-		return fn, c.ArgErr()
-	}
-
-	nonblock := false
-	if len(args) > 1 && args[len(args)-1] == "&" {
-		// Run command in background; non-blocking
-		nonblock = true
-		args = args[:len(args)-1]
-	}
-
-	command, args, err := caddy.SplitCommandAndArgs(strings.Join(args, " "))
-	if err != nil {
-		return fn, c.Err(err.Error())
-	}
-
-	fn = func(r *http.Request, c *Config, u *User) error {
-		path := strings.Replace(r.URL.Path, c.WebDavURL, "", 1)
-		path = u.Scope + "/" + path
-		path = filepath.Clean(path)
-
-		for i := range args {
-			args[i] = strings.Replace(args[i], "{path}", path, -1)
-		}
-
-		cmd := exec.Command(command, args...)
-		cmd.Stdin = os.Stdin
-		cmd.Stdout = os.Stdout
-		cmd.Stderr = os.Stderr
-
-		if nonblock {
-			log.Printf("[INFO] Nonblocking Command:\"%s %s\"", command, strings.Join(args, " "))
-			return cmd.Start()
-		}
-
-		log.Printf("[INFO] Blocking Command:\"%s %s\"", command, strings.Join(args, " "))
-		return cmd.Run()
-	}
-
-	return fn, nil
-}
diff --git a/config/config.go b/config/config.go
deleted file mode 100644
index bfad2ad7..00000000
--- a/config/config.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package config
-
-import (
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"regexp"
-	"strconv"
-	"strings"
-
-	"golang.org/x/net/webdav"
-
-	"github.com/mholt/caddy"
-	"github.com/mholt/caddy/caddyhttp/httpserver"
-)
-
-// Config is a configuration for browsing in a particular path.
-type Config struct {
-	*User
-	PrefixURL   string
-	BaseURL     string
-	WebDavURL   string
-	HugoEnabled bool // Enables the Hugo plugin for File Manager
-	Users       map[string]*User
-	BeforeSave  CommandFunc
-	AfterSave   CommandFunc
-}
-
-// AbsoluteURL ...
-func (c Config) AbsoluteURL() string {
-	return c.PrefixURL + c.BaseURL
-}
-
-// AbsoluteWebdavURL ...
-func (c Config) AbsoluteWebdavURL() string {
-	return c.PrefixURL + c.WebDavURL
-}
-
-// Rule is a dissalow/allow rule
-type Rule struct {
-	Regex  bool
-	Allow  bool
-	Path   string
-	Regexp *regexp.Regexp
-}
-
-// Parse parses the configuration set by the user so it can
-// be used by the middleware
-func Parse(c *caddy.Controller) ([]Config, error) {
-	var (
-		configs []Config
-		err     error
-		user    *User
-	)
-
-	appendConfig := func(cfg Config) error {
-		for _, c := range configs {
-			if c.Scope == cfg.Scope {
-				return fmt.Errorf("duplicate file managing config for %s", c.Scope)
-			}
-		}
-		configs = append(configs, cfg)
-		return nil
-	}
-
-	for c.Next() {
-		// Initialize the configuration with the default settings
-		cfg := Config{User: &User{}}
-		cfg.Scope = "."
-		cfg.FileSystem = webdav.Dir(cfg.Scope)
-		cfg.BaseURL = ""
-		cfg.HugoEnabled = false
-		cfg.Users = map[string]*User{}
-		cfg.AllowCommands = true
-		cfg.AllowEdit = true
-		cfg.AllowNew = true
-		cfg.Commands = []string{"git", "svn", "hg"}
-		cfg.BeforeSave = func(r *http.Request, c *Config, u *User) error { return nil }
-		cfg.AfterSave = func(r *http.Request, c *Config, u *User) error { return nil }
-		cfg.Rules = []*Rule{{
-			Regex:  true,
-			Allow:  false,
-			Regexp: regexp.MustCompile("\\/\\..+"),
-		}}
-
-		// Get the baseURL
-		args := c.RemainingArgs()
-
-		if len(args) > 0 {
-			cfg.BaseURL = args[0]
-		}
-
-		cfg.BaseURL = strings.TrimPrefix(cfg.BaseURL, "/")
-		cfg.BaseURL = strings.TrimSuffix(cfg.BaseURL, "/")
-		cfg.BaseURL = "/" + cfg.BaseURL
-		cfg.WebDavURL = ""
-
-		if cfg.BaseURL == "/" {
-			cfg.BaseURL = ""
-		}
-
-		// Set the first user, the global user
-		user = cfg.User
-
-		for c.NextBlock() {
-			switch c.Val() {
-			case "before_save":
-				if cfg.BeforeSave, err = CommandRunner(c); err != nil {
-					return configs, err
-				}
-			case "after_save":
-				if cfg.AfterSave, err = CommandRunner(c); err != nil {
-					return configs, err
-				}
-			case "webdav":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				prefix := c.Val()
-				prefix = strings.TrimPrefix(prefix, "/")
-				prefix = strings.TrimSuffix(prefix, "/")
-				cfg.WebDavURL = prefix
-			case "show":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				user.Scope = c.Val()
-				user.Scope = strings.TrimSuffix(user.Scope, "/")
-				user.FileSystem = webdav.Dir(user.Scope)
-			case "styles":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				var tplBytes []byte
-				tplBytes, err = ioutil.ReadFile(c.Val())
-				if err != nil {
-					return configs, err
-				}
-				user.StyleSheet = string(tplBytes)
-			case "allow_new":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				user.AllowNew, err = strconv.ParseBool(c.Val())
-				if err != nil {
-					return configs, err
-				}
-			case "allow_edit":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				user.AllowEdit, err = strconv.ParseBool(c.Val())
-				if err != nil {
-					return configs, err
-				}
-			case "allow_commands":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				user.AllowCommands, err = strconv.ParseBool(c.Val())
-				if err != nil {
-					return configs, err
-				}
-			case "allow_command":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				user.Commands = append(user.Commands, c.Val())
-			case "block_command":
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				index := 0
-
-				for i, val := range user.Commands {
-					if val == c.Val() {
-						index = i
-					}
-				}
-
-				user.Commands = append(user.Commands[:index], user.Commands[index+1:]...)
-			case "allow", "allow_r", "block", "block_r":
-				ruleType := c.Val()
-
-				if !c.NextArg() {
-					return configs, c.ArgErr()
-				}
-
-				if c.Val() == "dotfiles" && !strings.HasSuffix(ruleType, "_r") {
-					ruleType += "_r"
-				}
-
-				rule := &Rule{
-					Allow: ruleType == "allow" || ruleType == "allow_r",
-					Regex: ruleType == "allow_r" || ruleType == "block_r",
-				}
-
-				if rule.Regex && c.Val() == "dotfiles" {
-					rule.Regexp = regexp.MustCompile("\\/\\..+")
-				} else if rule.Regex {
-					rule.Regexp = regexp.MustCompile(c.Val())
-				} else {
-					rule.Path = c.Val()
-				}
-
-				user.Rules = append(user.Rules, rule)
-			// NEW USER BLOCK?
-			default:
-				val := c.Val()
-
-				// Checks if it's a new user
-				if !strings.HasSuffix(val, ":") {
-					fmt.Println("Unknown option " + val)
-				}
-
-				// Get the username, sets the current user, and initializes it
-				val = strings.TrimSuffix(val, ":")
-				cfg.Users[val] = &User{}
-
-				// Initialize the new user
-				user = cfg.Users[val]
-				user.AllowCommands = cfg.AllowCommands
-				user.AllowEdit = cfg.AllowEdit
-				user.AllowNew = cfg.AllowEdit
-				user.Commands = cfg.Commands
-				user.Scope = cfg.Scope
-				user.FileSystem = cfg.FileSystem
-				user.Rules = cfg.Rules
-				user.StyleSheet = cfg.StyleSheet
-			}
-		}
-
-		if cfg.WebDavURL == "" {
-			cfg.WebDavURL = "webdav"
-		}
-
-		caddyConf := httpserver.GetConfig(c)
-
-		cfg.PrefixURL = strings.TrimSuffix(caddyConf.Addr.Path, "/")
-		cfg.WebDavURL = cfg.BaseURL + "/" + strings.TrimPrefix(cfg.WebDavURL, "/")
-		cfg.Handler = &webdav.Handler{
-			Prefix:     cfg.WebDavURL,
-			FileSystem: cfg.FileSystem,
-			LockSystem: webdav.NewMemLS(),
-		}
-
-		if err := appendConfig(cfg); err != nil {
-			return configs, err
-		}
-	}
-
-	return configs, nil
-}
diff --git a/config/user.go b/config/user.go
deleted file mode 100644
index 4f4f47f6..00000000
--- a/config/user.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package config
-
-import (
-	"strings"
-
-	"golang.org/x/net/webdav"
-)
-
-// User contains the configuration for each user
-type User struct {
-	Scope         string            `json:"-"` // Path the user have access
-	FileSystem    webdav.FileSystem `json:"-"` // The virtual file system the user have access
-	Handler       *webdav.Handler   `json:"-"` // The WebDav HTTP Handler
-	StyleSheet    string            `json:"-"` // Costum stylesheet
-	AllowNew      bool              // Can create files and folders
-	AllowEdit     bool              // Can edit/rename files
-	AllowCommands bool              // Can execute commands
-	Commands      []string          // Available Commands
-	Rules         []*Rule           `json:"-"` // Access rules
-}
-
-// Allowed checks if the user has permission to access a directory/file
-func (u User) Allowed(url string) bool {
-	var rule *Rule
-	i := len(u.Rules) - 1
-
-	for i >= 0 {
-		rule = u.Rules[i]
-
-		if rule.Regex {
-			if rule.Regexp.MatchString(url) {
-				return rule.Allow
-			}
-		} else if strings.HasPrefix(url, rule.Path) {
-			return rule.Allow
-		}
-
-		i--
-	}
-
-	return true
-}
diff --git a/file/info.go b/file/info.go
deleted file mode 100644
index cd1c6f71..00000000
--- a/file/info.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package file
-
-import (
-	"io/ioutil"
-	"mime"
-	"net/http"
-	"net/url"
-	"os"
-	"path/filepath"
-	"strings"
-	"time"
-
-	humanize "github.com/dustin/go-humanize"
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/utils/errors"
-)
-
-// Info contains the information about a particular file or directory
-type Info struct {
-	Name        string
-	Size        int64
-	URL         string
-	Extension   string
-	ModTime     time.Time
-	Mode        os.FileMode
-	IsDir       bool
-	Path        string // Relative path to Caddyfile
-	VirtualPath string // Relative path to u.FileSystem
-	Mimetype    string
-	Content     []byte
-	Type        string
-	UserAllowed bool // Indicates if the user has enough permissions
-}
-
-// GetInfo gets the file information and, in case of error, returns the
-// respective HTTP error code
-func GetInfo(url *url.URL, c *config.Config, u *config.User) (*Info, int, error) {
-	var err error
-
-	i := &Info{URL: c.PrefixURL + url.Path}
-	i.VirtualPath = strings.Replace(url.Path, c.BaseURL, "", 1)
-	i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/")
-	i.VirtualPath = "/" + i.VirtualPath
-
-	i.Path = u.Scope + i.VirtualPath
-	i.Path = filepath.Clean(i.Path)
-
-	info, err := os.Stat(i.Path)
-	if err != nil {
-		return i, errors.ErrorToHTTPCode(err, false), err
-	}
-
-	i.Name = info.Name()
-	i.ModTime = info.ModTime()
-	i.Mode = info.Mode()
-	i.IsDir = info.IsDir()
-	i.Size = info.Size()
-	i.Extension = filepath.Ext(i.Name)
-	return i, 0, nil
-}
-
-var textExtensions = [...]string{
-	".md", ".markdown", ".mdown", ".mmark",
-	".asciidoc", ".adoc", ".ad",
-	".rst",
-	".json", ".toml", ".yaml", ".csv", ".xml", ".rss", ".conf", ".ini",
-	".tex", ".sty",
-	".css", ".sass", ".scss",
-	".js",
-	".html",
-	".txt", ".rtf",
-	".sh", ".bash", ".ps1", ".bat", ".cmd",
-	".php", ".pl", ".py",
-	"Caddyfile",
-	".c", ".cc", ".h", ".hh", ".cpp", ".hpp", ".f90",
-	".f", ".bas", ".d", ".ada", ".nim", ".cr", ".java", ".cs", ".vala", ".vapi",
-}
-
-// RetrieveFileType obtains the mimetype and a simplified internal Type
-// using the first 512 bytes from the file.
-func (i *Info) RetrieveFileType() error {
-	i.Mimetype = mime.TypeByExtension(i.Extension)
-
-	if i.Mimetype == "" {
-		err := i.Read()
-		if err != nil {
-			return err
-		}
-
-		i.Mimetype = http.DetectContentType(i.Content)
-	}
-
-	if strings.HasPrefix(i.Mimetype, "video") {
-		i.Type = "video"
-		return nil
-	}
-
-	if strings.HasPrefix(i.Mimetype, "audio") {
-		i.Type = "audio"
-		return nil
-	}
-
-	if strings.HasPrefix(i.Mimetype, "image") {
-		i.Type = "image"
-		return nil
-	}
-
-	if strings.HasPrefix(i.Mimetype, "text") {
-		i.Type = "text"
-		return nil
-	}
-
-	if strings.HasPrefix(i.Mimetype, "application/javascript") {
-		i.Type = "text"
-		return nil
-	}
-
-	// If the type isn't text (and is blob for example), it will check some
-	// common types that are mistaken not to be text.
-	for _, extension := range textExtensions {
-		if strings.HasSuffix(i.Name, extension) {
-			i.Type = "text"
-			return nil
-		}
-	}
-
-	i.Type = "blob"
-	return nil
-}
-
-// Reads the file.
-func (i *Info) Read() error {
-	if len(i.Content) != 0 {
-		return nil
-	}
-
-	var err error
-	i.Content, err = ioutil.ReadFile(i.Path)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-// StringifyContent returns the string version of Raw
-func (i Info) StringifyContent() string {
-	return string(i.Content)
-}
-
-// HumanSize returns the size of the file as a human-readable string
-// in IEC format (i.e. power of 2 or base 1024).
-func (i Info) HumanSize() string {
-	return humanize.IBytes(uint64(i.Size))
-}
-
-// HumanModTime returns the modified time of the file as a human-readable string.
-func (i Info) HumanModTime(format string) string {
-	return i.ModTime.Format(format)
-}
-
-// CanBeEdited checks if the extension of a file is supported by the editor
-func (i Info) CanBeEdited() bool {
-	return i.Type == "text"
-}
diff --git a/file/listing.go b/file/listing.go
deleted file mode 100644
index 4b48a0c0..00000000
--- a/file/listing.go
+++ /dev/null
@@ -1,186 +0,0 @@
-package file
-
-import (
-	"context"
-	"net/url"
-	"os"
-	"path"
-	"sort"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-
-	"github.com/mholt/caddy/caddyhttp/httpserver"
-)
-
-// A Listing is the context used to fill out a template.
-type Listing struct {
-	// The name of the directory (the last element of the path)
-	Name string
-	// The full path of the request relatively to a File System
-	Path string
-	// The items (files and folders) in the path
-	Items []Info
-	// The number of directories in the listing
-	NumDirs int
-	// The number of files (items that aren't directories) in the listing
-	NumFiles int
-	// Which sorting order is used
-	Sort string
-	// And which order
-	Order string
-	// If ≠0 then Items have been limited to that many elements
-	ItemsLimitedTo     int
-	httpserver.Context `json:"-"`
-}
-
-// GetListing gets the information about a specific directory and its files.
-func GetListing(u *config.User, filePath string, baseURL string) (*Listing, error) {
-	// Gets the directory information using the Virtual File System of
-	// the user configuration.
-	file, err := u.FileSystem.OpenFile(context.TODO(), filePath, os.O_RDONLY, 0)
-	if err != nil {
-		return nil, err
-	}
-	defer file.Close()
-
-	// Reads the directory and gets the information about the files.
-	files, err := file.Readdir(-1)
-	if err != nil {
-		return nil, err
-	}
-
-	var (
-		fileinfos           []Info
-		dirCount, fileCount int
-	)
-
-	for _, f := range files {
-		name := f.Name()
-		allowed := u.Allowed("/" + name)
-
-		if !allowed {
-			continue
-		}
-
-		if f.IsDir() {
-			name += "/"
-			dirCount++
-		} else {
-			fileCount++
-		}
-
-		// Absolute URL
-		url := url.URL{Path: baseURL + name}
-
-		i := Info{
-			Name:        f.Name(),
-			Size:        f.Size(),
-			ModTime:     f.ModTime(),
-			Mode:        f.Mode(),
-			IsDir:       f.IsDir(),
-			URL:         url.String(),
-			UserAllowed: allowed,
-		}
-		i.RetrieveFileType()
-
-		fileinfos = append(fileinfos, i)
-	}
-
-	return &Listing{
-		Name:     path.Base(filePath),
-		Path:     filePath,
-		Items:    fileinfos,
-		NumDirs:  dirCount,
-		NumFiles: fileCount,
-	}, nil
-}
-
-// ApplySort applies the sort order using .Order and .Sort
-func (l Listing) ApplySort() {
-	// Check '.Order' to know how to sort
-	if l.Order == "desc" {
-		switch l.Sort {
-		case "name":
-			sort.Sort(sort.Reverse(byName(l)))
-		case "size":
-			sort.Sort(sort.Reverse(bySize(l)))
-		case "time":
-			sort.Sort(sort.Reverse(byTime(l)))
-		default:
-			// If not one of the above, do nothing
-			return
-		}
-	} else { // If we had more Orderings we could add them here
-		switch l.Sort {
-		case "name":
-			sort.Sort(byName(l))
-		case "size":
-			sort.Sort(bySize(l))
-		case "time":
-			sort.Sort(byTime(l))
-		default:
-			sort.Sort(byName(l))
-			return
-		}
-	}
-}
-
-// Implement sorting for Listing
-type byName Listing
-type bySize Listing
-type byTime Listing
-
-// By Name
-func (l byName) Len() int {
-	return len(l.Items)
-}
-
-func (l byName) Swap(i, j int) {
-	l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
-}
-
-// Treat upper and lower case equally
-func (l byName) Less(i, j int) bool {
-	if l.Items[i].IsDir && !l.Items[j].IsDir {
-		return true
-	}
-
-	if !l.Items[i].IsDir && l.Items[j].IsDir {
-		return false
-	}
-
-	return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
-}
-
-// By Size
-func (l bySize) Len() int {
-	return len(l.Items)
-}
-
-func (l bySize) Swap(i, j int) {
-	l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
-}
-
-const directoryOffset = -1 << 31 // = math.MinInt32
-func (l bySize) Less(i, j int) bool {
-	iSize, jSize := l.Items[i].Size, l.Items[j].Size
-	if l.Items[i].IsDir {
-		iSize = directoryOffset + iSize
-	}
-	if l.Items[j].IsDir {
-		jSize = directoryOffset + jSize
-	}
-	return iSize < jSize
-}
-
-// By Time
-func (l byTime) Len() int {
-	return len(l.Items)
-}
-func (l byTime) Swap(i, j int) {
-	l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
-}
-func (l byTime) Less(i, j int) bool {
-	return l.Items[i].ModTime.Before(l.Items[j].ModTime)
-}
diff --git a/filemanager.go b/filemanager.go
index a17983a8..41d48a9a 100644
--- a/filemanager.go
+++ b/filemanager.go
@@ -4,18 +4,9 @@
 package filemanager
 
 import (
-	e "errors"
 	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
 
-	"github.com/hacdias/caddy-filemanager/assets"
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/hacdias/caddy-filemanager/handlers"
-	"github.com/hacdias/caddy-filemanager/page"
-	"github.com/hacdias/caddy-filemanager/wrapper"
+	"github.com/hacdias/filemanager"
 	"github.com/mholt/caddy/caddyhttp/httpserver"
 )
 
@@ -23,173 +14,18 @@ import (
 // directories in the given paths are specified.
 type FileManager struct {
 	Next    httpserver.Handler
-	Configs []config.Config
+	Configs []*filemanager.FileManager
 }
 
 // ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
 func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
-	var (
-		c    *config.Config
-		fi   *file.Info
-		code int
-		err  error
-		user *config.User
-	)
-
 	for i := range f.Configs {
 		// Checks if this Path should be handled by File Manager.
 		if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
 			continue
 		}
 
-		c = &f.Configs[i]
-
-		// Checks if the URL matches the Assets URL. Returns the asset if the
-		// method is GET and Status Forbidden otherwise.
-		if httpserver.Path(r.URL.Path).Matches(c.BaseURL + assets.BaseURL) {
-			if r.Method == http.MethodGet {
-				return assets.Serve(w, r, c)
-			}
-
-			return http.StatusForbidden, nil
-		}
-
-		// Obtains the user. See https://github.com/mholt/caddy/blob/master/caddyhttp/basicauth/basicauth.go#L66
-		username, _ := r.Context().Value(httpserver.RemoteUserCtxKey).(string)
-		if _, ok := c.Users[username]; ok {
-			user = c.Users[username]
-		} else {
-			user = c.User
-		}
-
-		// Checks if the request URL is for the WebDav server
-		if httpserver.Path(r.URL.Path).Matches(c.WebDavURL) {
-			// Checks for user permissions relatively to this PATH
-			if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.WebDavURL)) {
-				return http.StatusForbidden, nil
-			}
-
-			switch r.Method {
-			case "GET", "HEAD":
-				// Excerpt from RFC4918, section 9.4:
-				//
-				// 		GET, when applied to a collection, may return the contents of an
-				//		"index.html" resource, a human-readable view of the contents of
-				//		the collection, or something else altogether.
-				//
-				// It was decided on https://github.com/hacdias/caddy-filemanager/issues/85
-				// that GET, for collections, will return the same as PROPFIND method.
-				path := strings.Replace(r.URL.Path, c.WebDavURL, "", 1)
-				path = user.Scope + "/" + path
-				path = filepath.Clean(path)
-
-				var i os.FileInfo
-				i, err = os.Stat(path)
-				if err != nil {
-					// Is there any error? WebDav will handle it... no worries.
-					break
-				}
-
-				if i.IsDir() {
-					r.Method = "PROPFIND"
-
-					if r.Method == "HEAD" {
-						w = wrapper.NewResponseWriterNoBody(w)
-					}
-				}
-			case "PROPPATCH", "MOVE", "PATCH", "PUT", "DELETE":
-				if !user.AllowEdit {
-					return http.StatusForbidden, nil
-				}
-			case "MKCOL", "COPY":
-				if !user.AllowNew {
-					return http.StatusForbidden, nil
-				}
-			}
-
-			// Preprocess the PUT request if it's the case
-			if r.Method == http.MethodPut {
-				if err = c.BeforeSave(r, c, user); err != nil {
-					return http.StatusInternalServerError, err
-				}
-
-				if handlers.PreProccessPUT(w, r, c, user) != nil {
-					return http.StatusInternalServerError, err
-				}
-			}
-
-			c.Handler.ServeHTTP(w, r)
-			if err = c.AfterSave(r, c, user); err != nil {
-				return http.StatusInternalServerError, err
-			}
-
-			return 0, nil
-		}
-
-		w.Header().Set("x-frame-options", "SAMEORIGIN")
-		w.Header().Set("x-content-type", "nosniff")
-		w.Header().Set("x-xss-protection", "1; mode=block")
-
-		// Checks if the User is allowed to access this file
-		if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.BaseURL)) {
-			if r.Method == http.MethodGet {
-				return page.PrintErrorHTML(
-					w, http.StatusForbidden,
-					e.New("You don't have permission to access this page."),
-				)
-			}
-
-			return http.StatusForbidden, nil
-		}
-
-		if r.URL.Query().Get("search") != "" {
-			return handlers.Search(w, r, c, user)
-		}
-
-		if r.URL.Query().Get("command") != "" {
-			return handlers.Command(w, r, c, user)
-		}
-
-		if r.Method == http.MethodGet {
-			// Gets the information of the directory/file
-			fi, code, err = file.GetInfo(r.URL, c, user)
-			if err != nil {
-				if r.Method == http.MethodGet {
-					return page.PrintErrorHTML(w, code, err)
-				}
-				return code, err
-			}
-
-			// If it's a dir and the path doesn't end with a trailing slash,
-			// redirect the user.
-			if fi.IsDir && !strings.HasSuffix(r.URL.Path, "/") {
-				http.Redirect(w, r, c.PrefixURL+r.URL.Path+"/", http.StatusTemporaryRedirect)
-				return 0, nil
-			}
-
-			switch {
-			case r.URL.Query().Get("download") != "":
-				code, err = handlers.Download(w, r, c, fi)
-			case r.URL.Query().Get("raw") == "true" && !fi.IsDir:
-				http.ServeFile(w, r, fi.Path)
-				code, err = 0, nil
-			case !fi.IsDir && r.URL.Query().Get("checksum") != "":
-				code, err = handlers.Checksum(w, r, c, fi)
-			case fi.IsDir:
-				code, err = handlers.ServeListing(w, r, c, user, fi)
-			default:
-				code, err = handlers.ServeSingle(w, r, c, user, fi)
-			}
-
-			if err != nil {
-				code, err = page.PrintErrorHTML(w, code, err)
-			}
-
-			return code, err
-		}
-
-		return http.StatusNotImplemented, nil
-
+		return f.Configs[i].ServeHTTP(w, r)
 	}
 
 	return f.Next.ServeHTTP(w, r)
diff --git a/frontmatter/frontmatter.go b/frontmatter/frontmatter.go
deleted file mode 100644
index a0deb86c..00000000
--- a/frontmatter/frontmatter.go
+++ /dev/null
@@ -1,276 +0,0 @@
-package frontmatter
-
-import (
-	"bytes"
-	"encoding/json"
-	"errors"
-	"log"
-	"reflect"
-	"sort"
-	"strconv"
-	"strings"
-
-	"gopkg.in/yaml.v2"
-
-	"github.com/BurntSushi/toml"
-	"github.com/hacdias/caddy-filemanager/utils/variables"
-
-	"github.com/spf13/cast"
-)
-
-const (
-	mainName   = "#MAIN#"
-	objectType = "object"
-	arrayType  = "array"
-)
-
-var mainTitle = ""
-
-// Pretty creates a new FrontMatter object
-func Pretty(content []byte) (*Content, string, error) {
-	data, err := Unmarshal(content)
-
-	if err != nil {
-		return &Content{}, "", err
-	}
-
-	kind := reflect.ValueOf(data).Kind()
-
-	if kind == reflect.Invalid {
-		return &Content{}, "", nil
-	}
-
-	object := new(Block)
-	object.Type = objectType
-	object.Name = mainName
-
-	if kind == reflect.Map {
-		object.Type = objectType
-	} else if kind == reflect.Slice || kind == reflect.Array {
-		object.Type = arrayType
-	}
-
-	return rawToPretty(data, object), mainTitle, nil
-}
-
-// Unmarshal returns the data of the frontmatter
-func Unmarshal(content []byte) (interface{}, error) {
-	mark := rune(content[0])
-	var data interface{}
-
-	switch mark {
-	case '-':
-		// If it's YAML
-		if err := yaml.Unmarshal(content, &data); err != nil {
-			return nil, err
-		}
-	case '+':
-		// If it's TOML
-		content = bytes.Replace(content, []byte("+"), []byte(""), -1)
-		if _, err := toml.Decode(string(content), &data); err != nil {
-			return nil, err
-		}
-	case '{', '[':
-		// If it's JSON
-		if err := json.Unmarshal(content, &data); err != nil {
-			return nil, err
-		}
-	default:
-		return nil, errors.New("Invalid frontmatter type")
-	}
-
-	return data, nil
-}
-
-// Marshal encodes the interface in a specific format
-func Marshal(data interface{}, mark rune) ([]byte, error) {
-	b := new(bytes.Buffer)
-
-	switch mark {
-	case '+':
-		enc := toml.NewEncoder(b)
-		err := enc.Encode(data)
-		if err != nil {
-			return nil, err
-		}
-		return b.Bytes(), nil
-	case '{':
-		by, err := json.MarshalIndent(data, "", "   ")
-		if err != nil {
-			return nil, err
-		}
-		b.Write(by)
-		_, err = b.Write([]byte("\n"))
-		if err != nil {
-			return nil, err
-		}
-		return b.Bytes(), nil
-	case '-':
-		by, err := yaml.Marshal(data)
-		if err != nil {
-			return nil, err
-		}
-		b.Write(by)
-		_, err = b.Write([]byte("..."))
-		if err != nil {
-			return nil, err
-		}
-		return b.Bytes(), nil
-	default:
-		return nil, errors.New("Unsupported Format provided")
-	}
-}
-
-// Content is the block content
-type Content struct {
-	Other   interface{}
-	Fields  []*Block
-	Arrays  []*Block
-	Objects []*Block
-}
-
-// Block is a block
-type Block struct {
-	Name     string
-	Title    string
-	Type     string
-	HTMLType string
-	Content  *Content
-	Parent   *Block
-}
-
-func rawToPretty(config interface{}, parent *Block) *Content {
-	objects := []*Block{}
-	arrays := []*Block{}
-	fields := []*Block{}
-
-	cnf := map[string]interface{}{}
-	kind := reflect.TypeOf(config)
-
-	switch kind {
-	case reflect.TypeOf(map[interface{}]interface{}{}):
-		for key, value := range config.(map[interface{}]interface{}) {
-			cnf[key.(string)] = value
-		}
-	case reflect.TypeOf([]map[string]interface{}{}):
-		for index, value := range config.([]map[string]interface{}) {
-			cnf[strconv.Itoa(index)] = value
-		}
-	case reflect.TypeOf([]map[interface{}]interface{}{}):
-		for index, value := range config.([]map[interface{}]interface{}) {
-			cnf[strconv.Itoa(index)] = value
-		}
-	case reflect.TypeOf([]interface{}{}):
-		for index, value := range config.([]interface{}) {
-			cnf[strconv.Itoa(index)] = value
-		}
-	default:
-		cnf = config.(map[string]interface{})
-	}
-
-	for name, element := range cnf {
-		if variables.IsMap(element) {
-			objects = append(objects, handleObjects(element, parent, name))
-		} else if variables.IsSlice(element) {
-			arrays = append(arrays, handleArrays(element, parent, name))
-		} else {
-			if name == "title" && parent.Name == mainName {
-				mainTitle = element.(string)
-			}
-			fields = append(fields, handleFlatValues(element, parent, name))
-		}
-	}
-
-	sort.Sort(sortByTitle(fields))
-	sort.Sort(sortByTitle(arrays))
-	sort.Sort(sortByTitle(objects))
-	return &Content{
-		Fields:  fields,
-		Arrays:  arrays,
-		Objects: objects,
-	}
-}
-
-type sortByTitle []*Block
-
-func (f sortByTitle) Len() int      { return len(f) }
-func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
-func (f sortByTitle) Less(i, j int) bool {
-	return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name)
-}
-
-func handleObjects(content interface{}, parent *Block, name string) *Block {
-	c := new(Block)
-	c.Parent = parent
-	c.Type = objectType
-	c.Title = name
-
-	if parent.Name == mainName {
-		c.Name = c.Title
-	} else if parent.Type == arrayType {
-		c.Name = parent.Name + "[" + name + "]"
-	} else {
-		c.Name = parent.Name + "." + c.Title
-	}
-
-	c.Content = rawToPretty(content, c)
-	return c
-}
-
-func handleArrays(content interface{}, parent *Block, name string) *Block {
-	c := new(Block)
-	c.Parent = parent
-	c.Type = arrayType
-	c.Title = name
-
-	if parent.Name == mainName {
-		c.Name = name
-	} else {
-		c.Name = parent.Name + "." + name
-	}
-
-	c.Content = rawToPretty(content, c)
-	return c
-}
-
-func handleFlatValues(content interface{}, parent *Block, name string) *Block {
-	c := new(Block)
-	c.Parent = parent
-
-	switch content.(type) {
-	case bool:
-		c.Type = "boolean"
-	case int, float32, float64:
-		c.Type = "number"
-	default:
-		c.Type = "string"
-	}
-
-	c.Content = &Content{Other: content}
-
-	switch strings.ToLower(name) {
-	case "description":
-		c.HTMLType = "textarea"
-	case "date", "publishdate":
-		c.HTMLType = "datetime"
-		c.Content = &Content{Other: cast.ToTime(content)}
-	default:
-		c.HTMLType = "text"
-	}
-
-	if parent.Type == arrayType {
-		c.Name = parent.Name + "[]"
-		c.Title = content.(string)
-	} else if parent.Type == objectType {
-		c.Title = name
-		c.Name = parent.Name + "." + name
-
-		if parent.Name == mainName {
-			c.Name = name
-		}
-	} else {
-		log.Panic("Parent type not allowed in handleFlatValues.")
-	}
-
-	return c
-}
diff --git a/frontmatter/runes.go b/frontmatter/runes.go
deleted file mode 100644
index b4ad1dc2..00000000
--- a/frontmatter/runes.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package frontmatter
-
-import (
-	"bytes"
-	"errors"
-	"strings"
-)
-
-// HasRune checks if the file has the frontmatter rune
-func HasRune(file []byte) bool {
-	return strings.HasPrefix(string(file), "---") ||
-		strings.HasPrefix(string(file), "+++") ||
-		strings.HasPrefix(string(file), "{")
-}
-
-// AppendRune appends the frontmatter rune to a file
-func AppendRune(frontmatter []byte, mark rune) []byte {
-	frontmatter = bytes.TrimSpace(frontmatter)
-
-	switch mark {
-	case '-':
-		return []byte("---\n" + string(frontmatter) + "\n---")
-	case '+':
-		return []byte("+++\n" + string(frontmatter) + "\n+++")
-	case '{':
-		return []byte("{\n" + string(frontmatter) + "\n}")
-	}
-
-	return frontmatter
-}
-
-// RuneToStringFormat converts the rune to a string with the format
-func RuneToStringFormat(mark rune) (string, error) {
-	switch mark {
-	case '-':
-		return "yaml", nil
-	case '+':
-		return "toml", nil
-	case '{', '}':
-		return "json", nil
-	default:
-		return "", errors.New("Unsupported format type")
-	}
-}
-
-// StringFormatToRune converts the format name to its rune
-func StringFormatToRune(format string) (rune, error) {
-	switch format {
-	case "yaml":
-		return '-', nil
-	case "toml":
-		return '+', nil
-	case "json":
-		return '{', nil
-	default:
-		return '0', errors.New("Unsupported format type")
-	}
-}
diff --git a/frontmatter/runes_test.go b/frontmatter/runes_test.go
deleted file mode 100644
index 6d120948..00000000
--- a/frontmatter/runes_test.go
+++ /dev/null
@@ -1,131 +0,0 @@
-package frontmatter
-
-import "testing"
-
-type hasRuneTest struct {
-	File   []byte
-	Return bool
-}
-
-var testHasRune = []hasRuneTest{
-	hasRuneTest{
-		File: []byte(`---
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
-Sed auctor libero eget ante fermentum commodo. 
----`),
-		Return: true,
-	},
-	hasRuneTest{
-		File: []byte(`+++
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
-Sed auctor libero eget ante fermentum commodo. 
-+++`),
-		Return: true,
-	},
-	hasRuneTest{
-		File: []byte(`{
-	"json": "Lorem ipsum dolor sit amet"
-}`),
-		Return: true,
-	},
-	hasRuneTest{
-		File:   []byte(`+`),
-		Return: false,
-	},
-	hasRuneTest{
-		File:   []byte(`++`),
-		Return: false,
-	},
-	hasRuneTest{
-		File:   []byte(`-`),
-		Return: false,
-	},
-	hasRuneTest{
-		File:   []byte(`--`),
-		Return: false,
-	},
-	hasRuneTest{
-		File:   []byte(`Lorem ipsum`),
-		Return: false,
-	},
-}
-
-func TestHasRune(t *testing.T) {
-	for _, test := range testHasRune {
-		if HasRune(test.File) != test.Return {
-			t.Error("Incorrect value on HasRune")
-		}
-	}
-}
-
-type appendRuneTest struct {
-	Before []byte
-	After  []byte
-	Mark   rune
-}
-
-var testAppendRuneTest = []appendRuneTest{}
-
-func TestAppendRune(t *testing.T) {
-	for i, test := range testAppendRuneTest {
-		if !compareByte(AppendRune(test.Before, test.Mark), test.After) {
-			t.Errorf("Incorrect value on AppendRune of Test %d", i)
-		}
-	}
-}
-
-func compareByte(a, b []byte) bool {
-	if a == nil && b == nil {
-		return true
-	}
-
-	if a == nil || b == nil {
-		return false
-	}
-
-	if len(a) != len(b) {
-		return false
-	}
-
-	for i := range a {
-		if a[i] != b[i] {
-			return false
-		}
-	}
-
-	return true
-}
-
-var testRuneToStringFormat = map[rune]string{
-	'-': "yaml",
-	'+': "toml",
-	'{': "json",
-	'}': "json",
-	'1': "",
-	'a': "",
-}
-
-func TestRuneToStringFormat(t *testing.T) {
-	for mark, format := range testRuneToStringFormat {
-		val, _ := RuneToStringFormat(mark)
-		if val != format {
-			t.Errorf("Incorrect value on RuneToStringFormat of %v; want: %s; got: %s", mark, format, val)
-		}
-	}
-}
-
-var testStringFormatToRune = map[string]rune{
-	"yaml":  '-',
-	"toml":  '+',
-	"json":  '{',
-	"lorem": '0',
-}
-
-func TestStringFormatToRune(t *testing.T) {
-	for format, mark := range testStringFormatToRune {
-		val, _ := StringFormatToRune(format)
-		if val != mark {
-			t.Errorf("Incorrect value on StringFormatToRune of %s; want: %v; got: %v", format, mark, val)
-		}
-	}
-}
diff --git a/handlers/checksum.go b/handlers/checksum.go
deleted file mode 100644
index cf5c2a64..00000000
--- a/handlers/checksum.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package handlers
-
-import (
-	"crypto/md5"
-	"crypto/sha1"
-	"crypto/sha256"
-	"crypto/sha512"
-	"encoding/hex"
-	e "errors"
-	"hash"
-	"io"
-	"net/http"
-	"os"
-
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/hacdias/caddy-filemanager/utils/errors"
-)
-
-// Checksum calculates the hash of a file. Supports MD5, SHA1, SHA256 and SHA512.
-func Checksum(w http.ResponseWriter, r *http.Request, c *config.Config, i *file.Info) (int, error) {
-	query := r.URL.Query().Get("checksum")
-
-	file, err := os.Open(i.Path)
-	if err != nil {
-		return errors.ErrorToHTTPCode(err, true), err
-	}
-
-	defer file.Close()
-
-	var h hash.Hash
-
-	switch query {
-	case "md5":
-		h = md5.New()
-	case "sha1":
-		h = sha1.New()
-	case "sha256":
-		h = sha256.New()
-	case "sha512":
-		h = sha512.New()
-	default:
-		return http.StatusBadRequest, e.New("Unknown HASH type")
-	}
-
-	_, err = io.Copy(h, file)
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	val := hex.EncodeToString(h.Sum(nil))
-	w.Write([]byte(val))
-	return http.StatusOK, nil
-}
diff --git a/handlers/command.go b/handlers/command.go
deleted file mode 100644
index 0b2ee12e..00000000
--- a/handlers/command.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package handlers
-
-import (
-	"bytes"
-	"net/http"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/gorilla/websocket"
-	"github.com/hacdias/caddy-filemanager/config"
-)
-
-var upgrader = websocket.Upgrader{
-	ReadBufferSize:  1024,
-	WriteBufferSize: 1024,
-}
-
-var (
-	cmdNotImplemented = []byte("Command not implemented.")
-	cmdNotAllowed     = []byte("Command not allowed.")
-)
-
-// Command handles the requests for VCS related commands: git, svn and mercurial
-func Command(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
-	// Upgrades the connection to a websocket and checks for errors.
-	conn, err := upgrader.Upgrade(w, r, nil)
-	if err != nil {
-		return 0, err
-	}
-	defer conn.Close()
-
-	var (
-		message []byte
-		command []string
-	)
-
-	// Starts an infinite loop until a valid command is captured.
-	for {
-		_, message, err = conn.ReadMessage()
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		command = strings.Split(string(message), " ")
-		if len(command) != 0 {
-			break
-		}
-	}
-
-	// Check if the command is allowed
-	allowed := false
-
-	for _, cmd := range u.Commands {
-		if cmd == command[0] {
-			allowed = true
-		}
-	}
-
-	if !allowed {
-		err = conn.WriteMessage(websocket.BinaryMessage, cmdNotAllowed)
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		return 0, nil
-	}
-
-	// Check if the program is talled is installed on the computer.
-	if _, err = exec.LookPath(command[0]); err != nil {
-		err = conn.WriteMessage(websocket.BinaryMessage, cmdNotImplemented)
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		return http.StatusNotImplemented, nil
-	}
-
-	// Gets the path and initializes a buffer.
-	path := strings.Replace(r.URL.Path, c.BaseURL, c.Scope, 1)
-	path = filepath.Clean(path)
-	buff := new(bytes.Buffer)
-
-	// Sets up the command executation.
-	cmd := exec.Command(command[0], command[1:]...)
-	cmd.Dir = path
-	cmd.Stderr = buff
-	cmd.Stdout = buff
-
-	// Starts the command and checks for errors.
-	err = cmd.Start()
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	// Set a 'done' variable to check whetever the command has already finished
-	// running or not. This verification is done using a goroutine that uses the
-	// method .Wait() from the command.
-	done := false
-	go func() {
-		err = cmd.Wait()
-		done = true
-	}()
-
-	// Function to print the current information on the buffer to the connection.
-	print := func() error {
-		by := buff.Bytes()
-		if len(by) > 0 {
-			err = conn.WriteMessage(websocket.TextMessage, by)
-			if err != nil {
-				return err
-			}
-		}
-
-		return nil
-	}
-
-	// While the command hasn't finished running, continue sending the output
-	// to the client in intervals of 100 milliseconds.
-	for !done {
-		if err = print(); err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		time.Sleep(100 * time.Millisecond)
-	}
-
-	// After the command is done executing, send the output one more time to the
-	// browser to make sure it gets the latest information.
-	if err = print(); err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	return 0, nil
-}
diff --git a/handlers/download.go b/handlers/download.go
deleted file mode 100644
index 1f768630..00000000
--- a/handlers/download.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package handlers
-
-import (
-	"io"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/mholt/archiver"
-)
-
-// Download creates an archive in one of the supported formats (zip, tar,
-// tar.gz or tar.bz2) and sends it to be downloaded.
-func Download(w http.ResponseWriter, r *http.Request, c *config.Config, i *file.Info) (int, error) {
-	query := r.URL.Query().Get("download")
-
-	if !i.IsDir {
-		w.Header().Set("Content-Disposition", "attachment; filename="+i.Name)
-		http.ServeFile(w, r, i.Path)
-		return 0, nil
-	}
-
-	files := []string{}
-	names := strings.Split(r.URL.Query().Get("files"), ",")
-
-	if len(names) != 0 {
-		for _, name := range names {
-			name, err := url.QueryUnescape(name)
-
-			if err != nil {
-				return http.StatusInternalServerError, err
-			}
-
-			files = append(files, filepath.Join(i.Path, name))
-		}
-
-	} else {
-		files = append(files, i.Path)
-	}
-
-	if query == "true" {
-		query = "zip"
-	}
-
-	var (
-		extension string
-		temp      string
-		err       error
-		tempfile  string
-	)
-
-	temp, err = ioutil.TempDir("", "")
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	defer os.RemoveAll(temp)
-	tempfile = filepath.Join(temp, "temp")
-
-	switch query {
-	case "zip":
-		extension, err = ".zip", archiver.Zip.Make(tempfile, files)
-	case "tar":
-		extension, err = ".tar", archiver.Tar.Make(tempfile, files)
-	case "targz":
-		extension, err = ".tar.gz", archiver.TarGz.Make(tempfile, files)
-	case "tarbz2":
-		extension, err = ".tar.bz2", archiver.TarBz2.Make(tempfile, files)
-	case "tarxz":
-		extension, err = ".tar.xz", archiver.TarXZ.Make(tempfile, files)
-	default:
-		return http.StatusNotImplemented, nil
-	}
-
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	file, err := os.Open(temp + "/temp")
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	name := i.Name
-	if name == "." || name == "" {
-		name = "download"
-	}
-
-	w.Header().Set("Content-Disposition", "attachment; filename="+name+extension)
-	io.Copy(w, file)
-	return http.StatusOK, nil
-}
diff --git a/handlers/editor.go b/handlers/editor.go
deleted file mode 100644
index 8440734b..00000000
--- a/handlers/editor.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package handlers
-
-import (
-	"bytes"
-	"errors"
-	"net/http"
-	"path/filepath"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/hacdias/caddy-filemanager/frontmatter"
-	"github.com/spf13/hugo/parser"
-)
-
-// Editor contains the information for the editor page
-type Editor struct {
-	Class       string
-	Mode        string
-	Visual      bool
-	Content     string
-	FrontMatter struct {
-		Content *frontmatter.Content
-		Rune    rune
-	}
-}
-
-// GetEditor gets the editor based on a FileInfo struct
-func GetEditor(r *http.Request, i *file.Info) (*Editor, error) {
-	var err error
-
-	// Create a new editor variable and set the mode
-	e := new(Editor)
-	e.Mode = editorMode(i.Name)
-	e.Class = editorClass(e.Mode)
-
-	if e.Class == "frontmatter-only" || e.Class == "complete" {
-		e.Visual = true
-	}
-
-	if r.URL.Query().Get("visual") == "false" {
-		e.Class = "content-only"
-	}
-
-	hasRune := frontmatter.HasRune(i.Content)
-
-	if e.Class == "frontmatter-only" && !hasRune {
-		e.FrontMatter.Rune, err = frontmatter.StringFormatToRune(e.Mode)
-		if err != nil {
-			goto Error
-		}
-		i.Content = frontmatter.AppendRune(i.Content, e.FrontMatter.Rune)
-		hasRune = true
-	}
-
-	if e.Class == "frontmatter-only" && hasRune {
-		e.FrontMatter.Content, _, err = frontmatter.Pretty(i.Content)
-		if err != nil {
-			goto Error
-		}
-	}
-
-	if e.Class == "complete" && hasRune {
-		var page parser.Page
-		// Starts a new buffer and parses the file using Hugo's functions
-		buffer := bytes.NewBuffer(i.Content)
-		page, err = parser.ReadFrom(buffer)
-
-		if err != nil {
-			goto Error
-		}
-
-		// Parses the page content and the frontmatter
-		e.Content = strings.TrimSpace(string(page.Content()))
-		e.FrontMatter.Rune = rune(i.Content[0])
-		e.FrontMatter.Content, _, err = frontmatter.Pretty(page.FrontMatter())
-	}
-
-	if e.Class == "complete" && !hasRune {
-		err = errors.New("Complete but without rune")
-	}
-
-Error:
-	if e.Class == "content-only" || err != nil {
-		e.Class = "content-only"
-		e.Content = i.StringifyContent()
-	}
-
-	return e, nil
-}
-
-func editorClass(mode string) string {
-	switch mode {
-	case "json", "toml", "yaml":
-		return "frontmatter-only"
-	case "markdown", "asciidoc", "rst":
-		return "complete"
-	}
-
-	return "content-only"
-}
-
-func editorMode(filename string) string {
-	mode := strings.TrimPrefix(filepath.Ext(filename), ".")
-
-	switch mode {
-	case "md", "markdown", "mdown", "mmark":
-		mode = "markdown"
-	case "asciidoc", "adoc", "ad":
-		mode = "asciidoc"
-	case "rst":
-		mode = "rst"
-	case "html", "htm":
-		mode = "html"
-	case "js":
-		mode = "javascript"
-	case "go":
-		mode = "golang"
-	}
-
-	return mode
-}
diff --git a/handlers/listing.go b/handlers/listing.go
deleted file mode 100644
index c0d7d148..00000000
--- a/handlers/listing.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package handlers
-
-import (
-	"encoding/json"
-	"net/http"
-	"strconv"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/hacdias/caddy-filemanager/page"
-	"github.com/hacdias/caddy-filemanager/utils/errors"
-	"github.com/mholt/caddy/caddyhttp/httpserver"
-)
-
-// ServeListing presents the user with a listage of a directory folder.
-func ServeListing(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User, i *file.Info) (int, error) {
-	var err error
-
-	// Loads the content of the directory
-	listing, err := file.GetListing(u, i.VirtualPath, c.PrefixURL+r.URL.Path)
-	if err != nil {
-		return errors.ErrorToHTTPCode(err, true), err
-	}
-
-	listing.Context = httpserver.Context{
-		Root: http.Dir(u.Scope),
-		Req:  r,
-		URL:  r.URL,
-	}
-
-	cookieScope := c.BaseURL
-	if cookieScope == "" {
-		cookieScope = "/"
-	}
-
-	// Copy the query values into the Listing struct
-	var limit int
-	listing.Sort, listing.Order, limit, err = handleSortOrder(w, r, cookieScope)
-	if err != nil {
-		return http.StatusBadRequest, err
-	}
-
-	listing.ApplySort()
-
-	if limit > 0 && limit <= len(listing.Items) {
-		listing.Items = listing.Items[:limit]
-		listing.ItemsLimitedTo = limit
-	}
-
-	if strings.Contains(r.Header.Get("Accept"), "application/json") {
-		marsh, err := json.Marshal(listing.Items)
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		w.Header().Set("Content-Type", "application/json; charset=utf-8")
-		if _, err := w.Write(marsh); err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		return http.StatusOK, nil
-	}
-
-	displayMode := r.URL.Query().Get("display")
-
-	if displayMode == "" {
-		if displayCookie, err := r.Cookie("display"); err == nil {
-			displayMode = displayCookie.Value
-		}
-	}
-
-	if displayMode == "" || (displayMode != "mosaic" && displayMode != "list") {
-		displayMode = "mosaic"
-	}
-
-	http.SetCookie(w, &http.Cookie{
-		Name:   "display",
-		Value:  displayMode,
-		Path:   cookieScope,
-		Secure: r.TLS != nil,
-	})
-
-	page := &page.Page{
-		Minimal: r.Header.Get("Minimal") == "true",
-		Info: &page.Info{
-			Name:    listing.Name,
-			Path:    i.VirtualPath,
-			IsDir:   true,
-			User:    u,
-			Config:  c,
-			Display: displayMode,
-			Data:    listing,
-		},
-	}
-
-	return page.PrintAsHTML(w, "listing")
-}
-
-// handleSortOrder gets and stores for a Listing the 'sort' and 'order',
-// and reads 'limit' if given. The latter is 0 if not given. Sets cookies.
-func handleSortOrder(w http.ResponseWriter, r *http.Request, scope string) (sort string, order string, limit int, err error) {
-	sort = r.URL.Query().Get("sort")
-	order = r.URL.Query().Get("order")
-	limitQuery := r.URL.Query().Get("limit")
-
-	// If the query 'sort' or 'order' is empty, use defaults or any values
-	// previously saved in Cookies.
-	switch sort {
-	case "":
-		sort = "name"
-		if sortCookie, sortErr := r.Cookie("sort"); sortErr == nil {
-			sort = sortCookie.Value
-		}
-	case "name", "size", "type":
-		http.SetCookie(w, &http.Cookie{
-			Name:   "sort",
-			Value:  sort,
-			Path:   scope,
-			Secure: r.TLS != nil,
-		})
-	}
-
-	switch order {
-	case "":
-		order = "asc"
-		if orderCookie, orderErr := r.Cookie("order"); orderErr == nil {
-			order = orderCookie.Value
-		}
-	case "asc", "desc":
-		http.SetCookie(w, &http.Cookie{
-			Name:   "order",
-			Value:  order,
-			Path:   scope,
-			Secure: r.TLS != nil,
-		})
-	}
-
-	if limitQuery != "" {
-		limit, err = strconv.Atoi(limitQuery)
-		// If the 'limit' query can't be interpreted as a number, return err.
-		if err != nil {
-			return
-		}
-	}
-
-	return
-}
diff --git a/handlers/put.go b/handlers/put.go
deleted file mode 100644
index 4b98e59b..00000000
--- a/handlers/put.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package handlers
-
-import (
-	"bytes"
-	"encoding/json"
-	"errors"
-	"io/ioutil"
-	"net/http"
-	"path/filepath"
-	"strconv"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/frontmatter"
-)
-
-// PreProccessPUT is used to update a file that was edited
-func PreProccessPUT(
-	w http.ResponseWriter,
-	r *http.Request,
-	c *config.Config,
-	u *config.User,
-) (err error) {
-	var (
-		data      = map[string]interface{}{}
-		file      []byte
-		kind      string
-		rawBuffer = new(bytes.Buffer)
-	)
-
-	kind = r.Header.Get("kind")
-	rawBuffer.ReadFrom(r.Body)
-
-	if kind != "" {
-		err = json.Unmarshal(rawBuffer.Bytes(), &data)
-
-		if err != nil {
-			return
-		}
-	}
-
-	switch kind {
-	case "frontmatter-only":
-		if file, err = ParseFrontMatterOnlyFile(data, r.URL.Path); err != nil {
-			return
-		}
-	case "content-only":
-		mainContent := data["content"].(string)
-		mainContent = strings.TrimSpace(mainContent)
-		file = []byte(mainContent)
-	case "complete":
-		var mark rune
-
-		if v := r.Header.Get("Rune"); v != "" {
-			var n int
-			n, err = strconv.Atoi(v)
-			if err != nil {
-				return err
-			}
-
-			mark = rune(n)
-		}
-
-		if file, err = ParseCompleteFile(data, r.URL.Path, mark); err != nil {
-			return
-		}
-	default:
-		file = rawBuffer.Bytes()
-	}
-
-	// Overwrite the request Body
-	r.Body = ioutil.NopCloser(bytes.NewReader(file))
-	return
-}
-
-// ParseFrontMatterOnlyFile parses a frontmatter only file
-func ParseFrontMatterOnlyFile(data interface{}, filename string) ([]byte, error) {
-	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
-	f, err := ParseFrontMatter(data, frontmatter)
-	fString := string(f)
-
-	// If it's toml or yaml, strip frontmatter identifier
-	if frontmatter == "toml" {
-		fString = strings.TrimSuffix(fString, "+++\n")
-		fString = strings.TrimPrefix(fString, "+++\n")
-	}
-
-	if frontmatter == "yaml" {
-		fString = strings.TrimSuffix(fString, "---\n")
-		fString = strings.TrimPrefix(fString, "---\n")
-	}
-
-	f = []byte(fString)
-	return f, err
-}
-
-// ParseFrontMatter is the frontmatter parser
-func ParseFrontMatter(data interface{}, front string) ([]byte, error) {
-	var mark rune
-
-	switch front {
-	case "toml":
-		mark = '+'
-	case "json":
-		mark = '{'
-	case "yaml":
-		mark = '-'
-	default:
-		return nil, errors.New("Unsupported Format provided")
-	}
-
-	return frontmatter.Marshal(data, mark)
-}
-
-// ParseCompleteFile parses a complete file
-func ParseCompleteFile(data map[string]interface{}, filename string, mark rune) ([]byte, error) {
-	mainContent := ""
-
-	if _, ok := data["content"]; ok {
-		// The main content of the file
-		mainContent = data["content"].(string)
-		mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n"
-
-		// Removes the main content from the rest of the frontmatter
-		delete(data, "content")
-	}
-
-	if _, ok := data["date"]; ok {
-		data["date"] = data["date"].(string) + ":00"
-	}
-
-	front, err := frontmatter.Marshal(data, mark)
-	if err != nil {
-		return []byte{}, err
-	}
-
-	front = frontmatter.AppendRune(front, mark)
-
-	// Generates the final file
-	f := new(bytes.Buffer)
-	f.Write(front)
-	f.Write([]byte(mainContent))
-	return f.Bytes(), nil
-}
diff --git a/handlers/search.go b/handlers/search.go
deleted file mode 100644
index 8a1265d3..00000000
--- a/handlers/search.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package handlers
-
-import (
-	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/gorilla/websocket"
-	"github.com/hacdias/caddy-filemanager/config"
-)
-
-type searchOptions struct {
-	CaseInsensitive bool
-	Terms           []string
-}
-
-func parseSearch(value string) *searchOptions {
-	opts := &searchOptions{
-		CaseInsensitive: strings.Contains(value, "case:insensitive"),
-	}
-
-	// removes the options from the value
-	value = strings.Replace(value, "case:insensitive", "", -1)
-	value = strings.Replace(value, "case:sensitive", "", -1)
-	value = strings.TrimSpace(value)
-
-	if opts.CaseInsensitive {
-		value = strings.ToLower(value)
-	}
-
-	// if the value starts with " and finishes what that character, we will
-	// only search for that term
-	if value[0] == '"' && value[len(value)-1] == '"' {
-		unique := strings.TrimPrefix(value, "\"")
-		unique = strings.TrimSuffix(unique, "\"")
-
-		opts.Terms = []string{unique}
-		return opts
-	}
-
-	opts.Terms = strings.Split(value, " ")
-	return opts
-}
-
-// Search ...
-func Search(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
-	// Upgrades the connection to a websocket and checks for errors.
-	conn, err := upgrader.Upgrade(w, r, nil)
-	if err != nil {
-		return 0, err
-	}
-	defer conn.Close()
-
-	var (
-		value   string
-		search  *searchOptions
-		message []byte
-	)
-
-	// Starts an infinite loop until a valid command is captured.
-	for {
-		_, message, err = conn.ReadMessage()
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		if len(message) != 0 {
-			value = string(message)
-			break
-		}
-	}
-
-	search = parseSearch(value)
-	scope := strings.Replace(r.URL.Path, c.BaseURL, "", 1)
-	scope = strings.TrimPrefix(scope, "/")
-	scope = "/" + scope
-	scope = u.Scope + scope
-	scope = strings.Replace(scope, "\\", "/", -1)
-	scope = filepath.Clean(scope)
-
-	err = filepath.Walk(scope, func(path string, f os.FileInfo, err error) error {
-		if search.CaseInsensitive {
-			path = strings.ToLower(path)
-		}
-
-		path = strings.Replace(path, "\\", "/", -1)
-		is := false
-
-		for _, term := range search.Terms {
-			if is {
-				break
-			}
-
-			if strings.Contains(path, term) {
-				if !u.Allowed(path) {
-					return nil
-				}
-
-				is = true
-			}
-		}
-
-		if !is {
-			return nil
-		}
-
-		path = strings.TrimPrefix(path, scope)
-		path = strings.TrimPrefix(path, "/")
-		return conn.WriteMessage(websocket.TextMessage, []byte(path))
-	})
-
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	return http.StatusOK, nil
-}
diff --git a/handlers/single.go b/handlers/single.go
deleted file mode 100644
index 2d804247..00000000
--- a/handlers/single.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package handlers
-
-import (
-	"net/http"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/file"
-	"github.com/hacdias/caddy-filemanager/page"
-	"github.com/hacdias/caddy-filemanager/utils/errors"
-)
-
-// ServeSingle serves a single file in an editor (if it is editable), shows the
-// plain file, or downloads it if it can't be shown.
-func ServeSingle(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User, i *file.Info) (int, error) {
-	var err error
-
-	if err = i.RetrieveFileType(); err != nil {
-		return errors.ErrorToHTTPCode(err, true), err
-	}
-
-	p := &page.Page{
-		Info: &page.Info{
-			Name:   i.Name,
-			Path:   i.VirtualPath,
-			IsDir:  false,
-			Data:   i,
-			User:   u,
-			Config: c,
-		},
-	}
-
-	// If the request accepts JSON, we send the file information.
-	if strings.Contains(r.Header.Get("Accept"), "application/json") {
-		return p.PrintAsJSON(w)
-	}
-
-	if i.Type == "text" {
-		if err = i.Read(); err != nil {
-			return errors.ErrorToHTTPCode(err, true), err
-		}
-	}
-
-	if i.CanBeEdited() && u.AllowEdit {
-		p.Data, err = GetEditor(r, i)
-		p.Editor = true
-		if err != nil {
-			return http.StatusInternalServerError, err
-		}
-
-		return p.PrintAsHTML(w, "frontmatter", "editor")
-	}
-
-	return p.PrintAsHTML(w, "single")
-}
diff --git a/page/error.go b/page/error.go
deleted file mode 100644
index cdd470f7..00000000
--- a/page/error.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package page
-
-import (
-	"net/http"
-	"strconv"
-	"strings"
-)
-
-const errTemplate = `<!DOCTYPE html>
-<html>
-<head>
-    <title>TITLE</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <meta charset="utf-8">
-    <style>
-    html {
-        background-color: #2196f3;
-        color: #fff;
-        font-family: sans-serif;
-    }
-    code {
-        background-color: rgba(0,0,0,0.1);
-        border-radius: 5px;
-        padding: 1em;
-        display: block;
-        box-sizing: border-box;
-    }
-    .center {
-        max-width: 40em;
-        margin: 2em auto 0;
-    }
-    a {
-        text-decoration: none;
-        color: #eee;
-        font-weight: bold;
-    }
-	p {
-		line-height: 1.3;
-	}
-    </style>
-</head>
-
-<body>
-    <div class="center">
-        <h1>TITLE</h1>
-
-        <p>Try reloading the page or hitting the back button. If this error persists, it seems that you may have found a bug! Please create an issue at <a href="https://github.com/hacdias/caddy-filemanager/issues">hacdias/caddy-filemanager</a> repository on GitHub with the code below.</p>
-
-        <code>CODE</code>
-    </div>
-</html>`
-
-// PrintErrorHTML prints the error page
-func PrintErrorHTML(w http.ResponseWriter, code int, err error) (int, error) {
-	tpl := errTemplate
-	tpl = strings.Replace(tpl, "TITLE", strconv.Itoa(code)+" "+http.StatusText(code), -1)
-	tpl = strings.Replace(tpl, "CODE", err.Error(), -1)
-
-	_, err = w.Write([]byte(tpl))
-
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-	return http.StatusOK, nil
-}
diff --git a/page/page.go b/page/page.go
deleted file mode 100644
index fd53f207..00000000
--- a/page/page.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// Package page is used to render the HTML to the end user
-package page
-
-import (
-	"bytes"
-	"encoding/base64"
-	"encoding/json"
-	"html/template"
-	"log"
-	"net/http"
-	"strings"
-
-	"github.com/hacdias/caddy-filemanager/assets"
-	"github.com/hacdias/caddy-filemanager/config"
-	"github.com/hacdias/caddy-filemanager/utils/variables"
-)
-
-// Page contains the informations and functions needed to show the Page
-type Page struct {
-	*Info
-	Minimal bool
-}
-
-// Info contains the information of a Page
-type Info struct {
-	Name    string
-	Path    string
-	IsDir   bool
-	User    *config.User
-	Config  *config.Config
-	Data    interface{}
-	Editor  bool
-	Display string
-	Token   string
-}
-
-// BreadcrumbMapItem ...
-type BreadcrumbMapItem struct {
-	Name string
-	URL  string
-}
-
-// BreadcrumbMap returns p.Path where every element is a map
-// of URLs and path segment names.
-func (i Info) BreadcrumbMap() []BreadcrumbMapItem {
-	result := []BreadcrumbMapItem{}
-
-	if len(i.Path) == 0 {
-		return result
-	}
-
-	// skip trailing slash
-	lpath := i.Path
-	if lpath[len(lpath)-1] == '/' {
-		lpath = lpath[:len(lpath)-1]
-	}
-
-	parts := strings.Split(lpath, "/")
-	for i, part := range parts {
-		if i == len(parts)-1 {
-			continue
-		}
-
-		if i == 0 && part == "" {
-			result = append([]BreadcrumbMapItem{{
-				Name: "/",
-				URL:  "/",
-			}}, result...)
-			continue
-		}
-
-		result = append([]BreadcrumbMapItem{{
-			Name: part,
-			URL:  strings.Join(parts[:i+1], "/") + "/",
-		}}, result...)
-	}
-
-	return result
-}
-
-// PreviousLink returns the path of the previous folder
-func (i Info) PreviousLink() string {
-	path := strings.TrimSuffix(i.Path, "/")
-	path = strings.TrimPrefix(path, "/")
-	path = i.Config.AbsoluteURL() + "/" + path
-	path = path[0 : len(path)-len(i.Name)]
-
-	if len(path) < len(i.Config.AbsoluteURL()+"/") {
-		return ""
-	}
-
-	return path
-}
-
-// PrintAsHTML formats the page in HTML and executes the template
-func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) {
-	// Create the functions map, then the template, check for erros and
-	// execute the template if there aren't errors
-	functions := template.FuncMap{
-		"Defined": variables.Defined,
-		"CSS": func(s string) template.CSS {
-			return template.CSS(s)
-		},
-		"Marshal": func(v interface{}) template.JS {
-			a, _ := json.Marshal(v)
-			return template.JS(a)
-		},
-		"EncodeBase64": func(s string) string {
-			return base64.StdEncoding.EncodeToString([]byte(s))
-		},
-	}
-
-	if p.Minimal {
-		templates = append(templates, "minimal")
-	} else {
-		templates = append(templates, "base")
-	}
-
-	var tpl *template.Template
-
-	// For each template, add it to the the tpl variable
-	for i, t := range templates {
-		// Get the template from the assets
-		Page, err := assets.Asset("templates/" + t + ".tmpl")
-
-		// Check if there is some error. If so, the template doesn't exist
-		if err != nil {
-			log.Print(err)
-			return http.StatusInternalServerError, err
-		}
-
-		// If it's the first iteration, creates a new template and add the
-		// functions map
-		if i == 0 {
-			tpl, err = template.New(t).Funcs(functions).Parse(string(Page))
-		} else {
-			tpl, err = tpl.Parse(string(Page))
-		}
-
-		if err != nil {
-			log.Print(err)
-			return http.StatusInternalServerError, err
-		}
-	}
-
-	buf := &bytes.Buffer{}
-	err := tpl.Execute(buf, p.Info)
-
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	w.Header().Set("Content-Type", "text/html; charset=utf-8")
-	_, err = buf.WriteTo(w)
-	return http.StatusOK, err
-}
-
-// PrintAsJSON prints the current Page information in JSON
-func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) {
-	marsh, err := json.MarshalIndent(p.Info.Data, "", "    ")
-	if err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	w.Header().Set("Content-Type", "application/json; charset=utf-8")
-	if _, err := w.Write(marsh); err != nil {
-		return http.StatusInternalServerError, err
-	}
-
-	return http.StatusOK, nil
-}
diff --git a/pre-build.sh b/pre-build.sh
deleted file mode 100644
index 66e8c638..00000000
--- a/pre-build.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-go get github.com/jteeuwen/go-bindata/go-bindata
-
-go-bindata -pkg assets -prefix "_embed" \
-  -o assets/binary.go -ignore "^.*theme-([^g]|g[^i]|gi[^t]|git[^h]|gith[^u]|githu[^b]).*\.js$"  \
-  _embed/templates/... _embed/public/js/... _embed/public/css/... _embed/public/ace/src-min/... \
-
-git add -A
diff --git a/setup.go b/setup.go
index a7c97b1b..f491d875 100644
--- a/setup.go
+++ b/setup.go
@@ -1,7 +1,18 @@
 package filemanager
 
 import (
-	"github.com/hacdias/caddy-filemanager/config"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/hacdias/filemanager"
 	"github.com/mholt/caddy"
 	"github.com/mholt/caddy/caddyhttp/httpserver"
 )
@@ -15,7 +26,7 @@ func init() {
 
 // setup configures a new FileManager middleware instance.
 func setup(c *caddy.Controller) error {
-	configs, err := config.Parse(c)
+	configs, err := parse(c)
 	if err != nil {
 		return err
 	}
@@ -26,3 +37,199 @@ func setup(c *caddy.Controller) error {
 
 	return nil
 }
+
+func parse(c *caddy.Controller) ([]*filemanager.FileManager, error) {
+	var (
+		configs []*filemanager.FileManager
+		err     error
+	)
+
+	for c.Next() {
+		var (
+			m    = filemanager.New(".")
+			u    = m.User
+			name = ""
+		)
+
+		// Get the baseURL
+		args := c.RemainingArgs()
+
+		if len(args) > 0 {
+			m.SetBaseURL(args[0])
+			m.SetWebDavURL("/webdav")
+		}
+
+		for c.NextBlock() {
+			switch c.Val() {
+			case "before_save":
+				/* if cfg.BeforeSave, err = CommandRunner(c); err != nil {
+					return configs, err
+				} */
+			case "after_save":
+				/*	if cfg.AfterSave, err = CommandRunner(c); err != nil {
+					return configs, err
+				} */
+			case "webdav":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				m.SetWebDavURL(c.Val())
+			case "show":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				m.SetScope(c.Val(), name)
+			case "styles":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				var tplBytes []byte
+				tplBytes, err = ioutil.ReadFile(c.Val())
+				if err != nil {
+					return configs, err
+				}
+
+				u.StyleSheet = string(tplBytes)
+			case "allow_new":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				u.AllowNew, err = strconv.ParseBool(c.Val())
+				if err != nil {
+					return configs, err
+				}
+			case "allow_edit":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				u.AllowEdit, err = strconv.ParseBool(c.Val())
+				if err != nil {
+					return configs, err
+				}
+			case "allow_commands":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				u.AllowCommands, err = strconv.ParseBool(c.Val())
+				if err != nil {
+					return configs, err
+				}
+			case "allow_command":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				u.Commands = append(u.Commands, c.Val())
+			case "block_command":
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				index := 0
+
+				for i, val := range u.Commands {
+					if val == c.Val() {
+						index = i
+					}
+				}
+
+				u.Commands = append(u.Commands[:index], u.Commands[index+1:]...)
+			case "allow", "allow_r", "block", "block_r":
+				ruleType := c.Val()
+
+				if !c.NextArg() {
+					return configs, c.ArgErr()
+				}
+
+				if c.Val() == "dotfiles" && !strings.HasSuffix(ruleType, "_r") {
+					ruleType += "_r"
+				}
+
+				rule := &filemanager.Rule{
+					Allow: ruleType == "allow" || ruleType == "allow_r",
+					Regex: ruleType == "allow_r" || ruleType == "block_r",
+				}
+
+				if rule.Regex && c.Val() == "dotfiles" {
+					rule.Regexp = regexp.MustCompile("\\/\\..+")
+				} else if rule.Regex {
+					rule.Regexp = regexp.MustCompile(c.Val())
+				} else {
+					rule.Path = c.Val()
+				}
+
+				u.Rules = append(u.Rules, rule)
+			default:
+				// Is it a new user? Is it?
+				val := c.Val()
+
+				// Checks if it's a new user!
+				if !strings.HasSuffix(val, ":") {
+					fmt.Println("Unknown option " + val)
+				}
+
+				// Get the username, sets the current user, and initializes it
+				val = strings.TrimSuffix(val, ":")
+				m.NewUser(val)
+				name = val
+			}
+		}
+
+		configs = append(configs, m)
+	}
+
+	return configs, nil
+}
+
+// CommandRunner ...
+func CommandRunner(c *caddy.Controller) (filemanager.Command, error) {
+	fn := func(r *http.Request, c *filemanager.FileManager, u *filemanager.User) error { return nil }
+
+	args := c.RemainingArgs()
+	if len(args) == 0 {
+		return fn, c.ArgErr()
+	}
+
+	nonblock := false
+	if len(args) > 1 && args[len(args)-1] == "&" {
+		// Run command in background; non-blocking
+		nonblock = true
+		args = args[:len(args)-1]
+	}
+
+	command, args, err := caddy.SplitCommandAndArgs(strings.Join(args, " "))
+	if err != nil {
+		return fn, c.Err(err.Error())
+	}
+
+	fn = func(r *http.Request, c *filemanager.FileManager, u *filemanager.User) error {
+		path := strings.Replace(r.URL.Path, c.WebDavURL, "", 1)
+		path = u.Scope() + "/" + path
+		path = filepath.Clean(path)
+
+		for i := range args {
+			args[i] = strings.Replace(args[i], "{path}", path, -1)
+		}
+
+		cmd := exec.Command(command, args...)
+		cmd.Stdin = os.Stdin
+		cmd.Stdout = os.Stdout
+		cmd.Stderr = os.Stderr
+
+		if nonblock {
+			log.Printf("[INFO] Nonblocking Command:\"%s %s\"", command, strings.Join(args, " "))
+			return cmd.Start()
+		}
+
+		log.Printf("[INFO] Blocking Command:\"%s %s\"", command, strings.Join(args, " "))
+		return cmd.Run()
+	}
+
+	return fn, nil
+}
diff --git a/utils/errors/errors.go b/utils/errors/errors.go
deleted file mode 100644
index 37781c1a..00000000
--- a/utils/errors/errors.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package errors
-
-import (
-	"net/http"
-	"os"
-)
-
-// ErrorToHTTPCode converts errors to HTTP Status Code.
-func ErrorToHTTPCode(err error, gone bool) int {
-	switch {
-	case os.IsPermission(err):
-		return http.StatusForbidden
-	case os.IsNotExist(err):
-		if !gone {
-			return http.StatusNotFound
-		}
-
-		return http.StatusGone
-	case os.IsExist(err):
-		return http.StatusGone
-	default:
-		return http.StatusInternalServerError
-	}
-}
diff --git a/utils/variables/types.go b/utils/variables/types.go
deleted file mode 100644
index ee43dad3..00000000
--- a/utils/variables/types.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package variables
-
-import "reflect"
-
-// IsMap checks if some variable is a map
-func IsMap(sth interface{}) bool {
-	return reflect.ValueOf(sth).Kind() == reflect.Map
-}
-
-// IsSlice checks if some variable is a slice
-func IsSlice(sth interface{}) bool {
-	return reflect.ValueOf(sth).Kind() == reflect.Slice
-}
diff --git a/utils/variables/types_test.go b/utils/variables/types_test.go
deleted file mode 100644
index 9955b9b2..00000000
--- a/utils/variables/types_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package variables
-
-import "testing"
-
-type interfaceToBool struct {
-	Value  interface{}
-	Result bool
-}
-
-var testIsMap = []*interfaceToBool{
-	&interfaceToBool{"teste", false},
-	&interfaceToBool{453478, false},
-	&interfaceToBool{-984512, false},
-	&interfaceToBool{true, false},
-	&interfaceToBool{map[string]bool{}, true},
-	&interfaceToBool{map[int]bool{}, true},
-	&interfaceToBool{map[interface{}]bool{}, true},
-	&interfaceToBool{[]string{}, false},
-}
-
-func TestIsMap(t *testing.T) {
-	for _, test := range testIsMap {
-		if IsMap(test.Value) != test.Result {
-			t.Errorf("Incorrect value on IsMap for %v; want: %v; got: %v", test.Value, test.Result, !test.Result)
-		}
-	}
-}
-
-var testIsSlice = []*interfaceToBool{
-	&interfaceToBool{"teste", false},
-	&interfaceToBool{453478, false},
-	&interfaceToBool{-984512, false},
-	&interfaceToBool{true, false},
-	&interfaceToBool{map[string]bool{}, false},
-	&interfaceToBool{map[int]bool{}, false},
-	&interfaceToBool{map[interface{}]bool{}, false},
-	&interfaceToBool{[]string{}, true},
-	&interfaceToBool{[]int{}, true},
-	&interfaceToBool{[]bool{}, true},
-	&interfaceToBool{[]interface{}{}, true},
-}
-
-func TestIsSlice(t *testing.T) {
-	for _, test := range testIsSlice {
-		if IsSlice(test.Value) != test.Result {
-			t.Errorf("Incorrect value on IsSlice for %v; want: %v; got: %v", test.Value, test.Result, !test.Result)
-		}
-	}
-}
diff --git a/utils/variables/variables.go b/utils/variables/variables.go
deleted file mode 100644
index 7a0168b4..00000000
--- a/utils/variables/variables.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package variables
-
-import (
-	"errors"
-	"log"
-	"reflect"
-)
-
-// Defined checks if variable is defined in a struct
-func Defined(data interface{}, field string) bool {
-	t := reflect.Indirect(reflect.ValueOf(data)).Type()
-
-	if t.Kind() != reflect.Struct {
-		log.Print("Non-struct type not allowed.")
-		return false
-	}
-
-	_, b := t.FieldByName(field)
-	return b
-}
-
-// Dict allows to send more than one variable into a template
-func Dict(values ...interface{}) (map[string]interface{}, error) {
-	if len(values)%2 != 0 {
-		return nil, errors.New("invalid dict call")
-	}
-	dict := make(map[string]interface{}, len(values)/2)
-	for i := 0; i < len(values); i += 2 {
-		key, ok := values[i].(string)
-		if !ok {
-			return nil, errors.New("dict keys must be strings")
-		}
-		dict[key] = values[i+1]
-	}
-
-	return dict, nil
-}
-
-// StringInSlice checks if a slice contains a string
-func StringInSlice(a string, list []string) (bool, int) {
-	for i, b := range list {
-		if b == a {
-			return true, i
-		}
-	}
-	return false, 0
-}
diff --git a/utils/variables/variables_test.go b/utils/variables/variables_test.go
deleted file mode 100644
index ec76d459..00000000
--- a/utils/variables/variables_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package variables
-
-import "testing"
-
-type testDefinedData struct {
-	f1 string
-	f2 bool
-	f3 int
-	f4 func()
-}
-
-type testDefined struct {
-	data   interface{}
-	field  string
-	result bool
-}
-
-var testDefinedCases = []testDefined{
-	{testDefinedData{}, "f1", true},
-	{testDefinedData{}, "f2", true},
-	{testDefinedData{}, "f3", true},
-	{testDefinedData{}, "f4", true},
-	{testDefinedData{}, "f5", false},
-	{[]string{}, "", false},
-	{map[string]int{"oi": 4}, "", false},
-	{"asa", "", false},
-	{"int", "", false},
-}
-
-func TestDefined(t *testing.T) {
-	for _, pair := range testDefinedCases {
-		v := Defined(pair.data, pair.field)
-		if v != pair.result {
-			t.Error(
-				"For", pair.data,
-				"expected", pair.result,
-				"got", v,
-			)
-		}
-	}
-}
diff --git a/wrapper/response_writer.go b/wrapper/response_writer.go
deleted file mode 100644
index 0af79376..00000000
--- a/wrapper/response_writer.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package wrapper
-
-import "net/http"
-
-// ResponseWriterNoBody is a wrapper used to suprress the body of the response
-// to a request. Mainly used for HEAD requests.
-type ResponseWriterNoBody struct {
-	http.ResponseWriter
-}
-
-// NewResponseWriterNoBody creates a new ResponseWriterNoBody.
-func NewResponseWriterNoBody(w http.ResponseWriter) *ResponseWriterNoBody {
-	return &ResponseWriterNoBody{w}
-}
-
-// Header executes the Header method from the http.ResponseWriter.
-func (w ResponseWriterNoBody) Header() http.Header {
-	return w.ResponseWriter.Header()
-}
-
-// Write suprresses the body.
-func (w ResponseWriterNoBody) Write(data []byte) (int, error) {
-	return 0, nil
-}
-
-// WriteHeader writes the header to the http.ResponseWriter.
-func (w ResponseWriterNoBody) WriteHeader(statusCode int) {
-	w.ResponseWriter.WriteHeader(statusCode)
-}