You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2019/12/18 00:58:25 UTC

[sling-org-apache-sling-app-cms] branch SLING-8919-fix-error-dialog updated: SLING-8919 - Major changes to make the use of JS requests more consistent

This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch SLING-8919-fix-error-dialog
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git


The following commit(s) were added to refs/heads/SLING-8919-fix-error-dialog by this push:
     new 4332bab  SLING-8919 - Major changes to make the use of JS requests more consistent
4332bab is described below

commit 4332bab73bfbaa2b2169081d19584296848179fc
Author: Dan Klco <dk...@apache.org>
AuthorDate: Tue Dec 17 16:57:51 2019 -0800

    SLING-8919 - Major changes to make the use of JS requests more consistent
---
 ui/.eslintrc.js                           |   21 +
 ui/gulpfile.js                            |  285 ++++---
 ui/package-lock.json                      | 1215 ++++++++++++++++++++++++++++-
 ui/package.json                           |   11 +-
 ui/pom.xml                                |  390 ++++-----
 ui/src/main/frontend/js/cms.draggable.js  |  129 ++-
 ui/src/main/frontend/js/cms.fields.js     |  272 ++++---
 ui/src/main/frontend/js/cms.form.js       |  289 ++++---
 ui/src/main/frontend/js/cms.job.js        |   48 +-
 ui/src/main/frontend/js/cms.js            |  339 ++++----
 ui/src/main/frontend/js/cms.labelfield.js |  126 ++-
 ui/src/main/frontend/js/cms.modal.js      |  192 ++---
 ui/src/main/frontend/js/cms.nav.js        |  194 +++--
 ui/src/main/frontend/js/cms.page.js       |  119 ++-
 ui/src/main/frontend/js/cms.pathfield.js  |  115 ++-
 ui/src/main/frontend/js/cms.toggle.js     |   63 +-
 ui/src/main/frontend/js/editor.js         |  573 +++++++-------
 17 files changed, 2735 insertions(+), 1646 deletions(-)

diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
new file mode 100644
index 0000000..300ca83
--- /dev/null
+++ b/ui/.eslintrc.js
@@ -0,0 +1,21 @@
+module.exports = {
+  env: {
+    browser: true,
+    es6: true,
+  },
+  extends: [
+    'airbnb-base',
+  ],
+  globals: {
+    Atomics: 'readonly',
+    Handlebars: 'writable',
+    rava: 'writable',
+    SharedArrayBuffer: 'readonly',
+    Sling: 'writable',
+  },
+  parserOptions: {
+    ecmaVersion: 2018,
+  },
+  rules: {
+  },
+};
diff --git a/ui/gulpfile.js b/ui/gulpfile.js
index facd7a6..eb9de2f 100755
--- a/ui/gulpfile.js
+++ b/ui/gulpfile.js
@@ -14,164 +14,141 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-var gulp = require('gulp');
-var sass = require('gulp-sass');
-var header = require('gulp-header');
-var cleanCSS = require('gulp-clean-css');
-var concat = require('gulp-concat');
-var rename = require('gulp-rename');
-var terser = require('gulp-terser');
-var streamqueue = require('streamqueue');
-var log = require('fancy-log');
+const gulp = require('gulp');
+const sass = require('gulp-sass');
+const header = require('gulp-header');
+const cleanCSS = require('gulp-clean-css');
+const concat = require('gulp-concat');
+const rename = require('gulp-rename');
+const terser = require('gulp-terser');
+const noop = require('gulp-noop');
+const streamqueue = require('streamqueue');
+
+let prod = false;
 
 const apache2License = [
-'/*',
-' * Licensed to the Apache Software Foundation (ASF) under one or more',
-' * contributor license agreements.  See the NOTICE file distributed with',
-' * this work for additional information regarding copyright ownership.',
-' * The ASF licenses this file to You under the Apache License, Version 2.0',
-' * (the "License"); you may not use this file except in compliance with',
-' * the License.  You may obtain a copy of the License at',
-' *',
-' *      http://www.apache.org/licenses/LICENSE-2.0',
-' *',
-' * Unless required by applicable law or agreed to in writing, software',
-' * distributed under the License is distributed on an "AS IS" BASIS,',
-' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
-' * See the License for the specific language governing permissions and',
-' * limitations under the License.',
-' */',
-''
+  '/*',
+  ' * Licensed to the Apache Software Foundation (ASF) under one or more',
+  ' * contributor license agreements.  See the NOTICE file distributed with',
+  ' * this work for additional information regarding copyright ownership.',
+  ' * The ASF licenses this file to You under the Apache License, Version 2.0',
+  ' * (the "License"); you may not use this file except in compliance with',
+  ' * the License.  You may obtain a copy of the License at',
+  ' *',
+  ' *      http://www.apache.org/licenses/LICENSE-2.0',
+  ' *',
+  ' * Unless required by applicable law or agreed to in writing, software',
+  ' * distributed under the License is distributed on an "AS IS" BASIS,',
+  ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
+  ' * See the License for the specific language governing permissions and',
+  ' * limitations under the License.',
+  ' */',
+  '',
 ].join('\n');
 
-let srcDir = function(sub) {
-    return './src/main/frontend/' + sub;
-};
-let distDir = function(sub) {
-    return './target/frontend/dist/' + sub;
+function srcDir(sub) {
+  return `./src/main/frontend/${sub}`;
+}
+function distDir(sub) {
+  return `./target/frontend/dist/${sub}`;
 }
 
-gulp.task('styles', function() {
-     return streamqueue ({objectMode: true},
-            gulp.src(srcDir('scss/*.scss'))
-                .pipe(header(apache2License))
-                .pipe(sass().on('error', sass.logError))
-                .pipe(cleanCSS()),
-            gulp.src([
-                './node_modules/jam-icons/css/jam.min.css',
-                './node_modules/js-autocomplete/auto-complete.css'
-            ])
-         )
-         .pipe(concat('styles.min.css'))
-         .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/css')))
-         .pipe(rename('bundle.css'))
-         .pipe(gulp.dest(distDir('jcr_root/content/starter/css')));
-});
-
-gulp.task('cms-assets', function() {
-    return gulp.src([srcDir('img/*')])
-        .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/img')));
-});
-
-gulp.task('cms-fonts', function() {
-    return gulp.src(['./node_modules/jam-icons/fonts/*',srcDir('fonts/*')])
-        .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/fonts')));
-});
-
-gulp.task('cms-js', function() {
-    return streamqueue ({objectMode: true},
-            gulp.src([
-                './node_modules/rava/dist/rava.min.js',
-                './node_modules/wysihtml/dist/minified/wysihtml.min.js',
-                './node_modules/wysihtml/dist/minified/wysihtml.all-commands.min.js',
-                './node_modules/wysihtml/dist/minified/wysihtml.table_editing.min.js',
-                './node_modules/wysihtml/dist/minified/wysihtml.toolbar.min.js',
-                './node_modules/handlebars/dist/handlebars.min.js',
-                './node_modules/js-autocomplete/auto-complete.min.js'
-            ]),
-            gulp.src([
-                './node_modules/sorttable/sorttable.js',
-                './node_modules/wysihtml/parser_rules/advanced_and_extended.js'
-            ])
-            .pipe(terser()),
-            gulp.src([
-                srcDir('js/cms.js'),
-                srcDir('js/cms.*.js')
-            ])
-            .pipe(terser())
-            .pipe(concat('cms.js'))
-            .pipe(header(apache2License))
-         )
-        .pipe(concat('scripts-all.min.js'))
-        .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/js')));
-});
-
-gulp.task('editor-fonts', function() {
-    return gulp.src(['./node_modules/jam-icons/fonts/*','./src/fonts/*'])
-        .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/fonts')));
-});
-
-gulp.task('editor-js', function() {
-    return gulp.src([
-            srcDir('js/editor.js')
-        ])
-        .pipe(terser())
-        .pipe(header(apache2License))
-        .pipe(concat('editor.min.js'))
-        .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/js')));
-});
-
-gulp.task('editor-styles', function() {
-     return streamqueue ({objectMode: true},
-            gulp.src([srcDir('scss/editor.scss')])
-                .pipe(sass().on('error', sass.logError))
-                .pipe(cleanCSS())
-                .pipe(header(apache2License)),
-            gulp.src([
-                './node_modules/jam-icons/css/jam.min.css'
-            ])
-         )
-         .pipe(concat('editor.min.css'))
-         .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/css')));
-});
-
-gulp.task('cms-styles', function() {
-     return streamqueue ({objectMode: true},
-            gulp.src(srcDir('scss/cms.scss'))
-                .pipe(sass().on('error', sass.logError))
-                .pipe(cleanCSS())
-                .pipe(header(apache2License)),
-            gulp.src([
-                './node_modules/jam-icons/css/jam.min.css',
-                './node_modules/js-autocomplete/auto-complete.css'
-            ])
-         )
-         .pipe(concat('styles.min.css'))
-         .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/css')));
-});
-
-gulp.task('starter-assets', function() {
-    return gulp.src(srcDir('img/*'))
-        .pipe(gulp.dest(distDir('jcr_root/content/starter/img')));
-});
-
-gulp.task('starter-fonts', function() {
-    return gulp.src([srcDir('fonts/*')])
-        .pipe(gulp.dest(distDir('jcr_root/content/starter/fonts')));
-});
-
-gulp.task('starter-logo', function() {
-    return gulp.src(srcDir('img/sling-logo.svg'))
-        .pipe(gulp.dest(distDir('jcr_root/content/starter')));
-});
-
-gulp.task('starter-styles', function() {
-    return gulp.src(srcDir('scss/starter.scss'))
-        .pipe(sass().on('error', sass.logError))
-        .pipe(cleanCSS())
-        .pipe(header(apache2License))
-        .pipe(rename('bundle.css'))
-        .pipe(gulp.dest(distDir('jcr_root/content/starter/css')));
+gulp.task('styles', () => streamqueue({ objectMode: true },
+  gulp.src(srcDir('scss/*.scss'))
+    .pipe(header(apache2License))
+    .pipe(sass().on('error', sass.logError))
+    .pipe(prod ? cleanCSS() : noop()),
+  gulp.src([
+    './node_modules/jam-icons/css/jam.min.css',
+    './node_modules/js-autocomplete/auto-complete.css',
+  ]))
+  .pipe(concat('styles.min.css'))
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/css')))
+  .pipe(rename('bundle.css'))
+  .pipe(gulp.dest(distDir('jcr_root/content/starter/css'))));
+
+gulp.task('cms-assets', () => gulp.src([srcDir('img/*')])
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/img'))));
+
+gulp.task('cms-fonts', () => gulp.src(['./node_modules/jam-icons/fonts/*', srcDir('fonts/*')])
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/fonts'))));
+
+gulp.task('cms-js', () => streamqueue({ objectMode: true },
+  gulp.src([
+    './node_modules/rava/dist/rava.min.js',
+    './node_modules/wysihtml/dist/minified/wysihtml.min.js',
+    './node_modules/wysihtml/dist/minified/wysihtml.all-commands.min.js',
+    './node_modules/wysihtml/dist/minified/wysihtml.table_editing.min.js',
+    './node_modules/wysihtml/dist/minified/wysihtml.toolbar.min.js',
+    './node_modules/handlebars/dist/handlebars.min.js',
+    './node_modules/js-autocomplete/auto-complete.min.js',
+  ]),
+  gulp.src([
+    './node_modules/sorttable/sorttable.js',
+    './node_modules/wysihtml/parser_rules/advanced_and_extended.js',
+  ]).pipe(prod ? terser() : noop()),
+  gulp.src([
+    srcDir('js/cms.js'),
+    srcDir('js/cms.*.js'),
+  ])
+    .pipe(prod ? terser() : noop())
+    .pipe(concat('cms.js'))
+    .pipe(header(apache2License)))
+  .pipe(concat('scripts-all.min.js'))
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/js'))));
+
+gulp.task('editor-fonts', () => gulp.src(['./node_modules/jam-icons/fonts/*', './src/fonts/*'])
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/fonts'))));
+
+gulp.task('editor-js', () => gulp.src([
+  srcDir('js/editor.js'),
+])
+  .pipe(terser())
+  .pipe(header(apache2License))
+  .pipe(concat('editor.min.js'))
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/js'))));
+
+gulp.task('editor-styles', () => streamqueue({ objectMode: true },
+  gulp.src([srcDir('scss/editor.scss')])
+    .pipe(sass().on('error', sass.logError))
+    .pipe(prod ? cleanCSS() : noop())
+    .pipe(header(apache2License)),
+  gulp.src([
+    './node_modules/jam-icons/css/jam.min.css',
+  ]))
+  .pipe(concat('editor.min.css'))
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms-editor/css'))));
+
+gulp.task('cms-styles', () => streamqueue({ objectMode: true },
+  gulp.src(srcDir('scss/cms.scss'))
+    .pipe(sass().on('error', sass.logError))
+    .pipe(prod ? cleanCSS() : noop())
+    .pipe(header(apache2License)),
+  gulp.src([
+    './node_modules/jam-icons/css/jam.min.css',
+    './node_modules/js-autocomplete/auto-complete.css',
+  ]))
+  .pipe(concat('styles.min.css'))
+  .pipe(gulp.dest(distDir('jcr_root/static/clientlibs/sling-cms/css'))));
+
+gulp.task('starter-assets', () => gulp.src(srcDir('img/*'))
+  .pipe(gulp.dest(distDir('jcr_root/content/starter/img'))));
+
+gulp.task('starter-fonts', () => gulp.src([srcDir('fonts/*')])
+  .pipe(gulp.dest(distDir('jcr_root/content/starter/fonts'))));
+
+gulp.task('starter-logo', () => gulp.src(srcDir('img/sling-logo.svg'))
+  .pipe(gulp.dest(distDir('jcr_root/content/starter'))));
+
+gulp.task('starter-styles', () => gulp.src(srcDir('scss/starter.scss'))
+  .pipe(sass().on('error', sass.logError))
+  .pipe(cleanCSS())
+  .pipe(header(apache2License))
+  .pipe(rename('bundle.css'))
+  .pipe(gulp.dest(distDir('jcr_root/content/starter/css'))));
+
+gulp.task('set-prod', () => {
+  prod = true;
 });
 
 gulp.task('cms', gulp.series('cms-styles', 'cms-js', 'cms-assets', 'cms-fonts'));
@@ -180,4 +157,6 @@ gulp.task('editor', gulp.series('editor-styles', 'editor-js', 'editor-fonts'));
 
 gulp.task('starter', gulp.series('starter-styles', 'starter-assets', 'starter-fonts', 'starter-logo'));
 
-gulp.task('default', gulp.series('starter', 'cms', 'editor'));
\ No newline at end of file
+gulp.task('dev', gulp.series('starter', 'cms', 'editor'));
+
+gulp.task('prod', gulp.series('set-prod', 'dev'));
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 50ab8f6..f6893c3 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -4,12 +4,44 @@
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
+        "@babel/code-frame": {
+            "version": "7.5.5",
+            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
+            "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
+            "dev": true,
+            "requires": {
+                "@babel/highlight": "^7.0.0"
+            }
+        },
+        "@babel/highlight": {
+            "version": "7.5.0",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz",
+            "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==",
+            "dev": true,
+            "requires": {
+                "chalk": "^2.0.0",
+                "esutils": "^2.0.2",
+                "js-tokens": "^4.0.0"
+            }
+        },
         "abbrev": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
             "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
             "dev": true
         },
+        "acorn": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
+            "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
+            "dev": true
+        },
+        "acorn-jsx": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
+            "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
+            "dev": true
+        },
         "ajv": {
             "version": "6.10.2",
             "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
@@ -37,6 +69,15 @@
                 "ansi-wrap": "^0.1.0"
             }
         },
+        "ansi-escapes": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+            "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^0.8.1"
+            }
+        },
         "ansi-gray": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
@@ -108,6 +149,15 @@
                 "readable-stream": "^2.0.6"
             }
         },
+        "argparse": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+            "dev": true,
+            "requires": {
+                "sprintf-js": "~1.0.2"
+            }
+        },
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -156,6 +206,16 @@
             "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
             "dev": true
         },
+        "array-includes": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.0.tgz",
+            "integrity": "sha512-ONOEQoKrvXPKk7Su92Co0YMqYO32FfqJTzkKU9u2UpIXyYZIzLSvpdg4AwvSw4mSUW0czu6inK+zby6Oj6gDjQ==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.0"
+            }
+        },
         "array-initial": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
@@ -222,6 +282,16 @@
             "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
             "dev": true
         },
+        "array.prototype.flat": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
+            "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1"
+            }
+        },
         "asn1": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -243,6 +313,12 @@
             "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
             "dev": true
         },
+        "astral-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+            "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+            "dev": true
+        },
         "async-done": {
             "version": "1.3.2",
             "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
@@ -454,9 +530,9 @@
             "dev": true
         },
         "bulma": {
-            "version": "0.7.5",
-            "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz",
-            "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw=="
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.8.0.tgz",
+            "integrity": "sha512-nhf3rGyiZh/VM7FrSJ/5KeLlfaFkXz0nYcXriynfPH4vVpnxnqyEwaNGdNCVzHyyCA3cHgkQAMpdF/SFbFGZfA=="
         },
         "cache-base": {
             "version": "1.0.1",
@@ -475,6 +551,12 @@
                 "unset-value": "^1.0.0"
             }
         },
+        "callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "dev": true
+        },
         "camelcase": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
@@ -516,6 +598,12 @@
                 "supports-color": "^5.3.0"
             }
         },
+        "chardet": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+            "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+            "dev": true
+        },
         "chokidar": {
             "version": "2.1.8",
             "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
@@ -576,6 +664,21 @@
                 "source-map": "~0.6.0"
             }
         },
+        "cli-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+            "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+            "dev": true,
+            "requires": {
+                "restore-cursor": "^3.1.0"
+            }
+        },
+        "cli-width": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+            "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+            "dev": true
+        },
         "cliui": {
             "version": "3.2.0",
             "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -676,7 +779,8 @@
         "commander": {
             "version": "2.20.0",
             "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
-            "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ=="
+            "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+            "dev": true
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -711,12 +815,24 @@
                 "source-map": "^0.6.1"
             }
         },
+        "confusing-browser-globals": {
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
+            "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
+            "dev": true
+        },
         "console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
             "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
             "dev": true
         },
+        "contains-path": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+            "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+            "dev": true
+        },
         "convert-source-map": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
@@ -745,8 +861,7 @@
         "core-util-is": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-            "dev": true
+            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
         },
         "cross-spawn": {
             "version": "3.0.1",
@@ -807,6 +922,12 @@
             "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
             "dev": true
         },
+        "deep-is": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+            "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+            "dev": true
+        },
         "default-compare": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
@@ -898,6 +1019,15 @@
             "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
             "dev": true
         },
+        "doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "requires": {
+                "esutils": "^2.0.2"
+            }
+        },
         "duplexify": {
             "version": "3.7.1",
             "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -930,6 +1060,12 @@
                 "safer-buffer": "^2.1.0"
             }
         },
+        "emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+            "dev": true
+        },
         "end-of-stream": {
             "version": "1.4.4",
             "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -948,6 +1084,44 @@
                 "is-arrayish": "^0.2.1"
             }
         },
+        "es-abstract": {
+            "version": "1.17.0-next.1",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0-next.1.tgz",
+            "integrity": "sha512-7MmGr03N7Rnuid6+wyhD9sHNE2n4tFSwExnU2lQl3lIo2ShXWGePY80zYaoMOmILWv57H0amMjZGHNzzGG70Rw==",
+            "dev": true,
+            "requires": {
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.1.4",
+                "is-regex": "^1.0.4",
+                "object-inspect": "^1.7.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.0",
+                "string.prototype.trimleft": "^2.1.0",
+                "string.prototype.trimright": "^2.1.0"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+                    "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+                    "dev": true
+                }
+            }
+        },
+        "es-to-primitive": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+            "dev": true,
+            "requires": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            }
+        },
         "es5-ext": {
             "version": "0.10.51",
             "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz",
@@ -998,6 +1172,311 @@
             "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
             "dev": true
         },
+        "eslint": {
+            "version": "6.7.2",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.2.tgz",
+            "integrity": "sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==",
+            "dev": true,
+            "requires": {
+                "@babel/code-frame": "^7.0.0",
+                "ajv": "^6.10.0",
+                "chalk": "^2.1.0",
+                "cross-spawn": "^6.0.5",
+                "debug": "^4.0.1",
+                "doctrine": "^3.0.0",
+                "eslint-scope": "^5.0.0",
+                "eslint-utils": "^1.4.3",
+                "eslint-visitor-keys": "^1.1.0",
+                "espree": "^6.1.2",
+                "esquery": "^1.0.1",
+                "esutils": "^2.0.2",
+                "file-entry-cache": "^5.0.1",
+                "functional-red-black-tree": "^1.0.1",
+                "glob-parent": "^5.0.0",
+                "globals": "^12.1.0",
+                "ignore": "^4.0.6",
+                "import-fresh": "^3.0.0",
+                "imurmurhash": "^0.1.4",
+                "inquirer": "^7.0.0",
+                "is-glob": "^4.0.0",
+                "js-yaml": "^3.13.1",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.3.0",
+                "lodash": "^4.17.14",
+                "minimatch": "^3.0.4",
+                "mkdirp": "^0.5.1",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.8.3",
+                "progress": "^2.0.0",
+                "regexpp": "^2.0.1",
+                "semver": "^6.1.2",
+                "strip-ansi": "^5.2.0",
+                "strip-json-comments": "^3.0.1",
+                "table": "^5.2.3",
+                "text-table": "^0.2.0",
+                "v8-compile-cache": "^2.0.3"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "6.0.5",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+                    "dev": true,
+                    "requires": {
+                        "nice-try": "^1.0.4",
+                        "path-key": "^2.0.1",
+                        "semver": "^5.5.0",
+                        "shebang-command": "^1.2.0",
+                        "which": "^1.2.9"
+                    },
+                    "dependencies": {
+                        "semver": {
+                            "version": "5.7.1",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "debug": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                },
+                "glob-parent": {
+                    "version": "5.1.0",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+                    "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.1"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                }
+            }
+        },
+        "eslint-config-airbnb-base": {
+            "version": "14.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz",
+            "integrity": "sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA==",
+            "dev": true,
+            "requires": {
+                "confusing-browser-globals": "^1.0.7",
+                "object.assign": "^4.1.0",
+                "object.entries": "^1.1.0"
+            }
+        },
+        "eslint-import-resolver-node": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+            "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+            "dev": true,
+            "requires": {
+                "debug": "^2.6.9",
+                "resolve": "^1.5.0"
+            }
+        },
+        "eslint-module-utils": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.0.tgz",
+            "integrity": "sha512-kCo8pZaNz2dsAW7nCUjuVoI11EBXXpIzfNxmaoLhXoRDOnqXLC4iSGVRdZPhOitfbdEfMEfKOiENaK6wDPZEGw==",
+            "dev": true,
+            "requires": {
+                "debug": "^2.6.9",
+                "pkg-dir": "^2.0.0"
+            }
+        },
+        "eslint-plugin-import": {
+            "version": "2.19.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.19.1.tgz",
+            "integrity": "sha512-x68131aKoCZlCae7rDXKSAQmbT5DQuManyXo2sK6fJJ0aK5CWAkv6A6HJZGgqC8IhjQxYPgo6/IY4Oz8AFsbBw==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.0.3",
+                "array.prototype.flat": "^1.2.1",
+                "contains-path": "^0.1.0",
+                "debug": "^2.6.9",
+                "doctrine": "1.5.0",
+                "eslint-import-resolver-node": "^0.3.2",
+                "eslint-module-utils": "^2.4.1",
+                "has": "^1.0.3",
+                "minimatch": "^3.0.4",
+                "object.values": "^1.1.0",
+                "read-pkg-up": "^2.0.0",
+                "resolve": "^1.12.0"
+            },
+            "dependencies": {
+                "doctrine": {
+                    "version": "1.5.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+                    "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2",
+                        "isarray": "^1.0.0"
+                    }
+                },
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+                    "dev": true,
+                    "requires": {
+                        "locate-path": "^2.0.0"
+                    }
+                },
+                "load-json-file": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+                    "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+                    "dev": true,
+                    "requires": {
+                        "graceful-fs": "^4.1.2",
+                        "parse-json": "^2.2.0",
+                        "pify": "^2.0.0",
+                        "strip-bom": "^3.0.0"
+                    }
+                },
+                "path-type": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+                    "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+                    "dev": true,
+                    "requires": {
+                        "pify": "^2.0.0"
+                    }
+                },
+                "read-pkg": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+                    "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+                    "dev": true,
+                    "requires": {
+                        "load-json-file": "^2.0.0",
+                        "normalize-package-data": "^2.3.2",
+                        "path-type": "^2.0.0"
+                    }
+                },
+                "read-pkg-up": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+                    "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+                    "dev": true,
+                    "requires": {
+                        "find-up": "^2.0.0",
+                        "read-pkg": "^2.0.0"
+                    }
+                },
+                "strip-bom": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+                    "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-scope": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+            "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+            "dev": true,
+            "requires": {
+                "esrecurse": "^4.1.0",
+                "estraverse": "^4.1.1"
+            }
+        },
+        "eslint-utils": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+            "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+            "dev": true,
+            "requires": {
+                "eslint-visitor-keys": "^1.1.0"
+            }
+        },
+        "eslint-visitor-keys": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+            "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+            "dev": true
+        },
+        "espree": {
+            "version": "6.1.2",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
+            "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
+            "dev": true,
+            "requires": {
+                "acorn": "^7.1.0",
+                "acorn-jsx": "^5.1.0",
+                "eslint-visitor-keys": "^1.1.0"
+            }
+        },
+        "esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+            "dev": true
+        },
+        "esquery": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+            "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^4.0.0"
+            }
+        },
+        "esrecurse": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+            "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^4.1.0"
+            }
+        },
+        "estraverse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+            "dev": true
+        },
+        "esutils": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+            "dev": true
+        },
         "expand-brackets": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
@@ -1069,6 +1548,17 @@
                 }
             }
         },
+        "external-editor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+            "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+            "dev": true,
+            "requires": {
+                "chardet": "^0.7.0",
+                "iconv-lite": "^0.4.24",
+                "tmp": "^0.0.33"
+            }
+        },
         "extglob": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -1164,6 +1654,30 @@
             "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
             "dev": true
         },
+        "fast-levenshtein": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+            "dev": true
+        },
+        "figures": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
+            "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
+            "dev": true,
+            "requires": {
+                "escape-string-regexp": "^1.0.5"
+            }
+        },
+        "file-entry-cache": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+            "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+            "dev": true,
+            "requires": {
+                "flat-cache": "^2.0.1"
+            }
+        },
         "fill-range": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -1228,6 +1742,34 @@
             "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
             "dev": true
         },
+        "flat-cache": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+            "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+            "dev": true,
+            "requires": {
+                "flatted": "^2.0.0",
+                "rimraf": "2.6.3",
+                "write": "1.0.3"
+            },
+            "dependencies": {
+                "rimraf": {
+                    "version": "2.6.3",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+                    "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                }
+            }
+        },
+        "flatted": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+            "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+            "dev": true
+        },
         "flush-write-stream": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
@@ -1861,6 +2403,12 @@
             "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
             "dev": true
         },
+        "functional-red-black-tree": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+            "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+            "dev": true
+        },
         "gauge": {
             "version": "2.7.4",
             "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -2004,6 +2552,15 @@
                 "which": "^1.2.14"
             }
         },
+        "globals": {
+            "version": "12.3.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
+            "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^0.8.1"
+            }
+        },
         "globule": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
@@ -2114,10 +2671,18 @@
                 "through2": "^2.0.0"
             }
         },
+        "gulp-noop": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/gulp-noop/-/gulp-noop-1.0.0.tgz",
+            "integrity": "sha1-CCsRQkk1Es5cWrLh7D57tItXXHk=",
+            "requires": {
+                "through2": "^2.0.1"
+            }
+        },
         "gulp-rename": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz",
-            "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz",
+            "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==",
             "dev": true
         },
         "gulp-sass": {
@@ -2186,9 +2751,9 @@
             }
         },
         "handlebars": {
-            "version": "4.4.3",
-            "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.3.tgz",
-            "integrity": "sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw==",
+            "version": "4.5.3",
+            "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
+            "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
             "requires": {
                 "neo-async": "^2.6.0",
                 "optimist": "^0.6.1",
@@ -2212,6 +2777,15 @@
                 "har-schema": "^2.0.0"
             }
         },
+        "has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dev": true,
+            "requires": {
+                "function-bind": "^1.1.1"
+            }
+        },
         "has-ansi": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
@@ -2297,6 +2871,37 @@
                 "sshpk": "^1.7.0"
             }
         },
+        "iconv-lite": {
+            "version": "0.4.24",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+            "dev": true,
+            "requires": {
+                "safer-buffer": ">= 2.1.2 < 3"
+            }
+        },
+        "ignore": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+            "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+            "dev": true
+        },
+        "import-fresh": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+            "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+            "dev": true,
+            "requires": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            }
+        },
+        "imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+            "dev": true
+        },
         "in-publish": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
@@ -2322,18 +2927,91 @@
                 "wrappy": "1"
             }
         },
-        "inherits": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-            "dev": true
-        },
-        "ini": {
-            "version": "1.3.5",
-            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
-            "dev": true
-        },
+        "inherits": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "ini": {
+            "version": "1.3.5",
+            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+            "dev": true
+        },
+        "inquirer": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.1.tgz",
+            "integrity": "sha512-V1FFQ3TIO15det8PijPLFR9M9baSlnRs9nL7zWu1MNVA2T9YVl9ZbrHJhYs7e9X8jeMZ3lr2JH/rdHFgNCBdYw==",
+            "dev": true,
+            "requires": {
+                "ansi-escapes": "^4.2.1",
+                "chalk": "^2.4.2",
+                "cli-cursor": "^3.1.0",
+                "cli-width": "^2.0.0",
+                "external-editor": "^3.0.3",
+                "figures": "^3.0.0",
+                "lodash": "^4.17.15",
+                "mute-stream": "0.0.8",
+                "run-async": "^2.2.0",
+                "rxjs": "^6.5.3",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^5.1.0",
+                "through": "^2.3.6"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+                    "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+                    "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.0"
+                    },
+                    "dependencies": {
+                        "strip-ansi": {
+                            "version": "6.0.0",
+                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+                            "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+                            "dev": true,
+                            "requires": {
+                                "ansi-regex": "^5.0.0"
+                            }
+                        }
+                    }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    },
+                    "dependencies": {
+                        "ansi-regex": {
+                            "version": "4.1.0",
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                            "dev": true
+                        }
+                    }
+                }
+            }
+        },
         "interpret": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
@@ -2397,6 +3075,12 @@
             "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
             "dev": true
         },
+        "is-callable": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+            "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
+            "dev": true
+        },
         "is-data-descriptor": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@@ -2417,6 +3101,12 @@
                 }
             }
         },
+        "is-date-object": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+            "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+            "dev": true
+        },
         "is-descriptor": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@@ -2510,6 +3200,21 @@
                 "isobject": "^3.0.1"
             }
         },
+        "is-promise": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+            "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+            "dev": true
+        },
+        "is-regex": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+            "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
         "is-relative": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
@@ -2519,6 +3224,23 @@
                 "is-unc-path": "^1.0.0"
             }
         },
+        "is-symbol": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+            "dev": true,
+            "requires": {
+                "has-symbols": "^1.0.1"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+                    "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+                    "dev": true
+                }
+            }
+        },
         "is-typedarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -2555,8 +3277,7 @@
         "isarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-            "dev": true
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
         },
         "isexe": {
             "version": "2.0.0",
@@ -2592,6 +3313,22 @@
             "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
             "dev": true
         },
+        "js-tokens": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+            "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+            "dev": true
+        },
+        "js-yaml": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+            "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+            "dev": true,
+            "requires": {
+                "argparse": "^1.0.7",
+                "esprima": "^4.0.0"
+            }
+        },
         "jsbn": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
@@ -2683,6 +3420,16 @@
                 "flush-write-stream": "^1.0.2"
             }
         },
+        "levn": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+            "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+            "dev": true,
+            "requires": {
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2"
+            }
+        },
         "liftoff": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
@@ -2712,6 +3459,24 @@
                 "strip-bom": "^2.0.0"
             }
         },
+        "locate-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+            "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+            "dev": true,
+            "requires": {
+                "p-locate": "^2.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "dependencies": {
+                "path-exists": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+                    "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+                    "dev": true
+                }
+            }
+        },
         "lodash": {
             "version": "4.17.15",
             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
@@ -2902,6 +3667,12 @@
                 "mime-db": "1.40.0"
             }
         },
+        "mimic-fn": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+            "dev": true
+        },
         "minimatch": {
             "version": "3.0.4",
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -2966,6 +3737,12 @@
             "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
             "dev": true
         },
+        "mute-stream": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+            "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+            "dev": true
+        },
         "nan": {
             "version": "2.14.0",
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
@@ -2991,6 +3768,12 @@
                 "to-regex": "^3.0.1"
             }
         },
+        "natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+            "dev": true
+        },
         "neo-async": {
             "version": "2.6.1",
             "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
@@ -3002,6 +3785,12 @@
             "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
             "dev": true
         },
+        "nice-try": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+            "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+            "dev": true
+        },
         "node-gyp": {
             "version": "3.8.0",
             "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
@@ -3182,6 +3971,12 @@
                 }
             }
         },
+        "object-inspect": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+            "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+            "dev": true
+        },
         "object-keys": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -3221,6 +4016,18 @@
                 "isobject": "^3.0.0"
             }
         },
+        "object.entries": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz",
+            "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3"
+            }
+        },
         "object.map": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
@@ -3250,6 +4057,18 @@
                 "make-iterator": "^1.0.0"
             }
         },
+        "object.values": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+            "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3"
+            }
+        },
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3259,6 +4078,15 @@
                 "wrappy": "1"
             }
         },
+        "onetime": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+            "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+            "dev": true,
+            "requires": {
+                "mimic-fn": "^2.1.0"
+            }
+        },
         "optimist": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@@ -3268,6 +4096,20 @@
                 "wordwrap": "~0.0.2"
             }
         },
+        "optionator": {
+            "version": "0.8.3",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+            "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+            "dev": true,
+            "requires": {
+                "deep-is": "~0.1.3",
+                "fast-levenshtein": "~2.0.6",
+                "levn": "~0.3.0",
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2",
+                "word-wrap": "~1.2.3"
+            }
+        },
         "ordered-read-streams": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
@@ -3308,6 +4150,39 @@
                 "os-tmpdir": "^1.0.0"
             }
         },
+        "p-limit": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+            "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+            "dev": true,
+            "requires": {
+                "p-try": "^1.0.0"
+            }
+        },
+        "p-locate": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+            "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+            "dev": true,
+            "requires": {
+                "p-limit": "^1.1.0"
+            }
+        },
+        "p-try": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+            "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+            "dev": true
+        },
+        "parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "dev": true,
+            "requires": {
+                "callsites": "^3.0.0"
+            }
+        },
         "parse-filepath": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
@@ -3367,6 +4242,12 @@
             "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
             "dev": true
         },
+        "path-key": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+            "dev": true
+        },
         "path-parse": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
@@ -3426,6 +4307,26 @@
                 "pinkie": "^2.0.0"
             }
         },
+        "pkg-dir": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+            "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+            "dev": true,
+            "requires": {
+                "find-up": "^2.1.0"
+            },
+            "dependencies": {
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+                    "dev": true,
+                    "requires": {
+                        "locate-path": "^2.0.0"
+                    }
+                }
+            }
+        },
         "plugin-error": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
@@ -3444,6 +4345,12 @@
             "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
             "dev": true
         },
+        "prelude-ls": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+            "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+            "dev": true
+        },
         "pretty-hrtime": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
@@ -3453,7 +4360,12 @@
         "process-nextick-args": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+        },
+        "progress": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+            "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
             "dev": true
         },
         "pseudomap": {
@@ -3531,7 +4443,6 @@
             "version": "2.3.6",
             "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-            "dev": true,
             "requires": {
                 "core-util-is": "~1.0.0",
                 "inherits": "~2.0.3",
@@ -3582,6 +4493,12 @@
                 "safe-regex": "^1.1.0"
             }
         },
+        "regexpp": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+            "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+            "dev": true
+        },
         "remove-bom-buffer": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
@@ -3706,6 +4623,12 @@
                 "global-modules": "^1.0.0"
             }
         },
+        "resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "dev": true
+        },
         "resolve-options": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
@@ -3721,6 +4644,16 @@
             "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
             "dev": true
         },
+        "restore-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+            "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+            "dev": true,
+            "requires": {
+                "onetime": "^5.1.0",
+                "signal-exit": "^3.0.2"
+            }
+        },
         "ret": {
             "version": "0.1.15",
             "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -3736,11 +4669,28 @@
                 "glob": "^7.1.3"
             }
         },
+        "run-async": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+            "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+            "dev": true,
+            "requires": {
+                "is-promise": "^2.1.0"
+            }
+        },
+        "rxjs": {
+            "version": "6.5.3",
+            "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz",
+            "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==",
+            "dev": true,
+            "requires": {
+                "tslib": "^1.9.0"
+            }
+        },
         "safe-buffer": {
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-            "dev": true
+            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
         },
         "safe-regex": {
             "version": "1.1.0",
@@ -3834,12 +4784,46 @@
                 }
             }
         },
+        "shebang-command": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+            "dev": true,
+            "requires": {
+                "shebang-regex": "^1.0.0"
+            }
+        },
+        "shebang-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+            "dev": true
+        },
         "signal-exit": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
             "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
             "dev": true
         },
+        "slice-ansi": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+            "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^3.2.0",
+                "astral-regex": "^1.0.0",
+                "is-fullwidth-code-point": "^2.0.0"
+            },
+            "dependencies": {
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                }
+            }
+        },
         "snapdragon": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -4039,6 +5023,12 @@
                 "extend-shallow": "^3.0.0"
             }
         },
+        "sprintf-js": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+            "dev": true
+        },
         "sshpk": {
             "version": "1.16.1",
             "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
@@ -4125,11 +5115,30 @@
                 "strip-ansi": "^3.0.0"
             }
         },
+        "string.prototype.trimleft": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
+            "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "function-bind": "^1.1.1"
+            }
+        },
+        "string.prototype.trimright": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
+            "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "function-bind": "^1.1.1"
+            }
+        },
         "string_decoder": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-            "dev": true,
             "requires": {
                 "safe-buffer": "~5.1.0"
             }
@@ -4161,6 +5170,12 @@
                 "get-stdin": "^4.0.1"
             }
         },
+        "strip-json-comments": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+            "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+            "dev": true
+        },
         "supports-color": {
             "version": "5.5.0",
             "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -4180,6 +5195,58 @@
                 "es6-symbol": "^3.1.1"
             }
         },
+        "table": {
+            "version": "5.4.6",
+            "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+            "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.10.2",
+                "lodash": "^4.17.14",
+                "slice-ansi": "^2.1.0",
+                "string-width": "^3.0.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
+                "emoji-regex": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                }
+            }
+        },
         "tar": {
             "version": "2.2.2",
             "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
@@ -4202,11 +5269,22 @@
                 "source-map-support": "~0.5.12"
             }
         },
+        "text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+            "dev": true
+        },
+        "through": {
+            "version": "2.3.8",
+            "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+            "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+            "dev": true
+        },
         "through2": {
             "version": "2.0.5",
             "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
             "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
-            "dev": true,
             "requires": {
                 "readable-stream": "~2.3.6",
                 "xtend": "~4.0.1"
@@ -4228,6 +5306,15 @@
             "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
             "dev": true
         },
+        "tmp": {
+            "version": "0.0.33",
+            "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+            "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+            "dev": true,
+            "requires": {
+                "os-tmpdir": "~1.0.2"
+            }
+        },
         "to-absolute-glob": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
@@ -4322,6 +5409,12 @@
                 "glob": "^7.1.2"
             }
         },
+        "tslib": {
+            "version": "1.10.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+            "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
+            "dev": true
+        },
         "tunnel-agent": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -4343,6 +5436,21 @@
             "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
             "dev": true
         },
+        "type-check": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+            "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+            "dev": true,
+            "requires": {
+                "prelude-ls": "~1.1.2"
+            }
+        },
+        "type-fest": {
+            "version": "0.8.1",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+            "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+            "dev": true
+        },
         "typedarray": {
             "version": "0.0.6",
             "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -4350,13 +5458,21 @@
             "dev": true
         },
         "uglify-js": {
-            "version": "3.6.2",
-            "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.2.tgz",
-            "integrity": "sha512-+gh/xFte41GPrgSMJ/oJVq15zYmqr74pY9VoM69UzMzq9NFk4YDylclb1/bhEzZSaUQjbW5RvniHeq1cdtRYjw==",
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz",
+            "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==",
             "optional": true,
             "requires": {
-                "commander": "2.20.0",
+                "commander": "~2.20.3",
                 "source-map": "~0.6.1"
+            },
+            "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+                    "optional": true
+                }
             }
         },
         "unc-path-regex": {
@@ -4480,8 +5596,7 @@
         "util-deprecate": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-            "dev": true
+            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
         },
         "uuid": {
             "version": "3.3.3",
@@ -4489,6 +5604,12 @@
             "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
             "dev": true
         },
+        "v8-compile-cache": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+            "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+            "dev": true
+        },
         "v8flags": {
             "version": "3.1.3",
             "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz",
@@ -4620,6 +5741,12 @@
                 "string-width": "^1.0.2 || 2"
             }
         },
+        "word-wrap": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+            "dev": true
+        },
         "wordwrap": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
@@ -4641,6 +5768,15 @@
             "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
             "dev": true
         },
+        "write": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+            "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+            "dev": true,
+            "requires": {
+                "mkdirp": "^0.5.1"
+            }
+        },
         "wysihtml": {
             "version": "0.6.0-beta1",
             "resolved": "https://registry.npmjs.org/wysihtml/-/wysihtml-0.6.0-beta1.tgz",
@@ -4649,8 +5785,7 @@
         "xtend": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-            "dev": true
+            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
         },
         "y18n": {
             "version": "3.2.1",
diff --git a/ui/package.json b/ui/package.json
index f957fd8..ed8d784 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -3,8 +3,9 @@
     "version": "1.0.0",
     "description": "Scripts to power the front-end of Apache Sling's CMS example.",
     "dependencies": {
-        "bulma": "^0.7.5",
-        "handlebars": "^4.1.2",
+        "bulma": "^0.8.0",
+        "gulp-noop": "^1.0.0",
+        "handlebars": "^4.5.3",
         "jam-icons": "^2.0.0",
         "js-autocomplete": "^1.0.4",
         "rava": "^2.1.0",
@@ -12,13 +13,15 @@
         "wysihtml": "^0.6.0-beta1"
     },
     "devDependencies": {
-        "fancy-log": "^1.3.3",
+        "eslint": "^6.7.2",
+        "eslint-config-airbnb-base": "^14.0.0",
+        "eslint-plugin-import": "^2.19.1",
         "gulp": "^4.0.1",
         "gulp-clean-css": "^4.2.0",
         "gulp-cli": "^2.2.0",
         "gulp-concat": "^2.6.1",
         "gulp-header": "^2.0.7",
-        "gulp-rename": "^1.4.0",
+        "gulp-rename": "^2.0.0",
         "gulp-sass": "^4.0.2",
         "gulp-terser": "^1.1.7",
         "streamqueue": "^1.1.2"
diff --git a/ui/pom.xml b/ui/pom.xml
index 10d05f0..bd0b537 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -1,205 +1,209 @@
 <?xml version="1.0" encoding="ISO-8859-1"?>
-<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional
-    information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except
-    in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to
-    in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
-    the License for the specific language governing permissions and limitations under the License. -->
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under
+the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in
+writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the
+License. -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    <modelVersion>4.0.0</modelVersion>
-    <parent>
-        <artifactId>org.apache.sling.cms</artifactId>
-        <groupId>org.apache.sling</groupId>
-        <version>0.14.1-SNAPSHOT</version>
-    </parent>
-    <artifactId>org.apache.sling.cms.ui</artifactId>
-    <packaging>bundle</packaging>
-    <name>Apache Sling - CMS UI</name>
-    <description>A bundle providing the User Interface for the Apache Sling Reference CMS Application</description>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>org.apache.sling.cms</artifactId>
+    <groupId>org.apache.sling</groupId>
+    <version>0.14.1-SNAPSHOT</version>
+  </parent>
+  <artifactId>org.apache.sling.cms.ui</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Sling - CMS UI</name>
+  <description>A bundle providing the User Interface for the Apache Sling Reference CMS Application</description>
 
-    <properties>
-        <frontend.target>target/frontend</frontend.target>
-    </properties>
+  <properties>
+    <frontend.target>target/frontend</frontend.target>
+    <frontend.mode>dev</frontend.mode>
+  </properties>
 
-    <dependencies>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>jstl</groupId>
-            <artifactId>jstl</artifactId>
-            <version>1.2</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.scripting.jsp.taglib</artifactId>
-            <version>2.3.0</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>javax.servlet</groupId>
-            <artifactId>jsp-api</artifactId>
-            <version>2.0</version>
-            <scope>provided</scope>
-        </dependency>
-    </dependencies>
+  <dependencies>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>jstl</groupId>
+      <artifactId>jstl</artifactId>
+      <version>1.2</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.scripting.jsp.taglib</artifactId>
+      <version>2.3.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>jsp-api</artifactId>
+      <version>2.0</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
 
-    <build>
-        <plugins>
+  <build>
+    <plugins>
 
-            <plugin>
-                <groupId>org.apache.rat</groupId>
-                <artifactId>apache-rat-plugin</artifactId>
-                <configuration>
-                    <excludes>
-                        <exclude>**/*.webmanifest</exclude>
-                    </excludes>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>verify</phase>
-                        <goals>
-                            <goal>check</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
-                <artifactId>maven-resources-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>copy-resources</id>
-                        <phase>validate</phase>
-                        <goals>
-                            <goal>copy-resources</goal>
-                        </goals>
-                        <configuration>
-                            <outputDirectory>${basedir}/${frontend.target}</outputDirectory>
-                            <resources>
-                                <resource>
-                                    <directory>src/main/frontend</directory>
-                                </resource>
-                            </resources>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>**/*.webmanifest</exclude>
+          </excludes>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>copy-resources</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${basedir}/${frontend.target}</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>src/main/frontend</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>com.github.eirslett</groupId>
+        <artifactId>frontend-maven-plugin</artifactId>
+        <version>1.7.6</version>
+        <executions>
+          <execution>
+            <id>install node and npm</id>
+            <goals>
+              <goal>install-node-and-npm</goal>
+            </goals>
+            <configuration>
+              <nodeVersion>v11.14.0</nodeVersion>
+              <npmVersion>6.9.0</npmVersion>
+            </configuration>
+          </execution>
+          <execution>
+            <id>npm install</id>
+            <goals>
+              <goal>npm</goal>
+            </goals>
+            <configuration>
+              <arguments>install</arguments>
+            </configuration>
+          </execution>
+          <execution>
+            <id>gulp build</id>
+            <goals>
+              <goal>gulp</goal>
+            </goals>
+            <configuration>
+              <arguments>${frontend.mode}</arguments>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Sling-Nodetypes>SLING-INF/nodetypes/nodetypes.cnd</Sling-Nodetypes>
+            <Sling-Initial-Content>
+              jcr_root;ignoreImportProviders:=xml, jcr_root/oak%3Aindex;overwrite:=false;uninstall:=true;path:=/oak:index, jcr_root/conf/global;overwrite:=false;uninstall:=true;path:=/conf/global,
+              jcr_root/etc/clientlibs;overwrite=true;ignoreImportProviders:=xml;path:=/etc/clientlibs, jcr_root/etc/taxonomy;overwrite:=false;uninstall:=true;path:=/etc/taxonomy,
+              jcr_root/libs/sling/servlet/errorhandler;overwriteProperties:=true;overwrite:=true;uninstall:=true;path:=/libs/sling/servlet/errorhandler, jcr_root/libs/sling-cms;overwrite:=true;uninstall:=true;path:=/libs/sling-cms,
+              jcr_root/static/clientlibs/sling-cms;overwrite:=true;uninstall:=true;path:=/static/clientlibs/sling-cms;ignoreImportProviders:=xml,
+              jcr_root/static/sling-cms/thumbnails;overwrite:=true;uninstall:=true;path:=/static/sling-cms/thumbnails;ignoreImportProviders:=xml
+            </Sling-Initial-Content>
+            <Include-Resource>{maven-resources},${basedir}/target/frontend/dist</Include-Resource>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
 
-            <plugin>
-                <groupId>com.github.eirslett</groupId>
-                <artifactId>frontend-maven-plugin</artifactId>
-                <version>1.7.6</version>
-                <executions>
-                    <execution>
-                        <id>install node and npm</id>
-                        <goals>
-                            <goal>install-node-and-npm</goal>
-                        </goals>
-                        <configuration>
-                            <nodeVersion>v11.14.0</nodeVersion>
-                            <npmVersion>6.9.0</npmVersion>
-                        </configuration>
-                    </execution>
-                    <execution>
-                        <id>npm install</id>
-                        <goals>
-                            <goal>npm</goal>
-                        </goals>
-                        <configuration>
-                            <arguments>install</arguments>
-                        </configuration>
-                    </execution>
-                    <execution>
-                        <id>gulp build</id>
-                        <goals>
-                            <goal>gulp</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>com.github.eirslett</groupId>
+                    <artifactId>frontend-maven-plugin</artifactId>
+                    <versionRange>[1.0.0,)</versionRange>
+                    <goals>
+                      <goal>install-node-and-npm</goal>
+                      <goal>npm</goal>
+                      <goal>gulp</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore/>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
 
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
+  <profiles>
+    <profile>
+      <id>release</id>
+      <properties>
+        <frontend.mode>prod</frontend.mode>
+      </properties>
+    </profile>
+    <profile>
+      <id>autoInstallBundle</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>maven-sling-plugin</artifactId>
+            <executions>
+              <execution>
+                <id>install-bundle</id>
+                <goals>
+                  <goal>install</goal>
+                </goals>
                 <configuration>
-                    <instructions>
-                        <Sling-Nodetypes>SLING-INF/nodetypes/nodetypes.cnd</Sling-Nodetypes>
-                        <Sling-Initial-Content>
-                            jcr_root;ignoreImportProviders:=xml,
-                            jcr_root/oak%3Aindex;overwrite:=false;uninstall:=true;path:=/oak:index,
-                            jcr_root/conf/global;overwrite:=false;uninstall:=true;path:=/conf/global,
-                            jcr_root/etc/clientlibs;overwrite=true;ignoreImportProviders:=xml;path:=/etc/clientlibs,
-                            jcr_root/etc/taxonomy;overwrite:=false;uninstall:=true;path:=/etc/taxonomy,
-                            jcr_root/libs/sling/servlet/errorhandler;overwriteProperties:=true;overwrite:=true;uninstall:=true;path:=/libs/sling/servlet/errorhandler,
-                            jcr_root/libs/sling-cms;overwrite:=true;uninstall:=true;path:=/libs/sling-cms,
-                            jcr_root/static/clientlibs/sling-cms;overwrite:=true;uninstall:=true;path:=/static/clientlibs/sling-cms;ignoreImportProviders:=xml,
-                            jcr_root/static/sling-cms/thumbnails;overwrite:=true;uninstall:=true;path:=/static/sling-cms/thumbnails;ignoreImportProviders:=xml
-                        </Sling-Initial-Content>
-                        <Include-Resource>{maven-resources},${basedir}/target/frontend/dist</Include-Resource>
-                    </instructions>
+                  <slingUrl>${sling.protocol}://${sling.host}:${sling.port}/system/console</slingUrl>
+                  <user>${sling.username}</user>
+                  <password>${sling.password}</password>
                 </configuration>
-            </plugin>
+              </execution>
+            </executions>
+          </plugin>
         </plugins>
-
-        <pluginManagement>
-            <plugins>
-                <plugin>
-                    <groupId>org.eclipse.m2e</groupId>
-                    <artifactId>lifecycle-mapping</artifactId>
-                    <version>1.0.0</version>
-                    <configuration>
-                        <lifecycleMappingMetadata>
-                            <pluginExecutions>
-                                <pluginExecution>
-                                    <pluginExecutionFilter>
-                                        <groupId>com.github.eirslett</groupId>
-                                        <artifactId>frontend-maven-plugin</artifactId>
-                                        <versionRange>[1.0.0,)</versionRange>
-                                        <goals>
-                                            <goal>install-node-and-npm</goal>
-                                            <goal>npm</goal>
-                                            <goal>gulp</goal>
-                                        </goals>
-                                    </pluginExecutionFilter>
-                                    <action>
-                                        <ignore />
-                                    </action>
-                                </pluginExecution>
-                            </pluginExecutions>
-                        </lifecycleMappingMetadata>
-                    </configuration>
-                </plugin>
-            </plugins>
-        </pluginManagement>
-    </build>
-
-
-    <profiles>
-        <profile>
-            <id>autoInstallBundle</id>
-            <build>
-                <plugins>
-                    <plugin>
-                        <groupId>org.apache.sling</groupId>
-                        <artifactId>maven-sling-plugin</artifactId>
-                        <executions>
-                            <execution>
-                                <id>install-bundle</id>
-                                <goals>
-                                    <goal>install</goal>
-                                </goals>
-                                <configuration>
-                                    <slingUrl>${sling.protocol}://${sling.host}:${sling.port}/system/console</slingUrl>
-                                    <user>${sling.username}</user>
-                                    <password>${sling.password}</password>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
-                </plugins>
-            </build>
-        </profile>
-    </profiles>
+      </build>
+    </profile>
+  </profiles>
 </project>
diff --git a/ui/src/main/frontend/js/cms.draggable.js b/ui/src/main/frontend/js/cms.draggable.js
index 91285b9..866d12f 100644
--- a/ui/src/main/frontend/js/cms.draggable.js
+++ b/ui/src/main/frontend/js/cms.draggable.js
@@ -1,67 +1,62 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/* eslint-env browser, es6 */
-(function (rava) {
-    'use strict';
-    var data = {
-        mouseX : 0,
-        mouseY : 0,
-        mouseDown : false,
-        elementX : 0,
-        elementY : 0
-    };
-    rava.bind(".is-draggable", {
-        methods : {
-            moveComplete: function () {
-                data.mouseDown = false;
-                data.elementX = parseInt(this.style.left, 10) || 0;
-                data.elementY = parseInt(this.style.top, 10) || 0;
-                return false;
-            }
-        },
-        events : {
-            ":root" : {
-                mouseup : function(){
-                    if (data.mouseDown === true) {
-                        this.moveComplete();
-                    }
-                },
-                mousemove : function(event){
-                    if (data.mouseDown === false) {
-                        return false;
-                    }
-                    var deltaX = event.clientX - data.mouseX,
-                        deltaY = event.clientY - data.mouseY;
-                    this.style.left = data.elementX + deltaX + 'px';
-                    this.style.top = data.elementY + deltaY + 'px';
-                    return false;
-                }
-            },
-            mousedown: function (event) {
-                if (!event.target.matches('.modal-title, .modal-title *')) {
-                    return;
-                }
-                data.mouseX = event.clientX;
-                data.mouseY = event.clientY;
-                data.mouseDown = true;
-            }
-        }
-    });
-}(window.rava = window.rava || {}));
\ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+const mousepos = {
+  mouseX: 0,
+  mouseY: 0,
+  mouseDown: false,
+  elementX: 0,
+  elementY: 0,
+};
+rava.bind('.is-draggable', {
+  methods: {
+    moveComplete() {
+      mousepos.mouseDown = false;
+      mousepos.elementX = parseInt(this.style.left, 10) || 0;
+      mousepos.elementY = parseInt(this.style.top, 10) || 0;
+      return false;
+    },
+  },
+  events: {
+    ':root': {
+      mouseup() {
+        if (mousepos.mouseDown === true) {
+          this.moveComplete();
+        }
+      },
+      mousemove(event) {
+        if (mousepos.mouseDown === false) {
+          return false;
+        }
+        const deltaX = event.clientX - mousepos.mouseX;
+        const deltaY = event.clientY - mousepos.mouseY;
+        this.style.left = `${mousepos.elementX + deltaX}px`;
+        this.style.top = `${mousepos.elementY + deltaY}px`;
+        return false;
+      },
+    },
+    mousedown(event) {
+      if (!event.target.matches('.modal-title, .modal-title *')) {
+        return;
+      }
+      mousepos.mouseX = event.clientX;
+      mousepos.mouseY = event.clientY;
+      mousepos.mouseDown = true;
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.fields.js b/ui/src/main/frontend/js/cms.fields.js
index 3a8ad84..3ee6765 100644
--- a/ui/src/main/frontend/js/cms.fields.js
+++ b/ui/src/main/frontend/js/cms.fields.js
@@ -16,164 +16,162 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+/* global wysihtml, wysihtmlParserRules */
 
-/**
- * Utility scripts for decorating form fields
- */
-/* eslint-env browser, es6 */
-(function (rava, wysihtml, wysihtmlParserRules) {
-    'use strict';
+rava.bind('.file', {
+  callbacks: {
+    created() {
+      const field = this;
+      const close = field.closest('form').querySelector('a.close');
 
-    /* Support for file uploads */
-    var setProgress = function(meter, progress){
-        meter.innerText = Math.round(progress)+'%';
+      function setProgress(m, progress) {
+        const meter = m;
+        meter.innerText = `${Math.round(progress)}%`;
         meter.value = Math.round(progress);
-    }
-    var uploadFile = function(meter, action, file) {
-        var formData = new FormData();
+      }
+      function uploadFile(meter, action, file) {
+        const formData = new FormData();
 
         formData.append('*', file);
-        formData.append("*@TypeHint", "sling:File");
+        formData.append('*@TypeHint', 'sling:File');
 
-        var xhr = new XMLHttpRequest();
-        xhr.upload.addEventListener('loadstart', function(){
-            setProgress(meter, 0);
+        const xhr = new XMLHttpRequest();
+        xhr.upload.addEventListener('loadstart', () => {
+          setProgress(meter, 0);
         }, false);
-        xhr.upload.addEventListener('progress', function(event) {
-            var percent = event.loaded / event.total * 100;
-            setProgress(meter, percent);
+        xhr.upload.addEventListener('progress', (event) => {
+          const percent = (event.loaded / event.total) * 100;
+          setProgress(meter, percent);
         }, false);
-        xhr.upload.addEventListener('load', function(){
-            meter.classList.add('is-info');
+        xhr.upload.addEventListener('load', () => {
+          meter.classList.add('is-info');
         }, false);
-        xhr.addEventListener('readystatechange', function(event) {
-            var status, text, readyState;
-            try {
-                readyState = event.target.readyState;
-                text = event.target.responseText;
-                status = event.target.status;
-            }catch(e) {
-            }
-            if (readyState == 4){
-                meter.classList.remove('is-info');
-                if(status == '200' && text) {
-                    meter.classList.add('is-success');
-                } else {
-                    meter.classList.add('is-danger');
-                    console.warn('Failed to upload %s, recieved message %s', file.name, text);
-                }
+        xhr.addEventListener('readystatechange', (event) => {
+          let status; let text; let
+            readyState;
+          try {
+            readyState = event.target.readyState;
+            text = event.target.responseText;
+            status = event.target.status;
+          } catch (e) {
+            meter.classList.add('is-danger');
+          }
+          if (readyState === 4) {
+            meter.classList.remove('is-info');
+            if (status === 200 && text) {
+              meter.classList.add('is-success');
+            } else {
+              meter.classList.add('is-danger');
+              console.warn('Failed to upload %s, recieved message %s', file.name, text);
             }
+          }
         }, false);
         xhr.open('POST', action, true);
         xhr.send(formData);
-    }
-    var handleFile = function(scope, file){
-        var it = document.createElement('div'),
-            ctr = scope.closest('.control').querySelector('.file-item-container'),
-            meter = null;
+      }
+      function handleFile(scope, file) {
+        const it = document.createElement('div');
+        const ctr = scope.closest('.control').querySelector('.file-item-container');
+        let meter = null;
         it.innerHTML = document.querySelector('.file-item-template').innerHTML;
         meter = it.querySelector('.progress');
         it.querySelector('.file-item-name').innerText = file.name;
         ctr.classList.remove('is-hidden');
         ctr.appendChild(it);
+        uploadFile(meter, scope.closest('form').action, file);
+      }
 
-        uploadFile(meter,scope.closest('form').action, file);
-    }
-    rava.bind(".file", {
-        callbacks: {
-            created : function () {
-                var field = this,
-                    close = field.closest('form').querySelector('a.close');
-
-                field.addEventListener("dragover", function(event) {
-                    event.preventDefault();
-                }, false);
-                field.addEventListener("dragenter", function(event) {
-                    event.preventDefault();
-                    field.classList.add('is-primary');
-                }, false);
-                field.addEventListener("dragleave", function(event) {
-                    event.preventDefault();
-                    field.classList.remove('is-primary');
-                }, false);
-                field.addEventListener("drop", function(event) {
-                    event.preventDefault();
-                    field.classList.remove('is-primary');
-                    if (event.dataTransfer.items) {
-                        for (var i = 0; i < event.dataTransfer.items.length; i++) {
-                            if (event.dataTransfer.items[i].kind === 'file') {
-                                handleFile(field, event.dataTransfer.items[i].getAsFile());
-                            }
-                        }
-                    } else {
-                        for (var i = 0; i < event.dataTransfer.files.length; i++) {
-                            handleFile(field, event.dataTransfer.files[i]);
-                        }
-                    }
-                }, false);
-                field.closest('form').querySelector('button[type=submit]').remove();
-                close.innerText = 'Done';
-                close.addEventListener('click', function(event){
-                    window.Sling.CMS.ui.reloadContext();
-                });
-                field.querySelector('input').addEventListener('change', function(event){
-                    for(var i = 0; i < event.target.files.length; i++){
-                        handleFile(event.target, event.target.files[i]);
-                    }
-                })
+      field.addEventListener('dragover', (event) => {
+        event.preventDefault();
+      }, false);
+      field.addEventListener('dragenter', (event) => {
+        event.preventDefault();
+        field.classList.add('is-primary');
+      }, false);
+      field.addEventListener('dragleave', (event) => {
+        event.preventDefault();
+        field.classList.remove('is-primary');
+      }, false);
+      field.addEventListener('drop', (event) => {
+        event.preventDefault();
+        field.classList.remove('is-primary');
+        if (event.dataTransfer.items) {
+          const { items } = event.dataTransfer;
+          for (let i = 0; i < items.length; i++) { // eslint-disable-line no-plusplus
+            if (items[i].kind === 'file') {
+              handleFile(field, items[i].getAsFile());
             }
+          }
+        } else {
+          const { files } = event.dataTransfer;
+          for (let i = 0; i < files.length; i++) { // eslint-disable-line no-plusplus
+            handleFile(field, files[i]);
+          }
         }
-    });
-
-    /* Support for updating the namehint when creating a component */
-    rava.bind(".namehint", {
-        callbacks: {
-            created : function () {
-                var field = this;
-                this.closest('.Form-Ajax').querySelector('select[name="sling:resourceType"]').addEventListener('change', function (evt) {
-                    var resourceType = evt.target.value.split("\/");
-                    field.value = resourceType[resourceType.length - 1];
-                });
-            }
+      }, false);
+      field.closest('form').querySelector('button[type=submit]').remove();
+      close.innerText = 'Done';
+      close.addEventListener('click', () => {
+        window.Sling.CMS.ui.reloadContext();
+      });
+      field.querySelector('input').addEventListener('change', (event) => {
+        const { files } = event.target;
+        for (let i = 0; i < files.length; i++) { // eslint-disable-line no-plusplus
+          handleFile(field, files[i]);
         }
-    });
+      });
+    },
+  },
+});
 
-    /* Support for repeating form fields */
-    rava.bind(".repeating", {
-        callbacks: {
-            created : function () {
-                var ctr = this;
-                this.querySelectorAll(".repeating__add").forEach(function (el) {
-                    el.addEventListener('click', function (event) {
-                        event.stopPropagation();
-                        event.preventDefault();
-                        var node = ctr.querySelector('.repeating__template > .repeating__item').cloneNode(true);
-                        ctr.querySelector('.repeating__container').appendChild(node);
-                    });
-                });
-            }
-        }
-    });
-    rava.bind(".repeating__item", {
-        events: {
-            ":scope .repeating__remove" : {
-                click: function (event) {
-                    event.stopPropagation();
-                    event.preventDefault();
-                    this.remove();
-                }
-            }
-        }
-    });
+/* Support for updating the namehint when creating a component */
+rava.bind('.namehint', {
+  callbacks: {
+    created() {
+      const field = this;
+      this.closest('.Form-Ajax').querySelector('select[name="sling:resourceType"]').addEventListener('change', (evt) => {
+        const resourceType = evt.target.value.split('/');
+        field.value = resourceType[resourceType.length - 1];
+      });
+    },
+  },
+});
 
-    rava.bind('.rte', {
-        callbacks : {
-            created : function () {
-                new wysihtml.Editor(this.querySelector('.rte-editor'), {
-                    toolbar: this.querySelector('.rte-toolbar'),
-                    parserRules:  wysihtmlParserRules
-                });
-            }
-        }
-    });
-}(window.rava = window.rava || {}, window.wysihtml = window.wysihtml || {}, window.wysihtmlParserRules = window.wysihtmlParserRules || {}));
+/* Support for repeating form fields */
+rava.bind('.repeating', {
+  callbacks: {
+    created() {
+      const ctr = this;
+      this.querySelectorAll('.repeating__add').forEach((el) => {
+        el.addEventListener('click', (event) => {
+          event.stopPropagation();
+          event.preventDefault();
+          const node = ctr.querySelector('.repeating__template > .repeating__item').cloneNode(true);
+          ctr.querySelector('.repeating__container').appendChild(node);
+        });
+      });
+    },
+  },
+});
+rava.bind('.repeating__item', {
+  events: {
+    ':scope .repeating__remove': {
+      click(event) {
+        event.stopPropagation();
+        event.preventDefault();
+        this.remove();
+      },
+    },
+  },
+});
+
+rava.bind('.rte', {
+  callbacks: {
+    created() {
+      new wysihtml.Editor(this.querySelector('.rte-editor'), { // eslint-disable-line no-new, new-cap
+        toolbar: this.querySelector('.rte-toolbar'),
+        parserRules: wysihtmlParserRules,
+      });
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.form.js b/ui/src/main/frontend/js/cms.form.js
index 7391d0c..ed221fc 100644
--- a/ui/src/main/frontend/js/cms.form.js
+++ b/ui/src/main/frontend/js/cms.form.js
@@ -1,150 +1,139 @@
-/*
-w * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/* eslint-env browser, es6 */
-(function (rava, Sling) {
-    'use strict';
-    
-    rava.bind(".Form-Ajax", {
-        events: {
-            ":scope .close" : {
-                click : function () {
-                    if (window.parent && window.parent.window && window.parent.window.CMSEditor) {
-                        window.parent.window.CMSEditor.ui.hideModal();
-                    }
-                }
-            },
-            submit : function (event) {
-                event.preventDefault();
-                var form = this,
-                    jcrcontent = false,
-                    callback = form.dataset.callback,
-                    formData = new FormData(form);
-                form.querySelectorAll('input,select,textarea').forEach(function (el) {
-                    if (el.name.indexOf('jcr:content') !== -1) {
-                        jcrcontent = true;
-                    }
-                });
-                if (form.dataset.addDate && !form.querySelector('input[name="jcr:content/jcr:lastModified"]')) {
-                    var dateContainer = document.createElement('div');
-                    if (jcrcontent) {
-                        dateContainer.innerHTML = '<input type="hidden" name="jcr:content/jcr:lastModified" />' +
-                            '<input type="hidden" name="jcr:content/jcr:lastModifiedBy" />' +
-                            '<input type="hidden" name="jcr:content/jcr:created" />' +
-                            '<input type="hidden" name="jcr:content/jcr:createdBy" />';
-                    } else {
-                        dateContainer.innerHTML = '<input type="hidden" name="jcr:lastModified" />' +
-                            '<input type="hidden" name="jcr:lastModifiedBy" />' +
-                            '<input type="hidden" name="jcr:created" />' +
-                            '<input type="hidden" name="jcr:createdBy" />';
-                    }
-                    form.appendChild(dateContainer);
-                }
-                fetch(form.action, {
-                    method: 'POST',
-                    body: formData,
-                    cache: 'no-cache',
-                    headers: {
-                        "Accept": "application/json"
-                    }
-                }).then(function (response) {
-                    if (!response.ok) {
-                        throw new Error(response.statusText);
-                    }
-                    return response.json();
-                }).catch(function (error) {
-                    if (window.self !== window.top) {
-                        window.top.Sling.CMS.ui.confirmMessage(error.message, error.message, function () {
-                            form.querySelector('.form-wrapper').disabled = false;
-                        });
-                    } else {
-                        Sling.CMS.ui.confirmMessage(error.message, error.message, function () {
-                            form.querySelector('.form-wrapper').disabled = false;
-                        });
-                    }
-                }).then(function (res) {
-                    if (callback && Sling.CMS.handlers[callback]) {
-                        Sling.CMS.handlers[callback](res, 'success');
-                    } else if (window.parent.window.CMSEditor) {
-                        var reloadParent = false,
-                            path = res.path;
-                        res.changes.forEach(function (change) {
-                            if (change.type !== 'modified') {
-                                reloadParent = true;
-                            }
-                        });
-                        if (reloadParent) {
-                            let pathArr = path.split('\/');
-                            pathArr.pop();
-                            path = pathArr.join('/');
-                        }
-                        Sling.CMS.ui.confirmReloadComponent(res, 'success', path);
-                    } else {
-                        Sling.CMS.ui.confirmReload(res, 'success');
-                    }
-                });
-                form.querySelector('.form-wrapper').disabled = true;
-            }
-        }
-    });
-
-    rava.bind('.get-form', {
-        events : {
-            submit : function (event) {
-                event.preventDefault();
-                event.stopPropagation();
-                var modal = Sling.CMS.ui.loaderModal('Loading...');
-                var form = this,
-                    wrapper = form.querySelector('.form-wrapper');
-                fetch(form.action + '?' + new URLSearchParams(new FormData(form)).toString()).then(function (response) {
-                    if (!response.ok) {
-                        modal.remove();
-                        throw new Error(response.statusText);
-                    }
-                    return response.text();
-                }).then(function (text) {
-                    var tmp = document.createElement('div');
-                    tmp.innerHTML = text;
-                    document.querySelector(form.dataset.target).innerHTML = tmp.querySelector(form.dataset.load).innerHTML;
-                    tmp.remove();
-                    if (wrapper) {
-                        wrapper.disabled = false;
-                    }
-                    modal.remove();
-                });
-                if (wrapper) {
-                    wrapper.disabled = true;
-                }
-            }
-        }
-    });
-    
-    rava.bind('.suffix-form', {
-        events: {
-            submit: function (event) {
-                event.preventDefault();
-                var suffix = this.querySelector('input[name=suffix]').value,
-                    path = this.action;
-                window.location = path + suffix;
-                return false;
-            }
-        }
-    });
-
-}(window.rava = window.rava || {}, window.Sling = window.Sling || {}));
\ No newline at end of file
+/*
+w * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+rava.bind('.Form-Ajax', {
+  events: {
+    ':scope .close': {
+      click() {
+        if (window.parent && window.parent.window && window.parent.window.CMSEditor) {
+          window.parent.window.CMSEditor.ui.hideModal();
+        }
+      },
+    },
+    submit(event) {
+      event.preventDefault();
+      const form = this;
+      async function submitForm() {
+        let jcrcontent = false;
+        const { callback } = form.dataset;
+        const formData = new FormData(form);
+        form.querySelectorAll('input,select,textarea').forEach((el) => {
+          if (el.name.indexOf('jcr:content') !== -1) {
+            jcrcontent = true;
+          }
+        });
+        if (form.dataset.addDate && !form.querySelector('input[name="jcr:content/jcr:lastModified"]')) {
+          const dateContainer = document.createElement('div');
+          if (jcrcontent) {
+            dateContainer.innerHTML = '<input type="hidden" name="jcr:content/jcr:lastModified" />'
+                              + '<input type="hidden" name="jcr:content/jcr:lastModifiedBy" />'
+                              + '<input type="hidden" name="jcr:content/jcr:created" />'
+                              + '<input type="hidden" name="jcr:content/jcr:createdBy" />';
+          } else {
+            dateContainer.innerHTML = '<input type="hidden" name="jcr:lastModified" />'
+                              + '<input type="hidden" name="jcr:lastModifiedBy" />'
+                              + '<input type="hidden" name="jcr:created" />'
+                              + '<input type="hidden" name="jcr:createdBy" />';
+          }
+          form.appendChild(dateContainer);
+        }
+        const response = await fetch(form.action, {
+          method: 'POST',
+          body: formData,
+          cache: 'no-cache',
+          headers: {
+            Accept: 'application/json',
+          },
+        });
+        if (Sling.CMS.utils.ok(response)) {
+          const res = await response.json();
+          form.querySelector('.form-wrapper').disabled = false;
+          if (callback && Sling.CMS.handlers[callback]) {
+            Sling.CMS.handlers[callback](res, 'success');
+          } else if (window.parent.window.CMSEditor) {
+            let reloadParent = false;
+            let { path } = res;
+            res.changes.forEach((change) => {
+              if (change.type !== 'modified') {
+                reloadParent = true;
+              }
+            });
+            if (reloadParent) {
+              const pathArr = path.split('/');
+              pathArr.pop();
+              path = pathArr.join('/');
+            }
+            Sling.CMS.ui.confirmReloadComponent(res, 'success', path);
+          } else {
+            Sling.CMS.ui.confirmReload(res, 'success');
+          }
+        } else {
+          form.querySelector('.form-wrapper').disabled = false;
+        }
+      }
+      submitForm();
+      form.querySelector('.form-wrapper').disabled = true;
+    },
+  },
+});
+
+rava.bind('.get-form', {
+  events: {
+    submit(event) {
+      event.preventDefault();
+      event.stopPropagation();
+      const modal = Sling.CMS.ui.loaderModal('Loading...');
+      const form = this;
+      const wrapper = form.querySelector('.form-wrapper');
+      async function doGet() {
+        const request = await fetch(`${form.action}?${new URLSearchParams(new FormData(form)).toString()}`);
+        if (Sling.CMS.utils.ok(request)) {
+          const tmp = document.createElement('div');
+          tmp.innerHTML = await request.text();
+          const target = document.querySelector(form.dataset.target);
+          target.innerHTML = tmp.querySelector(form.dataset.load).innerHTML;
+          tmp.remove();
+          if (wrapper) {
+            wrapper.disabled = false;
+          }
+          modal.remove();
+        } else {
+          modal.remove();
+        }
+        if (wrapper) {
+          wrapper.disabled = true;
+        }
+      }
+      doGet();
+    },
+  },
+});
+
+rava.bind('.suffix-form', {
+  events: {
+    submit(event) {
+      event.preventDefault();
+      const suffix = this.querySelector('input[name=suffix]').value;
+      const path = this.action;
+      window.location = path + suffix;
+      return false;
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.job.js b/ui/src/main/frontend/js/cms.job.js
index 72e4aa4..d9672a2 100644
--- a/ui/src/main/frontend/js/cms.job.js
+++ b/ui/src/main/frontend/js/cms.job.js
@@ -16,31 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/* eslint-env browser, es6 */
-(function (rava, Sling) {
-    'use strict';
-    
-    rava.bind('.job-properties-container', {
-        callbacks : {
-            created :  function () {
-                const container = this;
-                document.querySelector(container.dataset.source).addEventListener('change', async function () {
-                    const sourceSelect = this;
-                    const config = this.value;
-                    sourceSelect.disabled = true;
-                    container.innerHTML = '';
-                    
-                    const response = await fetch(container.dataset.path + config);
-                    if(response.ok){
-                        const formHtml = await response.text();
-                        container.innerHTML = formHtml;
-                        sourceSelect.disabled = false;
-                    } else {
-                        Sling.CMS.ui.confirmMessage(response.status, response.statusText, function () {});
-                    }
-                    
-                });
-            }
+
+rava.bind('.job-properties-container', {
+  callbacks: {
+    created() {
+      const container = this;
+      async function handleChange() {
+        const sourceSelect = this;
+        const config = this.value;
+        sourceSelect.disabled = true;
+        container.innerHTML = '';
+
+        const response = await fetch(container.dataset.path + config);
+        if (Sling.CMS.utils.ok(response)) {
+          const formHtml = await response.text();
+          container.innerHTML = formHtml;
+          sourceSelect.disabled = false;
         }
-    });
-}(window.rava = window.rava || {}, window.Sling = window.Sling || {}));
\ No newline at end of file
+      }
+      document.querySelector(container.dataset.source).addEventListener('change', handleChange);
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.js b/ui/src/main/frontend/js/cms.js
index 1e88feb..6b1071a 100644
--- a/ui/src/main/frontend/js/cms.js
+++ b/ui/src/main/frontend/js/cms.js
@@ -16,170 +16,191 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/* eslint-env es6, browser */
 
-(function (rava) {
-    'use strict';
-    window.Sling = window.Sling || {};
-    var Sling = window.Sling;
-    window.Sling.CMS = {
-        handlers: {
-            handledelete: function (res, msg) {
-                if (window.location.pathname.indexOf(res.path) !== -1) {
-                    window.top.Sling.CMS.ui.confirmMessage(msg, res.title, function () {
-                        window.location = '/cms';
-                    });
-                } else {
-                    Sling.CMS.ui.confirmReload(res, msg);
-                }
-            },
-            handlemove: function (res, msg) {
-                var changes = res.changes[0];
-                if (changes.type === 'moved' && window.location.pathname.indexOf(changes.argument[0]) !== -1) {
-                    window.top.Sling.CMS.ui.confirmMessage(msg, res.title, function () {
-                        window.location = window.location.href.replace(changes.argument[0], changes.argument[1]);
-                    });
-                } else {
-                    Sling.CMS.ui.confirmReload(res, msg);
-                }
-            },
-            handleugc: function (res, msg) {
-                Sling.CMS.ui.confirmMessage(msg, res.title, function () {
-                    window.location = '/cms/usergenerated/content.html' + res.parentLocation;
-                });
-            }
-        },
-        ui: {
-            confirmMessage: function (title, message, complete) {
-                var modal = document.createElement('div');
-                modal.innerHTML = '<div class="modal-background"></div><div class="is-draggable modal-content"><div class="box"><h3 class="modal-title">' + title + '</h3><p>' + message + '</p><br/><button type="button" class="close-modal button is-primary">OK</button></div></div><button class="modal-close is-large" aria-label="close"></button>';
-                document.body.appendChild(modal);
-                modal.classList.add('modal');
-                modal.classList.add('is-active');
-                modal.querySelectorAll('.modal-close,.close-modal,.modal-background').forEach(function (closer) {
-                    closer.addEventListener('click', function () {
-                        modal.remove();
-                        complete();
-                    });
-                });
-                modal.querySelector('.delete,.close-modal').focus();
-                return modal;
-            },
-            confirmReload: function (res, msg) {
-                if (window.self !== window.top) {
-                    window.top.Sling.CMS.ui.confirmMessage(msg, res.title, function () {
-                        window.top.location.reload();
-                    });
-                } else {
-                    Sling.CMS.ui.confirmMessage(msg, res ? res.title : '', function () {
-                        Sling.CMS.ui.reloadContext();
-                    });
-                }
-            },
-            confirmReloadComponent: function (res, msg, path) {
-                window.top.Sling.CMS.ui.confirmMessage(msg, res.title, function () {
-                    var modal = window.top.Sling.CMS.ui.loaderModal('Refreshing...');
-                    window.parent.window.CMSEditor.ui.reloadComponent(path, function () {
-                        modal.remove();
-                    });
-                });
-            },
-            loaderModal: function (message) {
-                message = message || 'Loading...';
-                var modal = document.createElement('div');
-                modal.classList.add('modal');
-                modal.innerHTML = '<div class="modal-background"></div><div class="modal-content"><div class="box"><h3>' + message + '</h3><div class="loader is-loading"></div></div></div>';
-                document.querySelector('body').appendChild(modal);
-                modal.classList.add('is-active');
-                return modal;
-            },
-            reloadContext: function () {
-                // close all existing modals
-                document.querySelectorAll('.modal').forEach(function (modal) {
-                    modal.remove();
-                });
-                // reset the actions
-                document.querySelectorAll('.actions-target *').forEach(function (child) {
-                    child.remove();
-                });
-                var containers = document.querySelectorAll('.reload-container'),
-                    modal = Sling.CMS.ui.loaderModal('Refreshing...'),
-                    count = containers.length;
-                if (count !== 0) {
-                    containers.forEach(function (container) {
-                        var request = new XMLHttpRequest(),
-                            link = container.dataset.path;
-                        if (link.indexOf('?') === -1) {
-                            link += '?tstamp=' + Date.now();
-                        } else {
-                            link += '&tstamp=' + Date.now();
-                        }
-                        request.open('GET', link, true);
-                        request.onload = function () {
-                            var tmp = document.createElement('div');
-                            tmp.innerHTML = request.responseText;
-                            container.replaceWith(tmp.querySelector('.reload-container'));
-                            tmp.remove();
-                            count = count - 1;
-                            if (count === 0) {
-                                modal.remove();
-                            }
-                        };
-                        request.send();
-                    });
-                } else {
-                    location.reload();
-                }
-            }
-        },
-        utils: {
-            form2Obj: function (form) {
-                var data = {};
-                form.querySelectorAll('input,textarea,select').forEach(function (f) {
-                    if (!f.disabled && f.name) {
-                        data[f.name] = f.value;
-                    }
-                });
-                return data;
+
+window.Sling = window.Sling || {};
+const {
+  Sling,
+} = window;
+window.Sling.CMS = {
+  handlers: {
+    handledelete(res, msg) {
+      if (window.location.pathname.indexOf(res.path) !== -1) {
+        window.top.Sling.CMS.ui.confirmMessage(msg, res.title, () => {
+          window.location = '/cms';
+        });
+      } else {
+        Sling.CMS.ui.confirmReload(res, msg);
+      }
+    },
+    handlemove(res, msg) {
+      const changes = res.changes[0];
+      if (changes.type === 'moved' && window.location.pathname.indexOf(changes.argument[0]) !== -1) {
+        window.top.Sling.CMS.ui.confirmMessage(msg, res.title, () => {
+          window.location = window.location.href.replace(changes.argument[0], changes.argument[1]);
+        });
+      } else {
+        Sling.CMS.ui.confirmReload(res, msg);
+      }
+    },
+    handleugc(res, msg) {
+      Sling.CMS.ui.confirmMessage(msg, res.title, () => {
+        window.location = `/cms/usergenerated/content.html${res.parentLocation}`;
+      });
+    },
+  },
+  ui: {
+    confirmMessage(title, message, complete) {
+      const modal = document.createElement('div');
+      modal.innerHTML = `<div class="modal-background"></div><div class="is-draggable modal-content"><div class="box"><h3 class="modal-title">${title}</h3><p>${message}</p><br/><button type="button" class="close-modal button is-primary">OK</button></div></div><button class="modal-close is-large" aria-label="close"></button>`;
+      document.body.appendChild(modal);
+      modal.classList.add('modal');
+      modal.classList.add('is-active');
+      modal.querySelectorAll('.modal-close,.close-modal,.modal-background').forEach((closer) => {
+        closer.addEventListener('click', () => {
+          modal.remove();
+          complete();
+        });
+      });
+      modal.querySelector('.delete,.close-modal').focus();
+      return modal;
+    },
+    confirmReload(res, msg) {
+      if (window.self !== window.top) {
+        window.top.Sling.CMS.ui.confirmMessage(msg, res.title, () => {
+          window.top.location.reload();
+        });
+      } else {
+        Sling.CMS.ui.confirmMessage(msg, res ? res.title : '', () => {
+          Sling.CMS.ui.reloadContext();
+        });
+      }
+    },
+    confirmReloadComponent(res, msg, path) {
+      window.top.Sling.CMS.ui.confirmMessage(msg, res.title, () => {
+        const modal = window.top.Sling.CMS.ui.loaderModal('Refreshing...');
+        window.parent.window.CMSEditor.ui.reloadComponent(path, () => {
+          modal.remove();
+        });
+      });
+    },
+    loaderModal(message) {
+      const modal = document.createElement('div');
+      modal.classList.add('modal');
+      modal.innerHTML = `<div class="modal-background"></div><div class="modal-content"><div class="box"><h3>${message || 'Loading...'}</h3><div class="loader is-loading"></div></div></div>`;
+      document.querySelector('body').appendChild(modal);
+      modal.classList.add('is-active');
+      return modal;
+    },
+    reloadContext() {
+      // close all existing modals
+      document.querySelectorAll('.modal').forEach((modal) => {
+        modal.remove();
+      });
+      // reset the actions
+      document.querySelectorAll('.actions-target *').forEach((child) => {
+        child.remove();
+      });
+      const containers = document.querySelectorAll('.reload-container');
+      const modal = Sling.CMS.ui.loaderModal('Refreshing...');
+      let count = containers.length;
+      if (count !== 0) {
+        containers.forEach(async (container) => {
+          let link = container.dataset.path;
+          if (link.indexOf('?') === -1) {
+            link += `?tstamp=${Date.now()}`;
+          } else {
+            link += `&tstamp=${Date.now()}`;
+          }
+          const response = await fetch(link);
+          if (Sling.CMS.utils.ok(response)) {
+            const responseText = await response.text();
+            const tmp = document.createElement('div');
+            tmp.innerHTML = responseText;
+            container.replaceWith(tmp.querySelector('.reload-container'));
+            tmp.remove();
+            count -= 1;
+            if (count === 0) {
+              modal.remove();
             }
+          }
+        });
+      } else {
+        window.location.reload();
+      }
+    },
+  },
+  utils: {
+    form2Obj(form) {
+      const data = {};
+      form.querySelectorAll('input,textarea,select').forEach((f) => {
+        if (!f.disabled && f.name) {
+          data[f.name] = f.value;
         }
-    };
+      });
+      return data;
+    },
+    ok(response) {
+      let { CMS } = Sling;
+      if (window.self !== window.top) {
+        CMS = window.top.Sling.CMS;
+      }
+      if (response.redirected && response.url.indexOf('/system/sling/form/login?resource=') !== -1) {
+        CMS.ui.confirmMessage('301', 'Not logged in, please login again', () => {
+          window.location = `/system/sling/form/login?resource=${encodeURIComponent(window.location.pathname)}`;
+        });
+        return false;
+      }
+      if (!response.ok) {
+        CMS.ui.confirmMessage(response.status, response.statusText, () => {
+          window.location = response.url;
+        });
+        return false;
+      }
+      return true;
+    },
+  },
+};
 
-    if (window.performance && window.performance.navigation.type === window.performance.navigation.TYPE_BACK_FORWARD) {
-        Sling.CMS.ui.reloadContext();
-    }
+if (window.performance
+  && window.performance.navigation.type
+  === window.performance.navigation.TYPE_BACK_FORWARD) {
+  Sling.CMS.ui.reloadContext();
+}
 
-    window.onbeforeunload = function () {
-        if (document.querySelector('.modal form')) {
-            return "Are you sure you want to leave this page?";
-        }
-    };
+window.onbeforeunload = () => {
+  if (document.querySelector('.modal form')) {
+    return 'Are you sure you want to leave this page?';
+  }
+  return null;
+};
 
-    if (document.querySelector('.breadcrumb .is-active') !== null) {
-        var itemTitle = document.querySelector('.breadcrumb .is-active').textContent.trim();
-        if (itemTitle.length > 0) {
-            document.title = itemTitle + ' :: ' + document.title;
-        }
-    }
+if (document.querySelector('.breadcrumb .is-active') !== null) {
+  const itemTitle = document.querySelector('.breadcrumb .is-active').textContent.trim();
+  if (itemTitle.length > 0) {
+    document.title = `${itemTitle} :: ${document.title}`;
+  }
+}
 
-    rava.bind('.sling-cms-include-config', {
-        callbacks : {
-            created :  function () {
-                var container = this,
-                    load = function () {
-                        var config = document.querySelector(container.dataset.source).selectedOptions[0].dataset.config;
-                        if (config) {
-                            fetch(config + container.closest('form').attributes.action.value).then(function (result) {
-                                return result.text();
-                            }).then(function (markup) {
-                                container.innerHTML = markup;
-                            });
-                        }
-                    };
-                document.querySelector(container.dataset.source).addEventListener('change', load);
-                load();
-            }
+rava.bind('.sling-cms-include-config', {
+  callbacks: {
+    created() {
+      const container = this;
+      async function loadContent() {
+        const srcEl = document.querySelector(container.dataset.source);
+        const {
+          config,
+        } = srcEl.selectedOptions[0].dataset;
+        if (config) {
+          const response = await fetch(config + container.closest('form').attributes.action.value);
+          if (Sling.CMS.utils.ok(response)) {
+            const markup = await response.text();
+            container.innerHTML = markup;
+          }
         }
-    });
-
-}(window.rava = window.rava || {}));
\ No newline at end of file
+      }
+      document.querySelector(container.dataset.source).addEventListener('change', loadContent);
+      loadContent();
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.labelfield.js b/ui/src/main/frontend/js/cms.labelfield.js
index 7c957f8..4a28cd6 100644
--- a/ui/src/main/frontend/js/cms.labelfield.js
+++ b/ui/src/main/frontend/js/cms.labelfield.js
@@ -1,66 +1,60 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/* eslint-env browser, es6 */
-(function (rava) {
-    'use strict';
-    
-    
-    rava.bind('.labelfield', {
-        events: {
-            '.labelfield__add, .labelfield__add *': {
-                click: function (event) {
-                    event.preventDefault();
-                    event.stopPropagation();
-                    var context = this,
-                        span = document.createElement('span'),
-                        val = context.querySelector('.labelfield__field input').value,
-                        found = false,
-                        title = context.querySelector('option[value="' + val + '"]').innerText;
-                    span.innerHTML = context.querySelector('.labelfield__template').innerHTML;
-                    context.querySelectorAll('.labelfield__item input').forEach(function (el) {
-                        if (el.value === val) {
-                            found = true;
-                        }
-                    });
-                    if (found) {
-                        return false;
-                    }
-                    span.querySelector('input').value = val;
-
-                    if (title !== '') {
-                        span.querySelector('.labelfield__title').innerText = title;
-                        this.closest('.labelfield').querySelector('.labelfield__container').appendChild(span);
-                        context.querySelector('.labelfield__field input').value = '';
-                    }
-                }
-            }
-        }
-    });
-
-    rava.bind('.labelfield__item, .labelfield__item *', {
-        events: {
-            click: function () {
-                event.preventDefault();
-                event.stopPropagation();
-                this.closest('.labelfield__item').remove();
-            }
-        }
-    });
-
-}(window.rava = window.rava || {}));
\ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+rava.bind('.labelfield', {
+  events: {
+    '.labelfield__add, .labelfield__add *': {
+      click(event) {
+        event.preventDefault();
+        event.stopPropagation();
+        const context = this;
+        const span = document.createElement('span');
+        const val = context.querySelector('.labelfield__field input').value;
+        let found = false;
+        const title = context.querySelector(`option[value="${val}"]`).innerText;
+        span.innerHTML = context.querySelector('.labelfield__template').innerHTML;
+        context.querySelectorAll('.labelfield__item input').forEach((el) => {
+          if (el.value === val) {
+            found = true;
+          }
+        });
+        if (found) {
+          return;
+        }
+        span.querySelector('input').value = val;
+
+        if (title !== '') {
+          span.querySelector('.labelfield__title').innerText = title;
+          this.closest('.labelfield').querySelector('.labelfield__container').appendChild(span);
+          context.querySelector('.labelfield__field input').value = '';
+        }
+      },
+    },
+  },
+});
+
+rava.bind('.labelfield__item, .labelfield__item *', {
+  events: {
+    click(event) {
+      event.preventDefault();
+      event.stopPropagation();
+      this.closest('.labelfield__item').remove();
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.modal.js b/ui/src/main/frontend/js/cms.modal.js
index 8c072e9..4752b60 100644
--- a/ui/src/main/frontend/js/cms.modal.js
+++ b/ui/src/main/frontend/js/cms.modal.js
@@ -1,106 +1,86 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/* eslint-env browser, es6 */
-(function (rava, Sling) {
-    'use strict';
-    rava.bind("a.Fetch-Modal", {
-        events: {
-            click: function (event) {
-                event.preventDefault();
-                this.disabled = true;
-                this.getModal(this.getAttribute('data-title'), this.getAttribute('href'), this);
-            }
-        },
-        methods: {
-            getModal: function (title, link, button) {
-                if (window.self !== window.top && window.parent.parent) {
-                    window.parent.parent.postMessage({
-                        "action": "slingcms.openmodal",
-                        "url": link,
-                        "title": title
-                    }, window.location.origin);
-                    button.removeAttribute('disabled');
-                } else {
-                    var modal = Sling.CMS.ui.loaderModal(),
-                        request = new XMLHttpRequest();
-                    request.open('GET', link, true);
-                    request.onload = function () {
-                        if (this.status === 500 || this.status === 404) {
-                            Sling.CMS.ui.confirmMessage(request.statusText, request.statusText, function () {
-                                Sling.CMS.ui.reloadContext();
-                            });
-                        } else {
-                            button.removeAttribute("disabled");
-                            if (request.responseURL.indexOf('/system/sling/form/login?resource=') !== -1) {
-                                window.location.reload();
-                            } else {
-                                modal.innerHTML = request.responseText;
-                            }
-                        }
-                    };
-                    modal.querySelector('.modal-background').addEventListener('click', function () {
-                        request.abort();
-                        button.removeAttribute('disabled');
-                    });
-                    request.send();
-                }
-            }
-        }
-    });
-    
-    rava.bind(".modal", {
-        events: {
-            ".close,.modal-close,.close-modal,.modal-background": {
-                click: function (event) {
-                    event.preventDefault();
-                    this.remove();
-                }
-            }
-        }
-    });
-    
-    window.addEventListener("message", function (event) {
-        if (event.data.action === 'slingcms.openmodal') {
-            Sling.CMS.pathfield = event.source;
-            var modal = Sling.CMS.ui.loaderModal(),
-                request = new XMLHttpRequest();
-            request.open('GET', event.data.url, true);
-            request.onload = function () {
-                if (this.status === 500 || this.status === 404) {
-                    Sling.CMS.ui.confirmMessage(request.statusText, request.statusText, function () {
-                        Sling.CMS.ui.reloadContext();
-                    });
-                } else {
-                    if (request.responseURL.indexOf('/system/sling/form/login?resource=') !== -1) {
-                        window.location.reload();
-                    } else {
-                        modal.innerHTML = request.responseText;
-                    }
-                }
-            };
-            modal.querySelector('.modal-background').addEventListener('click', function () {
-                request.abort();
-            });
-            request.send();
-        }
-        
-    }, false);
-
-}(window.rava = window.rava || {}, window.Sling = window.Sling || {}));
\ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+rava.bind('a.Fetch-Modal', {
+  events: {
+    click(event) {
+      event.preventDefault();
+      this.disabled = true;
+      this.getModal(this.getAttribute('data-title'), this.getAttribute('href'), this);
+    },
+  },
+  methods: {
+    getModal(title, link, button) {
+      const modal = Sling.CMS.ui.loaderModal();
+      async function loadModal() {
+        const controller = new AbortController();
+        const request = await fetch(link, {
+          signal: controller.signal,
+        });
+        if (Sling.CMS.utils.ok(request)) {
+          const responseText = await request.text();
+          modal.innerHTML = responseText;
+        }
+        modal.querySelector('.modal-background').addEventListener('click', () => {
+          controller.abort();
+          button.removeAttribute('disabled');
+        });
+      }
+      if (window.self !== window.top && window.parent.parent) {
+        window.parent.parent.postMessage({
+          action: 'slingcms.openmodal',
+          url: link,
+          title,
+        }, window.location.origin);
+        button.removeAttribute('disabled');
+      } else {
+        loadModal();
+      }
+    },
+  },
+});
+
+rava.bind('.modal', {
+  events: {
+    '.close,.modal-close,.close-modal,.modal-background': {
+      click(event) {
+        event.preventDefault();
+        this.remove();
+      },
+    },
+  },
+});
+
+window.addEventListener('message', async (event) => {
+  if (event.data.action === 'slingcms.openmodal') {
+    Sling.CMS.pathfield = event.source;
+    const modal = Sling.CMS.ui.loaderModal();
+    const controller = new AbortController();
+    const request = await fetch(event.data.url, {
+      signal: controller.signal,
+    });
+    if (Sling.CMS.utils.ok(request)) {
+      const responseText = await request.text();
+      modal.innerHTML = responseText;
+    }
+    modal.querySelector('.modal-background').addEventListener('click', () => {
+      controller.abort();
+    });
+  }
+}, false);
diff --git a/ui/src/main/frontend/js/cms.nav.js b/ui/src/main/frontend/js/cms.nav.js
index 743db79..cac2a49 100644
--- a/ui/src/main/frontend/js/cms.nav.js
+++ b/ui/src/main/frontend/js/cms.nav.js
@@ -1,99 +1,95 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/* eslint-env browser, es6 */
-(function (rava) {
-    'use strict';
-    
-    var urlParams = new URLSearchParams(window.location.search),
-        resourceParam = urlParams.get('resource'),
-        searchParam = urlParams.get('search');
-    
-    rava.bind(".navbar-burger", {
-        events: {
-            click: function () {
-                var target = document.querySelector(this.dataset.target);
-                target.classList.toggle('is-active');
-                this.classList.toggle('is-active');
-            }
-        }
-    });
-    
-    rava.bind('.layout-switch select', {
-        events: {
-            change: function () {
-                window.location = this.value;
-            }
-        }
-    });
-    
-    rava.bind('.contentnav', {
-        callbacks : {
-            created: function () {
-                let cnav = this;
-                var search = document.querySelector('.contentnav-search input[name=search]'),
-                    filter = function (event) {
-                        event.stopPropagation();
-                        event.preventDefault();
-                        var value = search.value.toLowerCase();
-                        cnav.querySelectorAll('.contentnav__item').forEach(function (item) {
-                            if (item.innerText.toLowerCase().indexOf(value) === -1 && !item.querySelector('*[data-value="' + resourceParam + '"]')) {
-                                item.classList.add('is-hidden');
-                            } else {
-                                item.classList.remove('is-hidden');
-                            }
-                        });
-                    };
-                if(search){
-                    search.addEventListener('keyup', filter);
-                    search.addEventListener('change', filter);
-                }
-                if (resourceParam) {
-                    cnav.querySelectorAll('.contentnav__item').forEach(function (item) {
-                        if (item.querySelector('*[data-value="' + resourceParam + '"]')) {
-                            item.classList.remove('is-hidden');
-                            item.click();
-                        } else {
-                            item.classList.add('is-hidden');
-                        }
-                    });
-                    document.querySelector('.contentnav-search input[name=search]').value = resourceParam;
-                } else if (searchParam) {
-                    document.querySelector('.contentnav-search input[name=search]').value = searchParam;
-                    filter(new Event('fake'));
-                }
-            }
-        }
-    });
-    rava.bind(".contentnav .contentnav__item", {
-        events: {
-            click: function () {
-                this.closest('.contentnav').querySelectorAll('.contentnav__item.is-selected').forEach(function (tr) {
-                    tr.classList.remove('is-selected');
-                });
-                this.classList.add('is-selected');
-                document.querySelector('.actions-target').innerHTML = this.querySelector('.cell-actions').innerHTML;
-            },
-            dblclick: function () {
-                if(this.querySelector('.item-link')){
-                    window.location = this.querySelector('.item-link').href;
-                }
-            }
-        }
-    });
-}(window.rava = window.rava || {}));
\ No newline at end of file
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+rava.bind('.navbar-burger', {
+  events: {
+    click() {
+      const target = document.querySelector(this.dataset.target);
+      target.classList.toggle('is-active');
+      this.classList.toggle('is-active');
+    },
+  },
+});
+
+rava.bind('.layout-switch select', {
+  events: {
+    change() {
+      window.location = this.value;
+    },
+  },
+});
+
+rava.bind('.contentnav', {
+  callbacks: {
+    created() {
+      const urlParams = new URLSearchParams(window.location.search);
+      const resourceParam = urlParams.get('resource');
+      const searchParam = urlParams.get('search');
+      const cnav = this;
+      const search = document.querySelector('.contentnav-search input[name=search]');
+      function filter(event) {
+        event.stopPropagation();
+        event.preventDefault();
+        const value = search.value.toLowerCase();
+        cnav.querySelectorAll('.contentnav__item').forEach((item) => {
+          if (item.innerText.toLowerCase().indexOf(value) === -1 && !item.querySelector(`*[data-value="${resourceParam}"]`)) {
+            item.classList.add('is-hidden');
+          } else {
+            item.classList.remove('is-hidden');
+          }
+        });
+      }
+      if (search) {
+        search.addEventListener('keyup', filter);
+        search.addEventListener('change', filter);
+      }
+      if (resourceParam) {
+        cnav.querySelectorAll('.contentnav__item').forEach((item) => {
+          if (item.querySelector(`*[data-value="${resourceParam}"]`)) {
+            item.classList.remove('is-hidden');
+            item.click();
+          } else {
+            item.classList.add('is-hidden');
+          }
+        });
+        document.querySelector('.contentnav-search input[name=search]').value = resourceParam;
+      } else if (searchParam) {
+        document.querySelector('.contentnav-search input[name=search]').value = searchParam;
+        filter(new Event('fake'));
+      }
+    },
+  },
+});
+rava.bind('.contentnav .contentnav__item', {
+  events: {
+    click() {
+      this.closest('.contentnav').querySelectorAll('.contentnav__item.is-selected').forEach((tr) => {
+        tr.classList.remove('is-selected');
+      });
+      this.classList.add('is-selected');
+      document.querySelector('.actions-target').innerHTML = this.querySelector('.cell-actions').innerHTML;
+    },
+    dblclick() {
+      if (this.querySelector('.item-link')) {
+        window.location = this.querySelector('.item-link').href;
+      }
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.page.js b/ui/src/main/frontend/js/cms.page.js
index a4f0911..f486c47 100644
--- a/ui/src/main/frontend/js/cms.page.js
+++ b/ui/src/main/frontend/js/cms.page.js
@@ -16,71 +16,66 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/* eslint-env browser, es6 */
-(function (Handlebars, rava, Sling) {
-    'use strict';
 
-    function removeProperty(obj, propertyToDelete) {
-        var prop;
-        for (prop in obj) {
-            if (prop === propertyToDelete) {
-                delete obj[prop];
-            } else if (typeof obj[prop] === 'object') {
-                removeProperty(obj[prop], propertyToDelete);
-            }
-        }
-    }
+rava.bind('.page-properties-container', {
+  callbacks: {
+    created() {
+      const container = this;
+      const wrapper = container.closest('.form-wrapper');
 
-    var getPageTemplate = function (pageConfig, cb) {
-        fetch(pageConfig + ".infinity.json").then(function (response) {
-            return response.json();
-        }).then(function (result) {
-            var template;
-            if (typeof result.template === "string") {
-                //String template
-                template = result.template;
-            } else if (typeof result.template === "object") {
-                //Json template
-                removeProperty(result.template, "jcr:created");
-                removeProperty(result.template, "jcr:createdBy");
-                template = JSON.stringify(result.template);
-            }
-            cb(template);
+      function removeProperty(obj, propertyToDelete) {
+        Object.keys(obj).forEach((key) => {
+          if (key === propertyToDelete) {
+            delete obj[key]; // eslint-disable-line no-param-reassign
+          } else if (typeof obj[key] === 'object') {
+            removeProperty(obj[key], propertyToDelete);
+          }
         });
-    };
+      }
 
-    rava.bind('.page-properties-container', {
-        callbacks : {
-            created :  function () {
-                var container = this,
-                    wrapper = container.closest('.form-wrapper');
-                document.querySelector(container.dataset.source).addEventListener('change', function () {
-                    var sourceSelect = this,
-                        config = this.value;
-                    sourceSelect.disabled = true;
-                    container.innerHTML = '';
-                    fetch(container.dataset.path + config).then(function (response) {
-                        return response.text();
-                    }).then(function (formHtml) {
-                        getPageTemplate(config, function (source) {
-                            var template = Handlebars.compile(source),
-                                updateContent = function () {
-                                    if (!wrapper.disabled) {
-                                        var data = Sling.CMS.utils.form2Obj(container.closest('form'));
-                                        document.querySelector('input[name=":content"]').value = template(data);
-                                    }
-                                };
-                            container.innerHTML = formHtml;
-                            document.querySelectorAll('input,textarea,select').forEach(function (el) {
-                                el.addEventListener('change', updateContent);
-                            });
-                            container.closest('form').addEventListener('submit', updateContent);
-                            sourceSelect.disabled = false;
-                        });
-                    });
-                });
+      async function getPageTemplate(pageConfig, cb) {
+        const response = await fetch(`${pageConfig}.infinity.json`);
+        if (Sling.CMS.utils.ok(response)) {
+          const result = await response.json();
+          let template;
+          if (typeof result.template === 'string') {
+            // String template
+            template = result.template;
+          } else if (typeof result.template === 'object') {
+            // Json template
+            removeProperty(result.template, 'jcr:created');
+            removeProperty(result.template, 'jcr:createdBy');
+            template = JSON.stringify(result.template);
+          }
+          cb(template);
+        }
+      }
+      async function handleChange() {
+        const sourceSelect = this;
+        const config = this.value;
+        sourceSelect.disabled = true;
+        container.innerHTML = '';
+        const response = await fetch(container.dataset.path + config);
+        if (Sling.CMS.utils.ok(response)) {
+          const formHtml = await response.text();
+          getPageTemplate(config, (source) => {
+            const template = Handlebars.compile(source);
+            function updateContent() {
+              if (!wrapper.disabled) {
+                const data = Sling.CMS.utils.form2Obj(container.closest('form'));
+                document.querySelector('input[name=":content"]').value = template(data);
+              }
             }
+            container.innerHTML = formHtml;
+            document.querySelectorAll('input,textarea,select').forEach((el) => {
+              el.addEventListener('change', updateContent);
+            });
+            container.closest('form').addEventListener('submit', updateContent);
+            sourceSelect.disabled = false;
+          });
         }
-    });
-
-}(window.Handlebars = window.Handlebars || {}, window.rava = window.rava || {}, window.Sling = window.Sling || {}));
\ No newline at end of file
+      }
+      document.querySelector(container.dataset.source).addEventListener('change', handleChange);
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/cms.pathfield.js b/ui/src/main/frontend/js/cms.pathfield.js
index ae5c288..bc5a41a 100644
--- a/ui/src/main/frontend/js/cms.pathfield.js
+++ b/ui/src/main/frontend/js/cms.pathfield.js
@@ -16,64 +16,59 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/* eslint-env browser, es6 */
-(function (rava, Sling, autoComplete) {
-    'use strict';
-    
-    Sling.CMS.pathfield = null;
-    
-    rava.bind("input.pathfield", {
-        callbacks: {
-            created : function () {
-                var type = this.dataset.type,
-                    base = this.dataset.base;
-                new autoComplete({
-                    minChars: 1,
-                    selector: this,
-                    source: function (term, response) {
-                        if (term === '/') {
-                            term = base;
-                        }
-                        fetch('/bin/cms/paths?path=' + encodeURIComponent(term) + '&type=' + encodeURIComponent(type)).then(function (response) {
-                            return response.json();
-                        }).then(function (data) {
-                            response(data);
-                        });
-                    }
-                });
-            }
-        }
-    });
-    
-    rava.bind('.search-button', {
-        events: {
-            click: function () {
-                Sling.CMS.pathfield =  this.closest('.field').querySelector('.pathfield');
-            }
-        }
-    });
-    
-    rava.bind('.search-select-button', {
-        events: {
-            click : function () {
-                var path = this.dataset.path;
-                if (Sling.CMS.pathfield instanceof HTMLInputElement) {
-                    Sling.CMS.pathfield.value = this.dataset.path;
-                } else {
-                    Sling.CMS.pathfield.postMessage({
-                        "action": "slingcms.setpath",
-                        "path": path
-                    }, window.location.origin);
-                }
-                this.closest('.modal').remove();
-            }
-        }
-    });
+/* global autoComplete */
+Sling.CMS.pathfield = null;
 
-    window.addEventListener("message", function (event) {
-        if (event.data.action === 'slingcms.setpath') {
-            Sling.CMS.pathfield.value = event.data.path;
-        }
-    }, false);
-    
-}(window.rava = window.rava || {}, window.Sling = window.Sling || {}, window.autoComplete = window.autoComplete || {}));
\ No newline at end of file
+rava.bind('input.pathfield', {
+  callbacks: {
+    created() {
+      const { type } = this.dataset;
+      const { base } = this.dataset;
+
+      new autoComplete({ // eslint-disable-line no-new, new-cap
+        minChars: 1,
+        selector: this,
+        source(t, response) {
+          let term = t;
+          if (term === '/') {
+            term = base;
+          }
+          fetch(`/bin/cms/paths?path=${encodeURIComponent(term)}&type=${encodeURIComponent(type)}`).then((resp) => resp.json()).then((data) => {
+            response(data);
+          });
+        },
+      });
+    },
+  },
+});
+
+rava.bind('.search-button', {
+  events: {
+    click() {
+      Sling.CMS.pathfield = this.closest('.field').querySelector('.pathfield');
+    },
+  },
+});
+
+rava.bind('.search-select-button', {
+  events: {
+    click() {
+      const { path } = this.dataset;
+      if (Sling.CMS.pathfield instanceof HTMLInputElement) {
+        Sling.CMS.pathfield.value = this.dataset.path;
+      } else {
+        Sling.CMS.pathfield.postMessage({
+          action: 'slingcms.setpath',
+          path,
+        }, window.location.origin);
+      }
+      this.closest('.modal').remove();
+    },
+  },
+});
+
+window.addEventListener('message', (event) => {
+  if (event.data.action === 'slingcms.setpath') {
+    Sling.CMS.pathfield.value = event.data.path;
+  }
+}, false);
diff --git a/ui/src/main/frontend/js/cms.toggle.js b/ui/src/main/frontend/js/cms.toggle.js
index e859ff4..1f4c77c 100644
--- a/ui/src/main/frontend/js/cms.toggle.js
+++ b/ui/src/main/frontend/js/cms.toggle.js
@@ -17,39 +17,34 @@
  * under the License.
  */
 
-/* eslint-env browser, es6 */
-(function (rava) {
-    'use strict';
-    
-    rava.bind('.toggle-hidden', {
-        events : {
-            click: function () {
-                var target = document.querySelectorAll(this.dataset.target);
-                target.forEach(function (el) {
-                    el.classList.toggle('is-hidden');
-                });
-            }
-        }
-    });
+rava.bind('.toggle-hidden', {
+  events: {
+    click() {
+      const target = document.querySelectorAll(this.dataset.target);
+      target.forEach((el) => {
+        el.classList.toggle('is-hidden');
+      });
+    },
+  },
+});
 
-    rava.bind('.toggle-value', {
-        callbacks: {
-            created: function () {
-                var source = this.getAttribute('data-toggle-source'),
-                    selector = 'input[name="' + source + '"], select[name="' + source + '"]',
-                    toggle = this,
-                    sourceEl = document.querySelector(selector);
-                if (sourceEl) {
-                    sourceEl.addEventListener('change', function () {
-                        if (this.value !== toggle.dataset.toggleValue) {
-                            toggle.classList.add('is-hidden');
-                        } else {
-                            toggle.classList.remove('is-hidden');
-                        }
-                    });
-                }
-            }
+rava.bind('.toggle-value', {
+  callbacks: {
+    created() {
+      const source = this.getAttribute('data-toggle-source');
+      const selector = `input[name="${source}"], select[name="${source}"]`;
+      const toggle = this;
+      const sourceEl = document.querySelector(selector);
+      function handleChange() {
+        if (this.value !== toggle.dataset.toggleValue) {
+          toggle.classList.add('is-hidden');
+        } else {
+          toggle.classList.remove('is-hidden');
         }
-    });
-
-}(window.rava = window.rava || {}));
\ No newline at end of file
+      }
+      if (sourceEl) {
+        sourceEl.addEventListener('change', handleChange);
+      }
+    },
+  },
+});
diff --git a/ui/src/main/frontend/js/editor.js b/ui/src/main/frontend/js/editor.js
index bff4680..62ab929 100644
--- a/ui/src/main/frontend/js/editor.js
+++ b/ui/src/main/frontend/js/editor.js
@@ -16,297 +16,292 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-/* eslint-env browser */
-(function () {
-    'use strict';
-    if (!window.CMSEditor) {
-        var mouseX,
-            mouseY,
-            CMSEditor = {
-                draggable: null,
-                init: function () {
-                    CMSEditor.util.attachEvents(document);
-                    // closing the modal
-                    CMSEditor.util.attachClick(document, '.sling-cms-editor .modal-close, .sling-cms-editor .modal-background', function () {
-                        CMSEditor.ui.hideModal();
-                    });
-                    window.addEventListener('keypress', function (e) {
-                        if (e.keyCode === 27 && CMSEditor.ui.modalDisplayed === true) {
-                            CMSEditor.ui.hideModal();
-                        }
-                    });
-                },
-                ui: {
-                    draggable: function (element) {
-                        CMSEditor.draggable = element;
-                        var mouseDown = false,
-                            elementX = 0,
-                            elementY = 0,
-                            moveComplete = function () {
-                                mouseDown = false;
-                                elementX = parseInt(CMSEditor.draggable.style.left, 10) || 0;
-                                elementY = parseInt(CMSEditor.draggable.style.top, 10) || 0;
-                                return false;
-                            };
 
-                        // mouse button down over the element
-                        CMSEditor.draggable.addEventListener('mousedown', function (evt) {
-                            if (!evt.target.matches('.modal-title, .modal-title *')) {
-                                return;
-                            }
-                            mouseX = evt.clientX;
-                            mouseY = evt.clientY;
-                            mouseDown = true;
-                        });
-                        CMSEditor.draggable.addEventListener('mouseup', moveComplete);
-                        document.addEventListener('mouseout', moveComplete);
-                        document.addEventListener('mousemove', function (event) {
-                            if (!mouseDown) {
-                                return;
-                            }
-                            var deltaX = event.clientX - mouseX, deltaY = event.clientY - mouseY;
-                            CMSEditor.draggable.style.left = elementX + deltaX + 'px';
-                            CMSEditor.draggable.style.top = elementY + deltaY + 'px';
-                            return false;
-                        });
-                    },
-                    modalDisplayed: false,
-                    hideModal: function () {
-                        if (CMSEditor.ui.modalDisplayed) {
-                            if (document.querySelector('.sling-cms-editor .modal')) {
-                                document.querySelector('.sling-cms-editor .modal').remove();
-                            }
-                            CMSEditor.ui.modalDisplayed = false;
-                        }
-                    },
-                    reloadComponent: function (path, cb) {
-                        var component = document.querySelector('.sling-cms-component[data-sling-cms-resource-path="' + path + '"]'),
-                            reloadPage = component.dataset.reload  === 'true';
-                        while (!component && path.length > 1) {
-                            var pathArr = path.split('\/');
-                            pathArr.pop();
-                            path = pathArr.join('/');
-                            component = document.querySelector('.sling-cms-component[data-sling-cms-resource-path="' + path + '"]');
-                            if(component.dataset.reload  === 'true') {
-                                reloadPage = true;
-                            }
-                        }
-                        if (!component || reloadPage) {
-                            CMSEditor.ui.hideModal();
-                            window.top.location.reload();
-                        }
-                        fetch('/cms/page/pagewrapper.html' + path + '?forceResourceType=' + component.dataset.slingCmsResourceType, {
-                            redirect: 'manual'
-                        }).then(function (response) {
-                            if (!response.ok) {
-                                CMSEditor.ui.hideModal();
-                                window.top.location.reload();
-                            }
-                            return response.text();
-                        }).then(function (html) {
-                            var tmp = document.createElement('div');
-                            tmp.innerHTML = html;
-                            CMSEditor.util.attachEvents(tmp);
-                            component.replaceWith(tmp.querySelector('.sling-cms-component'));
-                            tmp.remove();
-                            CMSEditor.ui.hideModal();
-                            cb();
-                        });
-                    },
-                    showModal: function (url, title) {
-                        title = title || '';
-                        if (CMSEditor.ui.modalDisplayed) {
-                            CMSEditor.ui.hideModal();
-                        }
-                        document.querySelector('.sling-cms-editor-final').innerHTML = '<div class="modal"><div class="modal-background"></div><div class="modal-content"><div class="box"><h3 class="modal-title"></h3><section class="modal-body"></section></div></div><button class="modal-close is-large" aria-label="close"></button>';
-                        document.querySelector('.sling-cms-editor .modal-title').innerText = title;
-                        document.querySelector('.sling-cms-editor .modal-body').innerHTML = '<iframe class="modal-frame" src="' + url + '"></iframe>';
-                        document.querySelector('.sling-cms-editor .modal').classList.add('is-active');
-                        CMSEditor.util.attachClick(document, '.sling-cms-editor .modal-background, .sling-cms-editor .modal-close', function () {
-                            CMSEditor.ui.hideModal();
-                        });
-                        CMSEditor.ui.draggable(document.querySelector('.sling-cms-editor .modal-content'));
-                        CMSEditor.ui.modalDisplayed = true;
-                    }
-                },
-                util: {
-                    attachClick: function (ctx, exp, cb) {
-                        ctx.querySelectorAll(exp).forEach(function (el) {
-                            el.addEventListener('click', cb, false);
-                        });
-                    },
-                    attachDrag: function(ed) {
-                        let dragImage;
-                        let dragActive = false;
-                        let handleOver = function(evt) {
-                            evt.preventDefault();
-                            document.querySelectorAll('.sling-cms-droptarget').forEach(dt => {
-                                dt.classList.remove('sling-cms-droptarget__is-over');
-                            });
-                            evt.target.classList.add('sling-cms-droptarget__is-over');
-                        };
-                        let handleLeave = function(evt) {
-                            evt.target.classList.remove('sling-cms-droptarget__is-over');
-                        };
-                        let handleDrop = function(evt) {
-                            evt.preventDefault();
+if (!window.CMSEditor) {
+  const { Sling } = window;
+  let mouseX;
+  let mouseY;
+  const CMSEditor = {
+    draggable: null,
+    init() {
+      CMSEditor.util.attachEvents(document);
+      // closing the modal
+      CMSEditor.util.attachClick(document, '.sling-cms-editor .modal-close, .sling-cms-editor .modal-background', () => {
+        CMSEditor.ui.hideModal();
+      });
+      window.addEventListener('keypress', (e) => {
+        if (e.keyCode === 27 && CMSEditor.ui.modalDisplayed === true) {
+          CMSEditor.ui.hideModal();
+        }
+      });
+    },
+    ui: {
+      draggable(element) {
+        CMSEditor.draggable = element;
+        let mouseDown = false;
+        let elementX = 0;
+        let elementY = 0;
+        function moveComplete() {
+          mouseDown = false;
+          elementX = parseInt(CMSEditor.draggable.style.left, 10) || 0;
+          elementY = parseInt(CMSEditor.draggable.style.top, 10) || 0;
+          return false;
+        }
+
+        // mouse button down over the element
+        CMSEditor.draggable.addEventListener('mousedown', (evt) => {
+          if (!evt.target.matches('.modal-title, .modal-title *')) {
+            return;
+          }
+          mouseX = evt.clientX;
+          mouseY = evt.clientY;
+          mouseDown = true;
+        });
+        CMSEditor.draggable.addEventListener('mouseup', moveComplete);
+        document.addEventListener('mouseout', moveComplete);
+        document.addEventListener('mousemove', (event) => {
+          if (!mouseDown) {
+            return;
+          }
+          const deltaX = event.clientX - mouseX;
+          const deltaY = event.clientY - mouseY;
+          CMSEditor.draggable.style.left = `${elementX + deltaX}px`;
+          CMSEditor.draggable.style.top = `${elementY + deltaY}px`;
+        });
+      },
+      modalDisplayed: false,
+      hideModal() {
+        if (CMSEditor.ui.modalDisplayed) {
+          if (document.querySelector('.sling-cms-editor .modal')) {
+            document.querySelector('.sling-cms-editor .modal').remove();
+          }
+          CMSEditor.ui.modalDisplayed = false;
+        }
+      },
+      reloadComponent(ip, cb) {
+        let path = ip;
+        let component = document.querySelector(`.sling-cms-component[data-sling-cms-resource-path="${path}"]`);
+        let reloadPage = component.dataset.reload === 'true';
+        while (!component && path.length > 1) {
+          const pathArr = path.split('/');
+          pathArr.pop();
+          path = pathArr.join('/');
+          component = document.querySelector(`.sling-cms-component[data-sling-cms-resource-path="${path}"]`);
+          if (component.dataset.reload === 'true') {
+            reloadPage = true;
+          }
+        }
+        if (!component || reloadPage) {
+          CMSEditor.ui.hideModal();
+          window.top.location.reload();
+        }
+        fetch(`/cms/page/pagewrapper.html${path}?forceResourceType=${component.dataset.slingCmsResourceType}`, {
+          redirect: 'manual',
+        }).then((response) => {
+          if (!response.ok) {
+            CMSEditor.ui.hideModal();
+            window.top.location.reload();
+          }
+          return response.text();
+        }).then((html) => {
+          const tmp = document.createElement('div');
+          tmp.innerHTML = html;
+          CMSEditor.util.attachEvents(tmp);
+          component.replaceWith(tmp.querySelector('.sling-cms-component'));
+          tmp.remove();
+          CMSEditor.ui.hideModal();
+          cb();
+        });
+      },
+      showModal(url, t) {
+        const title = t || '';
+        if (CMSEditor.ui.modalDisplayed) {
+          CMSEditor.ui.hideModal();
+        }
+        document.querySelector('.sling-cms-editor-final').innerHTML = '<div class="modal"><div class="modal-background"></div><div class="modal-content"><div class="box"><h3 class="modal-title"></h3><section class="modal-body"></section></div></div><button class="modal-close is-large" aria-label="close"></button>';
+        document.querySelector('.sling-cms-editor .modal-title').innerText = title;
+        document.querySelector('.sling-cms-editor .modal-body').innerHTML = `<iframe class="modal-frame" src="${url}"></iframe>`;
+        document.querySelector('.sling-cms-editor .modal').classList.add('is-active');
+        CMSEditor.util.attachClick(document, '.sling-cms-editor .modal-background, .sling-cms-editor .modal-close', () => {
+          CMSEditor.ui.hideModal();
+        });
+        CMSEditor.ui.draggable(document.querySelector('.sling-cms-editor .modal-content'));
+        CMSEditor.ui.modalDisplayed = true;
+      },
+    },
+    util: {
+      attachClick(ctx, exp, cb) {
+        ctx.querySelectorAll(exp).forEach((el) => {
+          el.addEventListener('click', cb, false);
+        });
+      },
+      attachDrag(ed) {
+        let dragImage;
+        let dragActive = false;
+        function handleOver(evt) {
+          evt.preventDefault();
+          document.querySelectorAll('.sling-cms-droptarget').forEach((dt) => {
+            dt.classList.remove('sling-cms-droptarget__is-over');
+          });
+          evt.target.classList.add('sling-cms-droptarget__is-over');
+        }
+        function handleLeave(evt) {
+          evt.target.classList.remove('sling-cms-droptarget__is-over');
+        }
+        function handleDrop(evt) {
+          evt.preventDefault();
 
-                            let sourcePath = evt.dataTransfer.getData('path');
-                            let sourceName = evt.dataTransfer.getData('name');
-                            let targetPath = evt.target.dataset.path + '/' + sourceName;
-                            
-                            let move = function() {
-                                var formData = new FormData();
-                                formData.append('_charset_', 'utf-8');
-                                if(sourcePath !== targetPath){
-                                    formData.append(':dest', targetPath);
-                                    formData.append(':operation', 'move');
-                                }
-                                formData.append(':nameHint', sourceName);
-                                formData.append(':order',  evt.target.dataset.order);
-                                let ui;
-                                if(typeof Sling !== 'undefined') {
-                                    ui = Sling.CMS.ui;
-                                } else {
-                                    ui = window.parent.Sling.CMS.ui
-                                }
-                                fetch(sourcePath, {
-                                    method: 'POST',
-                                    body: formData,
-                                    cache: 'no-cache',
-                                    headers: {
-                                        "Accept": "application/json"
-                                    }
-                                }).then(function (response) {
-                                    if (!response.ok) {
-                                        throw new Error(response.json());
-                                    }
-                                    return response.json();
-                                }).catch(function (error) {
-                                    ui.confirmMessage('Failed to move', 'Failed to move: '+error, function () {});
-                                }).then(function (res) {
-                                    if(res){
-                                        ui.confirmReload(res, 'success');
-                                    }
-                                });
-                                evt.target.classList.remove('sling-cms-droptarget__is-over');
-                            }
-                            if(evt.target.dataset.create) {
-                                var formData = new FormData();
-                                formData.append('_charset_', 'utf-8');
-                                formData.append('jcr:primaryType',  'nt:unstructured');
-                                let ui;
-                                if(typeof Sling !== 'undefined') {
-                                    ui = Sling.CMS.ui;
-                                } else {
-                                    ui = window.parent.Sling.CMS.ui
-                                }
-                                fetch(evt.target.dataset.path, {
-                                    method: 'POST',
-                                    body: formData,
-                                    cache: 'no-cache',
-                                    headers: {
-                                        "Accept": "application/json"
-                                    }
-                                }).then(function (response) {
-                                    if (!response.ok) {
-                                        throw new Error(response.statusText);
-                                    }
-                                    return response.json();
-                                }).catch(function (error) {
-                                    ui.confirmMessage(error.message, error.message, function () {});
-                                }).then(function (res) {
-                                    move();
-                                });
-                            } else {
-                                move();
-                            }
-                        };
-                        let activateTargets = function(){
-                            if(dragActive) {
-                                document.querySelectorAll('.sling-cms-droptarget').forEach(dt => {
-                                    dt.classList.add('sling-cms-droptarget__is-active');
-                                    dt.addEventListener('dragover', handleOver);
-                                    dt.addEventListener('dragleave', handleLeave);
-                                    dt.addEventListener('drop', handleDrop);
-                                });
-                            }
-                        }
-                        let deactivateTargets = function(){
-                            dragActive = false;
-                            document.querySelectorAll('.sling-cms-droptarget').forEach(dt => {
-                                dt.classList.remove('sling-cms-droptarget__is-active');
-                                dt.removeEventListener('dragover', handleOver);
-                                dt.removeEventListener('dragleave', handleLeave);
-                                dt.removeEventListener('drop', handleDrop);
-                            });
-                            if(dragImage) {
-                                dragImage.remove();
-                            }
-                        }
-                        ed.addEventListener('dragstart', function(evt) {
-                            evt.stopPropagation();
-                            dragActive = true;
-                            evt.dataTransfer.effectAllowed = 'move';
-                            dragImage = document.createElement('div');
-                            dragImage.setAttribute('style', 'position: absolute; left: 0px; top: 0px; width: ' + ed.offsetWidth + 
-                                'px; height: ' + ed.offsetHeight+  'px; padding: 0.5em; background: grey; z-index: -1');
-                            dragImage.innerText = ed.querySelector('.level-right').innerText;
-                            document.body.appendChild(dragImage);
-                            evt.dataTransfer.setDragImage(dragImage, 20, 20);
-                            evt.dataTransfer.setData('path', ed.closest('.sling-cms-component').dataset.slingCmsResourcePath);
-                            evt.dataTransfer.setData('name', ed.closest('.sling-cms-component').dataset.slingCmsResourceName);
-                            setTimeout(activateTargets, 10);
-                        });
-                        ed.addEventListener('dragend', deactivateTargets);
-                    },
-                    attachEvents: function (ctx) {
-                        CMSEditor.util.attachClick(ctx, '.sling-cms-editor .action-button', function (event) {
-                            event.preventDefault();
-                            CMSEditor.ui.showModal(this.href, this.title);
-                        });
-                        if (ctx.matches && ctx.matches('.sling-cms-component')) {
-                            CMSEditor.util.attachHover(c);
-                        }
-                        ctx.querySelectorAll('.sling-cms-component').forEach(c => {
-                            CMSEditor.util.attachHover(c);
-                        });
-                        ctx.querySelectorAll('.sling-cms-editor').forEach(CMSEditor.util.attachDrag);
-                    },
-                    attachHover: function (ctx) {
-                        let resetActive = function() {
-                            document.querySelectorAll('.sling-cms-component__is-active').forEach(a => {
-                                a.classList.remove('sling-cms-component__is-active'); 
-                            });
-                        }
-                        ctx.addEventListener('mouseover', function(evt){
-                            resetActive();
-                            ctx.classList.add('sling-cms-component__is-active');
-                            evt.stopPropagation();
-                        });
-                        ctx.addEventListener('mouseleave', resetActive);
-                    },
-                    findParent: function (el, exp) {
-                        if (el === null || el.parentElement === null) {
-                            return null;
-                        } else if (el.parentElement.matches(exp)) {
-                            return el.parentElement;
-                        } else {
-                            return CMSEditor.util.findParent(el.parentElement, exp);
-                        }
-                    }
-                }
-            };
-        window.CMSEditor = CMSEditor;
-        window.onbeforeunload = function () {
-            if (CMSEditor.ui.modalDisplayed) {
-                return "Are you sure you want to leave this page?";
+          const sourcePath = evt.dataTransfer.getData('path');
+          const sourceName = evt.dataTransfer.getData('name');
+          const targetPath = `${evt.target.dataset.path}/${sourceName}`;
+
+          async function move() {
+            const formData = new FormData();
+            formData.append('_charset_', 'utf-8');
+            if (sourcePath !== targetPath) {
+              formData.append(':dest', targetPath);
+              formData.append(':operation', 'move');
+            }
+            formData.append(':nameHint', sourceName);
+            formData.append(':order', evt.target.dataset.order);
+            let ui;
+            if (typeof Sling !== 'undefined') {
+              ui = Sling.CMS.ui;
+            } else {
+              ui = window.parent.Sling.CMS.ui;
+            }
+            const response = await fetch(sourcePath, {
+              method: 'POST',
+              body: formData,
+              cache: 'no-cache',
+              headers: {
+                Accept: 'application/json',
+              },
+            });
+            const res = await response.json();
+            if (!response.ok) {
+              ui.confirmMessage('Failed to move', res['status.message'] || response.statusText, () => {});
+            } else {
+              ui.confirmReload(res, 'success');
             }
-        };
-        if (document.readyState === 'complete') {
-            window.CMSEditor.init();
-        } else {
-            document.addEventListener('DOMContentLoaded', CMSEditor.init, false);
+            evt.target.classList.remove('sling-cms-droptarget__is-over');
+          }
+          if (evt.target.dataset.create) {
+            const formData = new FormData();
+            formData.append('_charset_', 'utf-8');
+            formData.append('jcr:primaryType', 'nt:unstructured');
+            let ui;
+            if (typeof Sling !== 'undefined') {
+              ui = Sling.CMS.ui;
+            } else {
+              ui = window.parent.Sling.CMS.ui;
+            }
+            fetch(evt.target.dataset.path, {
+              method: 'POST',
+              body: formData,
+              cache: 'no-cache',
+              headers: {
+                Accept: 'application/json',
+              },
+            }).then((response) => {
+              if (!response.ok) {
+                throw new Error(response.statusText);
+              }
+              return response.json();
+            }).catch((error) => {
+              ui.confirmMessage(error.message, error.message, () => {});
+            }).then(() => {
+              move();
+            });
+          } else {
+            move();
+          }
+        }
+        function activateTargets() {
+          if (dragActive) {
+            document.querySelectorAll('.sling-cms-droptarget').forEach((dt) => {
+              dt.classList.add('sling-cms-droptarget__is-active');
+              dt.addEventListener('dragover', handleOver);
+              dt.addEventListener('dragleave', handleLeave);
+              dt.addEventListener('drop', handleDrop);
+            });
+          }
+        }
+        function deactivateTargets() {
+          dragActive = false;
+          document.querySelectorAll('.sling-cms-droptarget').forEach((dt) => {
+            dt.classList.remove('sling-cms-droptarget__is-active');
+            dt.removeEventListener('dragover', handleOver);
+            dt.removeEventListener('dragleave', handleLeave);
+            dt.removeEventListener('drop', handleDrop);
+          });
+          if (dragImage) {
+            dragImage.remove();
+          }
+        }
+        ed.addEventListener('dragstart', (evt) => {
+          const event = evt;
+          event.stopPropagation();
+          dragActive = true;
+          event.dataTransfer.effectAllowed = 'move';
+          dragImage = document.createElement('div');
+          dragImage.setAttribute('style', `position: absolute; left: 0px; top: 0px; width: ${ed.offsetWidth
+          }px; height: ${ed.offsetHeight}px; padding: 0.5em; background: grey; z-index: -1`);
+          dragImage.innerText = ed.querySelector('.level-right').innerText;
+          document.body.appendChild(dragImage);
+          event.dataTransfer.setDragImage(dragImage, 20, 20);
+          event.dataTransfer.setData('path', ed.closest('.sling-cms-component').dataset.slingCmsResourcePath);
+          event.dataTransfer.setData('name', ed.closest('.sling-cms-component').dataset.slingCmsResourceName);
+          setTimeout(activateTargets, 10);
+        });
+        ed.addEventListener('dragend', deactivateTargets);
+      },
+      attachEvents(ctx) {
+        CMSEditor.util.attachClick(ctx, '.sling-cms-editor .action-button', function (event) {
+          event.preventDefault();
+          CMSEditor.ui.showModal(this.href, this.title);
+        });
+        if (ctx.matches && ctx.matches('.sling-cms-component')) {
+          CMSEditor.util.attachHover(ctx);
+        }
+        ctx.querySelectorAll('.sling-cms-component').forEach((c) => {
+          CMSEditor.util.attachHover(c);
+        });
+        ctx.querySelectorAll('.sling-cms-editor').forEach(CMSEditor.util.attachDrag);
+      },
+      attachHover(ctx) {
+        function resetActive() {
+          document.querySelectorAll('.sling-cms-component__is-active').forEach((a) => {
+            a.classList.remove('sling-cms-component__is-active');
+          });
+        }
+        ctx.addEventListener('mouseover', (evt) => {
+          resetActive();
+          ctx.classList.add('sling-cms-component__is-active');
+          evt.stopPropagation();
+        });
+        ctx.addEventListener('mouseleave', resetActive);
+      },
+      findParent(el, exp) {
+        if (el === null || el.parentElement === null) {
+          return null;
+        } if (el.parentElement.matches(exp)) {
+          return el.parentElement;
         }
+        return CMSEditor.util.findParent(el.parentElement, exp);
+      },
+    },
+  };
+  window.CMSEditor = CMSEditor;
+  window.onbeforeunload = function () {
+    if (CMSEditor.ui.modalDisplayed) {
+      return 'Are you sure you want to leave this page?';
     }
-}());
\ No newline at end of file
+    return null;
+  };
+  if (document.readyState === 'complete') {
+    window.CMSEditor.init();
+  } else {
+    document.addEventListener('DOMContentLoaded', CMSEditor.init, false);
+  }
+}