CVE-2019-10765: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: iobroker.admin before 3.6.12 allows attacker to include file contents from outside the `/log/file1/` directory.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 9.8
Published: 2019-11-20T16:15Z
Repository: https://github.com/ioBroker/ioBroker.admin
Language: JavaScript
Files changed: README.md, gulpfile.js, io-package.json, lib/socket.js, lib/web.js, main.js, package.json, src/js/admin.js, src/js/adminAdapters.js, src/login/index.html
Commit date: 2019-10-09 18:29:20 +0200
Commit message: ### 3.6.8 (2019-10-09)
* (bluefox) Log paths were sanitized
* (bluefox) NPM packages were updated

Diff: diff --git a/README.md b/README.md
index af6297f..e4b886b 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,18 @@
 ![Logo](admin/admin.png)
 # ioBroker.admin
-===================
 
 [![NPM version](http://img.shields.io/npm/v/iobroker.admin.svg)](https://www.npmjs.com/package/iobroker.admin)
 [![Downloads](https://img.shields.io/npm/dm/iobroker.admin.svg)](https://www.npmjs.com/package/iobroker.admin)
 [![Stable](http://iobroker.live/badges/admin-stable.svg)](http://iobroker.live/badges/admin-stable.svg)
 [![installed](http://iobroker.live/badges/admin-installed.svg)](http://iobroker.live/badges/admin-installed.svg)
 
 [![NPM](https://nodei.co/npm/iobroker.admin.png?downloads=true)](https://nodei.co/npm/iobroker.admin/)
 
-
-User interface for configuration and administration.
+User interface for configuration and administration of ioBroker.
 
 ## Using common.localLink
-
 - %ip% - ioBroker ip address (address of the admin)
 - %secure% or %protocol% - read from native.secure the value and use http or https
 - %web_protocol% - looking for the first instance of web (e.g. web.0) and get "native.secure" from "system.adapter.web.0"
 - %instance% - instance of the adapter
 - %someField% - get someField from "native" of this adapter instance
@@ -80,10 +77,14 @@ This project uses some icons from [Flaticon](https://www.flaticon.com/):
 - <img src="src/img/devices/light-bulb.svg" height="48" /> - Icons made by [Vectors Market](https://www.flaticon.com/authors/vectors-market) from [Flaticon](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/).
 - <img src="src/img/rooms/garage.svg" height="48" /> - designed by [Pause08](https://www.flaticon.com/authors/Pause08) from [Flaticon](https://www.flaticon.com/)
 - <img src="src/img/rooms/toilet.svg" height="48" /> - Icons made by [Freepik](http://www.freepik.com) from [www.flaticon.com](https://www.flaticon.com/) is licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/)
 
 ## Changelog
+### 3.6.8 (2019-10-09)
+* (bluefox) Log paths were sanitized
+* (bluefox) NPM packages were updated
+
 ### 3.6.7 (2019-09-24)
 * (ldittmar) Add Node.JS version check to popup messages
 
 ### 3.6.6 (2019-09-18)
 * (SchumyHao) Update Chinese translation
@@ -274,22 +275,22 @@ This project uses some icons from [Flaticon](https://www.flaticon.com/):
 * (bluefox) buttons layout fixed
 * (bluefox) event fixes
 
 ### 3.0.11 (2018-01-11)
 * (DeepCoreSystem) French update
-* (bluefox) fix error with empty ID 
+* (bluefox) fix error with empty ID
 * (bluefox) add sort by "recently updated"
 * (ldittmar) add readme and issues viewer
 
 ### 3.0.10 (2018-01-06)
 * (bluefox) Update indication
 * (ldittmar) Use jQuery3
 * (AlCalzone) German translations
 
 ### 3.0.7 (2018-01-01)
 * (soef) update instances, objects and other lists
-* (bluefox) rewrite interface with materialize 
+* (bluefox) rewrite interface with materialize
 
 ### 2.0.11 (2017-10-23)
 * (bluefox) Configurable event update disable threshold
 
 ### 2.0.10 (2017-10-22)
diff --git a/gulpfile.js b/gulpfile.js
index 3b920a9..86bb792 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -491,17 +491,17 @@ gulp.task('materializeJS', () => {
     ])
     .pipe(sourcemaps.init())
     .pipe(concat('materialize.js'))
     .pipe(babel({
         plugins: [
-            'transform-es2015-arrow-functions',
-            'transform-es2015-block-scoping',
-            'transform-es2015-classes',
-            'transform-es2015-template-literals'
+            '@babel/plugin-transform-arrow-functions',
+            '@babel/plugin-transform-block-scoping',
+            '@babel/plugin-transform-classes',
+            '@babel/plugin-transform-template-literals'
         ]
     }))
-    .pipe(uglify())    
+    .pipe(uglify())
     .pipe(sourcemaps.write('.'))
     .pipe(gulp.dest('./www/lib/js'));
 });
 
 gulp.task('configCSS', () => {
diff --git a/io-package.json b/io-package.json
index fc218a8..7bb1f0f 100644
--- a/io-package.json
+++ b/io-package.json
@@ -1,10 +1,10 @@
 {
   "common": {
     "name": "admin",
     "title": "Admin",
-    "version": "3.6.7",
+    "version": "3.6.8",
     "news": {
       "3.6.7": {
         "en": "Add Node.JS version check to popup messages",
         "de": "Node.JS-Versionsprüfung zu Popup-Nachrichten hinzufügen",
         "ru": "Добавить проверку версии Node.JS во всплывающие сообщения",
@@ -14,11 +14,22 @@
         "it": "Aggiungi il controllo versione Node.JS ai messaggi popup",
         "es": "Agregar verificación de versión de Node.JS a mensajes emergentes",
         "pl": "Dodaj sprawdzanie wersji Node.JS do wyskakujących wiadomości",
         "zh-cn": "将Node.JS版本检查添加到弹出消息"
       },
-      "3.6.6": {
+      "3.6.7": {
+        "en": "Add Node.JS version check to popup messages",
+        "de": "Node.JS-Versionsprüfung zu Popup-Nachrichten hinzufügen",
+        "ru": "Добавить проверку версии Node.JS во всплывающие сообщения",
+        "pt": "Adicione a verificação da versão do Node.JS às mensagens pop-up",
+        "nl": "Voeg Node.JS versiecontrole toe aan pop-upberichten",
+        "fr": "Ajouter la vérification de la version de Node.JS aux messages contextuels",
+        "it": "Aggiungi il controllo versione Node.JS ai messaggi popup",
+        "es": "Agregar verificación de versión de Node.JS a mensajes emergentes",
+        "pl": "Dodaj sprawdzanie wersji Node.JS do wyskakujących wiadomości",
+        "zh-cn": "将Node.JS版本检查添加到弹出消息"
+      },"3.6.6": {
         "en": "update some words translation",
         "de": "Aktualisieren Sie einige Wörter Übersetzung",
         "ru": "обновить перевод некоторых слов",
         "pt": "atualizar algumas palavras tradução",
         "nl": "update enkele woorden vertaling",
@@ -85,11 +96,11 @@
         "fr": "Intégration de l'adaptateur d'informations",
         "it": "Integrazione dell'adattatore di informazioni",
         "es": "Integración del adaptador de información",
         "pl": "Integracja adaptera informacyjnego",
         "zh-cn": "信息适配器集成"
-      },    
+      },
       "3.6.0": {
         "en": "Added Chinese translations\nNew informative states added about updates",
         "de": "Chinesische Übersetzungen hinzugefügt\nNeue Informationsstände zu Updates hinzugefügt",
         "ru": "Добавлены китайские переводы\nДобавлены новые информационные состояния об обновлениях",
         "pt": "Adicionado traduções chinesas\nNovos estados informativos adicionados sobre atualizações",
@@ -160,11 +171,11 @@
       "nl": "docs/nl/admin.md",
       "es": "docs/es/admin.md",
       "fr": "docs/fr/admin.md",
       "it": "docs/it/admin.md",
       "pl": "docs/pl/admin.md",
-      "zh-cn": "docs/zh-cn/admin.md"            
+      "zh-cn": "docs/zh-cn/admin.md"
     },
     "materialize": true,
     "mode": "daemon",
     "platform": "Javascript/Node.js",
     "loglevel": "info",
diff --git a/lib/socket.js b/lib/socket.js
index f4d4399..3f0ae4d 100644
--- a/lib/socket.js
+++ b/lib/socket.js
@@ -7,20 +7,23 @@
 const socketio = require('socket.io');
 const request  = require('request');
 const path     = require('path');
 const fs       = require('fs');
 
-function IOSocket(server, settings, adapter, objects, states, store) {
-    if (!(this instanceof IOSocket)) return new IOSocket(server, settings, adapter, objects, states, store);
+function IOSocket(server, settings, adapter, objects, store) {
+    if (!(this instanceof IOSocket)) {
+        return new IOSocket(server, settings, adapter, objects, store);
+    }
 
     const userKey = 'connect.sid'; // const
     const cmdSessions = {};
     const that = this;
     this.server = null;
     this.subscribes = {};
     let cookieParser;
     let passport;
+    let states = {};
 
     if (settings.auth) {
         cookieParser = require('cookie-parser');
         passport     = require('passport');
     }
@@ -431,14 +434,17 @@ function IOSocket(server, settings, adapter, objects, states, store) {
         }
     }
 
     this.unsubscribe = function (socket, type, pattern) {
         //console.log((socket._name || socket.id) + ' unsubscribe ' + pattern);
-        if (!this.subscribes[type]) this.subscribes[type] = {};
+        this.subscribes[type] = this.subscribes[type] || {};
 
         if (socket) {
-            if (!socket._subscribe || !socket._subscribe[type]) return;
+            if (!socket._subscribe || !socket._subscribe[type]) {
+                return;
+            }
+
             for (let i = socket._subscribe[type].length - 1; i >= 0; i--) {
                 if (socket._subscribe[type][i].pattern === pattern) {
 
                     // Remove pattern from global list
                     if (this.subscribes[type][pattern] !== undefined) {
@@ -537,11 +543,11 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                 } else if (type === 'objectChange') {
                     adapter.log.debug('Subscribe OBJECTS: ' + pattern);
                     adapter.subscribeForeignObjects && adapter.subscribeForeignObjects(pattern);
                 } else if (type === 'log') {
                     adapter.log.debug('Subscribe LOGS');
-                    if (adapter.requireLog) adapter.requireLog(true);
+                    adapter.requireLog && adapter.requireLog(true);
                 }
             } else {
                 that.subscribes[type][pattern]++;
             }
         }
@@ -690,55 +696,63 @@ function IOSocket(server, settings, adapter, objects, states, store) {
          *      states
          */
         socket.on('getStates', function (callback) {
             if (updateSession(socket) && checkPermissions(socket, 'getStates', callback)) {
                 if (typeof callback === 'function') {
-                    callback(null, states);
+                    adapter.getForeignStates('*', callback);
                 } else {
                     adapter.log.warn('[getStates] Invalid callback')
                 }
             }
         });
 
         socket.on('getState', function (id, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'getState', callback, id)) {
                 if (typeof callback === 'function') {
-                    callback(null, states[id]);
+                    if (states[id]) {
+                        callback(null, states[id]);
+                    } else {
+                        adapter.getForeignState(id, callback);
+                    }
                 } else {
                     adapter.log.warn('[getState] Invalid callback')
                 }
             }
         });
 
         socket.on('getForeignStates', function (pattern, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'getStates', callback)) {
-                adapter.getForeignStates(pattern, function (err, objs) {
-                    if (typeof callback === 'function') {
-                        callback(err, objs);
-                    } else {
-                        adapter.log.warn('[getForeignStates] Invalid callback')
-                    }
-                });
+                if (typeof callback === 'function') {
+                    adapter.getForeignStates(pattern, callback);
+                } else {
+                    adapter.log.warn('[getForeignStates] Invalid callback')
+                }
             }
         });
 
         socket.on('setState', function (id, state, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'setState', callback, id)) {
                 if (typeof state !== 'object') {
                     state = {val: state};
                 }
 
-                adapter.setForeignState(id, state, {user: this._acl.user}, function (err, res) {
-                    if (typeof callback === 'function') {
-                        callback(err, res);
-                    }
-                });
+                // clear cache
+                if (states[id]) {
+                    delete states[id];
+                }
+
+                adapter.setForeignState(id, state, {user: this._acl.user}, (err, res) =>
+                    typeof callback === 'function' && callback(err, res));
             }
         });
 
         socket.on('delState', function (id, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'delState', callback, id)) {
+                // clear cache
+                if (states[id]) {
+                    delete states[id];
+                }
                 adapter.delForeignState(id, {user: this._acl.user}, callback);
             }
         });
 
         socket.on('requireLog', function (isEnabled, callback) {
@@ -963,24 +977,26 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                     setImmediate(callback, null);
                 }
             }
         });
 
-
         socket.on('unsubscribe', function (pattern, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'unsubscribe', callback, pattern)) {
                 if (pattern && typeof pattern === 'object' && pattern instanceof Array) {
                     for (let p = 0; p < pattern.length; p++) {
                         that.unsubscribe(this, 'stateChange', pattern[p]);
                     }
                 } else {
                     that.unsubscribe(this, 'stateChange', pattern);
                 }
-                if (adapter.log.level === 'debug') showSubscribes(socket, 'stateChange');
-                if (typeof callback === 'function') {
-                    setImmediate(callback, null);
-                }
+
+                // reset states cache on unsubscribe
+                states = {};
+
+                adapter.log.level === 'debug' && showSubscribes(socket, 'stateChange');
+
+                typeof callback === 'function' && setImmediate(callback, null);
             }
         });
 
         // same as 'unsubscribe'
         socket.on('unsubscribeStates', function (pattern, callback) {
@@ -1166,25 +1182,28 @@ function IOSocket(server, settings, adapter, objects, states, store) {
             eventsThreshold.active = false;
             eventsThreshold.timeActivated = 0;
             adapter.log.info('Subscribe on all states again');
 
             setTimeout(function () {
-                if (readAll) {
+                /*if (readAll) {
                     adapter.getForeignStates('*', function (err, res) {
                         adapter.log.info('received all states');
                         for (const id in res) {
                             if (res.hasOwnProperty(id) && JSON.stringify(states[id]) !== JSON.stringify(res[id])) {
                                 that.server.sockets.emit('stateChange', id, res[id]);
                                 states[id] = res[id];
                             }
                         }
                     });
-                }
+                }*/
 
                 that.server.sockets.emit('eventsThreshold', false);
                 adapter.unsubscribeForeignStates('system.adapter.*');
-                adapter.subscribeForeignStates('*');
+
+                Object.keys(that.subscribes.stateChange).forEach(pattern =>
+                    adapter.subscribeForeignStates(pattern));
+
             }, 50);
         }
     }
 
     function enableEventThreshold() {
@@ -1194,11 +1213,14 @@ function IOSocket(server, settings, adapter, objects, states, store) {
             setTimeout(function () {
                 adapter.log.info('Unsubscribe from all states, except system\'s, because over ' + eventsThreshold.repeatSeconds + ' seconds the number of events is over ' + eventsThreshold.value + ' (in last second ' + eventsThreshold.count + ')');
                 eventsThreshold.timeActivated = new Date().getTime();
 
                 that.server.sockets.emit('eventsThreshold', true);
-                adapter.unsubscribeForeignStates('*');
+
+                Object.keys(that.subscribes.stateChange).forEach(pattern =>
+                    adapter.unsubscribeForeignStates(pattern));
+
                 adapter.subscribeForeignStates('system.adapter.*');
             }, 100);
         }
     }
 
@@ -1238,10 +1260,17 @@ function IOSocket(server, settings, adapter, objects, states, store) {
             that.server.sockets.emit('log', obj);
         }
     };
 
     this.stateChange = function (id, state) {
+        if (!state) {
+            if (states[id]) {
+                delete states[id];
+            }
+        } else {
+            states[id] = state;
+        }
         const clients = that.server.sockets.connected;
 
         if (!eventsThreshold.active) {
             eventsThreshold.count++;
         }
@@ -1307,6 +1336,7 @@ function IOSocket(server, settings, adapter, objects, states, store) {
         }
     })();
 
     return this;
 }
+
 module.exports = IOSocket;
\ No newline at end of file
diff --git a/lib/web.js b/lib/web.js
index ce42c41..cc2ac16 100644
--- a/lib/web.js
+++ b/lib/web.js
@@ -19,10 +19,59 @@ let LocalStrategy;
 let flash;
 let cookieParser;
 let fileUpload;
 let socketIoFile;
 
+// copied from here: https://github.com/component/escape-html/blob/master/index.js
+const matchHtmlRegExp = /["'&<>]/;
+function escapeHtml (string) {
+    const str = '' + string;
+    const match = matchHtmlRegExp.exec(str);
+
+    if (!match) {
+        return str;
+    }
+
+    let escape;
+    let html = '';
+    let index = 0;
+    let lastIndex = 0;
+
+    for (index = match.index; index < str.length; index++) {
+        switch (str.charCodeAt(index)) {
+            case 34: // "
+                escape = '&quot;';
+                break;
+            case 38: // &
+                escape = '&amp;';
+                break;
+            case 39: // '
+                escape = '&#39;';
+                break;
+            case 60: // <
+                escape = '&lt;';
+                break;
+            case 62: // >
+                escape = '&gt;';
+                break;
+            default:
+                continue;
+        }
+
+        if (lastIndex !== index) {
+            html += str.substring(lastIndex, index);
+        }
+
+        lastIndex = index + 1;
+        html += escape;
+    }
+
+    return lastIndex !== index
+        ? html + str.substring(lastIndex, index)
+        : html;
+}
+
 function Web(settings, adapter, onReady) {
     if (!(this instanceof Web)) return new Web(settings, adapter, onReady);
     const server = {
         app:       null,
         server:    null
@@ -229,19 +278,27 @@ function Web(settings, adapter, onReady) {
                 server.app.use(passport.session());
                 server.app.use(flash());
 
                 server.app.post('/login', (req, res, next) => {
                     let redirect = '/';
-                    if (req.body.origin) {
-                        const parts = req.body.origin.match(/href=(.+)$/);
+                    const origin = (req.body && req.body.origin) || '?href=%2F';
+                    if (origin) {
+                        const parts = origin.match(/href=(.+)$/);
                         if (parts && parts[1]) {
                             redirect = decodeURIComponent(parts[1]);
+                            // if some invalid characters in redirect
+                            if (redirect.match(/[^-_a-zA-Z0-9&%?.]/)) {
+                                redirect = '/';
+                            }
                         }
                     }
+                    req.body.password = ((req.body && req.body.password) || '').toString();
+                    req.body.username = ((req.body && req.body.username) || '').toString();
+
                     passport.authenticate('local', {
                         successRedirect: redirect,
-                        failureRedirect: '/login/index.html' + req.body.origin + (req.body.origin ? '&error' : '?error'),
+                        failureRedirect: '/login/index.html' + origin + (origin ? '&error' : '?error'),
                         failureFlash:    'Invalid username or password.'
                     })(req, res, next);
                 });
 
                 server.app.get('/logout', (req, res) => {
@@ -322,11 +379,11 @@ function Web(settings, adapter, onReady) {
                 let parts = req.url.split('/');
                 let filename = parts.pop();
 
                 adapter.getBinaryState('system.host.' + adapter.host + '.zip.' + filename, (err, buff) => {
                     if (err) {
-                        res.status(500).send(err);
+                        res.status(500).send(escapeHtml(typeof err === 'string' ? err : JSON.stringify(err)));
                     } else {
                         if (!buff) {
                             res.status(404).send('File package.zip not found');
                         } else {
                             // remove file
@@ -338,31 +395,34 @@ function Web(settings, adapter, onReady) {
                 });
             });
 
             // send log files
             server.app.get('/log/*', (req, res) => {
-                let parts = req.url.split('/');
+                let parts = decodeURIComponent(req.url).split('/');
                 parts = parts.splice(2);
                 const transport = parts.shift();
                 let filename = parts.join('/');
                 const config = adapter.systemConfig;
                 // detect file log
                 if (config && config.log && config.log.transport) {
                     if (config.log.transport.hasOwnProperty(transport) && config.log.transport[transport].type === 'file') {
+                        let logFolder;
                         if (config.log.transport[transport].filename) {
                             parts = config.log.transport[transport].filename.replace(/\\/g, '/').split('/');
                             parts.pop();
-                            filename = path.join(parts.join('/'), filename);
+                            logFolder = path.normalize(parts.join('/'));
                         } else {
-                            filename = path.join('log/', filename) ;
+                            logFolder = path.join(process.cwd(), 'log');
                         }
 
-                        if (filename[0] !== '/' && !filename.match(/^\W:/)) {
-                            filename = path.normalize(__dirname + '/../../../') + filename;
+                        if (logFolder[0] !== '/' && logFolder[0] !== '\\' && !logFolder.match(/^[a-zA-Z]:/)) {
+                            logFolder = path.normalize(path.join(__dirname + '/../../../', logFolder));
                         }
 
-                        if (fs.existsSync(filename)) {
+                        filename = path.normalize(path.join(logFolder, filename));
+
+                        if (filename.startsWith(logFolder) && fs.existsSync(filename)) {
                             const stat = fs.lstatSync(filename);
                             if (stat.size > 2 * 1024 * 1024) {
                                 res.sendFile(filename);
                             } else {
                                 res.send(decorateLogFile(filename));
@@ -370,11 +430,11 @@ function Web(settings, adapter, onReady) {
 
                             return;
                         }
                     }
                 }
-                res.status(404).send('File ' + filename + ' not found');
+                res.status(404).send('File ' + escapeHtml(filename) + ' not found');
             });
             const appOptions = {};
             if (settings.cache) {
                 appOptions.maxAge = 30758400000;
             }
@@ -382,19 +442,20 @@ function Web(settings, adapter, onReady) {
             if (settings.tmpPathAllow && settings.tmpPath) {
                 server.app.use('/tmp/', express.static(settings.tmpPath, {maxAge: 0}));
                 fileUpload = fileUpload || require('express-fileupload');
                 server.app.use(fileUpload({
                     useTempFiles: true,
-                    tempFilePath: settings.tmpPath
+                    tempFileDir: settings.tmpPath
                 }));
                 server.app.post('/upload', (req, res) => {
                     if (!req.files) {
                         return res.status(400).send('No files were uploaded.');
                     }
 
                     // The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
                     let myFile;
+                    // take first non empty file
                     for (const name in req.files) {
                         if (req.files.hasOwnProperty(name)) {
                             myFile = req.files[name];
                             break;
                         }
@@ -405,11 +466,11 @@ function Web(settings, adapter, onReady) {
                             return res.status(500).send('File is too big. (Max 600MB)');
                         }
                         // Use the mv() method to place the file somewhere on your server
                         myFile.mv(settings.tmpPath + '/restore.iob', err =>  {
                             if (err) {
-                                res.status(500).send(err);
+                                res.status(500).send(escapeHtml(typeof err === 'string' ? err : JSON.stringify(err)));
                             } else {
                                 res.send('File uploaded!');
                             }
                         });
                     } else {
@@ -426,11 +487,10 @@ function Web(settings, adapter, onReady) {
                 server.app.use('/', express.static(__dirname + '/../www', appOptions));
             }
 
             // reverse proxy with url rewrite for couchdb attachments in <adapter-name>.admin
             server.app.use('/adapter/', (req, res) => {
-
                 // Example: /example/?0
                 let url = req.url;
 
                 // add index.html
                 url = url.replace(/\/($|\?|#)/, '/index.html$1');
@@ -468,11 +528,11 @@ function Web(settings, adapter, onReady) {
                     url = url.substring(0, pos);
                 }
                 adapter.readFile(id, url, null, (err, buffer, mimeType) => {
                     if (!buffer || err) {
                         res.contentType('text/html');
-                        res.status(404).send('File ' + url + ' not found');
+                        res.status(404).send('File ' + escapeHtml(url) + ' not found');
                     } else {
                         if (mimeType) {
                             res.contentType(mimeType['content-type'] || mimeType);
                         } else {
                             res.contentType('text/javascript');
diff --git a/main.js b/main.js
index 6679cb6..346794f 100644
--- a/main.js
+++ b/main.js
@@ -12,21 +12,23 @@
 /* jshint strict: false */
 /* jslint node: true */
 'use strict';
 
 const adapterName = require('./package.json').name.split('.').pop();
-const utils = require('@iobroker/adapter-core'); // Get common adapter utils
+const utils       = require('@iobroker/adapter-core'); // Get common adapter utils
 const tools 	  = require(utils.controllerDir + '/lib/tools.js');
 const SocketIO    = require('./lib/socket');
 const Web         = require('./lib/web');
 
-let socket      = null;
-let webServer   = null;
+const ONE_HOUR_MS = 3600000;
+const ERROR_PERMISSION = 'permissionError';
 
-let objects     = {};
-let states      = {};
-let secret      = 'Zgfr56gFe87jJOM'; // Will be generated by first start
+let socket        = null;
+let webServer     = null;
+
+let objects       = {};
+let secret        = 'Zgfr56gFe87jJOM'; // Will be generated by first start
 let adapter;
 
 function startAdapter(options) {
     options = options || {};
 	Object.assign(options, {
@@ -53,27 +55,15 @@ function startAdapter(options) {
                 delete objects[id];
             }
         }
 
         // TODO Build in some threshold of messages
-        if (socket) {
-            socket.objectChange(id, obj);
-        }
+        socket && socket.objectChange(id, obj);
     });
 
-    adapter.on('stateChange', (id, state) => {
-        if (!state) {
-            if (states[id]) {
-                delete states[id];
-            }
-        } else {
-            states[id] = state;
-        }
-        if (socket) {
-            socket.stateChange(id, state);
-        }
-    });
+    adapter.on('stateChange', (id, state) =>
+        socket && socket.stateChange(id, state));
 
     adapter.on('ready', () => {
         adapter.getForeignObject('system.config', (err, obj) => {
             if (!err && obj) {
                 obj.native = obj.native || {};
@@ -97,22 +87,18 @@ function startAdapter(options) {
     adapter.on('message', obj => {
         if (!obj || !obj.message) {
             return false;
         }
 
-        if (socket) {
-            socket.sendCommand(obj);
-        }
+        socket && socket.sendCommand(obj);
 
         return true;
     });
 
     adapter.on('unload', callback => {
-        if (socket) {
-            // unsubscribe all
-            socket.unsubscribeAll();
-        }
+        // unsubscribe all
+        socket && socket.unsubscribeAll();
 
         try {
             adapter.log.info('terminating http' + (adapter.config.secure ? 's' : '') + ' server on port ' + adapter.config.port);
             webServer.close();
             callback();
@@ -318,57 +304,14 @@ function writeUpdateInfo(sources) {
         adapter.setState('info.newUpdates', newUpdateIndicator, true);
         adapter.setState('info.updatesJson', JSON.stringify(updatesJson), true);
         let updateTime = new Date();
         adapter.setState('info.lastUpdateCheck', new Date(updateTime - updateTime.getTimezoneOffset() * 60000).toISOString(), true);
     });
-
-}
-
-// to do => remove it later, when all repositories patched.
-function patchRepos(callback) {
-    return callback && callback();
-    // do not patch any more. Delete it later 2018.04.23
-    /*
-    adapter.getForeignObject('system.repositories', (err, obj) => {
-        let changed = false;
-        if (obj && obj.native && obj.native.repositories) {
-            // default link should point to stable
-            if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.iobroker.net/sources-dist.json') {
-                changed = true;
-                obj.native.repositories.default = {
-                    link: 'http://download.iobroker.net/sources-dist.json'
-                };
-            }
-            // latest link should point to latest
-            if (!obj.native.repositories.latest) {
-                obj.native.repositories.latest = {
-                    link: 'http://download.iobroker.net/sources-dist-latest.json'
-                };
-                changed = true;
-            }
-
-            // change URL of raw sources from ioBroker.js-controller to ioBroker.repositories
-            for (let r in obj.native.repositories) {
-                if (obj.native.repositories.hasOwnProperty(r) &&
-                    obj.native.repositories[r].link === 'https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/conf/sources-dist.json') {
-                    obj.native.repositories[r].link = 'https://raw.githubusercontent.com/ioBroker/ioBroker.repositories/master/sources-dist.json';
-                    changed = true;
-                }
-            }
-        }
-        if (changed) {
-            adapter.setForeignObject(obj._id, obj, function () {
-                callback && callback();
-            });
-        } else {
-            callback && callback();
-        }
-    });*/
 }
 
 function initSocket(server, store) {
-    socket = new SocketIO(server, adapter.config, adapter, objects, states, store);
+    socket = new SocketIO(server, adapter.config, adapter, objects, store);
     socket.subscribe(null, 'objectChange', '*');
 }
 
 function main() {
     // adapter.subscribeForeignStates('*');
@@ -389,34 +332,34 @@ function main() {
         });
     } else {
         getData(() => webServer = new Web(adapter.config, adapter, initSocket));
     }
 
-    patchRepos(() => {
-        // By default update repository every 24 hours
-        if (adapter.config.autoUpdate === undefined) {
-            adapter.config.autoUpdate = 24;
-        }
-        adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
-        if (adapter.config.autoUpdate) {
-            setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000);
-            updateRegister();
-        }
-    });
+    // By default update repository every 24 hours
+    if (adapter.config.autoUpdate === undefined || adapter.config.autoUpdate === null) {
+        adapter.config.autoUpdate = 24;
+    }
+
+    // interval in hours
+    adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
+
+    adapter.config.autoUpdate && updateRegister();
 }
 
 function getData(callback) {
     adapter.log.info('requesting all states');
-    let tasks = 0;
+    /*
     tasks++;
+
     adapter.getForeignStates('*', (err, res) => {
         adapter.log.info('received all states');
         states = res;
         !--tasks && callback && callback();
-    });
+    });*/
+
     adapter.log.info('requesting all objects');
-    tasks++;
+
     adapter.objects.getObjectList({include_docs: true}, (err, res) => {
         adapter.log.info('received all objects');
         if (res) {
             res = res.rows;
             objects = {};
@@ -437,29 +380,63 @@ function getData(callback) {
 
             createUpdateInfo();
             writeUpdateInfo();
         }
 
-        !--tasks && callback && callback();
+        callback && callback();
     });
 }
 
 // read repository information from active repository
-function updateRegister() {
-    adapter.log.info('Request actual repository...');
-    adapter.getForeignObject('system.config', (err, data) => {
-        if (data && data.common) {
-            adapter.sendToHost(adapter.host, 'getRepository', {
-                repo:   data.common.activeRepo,
-                update: true
-            }, _repository => {
-                if (_repository === 'permissionError') {
-                    adapter.log.error('May not read "getRepository"');
-                } else {
-                    adapter.log.info('Repository received successfully.');
+function updateRegister(isForce) {
+    adapter.getForeignObject('system.config', (err, systemConfig) => {
+        err && adapter.log.error('May not read "system.config"');
+
+        if (systemConfig && systemConfig.common) {
+            adapter.getForeignObject('system.repositories', (err, repos) => {
+                err && adapter.log.error('May not read "system.repositories"');
+                // Check if repositories exists
+                let exists = false;
+                const active = systemConfig.common.activeRepo;
+
+                // if repo is valid and actual
+                if (!err &&
+                    repos &&
+                    repos.native &&
+                    repos.native.repositories &&
+                    repos.native.repositories[active] &&
+                    Date.now() < repos.ts + adapter.config.autoUpdate * ONE_HOUR_MS) {
+                    exists = true;
+                }
 
-                    socket && socket.repoUpdated();
+                if (!exists || isForce) {
+                    adapter.log.info('Request actual repository...');
+                    // request repo from host
+                    adapter.sendToHost(adapter.host, 'getRepository', {
+                        repo:   active,
+                        update: true
+                    }, _repository => {
+                        if (_repository === ERROR_PERMISSION) {
+                            adapter.log.error('May not read "getRepository"');
+                        } else {
+                            adapter.log.info('Repository received successfully.');
+
+                            socket && socket.repoUpdated();
+                        }
+
+                        // start next cycle
+                        if (adapter.config.autoUpdate) {
+                            adapter.timerRepo && clearInterval(adapter.timerRepo);
+                            adapter.log.debug('Next repo update on ' + new Date(Date.now() + adapter.config.autoUpdate * ONE_HOUR_MS + 1).toLocaleString());
+                            adapter.timerRepo = setTimeout(() => updateRegister(), adapter.config.autoUpdate * ONE_HOUR_MS + 1);
+                        }
+                    });
+                } else if (adapter.config.autoUpdate) {
+                    const interval = repos.ts + adapter.config.autoUpdate * ONE_HOUR_MS - Date.now() + 1;
+                    adapter.log.debug('Next repo update on ' + new Date(Date.now() + interval).toLocaleString());
+                    adapter.timerRepo && clearInterval(adapter.timerRepo);
+                    adapter.timerRepo = setTimeout(() => updateRegister(), interval);
                 }
             });
         }
     });
 }
diff --git a/package.json b/package.json
index 7c6dfef..6bb3b93 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
 {
   "name": "iobroker.admin",
   "description": "The adapter opens a webserver for the ioBroker admin UI.",
-  "version": "3.6.7",
+  "version": "3.6.8",
   "contributors": [
     "bluefox <dogafox@gmail.com>",
     "apollon77",
     "soef <soef@gmx.net>",
     "hobbyquaker <hq@ccu.io>"
@@ -17,45 +17,45 @@
   "keywords": [
     "ioBroker",
     "setup"
   ],
   "dependencies": {
-    "body-parser": "^1.18.2",
+    "body-parser": "^1.19.0",
     "connect-flash": "^0.1.1",
-    "cookie-parser": "^1.4.3",
-    "express": "^4.16.3",
-    "express-fileupload": "https://github.com/screenmeet/express-fileupload/tarball/8d8f5d9b7cec2d7c17e6d85654edf1c0aaa5fa82",
-    "express-session": "^1.15.6",
+    "cookie-parser": "^1.4.4",
+    "express": "^4.17.1",
+    "express-fileupload": "^1.1.6-alpha.5",
+    "express-session": "^1.16.2",
     "passport": "^0.4.0",
     "passport-local": "^1.0.0",
     "passport.socketio": "^3.7.0",
-    "request": "^2.85.0",
+    "request": "^2.88.0",
     "socket.io": "1.7.2",
-    "xtend": "^4.0.1",
+    "xtend": "^4.0.2",
     "@iobroker/adapter-core": "^1.0.3"
   },
   "devDependencies": {
-    "@iobroker/testing": "^1.2.0",
-    "babel-core": "^6.26.3",
-    "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
-    "babel-plugin-transform-es2015-block-scoping": "^6.26.0",
-    "babel-plugin-transform-es2015-classes": "^6.24.1",
-    "babel-plugin-transform-es2015-template-literals": "^6.22.0",
-    "chai": "^4.1.2",
-    "gulp": "^4.0.0",
-    "gulp-babel": "^7.0.1",
-    "gulp-clean-css": "^3.9.4",
+    "@iobroker/testing": "^1.2.5",
+    "@babel/core": "^7.6.3",
+    "@babel/plugin-transform-arrow-functions": "^7.2.0",
+    "@babel/plugin-transform-block-scoping": "^7.6.3",
+    "@babel/plugin-transform-classes": "^7.5.5",
+    "@babel/plugin-transform-template-literals": "^7.4.4",
+    "chai": "^4.2.0",
+    "gulp": "^4.0.2",
+    "gulp-babel": "^8.0.0",
+    "gulp-clean-css": "^4.2.0",
     "gulp-concat": "^2.6.1",
-    "gulp-htmlmin": "^4.0.0",
-    "gulp-less": "^4.0.0",
-    "gulp-sass": "^3.1.0",
-    "gulp-sourcemaps": "^2.6.4",
-    "gulp-terser": "^1.1.7",
+    "gulp-htmlmin": "^5.0.1",
+    "gulp-less": "^4.0.1",
+    "gulp-sass": "^4.0.2",
+    "gulp-sourcemaps": "^2.6.5",
+    "gulp-terser": "^1.2.0",
     "fancy-log": "^1.3.3",
-    "ansi-colors": "^3.2.4",
+    "ansi-colors": "^4.1.1",
     "materialize-css": "^1.0.0",
-    "mocha": "^6.1.4"
+    "mocha": "^6.2.1"
   },
   "bugs": {
     "url": "https://github.com/ioBroker/ioBroker.admin/issues"
   },
   "main": "main.js",
diff --git a/src/js/admin.js b/src/js/admin.js
index 85113b7..5af2bcb 100644
--- a/src/js/admin.js
+++ b/src/js/admin.js
@@ -1,8 +1,8 @@
 /* jshint -W097 */
 /* jshint strict:true */
-/* jslint vars: true */
+/* jslint consts: true */
 /* global io:false */
 /* global jQuery:false */
 /* jslint browser:true */
 /* jshint browser:true */
 /* global _ */
@@ -17,14 +17,14 @@
 'use strict';
 
 //if (typeof Worker === 'undefined') alert('your browser does not support WebWorkers :-(');
 
 Array.prototype.remove = function () {
-    var what;
-    var a = arguments;
-    var L = a.length;
-    var ax;
+    let what;
+    const a = arguments;
+    let L = a.length;
+    let ax;
     while (L && this.length) {
         what = a[--L];
         while ((ax = this.indexOf(what)) !== -1) {
             this.splice(ax, 1);
         }
@@ -49,47 +49,48 @@ async function asyncForEach(array, callback) {
     for (let index = 0; index < array.length; index++) {
         await callback(array [index], index, array);
     }
 }
 
-var $iframeDialog = null; // used in adapter settings window
-var configNotSaved = null; // used in adapter settings window
-var showConfig = null; // used in adapter settings window
-var defaults = {};
-var customPostInits = {};
-var customPostOnSave = {};
-var FORBIDDEN_CHARS = /[\]\[*,;'"`<>\\\s?]/g;
+let $iframeDialog = null;  // used in adapter settings window
+let configNotSaved = null; // used in adapter settings window
+let showConfig = null;     // used in adapter settings window
+
+const defaults = {};
+const customPostInits = {};
+const customPostOnSave = {};
+const FORBIDDEN_CHARS = /[\]\[*,;'"`<>\\\s?]/g;
 
 // used in adapter settings window
-var adapterRedirect = function (redirect, timeout) {
+const adapterRedirect = function (redirect, timeout) {
     if (redirect) {
         setTimeout(function () {
             redirect += document.location.pathname;
             redirect += document.location.hash;
             document.location.href = redirect;
         }, timeout || 5000);
     }
 };
-var gMain = null; // for google maps
+let gMain = null; // for google maps
 
 function detectIE() {
-    var ua = window.navigator.userAgent;
+    const ua = window.navigator.userAgent;
 
-    var msie = ua.indexOf('MSIE ');
+    const msie = ua.indexOf('MSIE ');
     if (msie > 0) {
         // IE 10 or older => return version number
         return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
     }
 
-    var trident = ua.indexOf('Trident/');
+    const trident = ua.indexOf('Trident/');
     if (trident > 0) {
         // IE 11 => return version number
-        var rv = ua.indexOf('rv:');
+        const rv = ua.indexOf('rv:');
         return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
     }
 
-    var edge = ua.indexOf('Edge/');
+    const edge = ua.indexOf('Edge/');
     if (edge > 0) {
         // Edge (IE 12+) => return version number
         return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
     }
 
@@ -97,18 +98,18 @@ function detectIE() {
     return false;
 }
 
 (function ($) {
 $(document).ready(function () {
-    var path = location.pathname + 'socket.io';
+    let path = location.pathname + 'socket.io';
     if (location.pathname.match(/^\/admin\//)) {
         path = '/socket.io';
     }
 
-    var allTabs = {};
+    let allTabs = {};
 
-    var main = {
+    const main = {
         objects:        {},
         states:         {},
         currentHost:    '',
         currentTab:     null,
         currentDialog:  null,
@@ -219,21 +220,21 @@ $(document).ready(function () {
                 callback = buttons;
                 $dialogConfirm.find('.modal-footer').html(
                     '<a class="modal-action modal-close waves-effect waves-green btn-flat translate" data-result="true">' + _('Ok') + '</a>' +
                     '<a class="modal-action modal-close waves-effect waves-green btn-flat translate">' + _('Cancel') + '</a>');
                 $dialogConfirm.find('.modal-footer .modal-action').on('click', function () {
-                    var cb = $dialogConfirm.data('callback');
+                    const cb = $dialogConfirm.data('callback');
                     cb && cb($(this).data('result'));
                 });
             } else if (typeof buttons === 'object') {
-                var tButtons = '';
-                for (var b = buttons.length - 1; b >= 0; b--) {
+                let tButtons = '';
+                for (let b = buttons.length - 1; b >= 0; b--) {
                     tButtons += '<a class="modal-action modal-close waves-effect waves-green btn-flat translate" data-id="' + b + '">' + buttons[b] + '</a>';
                 }
                 $dialogConfirm.find('.modal-footer').html(tButtons);
                 $dialogConfirm.find('.modal-footer .modal-action').on('click', function () {
-                    var cb = $dialogConfirm.data('callback');
+                    const cb = $dialogConfirm.data('callback');
                     cb && cb($(this).data('id'));
                 });
             }
 
             $dialogConfirm.find('.dialog-title').text(title || _('Please confirm'));
@@ -288,22 +289,22 @@ $(document).ready(function () {
             //    ("0" + (dateObj.getHours()).toString(10)).slice(-2) + ':' +
             //    ("0" + (dateObj.getMinutes()).toString(10)).slice(-2) + ':' +
             //    ("0" + (dateObj.getSeconds()).toString(10)).slice(-2);
             // Following implementation is 5 times faster
             if (!dateObj) return '';
-            var text = typeof dateObj;
+            let text = typeof dateObj;
             if (text === 'string') {
                 if (justTime) {
                     return dateObj.substring(8);
                 } else {
                     return dateObj;
                 }
             }
             // if less 2000.01.01 00:00:00
             if (text !== 'object') dateObj = dateObj < 946681200000 ? new Date(dateObj * 1000) : new Date(dateObj);
 
-            var v;
+            let v;
             if (!justTime) {
                 text = dateObj.getFullYear();
                 v = dateObj.getMonth() + 1;
                 if (v < 10) {
                     text += '-0' + v;
@@ -383,11 +384,11 @@ $(document).ready(function () {
                 columns: ['image', 'name', 'role', 'room', 'value']
             });
             return main.selectId;
         },*/
         updateWizard:   function () {
-            var $wizard = $('#button-wizard');
+            const $wizard = $('#button-wizard');
             if (main.objects['system.adapter.discovery.0']) {
                 if (!$wizard.data('inited')) {
                     $wizard.data('inited', true);
                     $wizard/*.button({
                         icons: {primary: ' ui-icon-search'},
@@ -416,45 +417,45 @@ $(document).ready(function () {
                 main.socket.emit('authEnabled', function (auth, user) {
                     main.currentUser = 'system.user.' + user;
                     if (!auth) {
                         $('#button-logout').remove();
                     } else {
-                        main._lastTimer = (new Date()).getTime();
+                        main._lastTimer = Date.now();
                         monitor();
                     }
                 });
             } else if (main.objects[main.currentUser]) {
-                var obj = main.objects[main.currentUser];
-                var name = '';
+                const obj = main.objects[main.currentUser];
+                let name = '';
                 if (!obj || !obj.common || !obj.common.name) {
                     name = main.currentUser.replace(/^system\.user\./);
                     name = name[0].toUpperCase() + name.substring(1).toLowerCase();
                 } else {
                     name = translateName(obj.common.name);
                 }
                 if (obj && obj.common && obj.common.icon) {
-                    var objs = {};
+                    const objs = {};
                     objs[main.currentUser] = obj;
                     $('#current-user-icon').html(main.getIcon(main.currentUser, null, objs));
                 } else {
                     $('#current-user-icon').html('<i class="large material-icons">account_circle</i>');
                 }
                 $('#current-user').html(name);
-                var groups = [];
-                for (var i = 0; i < tabs.users.groups.length; i++) {
-                    var group = main.objects[tabs.users.groups[i]];
+                const groups = [];
+                for (let i = 0; i < tabs.users.groups.length; i++) {
+                    const group = main.objects[tabs.users.groups[i]];
                     if (group && group.common && group.common.members && group.common.members.indexOf(main.currentUser) !== -1) {
                         groups.push(_(translateName(group.common.name)));
                     }
                 }
                 $('#current-group').html(groups.join(', '));
             }
         },
 
         // Delete objects
         _delObject:     function (idOrList, callback) {
-            var id;
+            let id;
             if (!Array.isArray(idOrList)) {
                 if (typeof idOrList !== 'string') return callback && callback('invalid idOrList parameter');
                 idOrList = [idOrList];
             }
 
@@ -465,11 +466,11 @@ $(document).ready(function () {
                 id = idOrList.pop();
                 if (main.objects[id] && main.objects[id].common && (main.objects[id].common['object-non-deletable'] || main.objects[id].common.dontDelete)) {
                     main.showMessage (_ ('Cannot delete "%s" because not allowed', id), '', 'notifications');
                     setTimeout(doIt, 0);
                 } else {
-                    var obj = main.objects[id];
+                    const obj = main.objects[id];
                     main.socket.emit('delObject', id, function (err) {
                         if (err && err !== 'Not exists') {
                             main.showError (err);
                             return callback(err);
                         }
@@ -486,17 +487,17 @@ $(document).ready(function () {
                         }
                     });
                 }
             }
             doIt();
-        },     
+        },
         _delObjects:    function (rootId, isAll, callback) {
             if (!isAll) {
                 this._delObject(rootId, callback);
             } else {
-                var list = [];
-                for (var id in main.objects) {
+                const list = [];
+                for (const id in main.objects) {
                     if (main.objects.hasOwnProperty(id) && id.substring(0, rootId.length + 1) === rootId + '.') {
                         list.push(id);
                     }
                 }
                 list.push(rootId);
@@ -506,11 +507,11 @@ $(document).ready(function () {
                     if (callback) callback();
                 });
             }
         },
         delObject:      function ($tree, id, callback) {
-            var leaf = $tree ? $tree.selectId('getTreeInfo', id) : null;
+            const leaf = $tree ? $tree.selectId('getTreeInfo', id) : null;
             if (main.objects[id]) {
                 if (leaf && leaf.children) {
                     // ask if only object must be deleted or just this one
                     main.confirmMessage(_('Do you want to delete just <span style="color: blue">one object</span> or <span style="color: red">all</span> children of %s too?', id), null, 'help_outline', [_('_All'), _('Only one'), _('Cancel')], function (result) {
                         // If all
@@ -542,11 +543,11 @@ $(document).ready(function () {
         }
     };
 
     gMain = main; // for google maps
 
-    var tabs = {
+    const tabs = {
         hosts:      new Hosts(main), // must be first to read the list of hosts
         objects:    new Objects(main),
         adapters:   new Adapters(main),
         instances:  new Instances(main),
         users:      new Users(main),
@@ -571,25 +572,25 @@ $(document).ready(function () {
         editobject: new EditObject(main),
         issue:      new Issue(main),
         readme:     new Readme(main)
     };
 
-    var stdout;
-    var cmdCallback    = null;
-    var activeCmdId    = null;
-    var $stdout        = $('#stdout');
+    let stdout;
+    let cmdCallback      = null;
+    let activeCmdId      = null;
+    const $stdout        = $('#stdout');
 
-    var $dialogCommand = $('#dialog-command');
-    var $dialogLicense = $('#dialog-license-main');
-    var $dialogMessage = $('#dialog-message');
-    var $dialogConfirm = $('#dialog-confirm');
-    var $dialogCommandProgress = $dialogCommand.find('.progress div');
+    const $dialogCommand = $('#dialog-command');
+    const $dialogLicense = $('#dialog-license-main');
+    const $dialogMessage = $('#dialog-message');
+    const $dialogConfirm = $('#dialog-confirm');
+    const $dialogCommandProgress = $dialogCommand.find('.progress div');
 
-    var $adminSideMenu = $('#admin_sidemenu_menu');
-    var $adminSideMain = $('#admin_sidemenu_main');
+    const $adminSideMenu = $('#admin_sidemenu_menu');
+    const $adminSideMain = $('#admin_sidemenu_main');
 
-    var firstConnect   = true;
+    let firstConnect     = true;
 
     // detect touch devices
     if (!('ontouchstart' in window || navigator.maxTouchPoints)) {
         $('body').addClass('desktop-screen');
     }
@@ -631,50 +632,50 @@ $(document).ready(function () {
         $('html').off('click', globalClickHandler);
     }
 
     function initHtmlButtons() {
         main.socket.emit('getVersion', function (err, version) {
-			var $versionBtn = $('.button-version');
+			const $versionBtn = $('.button-version');
 	        if (!$versionBtn.hasClass('vendor')) {
 	            $versionBtn.text('ioBroker.admin ' + version);
 	        }
         });
 
         $('.choose-tabs-config-button').off('click').on('click', function(event) {
-            var $dialog = $('#admin_sidemenu_dialog');
-            var html = $dialog.html();
+            const $dialog = $('#admin_sidemenu_dialog');
+            const html = $dialog.html();
             if (html) {
                 $dialog.html('');
                 // disable global handler
                 $('html').off('click', globalClickHandler);
                 return;
             }
             setTimeout(function () {
                 // enable global handler
                 $('html').on('click', globalClickHandler);
             }, 100);
-            var $e = $(event.target);
-            var offs = $e.offset();
+            const $e = $(event.target);
+            const offs = $e.offset();
             offs.top += $e.height() - 2;
 
-            var text =
+            let text =
                 '<dialog open class="tab-selector m" style="top: ' + offs.top + 'px; left: ' + offs.left + 'px;">' + // style="overflow: visible; z-index: 999; ">'
                 '<div>' +
                 '<ul style="">';
 
-            var $lis = $adminSideMenu;
-            for (var tid in allTabs) {
-                var name = allTabs[tid];
-                var found = $adminSideMenu.find('.admin-sidemenu-items[data-tab="' + tid + '"]').length;
+            const $lis = $adminSideMenu;
+            for (const tid in allTabs) {
+                const name = allTabs[tid];
+                const found = $adminSideMenu.find('.admin-sidemenu-items[data-tab="' + tid + '"]').length;
                 // TABS
                 /*$adminSideMenu.each(function (i, e) {
                     if (tid === $(e).attr('aria-controls')) {
                         found = $(e);
                         return false;
                     }
                 });*/
-                var id = 'chk-' + tid;
+                const id = 'chk-' + tid;
                 text +=
                     '<li><input ' + (found ? 'checked' : 'unchecked') + ' class="chk-tab filled-in" type="checkbox" id="' + id + '" />' +
                     '<span for="' + id + '">' + _(name) + '</span></id>';
             }
             text += '' +
@@ -682,37 +683,37 @@ $(document).ready(function () {
                 '</div>' +
                 '</dialog>';
             $dialog.append(text);
 
             $dialog.find('.chk-tab').off('change').on('change', function (event) {
-                var id = $(this).attr('id').substr(4);
+                const id = $(this).attr('id').substr(4);
                 if ($(this).prop('checked')) {
                     main.systemConfig.common.tabs.push(id);
                 } else {
-                    var pos = main.systemConfig.common.tabs.indexOf(id);
+                    const pos = main.systemConfig.common.tabs.indexOf(id);
                     if (id !== -1) {
                         main.systemConfig.common.tabs.splice(pos, 1);
                     }
                 }
                 main.saveTabs();
                 initTabs();
             });
             // workaround for materialize checkbox problem
             $dialog.find('input[type="checkbox"]+span').off('click').on('click', function () {
-                var $input = $(this).prev();
+                const $input = $(this).prev();
                 if (!$input.prop('disabled')) {
                     $input.prop('checked', !$input.prop('checked')).trigger('change');
                 }
             });
         });
 
         main.updateWizard();
-        
+
         $('#button-logout').on('click', function () {
             window.location.href = '/logout/';
         });
-        
+
         setTimeout(function () {
             main.tabs.info.init();
         }, 5000);
 
         window.onhashchange = function () {
@@ -732,19 +733,19 @@ $(document).ready(function () {
 
             $('#events_threshold').on('click', function () {
                 main.socket.emit('eventsThreshold', false);
             });
         } else {
-            var $menu = $adminSideMenu;
-            var panelSelector = $menu.data('problem-link');
+            const $menu = $adminSideMenu;
+            const panelSelector = $menu.data('problem-link');
             if (panelSelector) {
-                var $panel = $(panelSelector);
+                const $panel = $(panelSelector);
                 // Init source for iframe
                 if ($panel.length) {
-                    var link = $panel.data('src');
+                    const link = $panel.data('src');
                     if (link && link.indexOf('%') === -1) {
-                        var $iframe = $panel.find('iframe');
+                        const $iframe = $panel.find('iframe');
                         if ($iframe.length && !$iframe.attr('src')) {
                             $iframe.attr('src', link);
                             $menu.data('problem-link', null);
                         }
                     }
@@ -756,24 +757,24 @@ $(document).ready(function () {
         }
     }
 
     function initTabs() {
         // extract all additional instances
-        var text     = '';
-        var list     = [];
-        var addTabs = [];
+        let text      = '';
+        const list    = [];
+        const addTabs = [];
 
         allTabs = {};
-        for (var i = 0; i < main.instances.length; i++) {
-            var instance = main.instances[i];
-            var instanceObj = main.objects[instance];
+        for (let i = 0; i < main.instances.length; i++) {
+            const instance = main.instances[i];
+            const instanceObj = main.objects[instance];
             if (!instanceObj.common || !instanceObj.common.adminTab) continue;
             if (instanceObj.common.adminTab.singleton) {
-                var isFound = false;
-                var inst1 = instance.replace(/\.(\d+)$/, '.');
-                for (var j = 0; j < addTabs.length; j++) {
-                    var inst2 = addTabs[j].replace(/\.(\d+)$/, '.');
+                let isFound = false;
+                const inst1 = instance.replace(/\.(\d+)$/, '.');
+                for (let j = 0; j < addTabs.length; j++) {
+                    const inst2 = addTabs[j].replace(/\.(\d+)$/, '.');
                     if (inst1 === inst2) {
                         isFound = true;
                         break;
                     }
                 }
@@ -783,28 +784,28 @@ $(document).ready(function () {
             }
         }
 
         // Build the standard tabs together
         $('.admin-tab').each(function () {
-            var $this = $(this);
-            var id = $this.attr('id');
+            const $this = $(this);
+            const id = $this.attr('id');
             list.push(id);
             allTabs[id] = $this.data('name');
         });
 
         // Look for adapter tabs
-        for (var a = 0; a < addTabs.length; a++) {
-            var tab   = main.objects[addTabs[a]];
-            var name  = 'tab-' + tab.common.name;
+        for (let a = 0; a < addTabs.length; a++) {
+            const tab = main.objects[addTabs[a]];
+            let name  = 'tab-' + tab.common.name;
 
-            var link  = tab.common.adminTab.link || '/adapter/' + tab.common.name + '/tab.html';
+            let link  = tab.common.adminTab.link || '/adapter/' + tab.common.name + '/tab.html';
             if (tab.common.materializeTab) {
                 link  = tab.common.adminTab.link || '/adapter/' + tab.common.name + '/tab_m.html';
             }
 
-            var parts = addTabs[a].split('.');
-            var buttonName;
+            const parts = addTabs[a].split('.');
+            let buttonName;
 
             if (tab.common.adminTab.name) {
                 if (typeof tab.common.adminTab.name === 'object') {
                     if (tab.common.adminTab.name[systemLang]) {
                         buttonName = tab.common.adminTab.name[systemLang];
@@ -834,11 +835,11 @@ $(document).ready(function () {
 
             list.push(name);
             allTabs[name] = buttonName;
 
             if (!main.systemConfig.common.tabs || main.systemConfig.common.tabs.indexOf(name) !==-1) {
-                var isReplace = false;
+                let isReplace = false;
                 if (!link) {
                     link = '/adapter/' + parts[2] + '/tab.html';
                     if (tab.common.materilizeTab) {
                         link = '/adapter/' + parts[2] + '/tab_m.html';
                     }
@@ -848,20 +849,24 @@ $(document).ready(function () {
 
                 text += '<li><a href="#' + name + '">' + buttonName + '</a></li>\n';
 
                 // noinspection JSJQueryEfficiency
                 if (!$('#' + name).length) {
-                    var div = '<div id="' + name + '" data-name="' + buttonName + '" class="tab-custom ' + (isReplace ? 'link-replace' : '') + '" data-adapter="' + parts[2] + '" data-instance="' + parts[3] + '" data-src="' + link + '">' +
+                    const div = '<div id="' + name + '" data-name="' + buttonName + '" class="tab-custom ' + (isReplace ? 'link-replace' : '') + '" data-adapter="' + parts[2] + '" data-instance="' + parts[3] + '" data-src="' + link + '">' +
                         '<iframe class="iframe-in-tab" style="border: 0; solid #FFF; display: block; left: 0; top: 0; width: 100%; height: 100%"' +
                         '></iframe></div>';
                     $(div).hide().appendTo($('body'));
 
                     // TODO: temporary, until other tab will be adapted
                     $('#' + name).find ('.iframe-in-tab').on('load', function () {
-                        var elem = $ (this).contents ().find('body>header');
-                        if (!elem || !elem.length) elem = $(this).contents ().find('head');
-                        if (elem && elem.length) elem.append('<link rel="stylesheet" type="text/css" href="../../lib/css/iob/selectID.css"/>');
+                        let elem = $ (this).contents ().find('body>header');
+                        if (!elem || !elem.length) {
+                            elem = $(this).contents ().find('head');
+                        }
+                        if (elem && elem.length) {
+                            elem.append('<link rel="stylesheet" type="text/css" href="../../lib/css/iob/selectID.css"/>');
+                        }
                     });
                 } else {
                     $('#' + name).hide().appendTo($('body'));
                 }
             } else {
@@ -875,14 +880,14 @@ $(document).ready(function () {
         });
 
         if (!main.systemConfig.common.tabs) main.systemConfig.common.tabs = list;
 
         if ($('.link-replace').length) {
-            var countLink = 0;
+            let countLink = 0;
 
             // If some objects cannot be read => go by timeout
-            var loadTimeout = setTimeout(function() {
+            let loadTimeout = setTimeout(function() {
                 loadTimeout = null;
                 initHtmlTabs(/*showTabs*/);
             }, 100);
 
             $('.link-replace').each(function () {
@@ -904,34 +909,34 @@ $(document).ready(function () {
         }
     }
 
     main.initHostsList = function (isFirstInit) {
         // fill the host list (select) on adapter tab
-        var $selHosts = $('#host-adapters');
+        const $selHosts = $('#host-adapters');
         if (isFirstInit && $selHosts.data('inited')) {
             return;
         }
 
         $selHosts.data('inited', true);
 
         main.currentHost = main.currentHost || main.config.currentHost || '';
 
-        var lines = [];
-        var color;
-        var curId;
-        for (var i = 0; i < main.tabs.hosts.list.length; i++) {
+        const lines = [];
+        let color;
+        let curId;
+        for (let i = 0; i < main.tabs.hosts.list.length; i++) {
             lines.push('<li><a data-value="' + main.tabs.hosts.list[i].name + '">' + main.getHostIcon(main.objects[main.tabs.hosts.list[i].id], 'imgHost left') + main.tabs.hosts.list[i].name + '</a></li>');
             if (!main.currentHost) {
                 main.currentHost = main.tabs.hosts.list[i].name;
             }
             if (main.currentHost === main.tabs.hosts.list[i].name) {
                 curId = main.tabs.hosts.list[i].id;
             }
         }
         $selHosts.html(lines);
 
-        var $selBtn = $('#host-adapters-btn').show();
+        const $selBtn = $('#host-adapters-btn').show();
         $selBtn
             .text(_('Host:') + ' ' + main.currentHost)
             .dropdown();
 
         if (main.objects[curId] && main.objects[curId].common) {
@@ -949,12 +954,12 @@ $(document).ready(function () {
             $selBtn.removeClass('disabled');
         }
 
         // host selector
         $selHosts.find('a').on('click', function () {
-            var val = $(this).data('value');
-            var id  = 'system.host.' + val + '.alive';
+            const val = $(this).data('value');
+            const id  = 'system.host.' + val + '.alive';
             if (!main.states[id] || !main.states[id].val || main.states[id].val === 'null') {
                 main.showMessage(_('Host %s is offline', $(this).val()));
                 return;
             }
 
@@ -995,11 +1000,11 @@ $(document).ready(function () {
         $dialogConfirm.modal({
             dismissible: false
         });
 
         $dialogCommand.find('.progress-show-more').off('change').on('change', function () {
-            var val = $(this).prop('checked');
+            const val = $(this).prop('checked');
             main.saveConfig('progressMore', val);
             if (val) {
                 $dialogCommand.find('.textarea').show();
             } else {
                 $dialogCommand.find('.textarea').hide();
@@ -1011,11 +1016,11 @@ $(document).ready(function () {
         $dialogCommand.find('.progress-dont-close input').on('change', function () {
             main.saveConfig('progressClose', $(this).prop('checked'));
         });
         // workaround for materialize checkbox problem
         $dialogCommand.find('input[type="checkbox"]+span').off('click').on('click', function () {
-            var $input = $(this).prev();
+            const $input = $(this).prev();
             // ignore switch
             if ($input.parent().parent().hasClass('switch')) return;
 
             if (!$input.prop('disabled')) {
                 $input.prop('checked', !$input.prop('checked')).trigger('change');
@@ -1039,11 +1044,11 @@ $(document).ready(function () {
     function checkNodeJsVersions(hosts, index) {
         index = index || 0;
         if (hosts && index < hosts.length) {
             main.socket.emit('sendToHost', hosts[index].name, 'getHostInfo', null, function (result) {
                 if (result && result['Node.js']) {
-                    var major = parseInt(result['Node.js'].split('.').shift().replace('v', ''), 10);
+                    const major = parseInt(result['Node.js'].split('.').shift().replace('v', ''), 10);
                     if (major < 6 || major === 7 || major === 9  || major === 11 ) { // we allow 6, 8, 10 and 12+
                         main.showMessage(_('This version of node.js "%s" on "%s" is deprecated. Please install node.js 6, 8 or newer', result['Node.js'], hosts[index].name), _('Suggestion'), 'error_outline');
                     }
                 }
                 setTimeout(function () {
@@ -1063,13 +1068,13 @@ $(document).ready(function () {
                 window.alert(_(err));
                 return;
             }
 
             setTimeout(function () {
-                var obj;
+                let obj;
                 main.objects = res;
-                for (var id in main.objects) {
+                for (const id in main.objects) {
                     if (!main.objects.hasOwnProperty(id) || id.slice(0, 7) === '_design') continue;
 
                     obj = main.objects[id];
 
                     if (obj.type === 'instance') main.instances.push(id);
@@ -1088,11 +1093,11 @@ $(document).ready(function () {
                 main.objectsLoaded = true;
                 main.initHostsList(true);
 
                 initTabs();
                 // init dialogs
-                for (var dialog in main.dialogs) {
+                for (const dialog in main.dialogs) {
                     if (main.dialogs.hasOwnProperty(dialog) && typeof main.dialogs[dialog].prepare === 'function') {
                         main.dialogs[dialog].prepare();
                     }
                 }
 
@@ -1142,15 +1147,13 @@ $(document).ready(function () {
             main.addEventMessage(id, state, true, true);
         }
     }
 
     function objectChange(id, obj) {
-        //var changed = false;
-        var oldObj = null;
-        var action = 'update';
-
-        oldObj = main.objects[id];
+        //const changed = false;
+        const oldObj = main.objects[id];
+        let action = 'update';
 
         // update main.objects cache
         if (obj) {
             if (obj._rev && main.objects[id]) main.objects[id]._rev = obj._rev;
             if (!main.objects[id]) {
@@ -1203,15 +1206,15 @@ $(document).ready(function () {
             if (obj.common &&
                 obj.common.adminTab &&
                 !obj.common.adminTab.ignoreConfigUpdate
             ) {
                 // Detect enable/disable change and do not update tabs (To able to work with global scripts normally)
-                var ignore = false;
+                let ignore = false;
                 // try to detect if javascript just enabled or disabled
                 if (oldObj && obj) {
                     if (oldObj.common && obj.common) {
-                        var newObj = JSON.parse(JSON.stringify(obj));
+                        const newObj = JSON.parse(JSON.stringify(obj));
                         newObj.common.enabled = oldObj.common.enabled;
                         newObj.ts = 0;
                         oldObj.ts = 0;
                         console.log(JSON.stringify(newObj));
                         console.log(JSON.stringify(oldObj));
@@ -1243,35 +1246,36 @@ $(document).ready(function () {
         }
     }
 
     function monitor() {
         if (main._timer) return;
-        var ts = (new Date()).getTime();
+        const ts = Date.now();
+
         if (ts - main._lastTimer > 30000) {
             // It seems, that PC was in a sleep => Reload page to request authentication anew
             location.reload();
         } else {
             main._lastTimer = ts;
         }
-        main._timer = setTimeout(function () {
+        main._timer = setTimeout(() => {
             main._timer = null;
             monitor();
         }, 10000);
     }
 
     // ---------------------------- Subscribes ---------------------------------------------
     main.resubscribeStates = function () {
-        for (var pattern in main.subscribesStates) {
+        for (const pattern in main.subscribesStates) {
             if (main.subscribesStates.hasOwnProperty(pattern) && main.subscribesStates[pattern]) {
                 console.debug('Re-Subscribe: ' + pattern);
                 main.socket.emit('subscribe', pattern);
             }
         }
     };
 
     main.resubscribeObjects = function () {
-        for (var pattern in main.subscribesObjects) {
+        for (const pattern in main.subscribesObjects) {
             if (main.subscribesObjects.hasOwnProperty(pattern) && main.subscribesObjects[pattern]) {
                 main.socket.emit('subscribeObjects', pattern);
             }
         }
     };
@@ -1284,11 +1288,11 @@ $(document).ready(function () {
     };
 
     main.subscribeStates = function (patterns) {
         if (!patterns) return;
         if (typeof patterns === 'object') {
-            for (var s = 0; s < patterns.length; s++) {
+            for (let s = 0; s < patterns.length; s++) {
                 main.subscribesStates[patterns[s]] = main.subscribesStates[patterns[s]] || 0;
                 main.subscribesStates[patterns[s]]++;
                 if (main.subscribesStates[patterns[s]] === 1) {
                     console.debug('Subscribe: ' + patterns[s]);
                     main.socket.emit('subscribe', patterns[s]);
@@ -1305,16 +1309,16 @@ $(document).ready(function () {
     };
 
     main.unsubscribeStates = function (patterns) {
         if (!patterns) return;
         if (typeof patterns === 'object') {
-            for (var s = 0; s < patterns.length; s++) {
+            for (let s = 0; s < patterns.length; s++) {
                 if (main.subscribesStates[patterns[s]]) {
                     main.subscribesStates[patterns[s]]--;
                 }
                 if (main.subscribesStates[patterns[s]] === 0) {
-                    console.debug('Unsibscribe: ' + patterns[s]);
+                    console.debug('Unsubscribe: ' + patterns[s]);
                     main.socket.emit('unsubscribe', patterns[s]);
                     delete main.subscribesStates[patterns[s]];
                 }
             }
         } else {
@@ -1330,11 +1334,11 @@ $(document).ready(function () {
     };
 
     main.subscribeObjects = function (patterns) {
         if (!patterns) return;
         if (typeof patterns === 'object') {
-            for (var s = 0; s < patterns.length; s++) {
+            for (let s = 0; s < patterns.length; s++) {
                 main.subscribesObjects[patterns[s]] = main.subscribesObjects[patterns[s]] || 0;
                 main.subscribesObjects[patterns[s]]++;
                 if (main.subscribesObjects[patterns[s]] === 1) {
                     main.socket.emit('subscribeObjects', patterns[s]);
                 }
@@ -1349,11 +1353,11 @@ $(document).ready(function () {
     };
 
     main.unsubscribeObjects = function (patterns) {
         if (!patterns) return;
         if (typeof patterns === 'object') {
-            for (var s = 0; s < patterns.length; s++) {
+            for (let s = 0; s < patterns.length; s++) {
                 if (main.subscribesObjects[patterns[s]]) {
                     main.subscribesObjects[patterns[s]]--;
                 }
                 if (main.subscribesObjects[patterns[s]] === 0) {
                     main.socket.emit('unsubscribeObjects', patterns[s]);
@@ -1405,11 +1409,11 @@ $(document).ready(function () {
         }
         callback(false);
     };
 
     main.navigateGetParams = function () {
-        var parts = decodeURI(window.location.hash).split('/');
+        const parts = decodeURI(window.location.hash).split('/');
         return parts[2] ? decodeURIComponent(parts[2]) : null;
     };
 
     main.navigate = function (options) {
         if (!options) {
@@ -1423,11 +1427,11 @@ $(document).ready(function () {
             };
         }
 
         // get actual tab
         if (!options.tab) {
-            var parts   = decodeURI(window.location.hash).split('/');
+            const parts   = decodeURI(window.location.hash).split('/');
             options.tab = parts[0].replace(/^#/, '').replace(/^tab-/, '');
         }
 
         window.location.hash = '#tab-' + encodeURIComponent(options.tab) + (options.dialog ? '/' + options.dialog + (options.params ? '/' + encodeURIComponent(options.params) : '') : '');
     };
@@ -1442,14 +1446,14 @@ $(document).ready(function () {
         main.navigateCheckDialog(function (err) {
             if (!err) {
                 configNotSaved = null;
                 main.currentHash = window.location.hash;
                 // hash has following structure => #tabName/dialogName/ids
-                var parts  = main.currentHash.split('/');
-                var tab    = parts[0].replace(/^#/, '').replace(/^tab-/, '');
-                var dialog = parts[1];
-                var params = decodeURIComponent(parts[2]);
+                const parts  = main.currentHash.split('/');
+                let tab      = parts[0].replace(/^#/, '').replace(/^tab-/, '');
+                const dialog = parts[1];
+                const params = decodeURIComponent(parts[2]);
 
                 // set default page
                 if (!tab || tab === '!') {
                     if (!main.systemConfig.common.tabs || main.systemConfig.common.tabs.indexOf('tab-intro') !== -1) {
                         tab = 'intro';
@@ -1459,31 +1463,31 @@ $(document).ready(function () {
                         tab = main.systemConfig.common.tabs[0].replace(/^#/, '').replace(/^tab-/, '');
                     }
                 }
                 // do tab is not found
 
-                var $adminBody = $('.admin-sidemenu-body');
-                var $actualTab = $adminBody.find('.admin-sidemenu-body-content');
-                var $panel     = $('#tab-' + tab);
+                const $adminBody = $('.admin-sidemenu-body');
+                let $actualTab   = $adminBody.find('.admin-sidemenu-body-content');
+                const $panel     = $('#tab-' + tab);
 
                 $adminBody.find('.admin-preloader').remove();
 
                 if (!$panel.length) {
                     tab = 'intro';
                 }
 
                 // if tab was changed
                 if (main.currentTab !== tab || !$actualTab.length) {
-                    var link;
+                    let link;
                     // destroy actual tab
                     if (main.currentTab && tabs[main.currentTab] && typeof tabs[main.currentTab].destroy === 'function') {
                         tabs[main.currentTab].destroy();
                     } else if (main.currentTab) {
-                        var $oldPanel = $('#tab-' + main.currentTab);
+                        const $oldPanel = $('#tab-' + main.currentTab);
                         // destroy current iframe
                         if ($oldPanel.length && (link = $oldPanel.data('src'))) {
-                            var $iframe_ = $oldPanel.find('>iframe');
+                            const $iframe_ = $oldPanel.find('>iframe');
                             if ($iframe_.attr('src')) {
                                 console.log('clear');
                                 $iframe_.attr('src', '');
                             }
                         }
@@ -1502,22 +1506,22 @@ $(document).ready(function () {
                     }
 
                     // if iframe like node-red
                     if ($panel.length && (link = $panel.data('src'))) {
                         if (link.indexOf('%') === -1) {
-                            var $iframe = $panel.find('>iframe');
+                            const $iframe = $panel.find('>iframe');
                             if ($iframe.length && !$iframe.attr('src')) {
                                 $iframe.attr('src', link);
                             }
                         } else {
                             $adminSideMenu.data('problem-link', 'tab-' + tab);
                         }
                     }
                 }
 
                 // select menu element
-                var  $tab = $adminSideMenu.find('.admin-sidemenu-items[data-tab="tab-' + tab + '"]');
+                const  $tab = $adminSideMenu.find('.admin-sidemenu-items[data-tab="tab-' + tab + '"]');
                 $adminSideMenu.find('.admin-sidemenu-items').not($tab).removeClass('admin-sidemenu-active');
                 $tab.addClass('admin-sidemenu-active');
 
                 if (tabsInfo['tab-' + tab] && tabsInfo['tab-' + tab].host) {
                     $('#host-adapters-btn').css('opacity', 1);
@@ -1551,18 +1555,18 @@ $(document).ready(function () {
             }
         });
     };
 
     function getIconHtml(obj, classes) {
-        var icon;
-        var alt;
-        var isCommon = obj && obj.common;
+        let icon;
+        let alt;
+        const isCommon = obj && obj.common;
 
         if (isCommon.icon) {
             if (!isCommon.icon.match(/^data:image\//)) {
                 if (isCommon.icon.indexOf('.') !== -1) {
-                    var instance;
+                    let instance;
                     if (obj.type === 'instance') {
                         icon = '/adapter/' + obj.common.name + '/' + obj.common.icon;
                     } else if (obj._id.match(/^system\.adapter\./)) {
                         instance = obj._id.split('.', 3);
                         if (isCommon.icon[0] === '/') {
@@ -1591,15 +1595,15 @@ $(document).ready(function () {
         }
         return {icon: icon, alt: alt};
     }
 
     main.getIconFromObj = function (obj, imgPath, classes) {
-        var icon     = '';
-        var alt      = '';
+        let icon     = '';
+        let alt      = '';
         if (obj && obj.common) {
             if (obj.common.icon) {
-                var result = getIconHtml(obj);
+                const result = getIconHtml(obj);
                 icon = result.icon;
                 alt = result.alt;
             } else {
                 imgPath = imgPath || 'lib/css/fancytree/';
                 if (obj.type === 'device') {
@@ -1623,15 +1627,15 @@ $(document).ready(function () {
     main.getIcon = function(id, imgPath, objects, classes) {
         return main.getIconFromObj((objects || main.objects)[id], imgPath, classes);
     };
 
     main.getHostIcon = function (obj, classes) {
-        var icon     = '';
-        var alt      = '';
+        let icon     = '';
+        let alt      = '';
 
         if (obj && obj.common && obj.common.icon) {
-            var result = getIconHtml(obj);
+            const result = getIconHtml(obj);
             icon = result.icon;
             alt = result.alt;
         }
         icon = icon || 'img/no-image.png';
         alt  = alt  || '';
@@ -1641,12 +1645,12 @@ $(document).ready(function () {
 
     main.formatBytes = function (bytes) {
         if (Math.abs(bytes) < 1024) {
             return bytes + ' B';
         }
-        var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
-        var u = -1;
+        const units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
+        let u = -1;
         do {
             bytes /= 1024;
             ++u;
         } while (Math.abs(bytes) >= 1024 && u < units.length - 1);
         return bytes.toFixed(1) + ' ' + units[u];
@@ -1662,18 +1666,18 @@ $(document).ready(function () {
             hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
         }
         if (hex.length !== 6) {
             return false;
         }
-        var r = parseInt(hex.slice(0, 2), 16),
+        const r = parseInt(hex.slice(0, 2), 16),
             g = parseInt(hex.slice(2, 4), 16),
             b = parseInt(hex.slice(4, 6), 16);
         // http://stackoverflow.com/a/3943023/112731
         return (r * 0.299 + g * 0.587 + b * 0.114) <= 186;
     };
 
-    var tabsInfo = {
+    const tabsInfo = {
         'tab-intro':            {order: 1,    icon: 'apps'},
         'tab-info':             {order: 5,    icon: 'info',              host: true},
         'tab-adapters':         {order: 10,   icon: 'store',             host: true},
         'tab-instances':        {order: 15,   icon: 'subtitles',         host: true},
         'tab-objects':          {order: 20,   icon: 'view_list'},
@@ -1687,41 +1691,41 @@ $(document).ready(function () {
         'tab-text2command-0':   {order: 55,   icon: 'ac_unit'},
         'tab-text2command-1':   {order: 56,   icon: 'ac_unit'},
         'tab-text2command-2':   {order: 57,   icon: 'ac_unit'},
         'tab-node-red-0':       {order: 60,   icon: 'device_hub'},
         'tab-node-red-1':       {order: 61,   icon: 'device_hub'},
-        'tab-node-red-2':       {order: 62,   icon: 'device_hub'},        
+        'tab-node-red-2':       {order: 62,   icon: 'device_hub'},
         'tab-fullcalendar-0':   {order: 65,   icon: 'perm_contact_calendar'},
         'tab-fullcalendar-1':   {order: 66,   icon: 'perm_contact_calendar'},
         'tab-fullcalendar-2':   {order: 67,   icon: 'perm_contact_calendar'},
         'tab-hosts':            {order: 100,  icon: 'storage'},
     };
 
     function initSideNav() {
-        var lines = '';
+        let lines = '';
 
-        var elements = [];
+        const elements = [];
         $('.admin-tab').each(function () {
-            var id = $(this).attr('id');
+            const id = $(this).attr('id');
             if (!main.systemConfig.common.tabs || main.systemConfig.common.tabs.indexOf(id) !== -1) {
                 elements.push({
                     line: '<li class="admin-sidemenu-items" data-tab="' + id + '"><a href="#' + id + '">' +
                             (tabsInfo[id] && tabsInfo[id].icon ? '<i class="material-icons left">' + tabsInfo[id].icon + '</i>' : '<i class="material-icons left">live_help</i>') +
                             _($(this).data('name')) + '</a></li>',
                     id: id
                 });
             }
         });
         $('.tab-custom').each(function () {
-            var id = $(this).attr('id');
+            const id = $(this).attr('id');
             if (!main.systemConfig.common.tabs || main.systemConfig.common.tabs.indexOf(id) !== -1) {
-                var icon;
+                let icon;
                 if (tabsInfo[id] && tabsInfo[id].icon) {
                     icon = tabsInfo[id].icon;
                 } else {
-                    var _id = 'system.adapter.' + id.substring(4);
-		    if (main.objects[_id] && main.objects[_id].common.adminTab && main.objects[_id].common.adminTab['fa-icon']) {
+                    const _id = 'system.adapter.' + id.substring(4);
+		            if (main.objects[_id] && main.objects[_id].common.adminTab && main.objects[_id].common.adminTab['fa-icon']) {
                         icon = main.objects[_id].common.adminTab['fa-icon'];
                     }
                 }
 
                 elements.push({
@@ -1740,11 +1744,11 @@ $(document).ready(function () {
             if (tabsInfo[a.id].order < tabsInfo[b.id].order) return -1;
             if (tabsInfo[a.id].order > tabsInfo[b.id].order) return 1;
             return 0;
         });
 
-        for (var e = 0; e < elements.length; e++) {
+        for (let e = 0; e < elements.length; e++) {
             lines += elements[e].line;
         }
         $adminSideMenu.find('.admin-sidemenu-menu').html(lines);
 
         $('.admin-sidemenu-close').off('click').on('click', function () {
@@ -1790,18 +1794,18 @@ $(document).ready(function () {
     main.socket.on('objectChange',      function (id, obj) {
         setTimeout(objectChange, 0, id, obj);
     });
     main.socket.on('cmdStdout',         function (_id, text) {
         if (activeCmdId === _id) {
-            var m = text.match(/^upload \[(\d+)]/);
+            let m = text.match(/^upload \[(\d+)]/);
             if (m) {
                 if ($dialogCommand.data('max') === null) {
                     $dialogCommand.data('max', parseInt(m[1], 10));
                     $dialogCommandProgress.removeClass('indeterminate').addClass('determinate');
                 }
-                var max = $dialogCommand.data('max');
-                var value = parseInt(m[1], 10);
+                const max = $dialogCommand.data('max');
+                const value = parseInt(m[1], 10);
                 $dialogCommandProgress.css('width', (100 - Math.round((value / max) * 100)) + '%');
             } else {
                 m = text.match(/^got [-_:\/\\.\w\d]+\/admin$/);
                 if (m) {
                     // upload of admin
@@ -1846,11 +1850,11 @@ $(document).ready(function () {
             $dialogCommand.find('.progress-dont-close').addClass('disabled');
             $dialogCommandProgress.removeClass('indeterminate').css({'width': '100%'});
             $dialogCommand.find('.btn').html(_('Close'));
             $dialogCommand.data('finished', true);
             $dialogCommand.data('max', true);
-            var $backButton = $adminSideMain.find('.button-command');
+            const $backButton = $adminSideMain.find('.button-command');
             $backButton.removeClass('in-progress');
 
             if (!exitCode) {
                 $dialogCommand.find('.progress-text').html(_('Success!'));
                 $backButton.hide();
@@ -1858,13 +1862,13 @@ $(document).ready(function () {
                     setTimeout(function () {
                         $dialogCommand.modal('close');
                     }, 1500);
                 }
             } else {
-                var error = $dialogCommand.data('error');
+                let error = $dialogCommand.data('error');
                 if (error) {
-                    var m = error.match(/error: (.*)$/);
+                    const m = error.match(/error: (.*)$/);
                     if (m) {
                         error = m[1];
                     }
 
                     $dialogCommand.find('.progress-text').html(_('Done with error: %s', _(error))).addClass('error');
@@ -1900,11 +1904,11 @@ $(document).ready(function () {
                 main.socket.emit('getObject', 'system.config', function (errConfig, data) {
                     main.systemConfig = data;
 
                     // set logo and set branding
                     if (data && data.native && data.native.vendor) {
-                        var vendor = data.native.vendor;
+                        const vendor = data.native.vendor;
                         if (vendor.icon) {
                             $('.admin-sidemenu-header .button-icon img').attr('src', data.native.vendor.icon);
                         }
                         if (vendor.name) {
                             $('.admin-sidemenu-header .button-version').html(data.native.vendor.name).addClass('vendor');
@@ -1922,11 +1926,11 @@ $(document).ready(function () {
                             if (vendor.admin.css.header) {
                                 $adminSideMain.find('.admin-sidemenu-header nav').css(vendor.admin.css.header);
                             }
                             // apply rules
                             if (vendor.admin.css.rules) {
-                                for (var r = 0; r < vendor.admin.css.rules.length; r++) {
+                                for (let r = 0; r < vendor.admin.css.rules.length; r++) {
                                     $(vendor.admin.css.rules[r].selector).css(vendor.admin.css.rules[r].css);
                                 }
                             }
                             if (vendor.admin.styles) {
                                 $('head').append('<style type="text/css">' + vendor.admin.styles + '</style>');
@@ -1934,11 +1938,11 @@ $(document).ready(function () {
                         }
                     }
 
                     // rename log => logs (back compatibility)
                     if (main.systemConfig && main.systemConfig.common && main.systemConfig.common.tabs) {
-                        var pos = main.systemConfig.common.tabs.indexOf('tab-log');
+                        const pos = main.systemConfig.common.tabs.indexOf('tab-log');
                         if (pos !== -1) {
                             main.systemConfig.common.tabs[pos] = 'tab-logs';
                         }
                     }
 
@@ -1957,12 +1961,14 @@ $(document).ready(function () {
                                         main.systemConfig.common.longitude = main.systemConfig.common.longitude || '';
                                         main.systemConfig.common.latitude  = main.systemConfig.common.latitude  || '';
 
                                         if (!main.systemConfig.common.licenseConfirmed) {
                                             // Show license agreement
-                                            var language = (main.systemConfig.common.language || window.navigator.userLanguage || window.navigator.language || '').substring(0, 2);
-                                            if (language !== 'en' && language !== 'de' && language !== 'ru') language = 'en';
+                                            let language = (main.systemConfig.common.language || window.navigator.userLanguage || window.navigator.language || '').substring(0, 2);
+                                            if (language !== 'en' && language !== 'de' && language !== 'ru') {
+                                                language = 'en';
+                                            }
 
                                             systemLang = language;
 
                                             $dialogLicense.find('.license_text').html(license[language] || license.en);
 
@@ -1991,11 +1997,11 @@ $(document).ready(function () {
                                                 }
                                             });
 
                                             // workaround for materialize checkbox problem
                                             $dialogLicense.find('input[type="checkbox"]+span').off('click').on('click', function () {
-                                                var $input = $(this).prev();
+                                                const $input = $(this).prev();
                                                 if (!$input.prop('disabled')) {
                                                     $input.prop('checked', !$input.prop('checked')).trigger('change');
                                                 }
                                             });
 
@@ -2075,11 +2081,11 @@ $(document).ready(function () {
                                 translateAll();
 
                                 // Here we go!
                                 initAllDialogs();
                                 // call prepare
-                                for (var t in tabs) {
+                                for (const t in tabs) {
                                     if (tabs.hasOwnProperty(t) && tabs[t] && typeof tabs[t].prepare === 'function') {
                                         tabs[t].prepare();
                                     }
                                 }
                                 // TABS
@@ -2094,22 +2100,19 @@ $(document).ready(function () {
         } else {
             main.resubscribeStates();
             main.resubscribeObjects();
             main.resubscribeLogs();
         }
-        if (main.waitForRestart) {
-            location.reload();
-        }
+
+        main.waitForRestart && location.reload();
     });
     main.socket.on('disconnect',        function () {
         $('#connecting').show();
     });
     main.socket.on('reconnect',         function () {
         $('#connecting').hide();
-        if (main.waitForRestart) {
-            location.reload();
-        }
+        main.waitForRestart && location.reload();
     });
     main.socket.on('repoUpdated',       function () {
         setTimeout(function () {
             tabs.adapters.init(true);
         }, 0);
diff --git a/src/js/adminAdapters.js b/src/js/adminAdapters.js
index 48baf48..0562db2 100644
--- a/src/js/adminAdapters.js
+++ b/src/js/adminAdapters.js
@@ -1,9 +1,9 @@
 function Adapters(main) {
     'use strict';
 
-    var that = this;
+    const that = this;
 
     this.curRepository     = null;
     this.curRepoLastUpdate = null;
     this.curInstalled      = null;
     this.curRepoLastHost   = null;
@@ -67,11 +67,11 @@ function Adapters(main) {
         occ:          'schedule'
     };
 
     function getVersionClass(version) {
         if (version) {
-            var tmp = version.split ('.');
+            const tmp = version.split ('.');
             if (tmp[0] === '0' && tmp[1] === '0' && tmp[2] === '0') {
                 version = 'planned';
             } else if (tmp[0] === '0' && tmp[1] === '0') {
                 version = 'alpha';
             } else if (tmp[0] === '0') {
@@ -104,13 +104,13 @@ function Adapters(main) {
                 show: function (currentId, filter, onSuccess) {
                     that.sortTree();
                 },
                 source:     that.tree,
                 renderColumns: function(event, data) {
-                    var node = data.node;
-                    var $tdList = $(node.tr).find('>td');
-                    var obj = that.data[node.key];
+                    const node = data.node;
+                    const $tdList = $(node.tr).find('>td');
+                    const obj = that.data[node.key];
 
                     function ellipsis(txt) {
                         return '<div class="text-ellipsis">' + txt + '</div>';
                     }
 
@@ -118,22 +118,22 @@ function Adapters(main) {
                         $tdList.eq(0).css({'font-weight': 'bold'});
                         $tdList.eq(0).find('img').remove();
                         $tdList.eq(0).find('span.fancytree-title').attr('style', 'padding-left: 0px !important');
 
                         // Calculate total count of adapter and count of installed adapter
-                        for (var c = 0; c < that.tree.length; c++) {
+                        for (let c = 0; c < that.tree.length; c++) {
                             if (that.tree[c].key === node.key) {
                                 $tdList.eq(1).html(that.tree[c].desc || '').css({'overflow': 'hidden', 'white-space': 'nowrap', position: 'relative'});
-                                var installed = 0;
-                                for (var k = 0; k < that.tree[c].children.length; k++) {
-                                    if (that.data[that.tree[c].children[k].key].installed) installed++;
+                                let installed = 0;
+                                for (let k = 0; k < that.tree[c].children.length; k++) {
+                                    if (that.data[that.tree[c].children[k].key].installed) {
+                                        installed++;
+                                    }
                                 }
                                 that.tree[c].installed = installed;
                                 node.data.installed = installed;
-                                var title;
-                                //if (!that.onlyInstalled && !that.onlyUpdatable) {
-                                title = '[<span title="' + _('Installed from group') + '">' + installed + '</span> / <span title="' + _('Total count in group') + '">' + that.tree[c].children.length + '</span>]';
+                                let title = '[<span title="' + _('Installed from group') + '">' + installed + '</span> / <span title="' + _('Total count in group') + '">' + that.tree[c].children.length + '</span>]';
                                 $tdList.eq(1).html(ellipsis('<span class="dark-green">' + installed + '</span> ' + _('of') + '<span class="dark-blue"> ' + that.tree[c].children.length + '</span> ' + _('Adapters from this Group installed')));
                                 break;
                             }
                         }
                         return;
@@ -143,12 +143,12 @@ function Adapters(main) {
 
                     function setHtml(no, html) {
                         return $tdList.eq(no).html(ellipsis(html));
                     }
 
-                    var idx = obj.desc.indexOf('<div');
-                    var desc = idx >= 0 ? obj.desc.substr(0, idx) : obj.desc;
+                    const idx = obj.desc.indexOf('<div');
+                    const desc = idx >= 0 ? obj.desc.substr(0, idx) : obj.desc;
                     $tdList.eq(1).html(ellipsis(obj.desc))
                         .attr('title', desc)
                         .css({'white-space': 'nowrap', position: 'relative', 'font-weight': obj.bold ? 'bold' : null}).find('>div>div')
                         .css('height: 22px !important')
                     ;
@@ -204,11 +204,11 @@ function Adapters(main) {
                     that.$tab.find('.process-adapters').hide();
                 }, 100);
             });
 
             that.$tab.find('#btn_list_adapters').show().off('click').on('click', function () {
-                var $processAdapters = that.$tab.find('.process-adapters');
+                const $processAdapters = that.$tab.find('.process-adapters');
                 $processAdapters.show();
                 that.isList = !that.isList;
                 if (that.isList) {
                     that.$tab.find('#btn_list_adapters').addClass('red lighten-3');
                     that.$tab.find('#btn_expand_adapters').hide();
@@ -276,15 +276,15 @@ function Adapters(main) {
             onOnlyUpdatableChanged();
         }
     }
 
     function filterTiles() {
-        var anyVisible = false;
+        let anyVisible = false;
         // filter
         if (that.currentFilter) {
             that.$tiles.find('.tile').each(function () {
-                var $this = $(this);
+                const $this = $(this);
                 if (that.currentType && !$this.hasClass('class-' + that.currentType)) {
                     $this.hide();
                     return;
                 }
 
@@ -381,22 +381,22 @@ function Adapters(main) {
 
         this.$tab.find('#btn_filter_custom_url')
             .off('click')
             .on('click', function () {
                 // prepare adapters
-                var data = {};
-                var order = [];
-                var url;
+                const data = {};
+                const order = [];
+                let url;
                 for (url in that.urls) {
                     if (that.urls.hasOwnProperty(url)) {
                         order.push(url);
                     }
                 }
                 order.sort();
 
-                for (var o = 0; o < order.length; o++) {
-                    var user = that.urls[order[o]].match(/\.com\/([-_$§A-Za-z0-9]+)\/([-._$§A-Za-z0-9]+)\//);
+                for (let o = 0; o < order.length; o++) {
+                    const user = that.urls[order[o]].match(/\.com\/([-_$§A-Za-z0-9]+)\/([-._$§A-Za-z0-9]+)\//);
                     if (user && user.length >= 2 && (that.main.config.expertMode || order[o].indexOf('js-controller') === -1)) {
                         //text += '<option value="https://github.com/' + user[1] + '/ioBroker.' + order[o] + '/tarball/master ' + order[o] + '">' + order[o] + '</option>';
                         data[order[o] + ' [' + user[1] + ']'] = null;
 
                     }
@@ -410,20 +410,20 @@ function Adapters(main) {
                 });
 
                 that.$installDialog.modal();
 
                 that.$installDialog.find('.btn-install').off('click').on('click', function () {
-                    var isCustom = !that.$installDialog.find('a[href="#tabs-install-github"]').hasClass('active');//!!that.$installDialog.find('#tabs-install').tabs('option', 'active');
-                    var url;
-                    var debug;
-                    var adapter;
+                    const isCustom = !that.$installDialog.find('a[href="#tabs-install-github"]').hasClass('active');//!!that.$installDialog.find('#tabs-install').tabs('option', 'active');
+                    let url;
+                    let debug;
+                    let adapter;
                     if (isCustom) {
                         url     = that.$installDialog.find('#install-url-link').val();
                         debug   = that.$installDialog.find('#install-url-debug').prop('checked') ? ' --debug' : '';
                         adapter = '';
                     } else {
-                        var parts = that.$installDialog.find('#install-github-link').val().split(' ');
+                        const parts = that.$installDialog.find('#install-github-link').val().split(' ');
                         url     = 'https://github.com/' + parts[1].replace(/^\[|]$/g, '') + '/ioBroker.' + parts[0] + '/tarball/master';
                         debug   = that.$installDialog.find('#install-github-debug').prop('checked') ? ' --debug' : '';
                         adapter = ' ' + parts[0];
                     }
 
@@ -438,11 +438,11 @@ function Adapters(main) {
                         }
                     });
                 });
                 // workaround for materialize checkbox problem
                 that.$installDialog.find('input[type="checkbox"]+span').off('click').on('click', function () {
-                    var $input = $(this).prev();
+                    const $input = $(this).prev();
                     if (!$input.prop('disabled')) {
                         $input.prop('checked', !$input.prop('checked')).trigger('change');
                     }
                 });
                 that.$installDialog.modal('open');
@@ -576,29 +576,29 @@ function Adapters(main) {
 
     function customFilter(node) {
         //if (node.parent && node.parent.match) return true;
 
         if (that.currentFilter) {
-             if (!that.data[node.key]) return false;
+            if (!that.data[node.key]) return false;
 
-             var title = that.data[node.key].title;
-             if (title && typeof title === 'object') {
+            let title = that.data[node.key].title;
+            if (title && typeof title === 'object') {
                  title = title[systemLang] || title.en;
-             }
-            var desc = that.data[node.key].desc;
+            }
+            let desc = that.data[node.key].desc;
             if (desc && typeof desc === 'object') {
-                desc = desc[systemLang] || desc.en;
+               desc = desc[systemLang] || desc.en;
             }
 
-            if ((that.data[node.key].name      && that.data[node.key].name.toLowerCase().indexOf(that.currentFilter)     !== -1) ||
-                 (title                        && title.toLowerCase().indexOf(that.currentFilter)                        !== -1) ||
-                 (that.data[node.key].keywords && that.data[node.key].keywords.toLowerCase().indexOf(that.currentFilter) !== -1) ||
-                 (desc                         && desc.toLowerCase().indexOf(that.currentFilter)                         !== -1)){
+            if ((that.data[node.key].name     && that.data[node.key].name.toLowerCase().indexOf(that.currentFilter)     !== -1) ||
+                (title                        && title.toLowerCase().indexOf(that.currentFilter)                        !== -1) ||
+                (that.data[node.key].keywords && that.data[node.key].keywords.toLowerCase().indexOf(that.currentFilter) !== -1) ||
+                (desc                         && desc.toLowerCase().indexOf(that.currentFilter)                         !== -1)){
                 return true;
-             } else {
-                 return false;
-             }
+            } else {
+                return false;
+            }
         } else {
             return true;
         }
     }
 
@@ -619,60 +619,62 @@ function Adapters(main) {
             return;
         }
 
         if (!this.curRepository || this.curRepoLastHost !== host) {
             this.curRepository = null;
-            this.main.socket.emit('sendToHost', host, 'getRepository', {repo: this.main.systemConfig.common.activeRepo, update: updateRepo}, function (_repository) {
+            this.main.socket.emit('sendToHost', host, 'getRepository', {repo: this.main.systemConfig.common.activeRepo, update: updateRepo}, _repository => {
                 if (_repository === 'permissionError') {
                     console.error('May not read "getRepository"');
                     _repository = {};
                 }
 
-                that.curRepository = _repository || {};
-                if (that.curRepository && that.curInstalled && that.curRunning) {
-                    that.curRepoLastUpdate = (new Date()).getTime();
-                    setTimeout(function () {
-                        for (var c = 0; c < that.curRunning.length; c++) {
-                            that.curRunning[c](that.curRepository, that.curInstalled);
-                        }
-                        that.curRunning = null;
+                this.curRepository = _repository || {};
+
+                if (this.curRepository && that.curInstalled && that.curRunning) {
+                    this.curRepoLastUpdate = Date.now();
+                    setTimeout(() => {
+                        this.curRunning.forEach(cb =>
+                            cb(this.curRepository, this.curInstalled));
+
+                        this.curRunning = null;
                     }, 0);
                 }
             });
         }
         if (!this.curInstalled || this.curRepoLastHost !== host) {
             this.curInstalled = null;
-            this.main.socket.emit('sendToHost', host, 'getInstalled', null, function (_installed) {
+            this.main.socket.emit('sendToHost', host, 'getInstalled', null, _installed => {
                 if (_installed === 'permissionError') {
                     console.error('May not read "getInstalled"');
                     _installed = {};
                 }
 
-                that.curInstalled = _installed || {};
-                if (that.curRepository && that.curInstalled) {
-                    that.curRepoLastUpdate = (new Date()).getTime();
-                    setTimeout(function () {
-                        for (var c = 0; c < that.curRunning.length; c++) {
-                            that.curRunning[c](that.curRepository, that.curInstalled);
-                        }
-                        that.curRunning = null;
+                this.curInstalled = _installed || {};
+
+                if (this.curRepository && that.curInstalled) {
+                    this.curRepoLastUpdate = Date.now();
+                    setTimeout(() => {
+                        this.curRunning.forEach(cb =>
+                            cb(this.curRepository, this.curInstalled));
+
+                        this.curRunning = null;
                     }, 0);
                 }
             });
         }
 
         this.curRepoLastHost = host;
 
         if (this.curInstalled && this.curRepository) {
-            setTimeout(function () {
-                if (that.curRunning) {
-                    for (var c = 0; c < that.curRunning.length; c++) {
-                        that.curRunning[c](that.curRepository, that.curInstalled);
-                    }
-                    that.curRunning = null;
+            setTimeout(() => {
+                if (this.curRunning) {
+                    this.curRunning.forEach(cb =>
+                        cb(this.curRepository, this.curInstalled));
+
+                    this.curRunning = null;
                 }
-                if (callback) callback(that.curRepository, that.curInstalled);
+                callback && callback(this.curRepository, this.curInstalled);
             }, 0);
         } else {
             this.curRunning = [callback];
         }
     };
@@ -683,13 +685,13 @@ function Adapters(main) {
             this.$grid.colResizable({liveDrag: true});
         }
     };
 
     function getNews(actualVersion, adapter) {
-        var text = '';
+        let text = '';
         if (adapter.news) {
-            for (var v in adapter.news) {
+            for (const v in adapter.news) {
                 if (adapter.news.hasOwnProperty(v)) {
                     if (systemLang === v) text += (text ? '\n' : '') + adapter.news[v];
                     if (v === 'en' || v === 'ru'  || v === 'de') continue;
                     if (v === actualVersion) break;
                     text += (text ? '\n' : '') + (adapter.news[v][systemLang] || adapter.news[v].en);
@@ -700,26 +702,26 @@ function Adapters(main) {
     }
 
     function checkDependencies(dependencies) {
         if (!dependencies) return '';
         // like [{"js-controller": ">=0.10.1"}]
-        var adapters;
+        let adapters;
         if (dependencies instanceof Array) {
             adapters = {};
-            for (var a = 0; a < dependencies.length; a++) {
+            for (let a = 0; a < dependencies.length; a++) {
                 if (typeof dependencies[a] === 'string') continue;
-                for (var b in dependencies[a]) {
+                for (const b in dependencies[a]) {
                     if (dependencies[a].hasOwnProperty(b)) {
                         adapters[b] = dependencies[a][b];
                     }
                 }
             }
         } else {
             adapters = dependencies;
         }
 
-        for (var adapter in adapters) {
+        for (const adapter in adapters) {
             if (adapters.hasOwnProperty(adapter)) {
                 if (adapter === 'js-controller') {
                     if (!semver.satisfies(that.main.objects['system.host.' + that.main.currentHost].common.installedVersion, adapters[adapter])) return _('Invalid version of %s. Required %s', adapter, adapters[adapter]);
                 } else {
                     if (!that.main.objects['system.adapter.' + adapter] || !that.main.objects['system.adapter.' + adapter].common || !that.main.objects['system.adapter.' + adapter].common.installedVersion) return _('No version of %s', adapter);
@@ -730,19 +732,19 @@ function Adapters(main) {
         return '';
     }
 
     this.sortTree = function() {
         function sort(c1, c2) {
-            //var d1 = that.data[c1.key], d2 = that.data[c1.key];
-            var inst1 = c1.data.installed || 0, inst2 = c2.data.installed || 0;
-            var ret = inst2 - inst1;
+            //const d1 = that.data[c1.key], d2 = that.data[c1.key];
+            const inst1 = c1.data.installed || 0, inst2 = c2.data.installed || 0;
+            const ret = inst2 - inst1;
             if (ret) return ret;
-            var t1 = c1.titleLang || c1.title || '';
+            let t1 = c1.titleLang || c1.title || '';
             if (typeof t1 === 'object') {
                 t1 = t1[systemLang] || t1.en;
             }
-            var t2 = c2.titleLang || c2.title || '';
+            let t2 = c2.titleLang || c2.title || '';
             if (typeof t2 === 'object') {
                 t2 = t2[systemLang] || t2.en;
             }
 
             t1 = t1.toLowerCase();
@@ -758,12 +760,12 @@ function Adapters(main) {
         now = now || Date.now();
         if (!time) return '';
         if (typeof time === 'string' || typeof time === 'number') {
             time = new Date(time);
         }
-        var interval = now.getTime() - time.getTime();
-        var days = Math.floor(interval / (24 * 3600000));
+        const interval = now.getTime() - time.getTime();
+        const days = Math.floor(interval / (24 * 3600000));
         if (days === 0) {
             if (now.getDate() === time.getDate()) {
                 return todayText;
             } else {
                 return yesterdayText;
@@ -773,12 +775,12 @@ function Adapters(main) {
                 return yesterdayText;
             } else {
                 return x2DaysAgoText.replace('%d', days + 1);
             }
         } else {
-            var t  = days % 10;
-            var tt = days % 100;
+            const t  = days % 10;
+            const tt = days % 100;
             // 2, 3, 4, 22, 23, 24, 32, 33, 34, 111, ...x2, x3, h4
             if ((tt < 10 || tt > 20) && t >= 2 && t <= 4) {
                 return x2DaysAgoText.replace('%d', days);
             } else
             // 1, 21, 31, 41, 121....
@@ -796,20 +798,20 @@ function Adapters(main) {
             that.$tab.find('.process-adapters').show();
 
             this.$grid.find('tbody').html('');
 
             this.getAdaptersInfo(this.main.currentHost, update, updateRepo, function (repository, installedList) {
-                var obj;
-                var version;
-                var rawVersion;
-                var adapter;
-                var adaptersToUpdate = 0;
-
-                var listInstalled = [];
-                var listNonInstalled = [];
-                var nowObj = new Date();
-                var localTexts = {
+                let obj;
+                let version;
+                let rawVersion;
+                let adapter;
+                let adaptersToUpdate = 0;
+
+                const listInstalled = [];
+                const listNonInstalled = [];
+                const nowObj = new Date();
+                const localTexts = {
                     'add instance':             _('add instance'),
                     'update':                   _('update'),
                     'upload':                   _('upload'),
                     'Available version:':       _('Available version:'),
                     'Active instances':         _('Active instances'),
@@ -856,13 +858,13 @@ function Adapters(main) {
                     listNonInstalled.push(adapter);
                 }
                 listNonInstalled.sort();
 
                 function getVersionString(version, updatable, news, updatableError) {
-                    //var span = getVersionSpan(version);
-                    var color = getVersionClass(version);
-                    var title = color + '\n\r' + (news || '');
+                    //const span = getVersionSpan(version);
+                    const color = getVersionClass(version);
+                    const title = color + '\n\r' + (news || '');
                     //version = '<table style="min-width: 80px; width: 100%; text-align: center; border: 0; border-spacing: 0px;' + (news ? 'font-weight: bold;' : '') + '" cellspacing="0" cellpadding="0" class="ui-widget">' +
                     version = //'<div style="height: 100% !important;">' +
                         '<table style="cursor: alias; width: 100%; text-align: center; border: 0; border-spacing: 0;' + (news ? 'color: blue;' : '') + '" cellspacing="0" cellpadding="0" class="ui-widget">' +
                         '<tr class="' + color + 'Bg">' +
                         '<td title="' + localTexts['Available version:'] + ' ' + title + '" class="actual-version">' + version + '</td>' +
@@ -876,31 +878,32 @@ function Adapters(main) {
 
                 that.tree = [];
                 that.data = {};
 
                 // list of the installed adapters
-                for (var i = 0; i < listInstalled.length; i++) {
+                for (const i = 0; i < listInstalled.length; i++) {
                     adapter = listInstalled[i];
 
                     obj = installedList ? installedList[adapter] : null;
 
                     if (!obj || obj.controller || adapter === 'hosts') continue;
-                    var installed = '';
-                    var rawInstalled = '';
-                    var icon = obj.icon;
+                    let installed = '';
+                    let rawInstalled = '';
+                    let icon = obj.icon;
                     version = '';
 
                     if (repository[adapter] && repository[adapter].version) version = repository[adapter].version;
 
                     if (repository[adapter] && repository[adapter].extIcon) icon = repository[adapter].extIcon;
 
-                    var _instances = 0;
-                    var _enabled   = 0;
+                    let _instances = 0;
+                    let _enabled   = 0;
                     if (obj.version) {
-                        var news = '';
-                        var updatable = false;
-                        var updatableError = '';
+                        let news = '';
+                        let updatable = false;
+                        let updatableError = '';
+
                         if (!that.main.upToDate(version, obj.version)) {
                             news = getNews(obj.version, repository[adapter]);
                             // check if version is compatible with current adapters and js-controller
                             updatable = true;
                             updatableError = checkDependencies(repository[adapter].dependencies);
@@ -909,11 +912,11 @@ function Adapters(main) {
                         // TODO: move style to class
                         installed = '<table style="min-width: 80px; text-align: center; border: 0; border-spacing: 0;" cellspacing="0" cellpadding="0" class="ui-widget">' +
                             '<tr>';
 
                         // Show information about installed and enabled instances
-                        for (var z = 0; z < that.main.instances.length; z++) {
+                        for (const z = 0; z < that.main.instances.length; z++) {
                             if (that.main.objects[that.main.instances[z]] &&
                                 that.main.objects[that.main.instances[z]].common.name === adapter) {
                                 _instances++;
                                 if (that.main.objects[that.main.instances[z]].common.enabled) _enabled++;
                             }
@@ -954,15 +957,15 @@ function Adapters(main) {
                         if (!updatable && that.onlyUpdatable) continue;
                     }
                     rawVersion = version;
                     version = getVersionString(version, updatable, news, updatableError);
 
-                    var group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
-                    var desc  = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
+                    const group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
+                    let desc  = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                     desc = desc || '';
                     desc += showUploadProgress(group, adapter, that.main.states['system.adapter.' + adapter + '.upload'] ? that.main.states['system.adapter.' + adapter + '.upload'].val : 0);
-                    var title = obj.titleLang || obj.title;
+                    let title = obj.titleLang || obj.title;
                     title = (typeof title === 'object') ? (title[systemLang] || title.en) : title;
 
                     that.data[adapter] = {
                         image:      icon ? '<img onerror="this.src=\'img/info-big.png\';" src="' + icon + '" class="adapter-table-icon" />' : '',
                         icon:       icon || '',
@@ -994,12 +997,12 @@ function Adapters(main) {
 
                     if (!obj.type) console.log('"' + adapter + '": "common adapters",');
                     if (obj.type && that.types[adapter]) console.log('Adapter "' + adapter + '" has own type. Remove from admin.');
 
                     if (!that.isList) {
-                        var iGroup = -1;
-                        for (var jj = 0; jj < that.tree.length; jj++) {
+                        let iGroup = -1;
+                        for (const jj = 0; jj < that.tree.length; jj++) {
                             if (that.tree[jj].key === that.data[adapter].group) {
                                 iGroup = jj;
                                 break;
                             }
                         }
@@ -1030,11 +1033,11 @@ function Adapters(main) {
                     }
                 }
                 //that.sortTree();
 
                 if (!that.onlyInstalled && !that.onlyUpdatable) {
-                    for (i = 0; i < listNonInstalled.length; i++) {
+                    for (let i = 0; i < listNonInstalled.length; i++) {
                         adapter = listNonInstalled[i];
 
                         obj = repository[adapter];
                         if (!obj || obj.controller) continue;
                         version = '';
@@ -1044,16 +1047,16 @@ function Adapters(main) {
                             version = obj.version;
                             rawVersion = version;
                             version = getVersionString(version);
                         }
 
-                        var group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
-                        var desc = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
+                        const group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
+                        let desc = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                         desc = desc || '';
                         desc += showUploadProgress(group, adapter, that.main.states['system.adapter.' + adapter + '.upload'] ? that.main.states['system.adapter.' + adapter + '.upload'].val : 0);
 
-                        title = obj.titleLang || obj.title;
+                        let title = obj.titleLang || obj.title;
                         title = (typeof title === 'object') ? (title[systemLang] || title.en) : title;
 
                         that.data[adapter] = {
                             image:      obj.extIcon ? '<img onerror="this.src=\'img/info-big.png\';" src="' + obj.extIcon + '" class="adapter-table-icon" />' : '',
                             icon:       obj.extIcon,
@@ -1080,12 +1083,12 @@ function Adapters(main) {
 
                         if (!obj.type) console.log('"' + adapter + '": "common adapters",');
                         if (obj.type && that.types[adapter]) console.log('Adapter "' + adapter + '" has own type. Remove from admin.');
 
                         if (!that.isList) {
-                            var igroup = -1;
-                            for (var j = 0; j < that.tree.length; j++){
+                            let igroup = -1;
+                            for (const j = 0; j < that.tree.length; j++){
                                 if (that.tree[j].key === that.data[adapter].group) {
                                     igroup = j;
                                     break;
                                 }
                             }
@@ -1116,11 +1119,11 @@ function Adapters(main) {
                         }
                     }
                 }
 
                 if (that.currentOrder === 'popular' || that.currentOrder === 'updated') {
-                    var akeys = Object.keys(that.data);
+                    const akeys = Object.keys(that.data);
 
                     if (that.currentOrder === 'popular') {
                         akeys.sort(function (a, b) {
                             if (that.data[a].stat > that.data[b].stat) return -1;
                             if (that.data[a].stat < that.data[b].stat) return 1;
@@ -1135,24 +1138,24 @@ function Adapters(main) {
                             if (a > b) return -1;
                             if (a < b) return 1;
                             return 0;
                         });
                     }
-                    var newData = {};
-                    for (var u = 0; u < akeys.length; u++) {
+                    const newData = {};
+                    for (let u = 0; u < akeys.length; u++) {
                         newData[akeys[u]] = that.data[akeys[u]];
                     }
                     that.data = newData;
                 }
 
                 // build tiles
                 if (that.isTiles && (that.main.browser !== 'ie' || that.main.browserVersion > 10)) {
-                    var text = '';
-                    var types = [];
-                    for (var a in that.data) {
+                    let text = '';
+                    const types = [];
+                    for (const a in that.data) {
                         if (!that.data.hasOwnProperty(a)) continue;
-                        var ad = that.data[a];
+                        const ad = that.data[a];
                         if (types.indexOf(ad.group) === -1) {
                             types.push(ad.group);
                         }
 //                        text += '<div class="tile class-' + ad.group + '" data-id="' + ad.name + '">';
 //                        text += '   <div class="card-header">';
@@ -1225,21 +1228,21 @@ function Adapters(main) {
                     text += '   </div>';
                     text += '</div>';
 
                     that.$tiles.html(text);
                     // init buttons
-                    for (var b in that.data) {
+                    for (const b in that.data) {
                         if (that.data.hasOwnProperty(b)) {
                             that.initButtons(b);
                         }
                     }
 
-                    var tTypes = '<li class="main-toolbar-table-types-item" data-type=""><a>' + localTexts['all'] + '</a></li>\n';
-                    for (var g = 0; g < types.length; g++) {
+                    let tTypes = '<li class="main-toolbar-table-types-item" data-type=""><a>' + localTexts['all'] + '</a></li>\n';
+                    for (let g = 0; g < types.length; g++) {
                         tTypes += '<li class="main-toolbar-table-types-item" data-type="' + types[g] + '"><a>' + _(types[g]) + '</a></li>\n';
                     }
-                    var $types = that.$tab.find('#main-toolbar-table-types');
+                    const $types = that.$tab.find('#main-toolbar-table-types');
                     $types.html(tTypes);
                     $types.find('.main-toolbar-table-types-item').show().off('click').on('click', function () {
                         that.currentType = $(this).data('type') || '';
                         filterTiles();
                         that.$tab.find('#main-toolbar-table-types-btn').html(_(that.currentType || 'all'));
@@ -1277,24 +1280,24 @@ function Adapters(main) {
                         if ($(this).attr('src')) {
                             $(this).css({width: 18, height: 18});
                         }
 
                         $(this).on('hover', function () {
-                            var text = '<div class="icon-large" style="' +
+                            const text = '<div class="icon-large" style="' +
                                 'left: ' + Math.round($(this).position().left + $(this).width() + 5) + 'px;"><img onerror="this.src=\'img/info-big.png\';" src="' + $(this).attr('src') + '"/></div>';
-                            var $big = $(text);
+                            const $big = $(text);
                             $big.insertAfter($(this));
                             $(this).data('big', $big[0]);
-                            var h   = parseFloat($big.height());
-                            var top = Math.round($(this).position().top - ((h - parseFloat($(this).height())) / 2));
+                            const h = parseFloat($big.height());
+                            let top = Math.round($(this).position().top - ((h - parseFloat($(this).height())) / 2));
                             if (h + top > (window.innerHeight || document.documentElement.clientHeight)) {
                                 top = (window.innerHeight || document.documentElement.clientHeight) - h;
                             }
                             $big.css({top: top});
 
                         }, function () {
-                            var big = $(this).data('big');
+                            const big = $(this).data('big');
                             $(big).remove();
                             $(this).data('big', undefined);
                         });
                     });
 
@@ -1302,21 +1305,21 @@ function Adapters(main) {
                         that.$grid.fancytree('getTree').filterNodes(customFilter, false);
                     }
 
                     that.sortTree();
                     that.enableColResize();
-                    var classes = [
+                    const classes = [
                         'tab-adapters-table-name',
                         'tab-adapters-table-description',
                         'tab-adapters-table-keywords',
                         'tab-adapters-table-installed',
                         'tab-adapters-table-available',
                         'tab-adapters-table-license',
                         'tab-adapters-table-install'
                     ];
                     that.$grid.find('tbody tr').each(function () {
-                        var i = 0;
+                        const i = 0;
                         $(this).find('td').each(function () {
                             $(this).addClass(classes[i]);
                             i++;
                         });
                     })
@@ -1340,28 +1343,30 @@ function Adapters(main) {
     };
 
     this.updateCounter = function (counter) {
         if (counter === undefined) {
             this.getAdaptersInfo(this.main.currentHost, false, false, function (repository, installedList) {
-                var adaptersToUpdate = 0;
+                let adaptersToUpdate = 0;
 
-                for (var adapter in installedList) {
+                for (const adapter in installedList) {
                     if (!installedList.hasOwnProperty(adapter)) continue;
-                    var obj = installedList ? installedList[adapter] : null;
+                    const obj = installedList ? installedList[adapter] : null;
                     if (!obj || obj.controller || adapter === 'hosts') continue;
 
-                    var version = '';
-                    if (repository[adapter] && repository[adapter].version) version = repository[adapter].version;
+                    let version = '';
+                    if (repository[adapter] && repository[adapter].version) {
+                        version = repository[adapter].version;
+                    }
 
                     if (obj.version && !that.main.upToDate(version, obj.version)) {
                         adaptersToUpdate++;
                     }
                 }
                 that.updateCounter(adaptersToUpdate);
             });
         } else if (counter) {
-            var $updates = $('#updates-for-adapters');
+            const $updates = $('#updates-for-adapters');
             if ($updates.length) {
                 $updates.text(counter);
             } else {
                 $('<span id="updates-for-adapters" title="' + _('updates') + '" class="new badge updates-for-adapters" data-badge-caption="">' + counter + '</span>').appendTo('.admin-sidemenu-items[data-tab="tab-adapters"] a');
             }
@@ -1407,18 +1412,18 @@ function Adapters(main) {
     function showAddInstanceDialog(adapter, desc, callback) {
         if (that.main.tabs.hosts.list.length <= 1 && !that.main.config.expertMode) {
             return callback(true, that.main.currentHost, '');
         }
 
-        var $dialogAddInstance = $('#dialog-add-instance');
+        const $dialogAddInstance = $('#dialog-add-instance');
         $dialogAddInstance.find('.dialog-add-instance-name').html(adapter);
         $dialogAddInstance.find('.dialog-add-description').html(desc);
 
         // fill the hosts
-        var text = '';
-        for (var h = 0; h < that.main.tabs.hosts.list.length; h++) {
-            var host = that.main.tabs.hosts.list[h];
+        let text = '';
+        for (const h = 0; h < that.main.tabs.hosts.list.length; h++) {
+            const host = that.main.tabs.hosts.list[h];
             text += '<option ' + (host.name === that.main.currentHost ? 'selected' : '') + ' value="' + host.name + '">' + host.name + '</option>';
         }
 
         if (that.main.tabs.hosts.list.length <= 1) {
             $dialogAddInstance.find('.dialog-add-instance-host').addClass('disabled').prop('disabled', true);
@@ -1426,25 +1431,25 @@ function Adapters(main) {
             $dialogAddInstance.find('.dialog-add-instance-host').removeClass('disabled').prop('disabled', false);
         }
         $dialogAddInstance.find('.dialog-add-instance-host').html(text).select();
 
         // find free instance numbers
-        var min = -1;
-        var used = [];
-        for (var i = 0; i < that.main.tabs.instances.list.length; i++) {
-            var parts = that.main.tabs.instances.list[i].split('.');
+        let min = -1;
+        const used = [];
+        for (const i = 0; i < that.main.tabs.instances.list.length; i++) {
+            const parts = that.main.tabs.instances.list[i].split('.');
             if (parts[parts.length - 2] === adapter) {
-                var index = parseInt(parts[parts.length - 1], 10);
+                const index = parseInt(parts[parts.length - 1], 10);
                 used.push(index);
                 if (index > min) {
                     min = index;
                 }
             }
         }
         min += 10;
         text = '<option selected value="">' + _('auto') + '</option>';
-        for (var m = 0; m < min; m++) {
+        for (const m = 0; m < min; m++) {
             if (used.indexOf(m) !== -1) continue;
             text += '<option value="' + m + '">' + m + '</option>';
         }
         $dialogAddInstance.find('.dialog-add-instance-number').html(text).select();
         $dialogAddInstance.find('.dialog-add-install-btn').off('click').on('click', function (e) {
@@ -1471,18 +1476,18 @@ function Adapters(main) {
             }
         }).modal('open');
     }
 
     function showLicenseDialog(adapter, callback) {
-        var $dialogLicense = $('#dialog-license');
+        const $dialogLicense = $('#dialog-license');
         // Is adapter installed
         if (that.data[adapter].installed || !that.data[adapter].licenseUrl) {
             callback(true);
             return;
         }
 
-        var timeout = setTimeout(function () {
+        let timeout = setTimeout(function () {
             timeout = null;
             callback(true);
         }, 10000);
 
         if (!that.data[adapter].licenseUrl) {
@@ -1542,44 +1547,41 @@ function Adapters(main) {
         });
     }
 
     this.initButtons = function (adapter) {
         this.$tab.find('.adapter-install-submit[data-adapter-name="' + adapter + '"]').off('click').on('click', function () {
-            var adapter = $(this).attr('data-adapter-name');
-            var desc = $(this).attr('data-adapter-desc');
+            const adapter = $(this).attr('data-adapter-name');
+            const desc = $(this).attr('data-adapter-desc');
 
             // show config dialog
             showAddInstanceDialog(adapter, desc, function (result, host, index) {
                 if (!result) return;
 
                 that.getAdaptersInfo(host, false, false, function (repo, installed) {
-                    var obj = repo[adapter];
+                    let obj = repo[adapter];
 
-                    if (!obj) obj = installed[adapter];
+                    obj = obj || installed[adapter];
 
-                    if (!obj) return;
+                    if (!obj) {
+                        return;
+                    }
 
                     if (obj.license && obj.license !== 'MIT') {
                         // Show license dialog!
-                        showLicenseDialog(adapter, function (isAgree) {
-                            if (isAgree) {
-                                that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, function (exitCode) {
-                                    if (!exitCode) that._postInit(true);
-                                });
-                            }
-                        });
+                        showLicenseDialog(adapter, isAgree =>
+                            isAgree && that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, exitCode =>
+                                !exitCode && that._postInit(true)));
                     } else {
-                        that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, function (exitCode) {
-                            if (!exitCode) that._postInit(true);
-                        });
+                        that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, exitCode =>
+                            !exitCode && that._postInit(true));
                     }
                 });
             });
         });
 
         this.$tab.find('.adapter-delete-submit[data-adapter-name="' + adapter + '"]').off('click').on('click', function () {
-            var name = $(this).attr('data-adapter-name');
+            const name = $(this).attr('data-adapter-name');
             that.main.confirmMessage(_('Are you sure you want to delete adapter %s?', name), _('Please confirm'), 'help', function (result) {
                 if (result) {
                     that.main.cmdExec(null, 'del ' + name, function (exitCode) {
                         if (!exitCode) that._postInit(true);
                     });
@@ -1594,47 +1596,47 @@ function Adapters(main) {
                 params:  $(this).data('adapter-name')
             });
         });
 
         this.$tab.find('.adapter-update-submit[data-adapter-name="' + adapter + '"]').off('click').on('click', function () {
-            var aName = $(this).attr('data-adapter-name');
+            const aName = $(this).attr('data-adapter-name');
             if (aName === 'admin') that.main.waitForRestart = true;
 
             that.main.cmdExec(null, 'upgrade ' + aName, function (exitCode) {
                 if (!exitCode) that._postInit(true);
             });
         });
 
         this.$tab.find('.adapter-upload-submit[data-adapter-name="' + adapter + '"]').off('click').on('click', function () {
-            var aName = $(this).attr('data-adapter-name');
+            const aName = $(this).attr('data-adapter-name');
 
             that.main.cmdExec(null, 'upload ' + aName, function (exitCode) {
                 if (!exitCode) that._postInit(true);
             });
         });
 
-        var $button = this.$tab.find('.adapter-update-custom-submit[data-adapter-name="' + adapter + '"]');
+        const $button = this.$tab.find('.adapter-update-custom-submit[data-adapter-name="' + adapter + '"]');
         $button.off('click').on('click', function () {
-            var versions = [];
+            const versions = [];
             if (that.main.objects['system.adapter.' + adapter].common.news) {
-                var news = that.main.objects['system.adapter.' + adapter].common.news;
-                for (var id in news) {
+                const news = that.main.objects['system.adapter.' + adapter].common.news;
+                for (const id in news) {
                     if (news.hasOwnProperty(id)) {
                         versions.push(id);
                     }
                 }
             } else {
                 versions.push(that.main.objects['system.adapter.' + adapter].common.version);
             }
-            var menu = '<div class="collection">';
-            for (var v = 0; v < versions.length; v++) {
-                var nnews = (news[versions[v]] ? news[versions[v]][systemLang] || news[versions[v]].en : '');
+            const menu = '<div class="collection">';
+            for (const v = 0; v < versions.length; v++) {
+                const nnews = (news[versions[v]] ? news[versions[v]][systemLang] || news[versions[v]].en : '');
                 menu += '<a data-version="' + versions[v] + '" data-position="left" data-delay="50" title="' + nnews + '" data-adapter-name="' + $(this).data('adapter-name') + '" class="collection-item adapters-versions-link tooltipped"><span class="adapters-versions-link-version">' + versions[v] + '</span> - <div class="adapters-versions-link-history">' + nnews + '</div></a>';
             }
             menu += '</div>';
 
-            var $adaptersMenu = $('#adapters-menu');
+            const $adaptersMenu = $('#adapters-menu');
             if (!$adaptersMenu.length) {
                 //$adaptersMenu = $('<div id="adapters-menu" class="dropdown-content m"></div>');
                 $adaptersMenu = $('<div id="adapters-menu" class="modal modal-fixed-footer"><div class="modal-content">' +
                     '<h4>Modal Header</h4><p></p></div><div class="modal-footer">' +
                     '<a class="modal-action modal-close waves-effect waves-green btn-flat ">' + _('Close') + '</a></div></div>');
@@ -1647,23 +1649,23 @@ function Adapters(main) {
             $adaptersMenu.find('h4').html(_('Versions of %s', adapter));
 
             $adaptersMenu.find('.adapters-versions-link').off('click').on('click', function () {
                 //if ($(this).data('link')) window.open($(this).data('link'), $(this).data('instance-id'));
                 $adaptersMenu.modal('close');
-                var adapter = $(this).data('adapter-name');
-                var version = $(this).data('version');
+                const adapter = $(this).data('adapter-name');
+                const version = $(this).data('version');
                 if (version && adapter) {
                     that.main.cmdExec(null, 'upgrade ' + adapter + '@' + version, function (exitCode) {
                         if (!exitCode) that._postInit(true);
                     });
                 }
             });
 
             /*$(this).dropdown({
                 onCloseEnd: function () {
-                    var $adaptersMenu = $('#adapters-menu');
-                    var trigger = $adaptersMenu.data('trigger');
+                    const $adaptersMenu = $('#adapters-menu');
+                    const trigger = $adaptersMenu.data('trigger');
                     $(trigger).dropdown('close').dropdown('destroy');
                     $adaptersMenu.data('trigger', null).hide();
                     $adaptersMenu.remove();
                 }
             }).dropdown('open');*/
@@ -1683,11 +1685,11 @@ function Adapters(main) {
         // Update Adapter Table
         if (id.match(/^system\.adapter\.[a-zA-Z0-9-_]+$/)) {
             if (obj) {
                 if (this.list.indexOf(id) === -1) this.list.push(id);
             } else {
-                var j = this.list.indexOf(id);
+                const j = this.list.indexOf(id);
                 if (j !== -1) {
                     this.list.splice(j, 1);
                 }
             }
 
@@ -1696,12 +1698,12 @@ function Adapters(main) {
             }
         }
     };
 
     function showUploadProgress(group, adapter, percent) {
-        var text = '';
-        var opened;
+        let text = '';
+        let opened;
         if (adapter || typeof group === 'string') {
             if (adapter) {
                // text += '<div class="adapter-upload-progress" data-adapter-name="' + adapter + '"';
             } else {
                 text += '<div class="group-upload-progress"';
@@ -1731,14 +1733,14 @@ function Adapters(main) {
         return text;
     }
 
     this.stateChange = function (id, state) {
         if (id && state) {
-            var adapter = id.match(/^system\.adapter\.([\w\d-]+)\.upload$/);
+            const adapter = id.match(/^system\.adapter\.([\w\d-]+)\.upload$/);
             if (adapter) {
-                var $adapter = this.$tab.find('.adapter-upload-progress[data-adapter-name="' + adapter[1] + '"]');
-                var text = showUploadProgress(state.val);
+                const $adapter = this.$tab.find('.adapter-upload-progress[data-adapter-name="' + adapter[1] + '"]');
+                const text = showUploadProgress(state.val);
                 $adapter.html(text).css({opacity: state.val ? 0.7 : 0});
                 this.$tab.find('.group-upload-progress[data-adapter-group="' + $adapter.data('adapter-group') + '"]').html(text).css({opacity: state.val ? 0.7 : 0});
             }
         }
     };
diff --git a/src/login/index.html b/src/login/index.html
index 2c2c5a2..9c922c4 100644
--- a/src/login/index.html
+++ b/src/login/index.html
@@ -1,10 +1,10 @@
 <html>
 <head>
     <style>
         * {
-            box-sizing:border-box;
+            box-sizing: border-box;
         }
 
         body {
             font-family: Helvetica;
             background: #64b5f6;
@@ -61,11 +61,13 @@
             border: none;
             border-radius: 0;
             border-bottom: 1px solid #757575;
         }
 
-        input:focus { outline: none; }
+        input:focus {
+            outline: none;
+        }
 
         /* Label */
 
         label {
             color: #999;
@@ -77,12 +79,12 @@
             top: 10px;
             transition: all 0.2s ease;
         }
 
         /* active */
-
-        input:focus ~ label, input.used ~ label {
+        input:focus ~ label,
+        input.used ~ label {
             top: -20px;
             transform: scale(.75); left: -2px;
             /* font-size: 14px; */
             color: #4a89dc;
         }
@@ -109,11 +111,14 @@
 
         .bar:after { right: 50%; }
 
         /* active */
 
-        input:focus ~ .bar:before, input:focus ~ .bar:after { width: 50%; }
+        input:focus ~ .bar:before,
+        input:focus ~ .bar:after {
+            width: 50%;
+        }
 
 
         /* Highlight */
 
         .highlight {
@@ -161,22 +166,25 @@
             border: 0;
             border-bottom: 2px solid #3160B6;
             cursor: pointer;
             transition: all 0.15s ease;
         }
-        .button:focus { outline: 0; }
 
+        .button:focus {
+            outline: 0;
+        }
 
         /* Button modifiers */
-
         .buttonBlue {
             background: #2196f3;
             text-shadow: 1px 1px 0 rgba(39, 110, 204, .5);
             border-radius: 2px;
         }
 
-        .buttonBlue:hover { background: #39a1f4; }
+        .buttonBlue:hover {
+            background: #39a1f4;
+        }
 
 
         /* Ripples container */
         .ripples {
             position: absolute;
@@ -266,15 +274,15 @@
 <header>
     <h1 id="login-label">ioBroker Admin</h1>
 </header>
 <form action="/login" method="post">
     <div class="group">
-        <input type="text" name="username" id="username"><span class="highlight"></span><span class="bar"></span>
+        <input type="text" name="username" id="username" class="used"><span class="highlight"></span><span class="bar"></span>
         <label id="username-label" for="username">Name</label>
     </div>
     <div class="group">
-        <input type="password" name="password" id="password"><span class="highlight"></span><span class="bar"></span>
+        <input type="password" name="password" id="password" class="used"><span class="highlight"></span><span class="bar"></span>
         <label id="password-label" for="password">Password</label>
     </div>
     <input type="hidden" id="origin" name="origin" value=""/>
     <button type="submit" class="button buttonBlue" id="submit"><span id="submit-label">Submit</span>
         <div class="ripples buttonRipples"><span class="ripplesCircle"></span></div>
@@ -353,21 +361,21 @@
     document.getElementById('login-label').innerHTML    = systemDictionary.title[userLang]          || systemDictionary.title.en;
     document.getElementById('submit-label').innerHTML   = systemDictionary.login[userLang]          || systemDictionary.login.en;
 
     var inputs = document.getElementsByTagName('INPUT');
     if (inputs) {
-        for (var i = 0; i < inputs.length; i++) {
+        /*for (var i = 0; i < inputs.length; i++) {
             inputs[i].addEventListener('blur', function() {
                 if (this.value) {
                     if (this.className.indexOf('used') === -1) {
                         this.className += ' used';
                     }
                 } else {
                     this.className = this.className.replace(/\s?used/, '');
                 }
             });
-        }
+        }*/
         setTimeout(function () {
             if (document.getElementById('username').value) {
                 document.getElementById('password').focus();
                 setTimeout(function () {
                     if (!document.getElementById('username').value) {
@@ -377,10 +385,11 @@
             } else {
                 document.getElementById('username').focus();
             }
         }, 300);
     }
+
     document.getElementById('username').addEventListener('keydown', function (e) {
         if (e.keyCode === 13) {
             e.preventDefault();
             setTimeout(function () {
                 document.getElementById('password').focus();

Vulnerable code: ===================

User interface for configuration and administration.

* (bluefox) fix error with empty ID 
* (bluefox) rewrite interface with materialize 
            'transform-es2015-arrow-functions',
            'transform-es2015-block-scoping',
            'transform-es2015-classes',
            'transform-es2015-template-literals'
    .pipe(uglify())    
    "version": "3.6.7",
      "3.6.6": {
      },    
      "zh-cn": "docs/zh-cn/admin.md"            
function IOSocket(server, settings, adapter, objects, states, store) {
    if (!(this instanceof IOSocket)) return new IOSocket(server, settings, adapter, objects, states, store);
        if (!this.subscribes[type]) this.subscribes[type] = {};
            if (!socket._subscribe || !socket._subscribe[type]) return;
                    if (adapter.requireLog) adapter.requireLog(true);
                    callback(null, states);
                    callback(null, states[id]);
                adapter.getForeignStates(pattern, function (err, objs) {
                    if (typeof callback === 'function') {
                        callback(err, objs);
                    } else {
                        adapter.log.warn('[getForeignStates] Invalid callback')
                    }
                });
                adapter.setForeignState(id, state, {user: this._acl.user}, function (err, res) {
                    if (typeof callback === 'function') {
                        callback(err, res);
                    }
                });

                if (adapter.log.level === 'debug') showSubscribes(socket, 'stateChange');
                if (typeof callback === 'function') {
                    setImmediate(callback, null);
                }
                if (readAll) {
                }
                adapter.subscribeForeignStates('*');
                adapter.unsubscribeForeignStates('*');
                    if (req.body.origin) {
                        const parts = req.body.origin.match(/href=(.+)$/);
                        failureRedirect: '/login/index.html' + req.body.origin + (req.body.origin ? '&error' : '?error'),
                        res.status(500).send(err);
                let parts = req.url.split('/');
                            filename = path.join(parts.join('/'), filename);
                            filename = path.join('log/', filename) ;
                        if (filename[0] !== '/' && !filename.match(/^\W:/)) {
                            filename = path.normalize(__dirname + '/../../../') + filename;
                        if (fs.existsSync(filename)) {
                res.status(404).send('File ' + filename + ' not found');
                    tempFilePath: settings.tmpPath
                                res.status(500).send(err);

                        res.status(404).send('File ' + url + ' not found');
const utils = require('@iobroker/adapter-core'); // Get common adapter utils
let socket      = null;
let webServer   = null;
let objects     = {};
let states      = {};
let secret      = 'Zgfr56gFe87jJOM'; // Will be generated by first start
        if (socket) {
            socket.objectChange(id, obj);
        }
    adapter.on('stateChange', (id, state) => {
        if (!state) {
            if (states[id]) {
                delete states[id];
            }
        } else {
            states[id] = state;
        }
        if (socket) {
            socket.stateChange(id, state);
        }
    });
        if (socket) {
            socket.sendCommand(obj);
        }
        if (socket) {
            // unsubscribe all
            socket.unsubscribeAll();
        }

}

// to do => remove it later, when all repositories patched.
function patchRepos(callback) {
    return callback && callback();
    // do not patch any more. Delete it later 2018.04.23
    /*
    adapter.getForeignObject('system.repositories', (err, obj) => {
        let changed = false;
        if (obj && obj.native && obj.native.repositories) {
            // default link should point to stable
            if (!obj.native.repositories.default || obj.native.repositories.default.link !== 'http://download.iobroker.net/sources-dist.json') {
                changed = true;
                obj.native.repositories.default = {
                    link: 'http://download.iobroker.net/sources-dist.json'
                };
            }
            // latest link should point to latest
            if (!obj.native.repositories.latest) {
                obj.native.repositories.latest = {
                    link: 'http://download.iobroker.net/sources-dist-latest.json'
                };
                changed = true;
            }

            // change URL of raw sources from ioBroker.js-controller to ioBroker.repositories
            for (let r in obj.native.repositories) {
                if (obj.native.repositories.hasOwnProperty(r) &&
                    obj.native.repositories[r].link === 'https://raw.githubusercontent.com/ioBroker/ioBroker.js-controller/master/conf/sources-dist.json') {
                    obj.native.repositories[r].link = 'https://raw.githubusercontent.com/ioBroker/ioBroker.repositories/master/sources-dist.json';
                    changed = true;
                }
            }
        }
        if (changed) {
            adapter.setForeignObject(obj._id, obj, function () {
                callback && callback();
            });
        } else {
            callback && callback();
        }
    });*/
    socket = new SocketIO(server, adapter.config, adapter, objects, states, store);
    patchRepos(() => {
        // By default update repository every 24 hours
        if (adapter.config.autoUpdate === undefined) {
            adapter.config.autoUpdate = 24;
        }
        adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;
        if (adapter.config.autoUpdate) {
            setInterval(() => updateRegister(), adapter.config.autoUpdate * 3600000);
            updateRegister();
        }
    });
    let tasks = 0;
    });
    tasks++;
        !--tasks && callback && callback();
function updateRegister() {
    adapter.log.info('Request actual repository...');
    adapter.getForeignObject('system.config', (err, data) => {
        if (data && data.common) {
            adapter.sendToHost(adapter.host, 'getRepository', {
                repo:   data.common.activeRepo,
                update: true
            }, _repository => {
                if (_repository === 'permissionError') {
                    adapter.log.error('May not read "getRepository"');
                } else {
                    adapter.log.info('Repository received successfully.');
                    socket && socket.repoUpdated();
  "version": "3.6.7",
    "body-parser": "^1.18.2",
    "cookie-parser": "^1.4.3",
    "express": "^4.16.3",
    "express-fileupload": "https://github.com/screenmeet/express-fileupload/tarball/8d8f5d9b7cec2d7c17e6d85654edf1c0aaa5fa82",
    "express-session": "^1.15.6",
    "request": "^2.85.0",
    "xtend": "^4.0.1",
    "@iobroker/testing": "^1.2.0",
    "babel-core": "^6.26.3",
    "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
    "babel-plugin-transform-es2015-block-scoping": "^6.26.0",
    "babel-plugin-transform-es2015-classes": "^6.24.1",
    "babel-plugin-transform-es2015-template-literals": "^6.22.0",
    "chai": "^4.1.2",
    "gulp": "^4.0.0",
    "gulp-babel": "^7.0.1",
    "gulp-clean-css": "^3.9.4",
    "gulp-htmlmin": "^4.0.0",
    "gulp-less": "^4.0.0",
    "gulp-sass": "^3.1.0",
    "gulp-sourcemaps": "^2.6.4",
    "gulp-terser": "^1.1.7",
    "ansi-colors": "^3.2.4",
    "mocha": "^6.1.4"
/* jslint vars: true */
    var what;
    var a = arguments;
    var L = a.length;
    var ax;
var $iframeDialog = null; // used in adapter settings window
var configNotSaved = null; // used in adapter settings window
var showConfig = null; // used in adapter settings window
var defaults = {};
var customPostInits = {};
var customPostOnSave = {};
var FORBIDDEN_CHARS = /[\]\[*,;'"`<>\\\s?]/g;
var adapterRedirect = function (redirect, timeout) {
var gMain = null; // for google maps
    var ua = window.navigator.userAgent;
    var msie = ua.indexOf('MSIE ');
    var trident = ua.indexOf('Trident/');
        var rv = ua.indexOf('rv:');
    var edge = ua.indexOf('Edge/');
    var path = location.pathname + 'socket.io';
    var allTabs = {};
    var main = {
                    var cb = $dialogConfirm.data('callback');
                var tButtons = '';
                for (var b = buttons.length - 1; b >= 0; b--) {
                    var cb = $dialogConfirm.data('callback');
            var text = typeof dateObj;
            var v;
            var $wizard = $('#button-wizard');
                        main._lastTimer = (new Date()).getTime();
                var obj = main.objects[main.currentUser];
                var name = '';
                    var objs = {};
                var groups = [];
                for (var i = 0; i < tabs.users.groups.length; i++) {
                    var group = main.objects[tabs.users.groups[i]];
            var id;
                    var obj = main.objects[id];
        },     
                var list = [];
                for (var id in main.objects) {
            var leaf = $tree ? $tree.selectId('getTreeInfo', id) : null;
    var tabs = {
    var stdout;
    var cmdCallback    = null;
    var activeCmdId    = null;
    var $stdout        = $('#stdout');
    var $dialogCommand = $('#dialog-command');
    var $dialogLicense = $('#dialog-license-main');
    var $dialogMessage = $('#dialog-message');
    var $dialogConfirm = $('#dialog-confirm');
    var $dialogCommandProgress = $dialogCommand.find('.progress div');
    var $adminSideMenu = $('#admin_sidemenu_menu');
    var $adminSideMain = $('#admin_sidemenu_main');
    var firstConnect   = true;
			var $versionBtn = $('.button-version');
            var $dialog = $('#admin_sidemenu_dialog');
            var html = $dialog.html();
            var $e = $(event.target);
            var offs = $e.offset();
            var text =
            var $lis = $adminSideMenu;
            for (var tid in allTabs) {
                var name = allTabs[tid];
                var found = $adminSideMenu.find('.admin-sidemenu-items[data-tab="' + tid + '"]').length;
                var id = 'chk-' + tid;
                var id = $(this).attr('id').substr(4);
                    var pos = main.systemConfig.common.tabs.indexOf(id);
                var $input = $(this).prev();
        
        
            var $menu = $adminSideMenu;
            var panelSelector = $menu.data('problem-link');
                var $panel = $(panelSelector);
                    var link = $panel.data('src');
                        var $iframe = $panel.find('iframe');
        var text     = '';
        var list     = [];
        var addTabs = [];
        for (var i = 0; i < main.instances.length; i++) {
            var instance = main.instances[i];
            var instanceObj = main.objects[instance];
                var isFound = false;
                var inst1 = instance.replace(/\.(\d+)$/, '.');
                for (var j = 0; j < addTabs.length; j++) {
                    var inst2 = addTabs[j].replace(/\.(\d+)$/, '.');
            var $this = $(this);
            var id = $this.attr('id');
        for (var a = 0; a < addTabs.length; a++) {
            var tab   = main.objects[addTabs[a]];
            var name  = 'tab-' + tab.common.name;
            var link  = tab.common.adminTab.link || '/adapter/' + tab.common.name + '/tab.html';
            var parts = addTabs[a].split('.');
            var buttonName;
                var isReplace = false;
                    var div = '<div id="' + name + '" data-name="' + buttonName + '" class="tab-custom ' + (isReplace ? 'link-replace' : '') + '" data-adapter="' + parts[2] + '" data-instance="' + parts[3] + '" data-src="' + link + '">' +
                        var elem = $ (this).contents ().find('body>header');
                        if (!elem || !elem.length) elem = $(this).contents ().find('head');
                        if (elem && elem.length) elem.append('<link rel="stylesheet" type="text/css" href="../../lib/css/iob/selectID.css"/>');
            var countLink = 0;
            var loadTimeout = setTimeout(function() {
        var $selHosts = $('#host-adapters');
        var lines = [];
        var color;
        var curId;
        for (var i = 0; i < main.tabs.hosts.list.length; i++) {
        var $selBtn = $('#host-adapters-btn').show();
            var val = $(this).data('value');
            var id  = 'system.host.' + val + '.alive';
            var val = $(this).prop('checked');
            var $input = $(this).prev();
                    var major = parseInt(result['Node.js'].split('.').shift().replace('v', ''), 10);
                var obj;
                for (var id in main.objects) {
                for (var dialog in main.dialogs) {
        //var changed = false;
        var oldObj = null;
        var action = 'update';

        oldObj = main.objects[id];
                var ignore = false;
                        var newObj = JSON.parse(JSON.stringify(obj));
        var ts = (new Date()).getTime();
        main._timer = setTimeout(function () {
        for (var pattern in main.subscribesStates) {
        for (var pattern in main.subscribesObjects) {
            for (var s = 0; s < patterns.length; s++) {
            for (var s = 0; s < patterns.length; s++) {
                    console.debug('Unsibscribe: ' + patterns[s]);
            for (var s = 0; s < patterns.length; s++) {
            for (var s = 0; s < patterns.length; s++) {
        var parts = decodeURI(window.location.hash).split('/');
            var parts   = decodeURI(window.location.hash).split('/');
                var parts  = main.currentHash.split('/');
                var tab    = parts[0].replace(/^#/, '').replace(/^tab-/, '');
                var dialog = parts[1];
                var params = decodeURIComponent(parts[2]);
                var $adminBody = $('.admin-sidemenu-body');
                var $actualTab = $adminBody.find('.admin-sidemenu-body-content');
                var $panel     = $('#tab-' + tab);
                    var link;
                        var $oldPanel = $('#tab-' + main.currentTab);
                            var $iframe_ = $oldPanel.find('>iframe');
                            var $iframe = $panel.find('>iframe');
                var  $tab = $adminSideMenu.find('.admin-sidemenu-items[data-tab="tab-' + tab + '"]');
        var icon;
        var alt;
        var isCommon = obj && obj.common;
                    var instance;
        var icon     = '';
        var alt      = '';
                var result = getIconHtml(obj);
        var icon     = '';
        var alt      = '';
            var result = getIconHtml(obj);
        var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
        var u = -1;
        var r = parseInt(hex.slice(0, 2), 16),
    var tabsInfo = {
        'tab-node-red-2':       {order: 62,   icon: 'device_hub'},        
        var lines = '';
        var elements = [];
            var id = $(this).attr('id');
            var id = $(this).attr('id');
                var icon;
                    var _id = 'system.adapter.' + id.substring(4);
		    if (main.objects[_id] && main.objects[_id].common.adminTab && main.objects[_id].common.adminTab['fa-icon']) {
        for (var e = 0; e < elements.length; e++) {
            var m = text.match(/^upload \[(\d+)]/);
                var max = $dialogCommand.data('max');
                var value = parseInt(m[1], 10);
            var $backButton = $adminSideMain.find('.button-command');
                var error = $dialogCommand.data('error');
                    var m = error.match(/error: (.*)$/);
                        var vendor = data.native.vendor;
                                for (var r = 0; r < vendor.admin.css.rules.length; r++) {
                        var pos = main.systemConfig.common.tabs.indexOf('tab-log');
                                            var language = (main.systemConfig.common.language || window.navigator.userLanguage || window.navigator.language || '').substring(0, 2);
                                            if (language !== 'en' && language !== 'de' && language !== 'ru') language = 'en';
                                                var $input = $(this).prev();
                                for (var t in tabs) {
        if (main.waitForRestart) {
            location.reload();
        }
        if (main.waitForRestart) {
            location.reload();
        }
    var that = this;
            var tmp = version.split ('.');
                    var node = data.node;
                    var $tdList = $(node.tr).find('>td');
                    var obj = that.data[node.key];
                        for (var c = 0; c < that.tree.length; c++) {
                                var installed = 0;
                                for (var k = 0; k < that.tree[c].children.length; k++) {
                                    if (that.data[that.tree[c].children[k].key].installed) installed++;
                                var title;
                                //if (!that.onlyInstalled && !that.onlyUpdatable) {
                                title = '[<span title="' + _('Installed from group') + '">' + installed + '</span> / <span title="' + _('Total count in group') + '">' + that.tree[c].children.length + '</span>]';
                    var idx = obj.desc.indexOf('<div');
                    var desc = idx >= 0 ? obj.desc.substr(0, idx) : obj.desc;
                var $processAdapters = that.$tab.find('.process-adapters');
        var anyVisible = false;
                var $this = $(this);
                var data = {};
                var order = [];
                var url;
                for (var o = 0; o < order.length; o++) {
                    var user = that.urls[order[o]].match(/\.com\/([-_$§A-Za-z0-9]+)\/([-._$§A-Za-z0-9]+)\//);
                    var isCustom = !that.$installDialog.find('a[href="#tabs-install-github"]').hasClass('active');//!!that.$installDialog.find('#tabs-install').tabs('option', 'active');
                    var url;
                    var debug;
                    var adapter;
                        var parts = that.$installDialog.find('#install-github-link').val().split(' ');
                    var $input = $(this).prev();
             if (!that.data[node.key]) return false;
             var title = that.data[node.key].title;
             if (title && typeof title === 'object') {
             }
            var desc = that.data[node.key].desc;
                desc = desc[systemLang] || desc.en;
            if ((that.data[node.key].name      && that.data[node.key].name.toLowerCase().indexOf(that.currentFilter)     !== -1) ||
                 (title                        && title.toLowerCase().indexOf(that.currentFilter)                        !== -1) ||
                 (that.data[node.key].keywords && that.data[node.key].keywords.toLowerCase().indexOf(that.currentFilter) !== -1) ||
                 (desc                         && desc.toLowerCase().indexOf(that.currentFilter)                         !== -1)){
             } else {
                 return false;
             }
            this.main.socket.emit('sendToHost', host, 'getRepository', {repo: this.main.systemConfig.common.activeRepo, update: updateRepo}, function (_repository) {
                that.curRepository = _repository || {};
                if (that.curRepository && that.curInstalled && that.curRunning) {
                    that.curRepoLastUpdate = (new Date()).getTime();
                    setTimeout(function () {
                        for (var c = 0; c < that.curRunning.length; c++) {
                            that.curRunning[c](that.curRepository, that.curInstalled);
                        }
                        that.curRunning = null;
            this.main.socket.emit('sendToHost', host, 'getInstalled', null, function (_installed) {
                that.curInstalled = _installed || {};
                if (that.curRepository && that.curInstalled) {
                    that.curRepoLastUpdate = (new Date()).getTime();
                    setTimeout(function () {
                        for (var c = 0; c < that.curRunning.length; c++) {
                            that.curRunning[c](that.curRepository, that.curInstalled);
                        }
                        that.curRunning = null;
            setTimeout(function () {
                if (that.curRunning) {
                    for (var c = 0; c < that.curRunning.length; c++) {
                        that.curRunning[c](that.curRepository, that.curInstalled);
                    }
                    that.curRunning = null;
                if (callback) callback(that.curRepository, that.curInstalled);
        var text = '';
            for (var v in adapter.news) {
        var adapters;
            for (var a = 0; a < dependencies.length; a++) {
                for (var b in dependencies[a]) {
        for (var adapter in adapters) {
            //var d1 = that.data[c1.key], d2 = that.data[c1.key];
            var inst1 = c1.data.installed || 0, inst2 = c2.data.installed || 0;
            var ret = inst2 - inst1;
            var t1 = c1.titleLang || c1.title || '';
            var t2 = c2.titleLang || c2.title || '';
        var interval = now.getTime() - time.getTime();
        var days = Math.floor(interval / (24 * 3600000));
            var t  = days % 10;
            var tt = days % 100;
                var obj;
                var version;
                var rawVersion;
                var adapter;
                var adaptersToUpdate = 0;

                var listInstalled = [];
                var listNonInstalled = [];
                var nowObj = new Date();
                var localTexts = {
                    //var span = getVersionSpan(version);
                    var color = getVersionClass(version);
                    var title = color + '\n\r' + (news || '');
                for (var i = 0; i < listInstalled.length; i++) {
                    var installed = '';
                    var rawInstalled = '';
                    var icon = obj.icon;
                    var _instances = 0;
                    var _enabled   = 0;
                        var news = '';
                        var updatable = false;
                        var updatableError = '';
                        for (var z = 0; z < that.main.instances.length; z++) {
                    var group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
                    var desc  = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                    var title = obj.titleLang || obj.title;
                        var iGroup = -1;
                        for (var jj = 0; jj < that.tree.length; jj++) {
                    for (i = 0; i < listNonInstalled.length; i++) {
                        var group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
                        var desc = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                        title = obj.titleLang || obj.title;
                            var igroup = -1;
                            for (var j = 0; j < that.tree.length; j++){
                    var akeys = Object.keys(that.data);
                    var newData = {};
                    for (var u = 0; u < akeys.length; u++) {
                    var text = '';
                    var types = [];
                    for (var a in that.data) {
                        var ad = that.data[a];
                    for (var b in that.data) {
                    var tTypes = '<li class="main-toolbar-table-types-item" data-type=""><a>' + localTexts['all'] + '</a></li>\n';
                    for (var g = 0; g < types.length; g++) {
                    var $types = that.$tab.find('#main-toolbar-table-types');
                            var text = '<div class="icon-large" style="' +
                            var $big = $(text);
                            var h   = parseFloat($big.height());
                            var top = Math.round($(this).position().top - ((h - parseFloat($(this).height())) / 2));
                            var big = $(this).data('big');
                    var classes = [
                        var i = 0;
                var adaptersToUpdate = 0;
                for (var adapter in installedList) {
                    var obj = installedList ? installedList[adapter] : null;
                    var version = '';
                    if (repository[adapter] && repository[adapter].version) version = repository[adapter].version;
            var $updates = $('#updates-for-adapters');
        var $dialogAddInstance = $('#dialog-add-instance');
        var text = '';
        for (var h = 0; h < that.main.tabs.hosts.list.length; h++) {
            var host = that.main.tabs.hosts.list[h];
        var min = -1;
        var used = [];
        for (var i = 0; i < that.main.tabs.instances.list.length; i++) {
            var parts = that.main.tabs.instances.list[i].split('.');
                var index = parseInt(parts[parts.length - 1], 10);
        for (var m = 0; m < min; m++) {
        var $dialogLicense = $('#dialog-license');
        var timeout = setTimeout(function () {
            var adapter = $(this).attr('data-adapter-name');
            var desc = $(this).attr('data-adapter-desc');
                    var obj = repo[adapter];
                    if (!obj) obj = installed[adapter];
                    if (!obj) return;
                        showLicenseDialog(adapter, function (isAgree) {
                            if (isAgree) {
                                that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, function (exitCode) {
                                    if (!exitCode) that._postInit(true);
                                });
                            }
                        });
                        that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, function (exitCode) {
                            if (!exitCode) that._postInit(true);
                        });
            var name = $(this).attr('data-adapter-name');
            var aName = $(this).attr('data-adapter-name');
            var aName = $(this).attr('data-adapter-name');
        var $button = this.$tab.find('.adapter-update-custom-submit[data-adapter-name="' + adapter + '"]');
            var versions = [];
                var news = that.main.objects['system.adapter.' + adapter].common.news;
                for (var id in news) {
            var menu = '<div class="collection">';
            for (var v = 0; v < versions.length; v++) {
                var nnews = (news[versions[v]] ? news[versions[v]][systemLang] || news[versions[v]].en : '');
            var $adaptersMenu = $('#adapters-menu');
                var adapter = $(this).data('adapter-name');
                var version = $(this).data('version');
                    var $adaptersMenu = $('#adapters-menu');
                    var trigger = $adaptersMenu.data('trigger');
                var j = this.list.indexOf(id);
        var text = '';
        var opened;
            var adapter = id.match(/^system\.adapter\.([\w\d-]+)\.upload$/);
                var $adapter = this.$tab.find('.adapter-upload-progress[data-adapter-name="' + adapter[1] + '"]');
                var text = showUploadProgress(state.val);
            box-sizing:border-box;
        input:focus { outline: none; }

        input:focus ~ label, input.used ~ label {
        input:focus ~ .bar:before, input:focus ~ .bar:after { width: 50%; }
        .button:focus { outline: 0; }

        .buttonBlue:hover { background: #39a1f4; }
        <input type="text" name="username" id="username"><span class="highlight"></span><span class="bar"></span>
        <input type="password" name="password" id="password"><span class="highlight"></span><span class="bar"></span>
        for (var i = 0; i < inputs.length; i++) {
        }
Fixed code: User interface for configuration and administration of ioBroker.
### 3.6.8 (2019-10-09)
* (bluefox) Log paths were sanitized
* (bluefox) NPM packages were updated

* (bluefox) fix error with empty ID
* (bluefox) rewrite interface with materialize
            '@babel/plugin-transform-arrow-functions',
            '@babel/plugin-transform-block-scoping',
            '@babel/plugin-transform-classes',
            '@babel/plugin-transform-template-literals'
    .pipe(uglify())
    "version": "3.6.8",
      "3.6.7": {
        "en": "Add Node.JS version check to popup messages",
        "de": "Node.JS-Versionsprüfung zu Popup-Nachrichten hinzufügen",
        "ru": "Добавить проверку версии Node.JS во всплывающие сообщения",
        "pt": "Adicione a verificação da versão do Node.JS às mensagens pop-up",
        "nl": "Voeg Node.JS versiecontrole toe aan pop-upberichten",
        "fr": "Ajouter la vérification de la version de Node.JS aux messages contextuels",
        "it": "Aggiungi il controllo versione Node.JS ai messaggi popup",
        "es": "Agregar verificación de versión de Node.JS a mensajes emergentes",
        "pl": "Dodaj sprawdzanie wersji Node.JS do wyskakujących wiadomości",
        "zh-cn": "将Node.JS版本检查添加到弹出消息"
      },"3.6.6": {
      },
      "zh-cn": "docs/zh-cn/admin.md"
function IOSocket(server, settings, adapter, objects, store) {
    if (!(this instanceof IOSocket)) {
        return new IOSocket(server, settings, adapter, objects, store);
    }
    let states = {};
        this.subscribes[type] = this.subscribes[type] || {};
            if (!socket._subscribe || !socket._subscribe[type]) {
                return;
            }

                    adapter.requireLog && adapter.requireLog(true);
                    adapter.getForeignStates('*', callback);
                    if (states[id]) {
                        callback(null, states[id]);
                    } else {
                        adapter.getForeignState(id, callback);
                    }
                if (typeof callback === 'function') {
                    adapter.getForeignStates(pattern, callback);
                } else {
                    adapter.log.warn('[getForeignStates] Invalid callback')
                }
                // clear cache
                if (states[id]) {
                    delete states[id];
                }

                adapter.setForeignState(id, state, {user: this._acl.user}, (err, res) =>
                    typeof callback === 'function' && callback(err, res));
                // clear cache
                if (states[id]) {
                    delete states[id];
                }

                // reset states cache on unsubscribe
                states = {};

                adapter.log.level === 'debug' && showSubscribes(socket, 'stateChange');

                typeof callback === 'function' && setImmediate(callback, null);
                /*if (readAll) {
                }*/

                Object.keys(that.subscribes.stateChange).forEach(pattern =>
                    adapter.subscribeForeignStates(pattern));


                Object.keys(that.subscribes.stateChange).forEach(pattern =>
                    adapter.unsubscribeForeignStates(pattern));

        if (!state) {
            if (states[id]) {
                delete states[id];
            }
        } else {
            states[id] = state;
        }

// copied from here: https://github.com/component/escape-html/blob/master/index.js
const matchHtmlRegExp = /["'&<>]/;
function escapeHtml (string) {
    const str = '' + string;
    const match = matchHtmlRegExp.exec(str);

    if (!match) {
        return str;
    }

    let escape;
    let html = '';
    let index = 0;
    let lastIndex = 0;

    for (index = match.index; index < str.length; index++) {
        switch (str.charCodeAt(index)) {
            case 34: // "
                escape = '&quot;';
                break;
            case 38: // &
                escape = '&amp;';
                break;
            case 39: // '
                escape = '&#39;';
                break;
            case 60: // <
                escape = '&lt;';
                break;
            case 62: // >
                escape = '&gt;';
                break;
            default:
                continue;
        }

        if (lastIndex !== index) {
            html += str.substring(lastIndex, index);
        }

        lastIndex = index + 1;
        html += escape;
    }

    return lastIndex !== index
        ? html + str.substring(lastIndex, index)
        : html;
}

                    const origin = (req.body && req.body.origin) || '?href=%2F';
                    if (origin) {
                        const parts = origin.match(/href=(.+)$/);
                            // if some invalid characters in redirect
                            if (redirect.match(/[^-_a-zA-Z0-9&%?.]/)) {
                                redirect = '/';
                            }
                    req.body.password = ((req.body && req.body.password) || '').toString();
                    req.body.username = ((req.body && req.body.username) || '').toString();

                        failureRedirect: '/login/index.html' + origin + (origin ? '&error' : '?error'),
                        res.status(500).send(escapeHtml(typeof err === 'string' ? err : JSON.stringify(err)));
                let parts = decodeURIComponent(req.url).split('/');
                        let logFolder;
                            logFolder = path.normalize(parts.join('/'));
                            logFolder = path.join(process.cwd(), 'log');
                        if (logFolder[0] !== '/' && logFolder[0] !== '\\' && !logFolder.match(/^[a-zA-Z]:/)) {
                            logFolder = path.normalize(path.join(__dirname + '/../../../', logFolder));
                        filename = path.normalize(path.join(logFolder, filename));

                        if (filename.startsWith(logFolder) && fs.existsSync(filename)) {
                res.status(404).send('File ' + escapeHtml(filename) + ' not found');
                    tempFileDir: settings.tmpPath
                    // take first non empty file
                                res.status(500).send(escapeHtml(typeof err === 'string' ? err : JSON.stringify(err)));
                        res.status(404).send('File ' + escapeHtml(url) + ' not found');
const utils       = require('@iobroker/adapter-core'); // Get common adapter utils
const ONE_HOUR_MS = 3600000;
const ERROR_PERMISSION = 'permissionError';
let socket        = null;
let webServer     = null;

let objects       = {};
let secret        = 'Zgfr56gFe87jJOM'; // Will be generated by first start
        socket && socket.objectChange(id, obj);
    adapter.on('stateChange', (id, state) =>
        socket && socket.stateChange(id, state));
        socket && socket.sendCommand(obj);
        // unsubscribe all
        socket && socket.unsubscribeAll();
    socket = new SocketIO(server, adapter.config, adapter, objects, store);
    // By default update repository every 24 hours
    if (adapter.config.autoUpdate === undefined || adapter.config.autoUpdate === null) {
        adapter.config.autoUpdate = 24;
    }

    // interval in hours
    adapter.config.autoUpdate = parseInt(adapter.config.autoUpdate, 10) || 0;

    adapter.config.autoUpdate && updateRegister();
    /*

    });*/


        callback && callback();
function updateRegister(isForce) {
    adapter.getForeignObject('system.config', (err, systemConfig) => {
        err && adapter.log.error('May not read "system.config"');

        if (systemConfig && systemConfig.common) {
            adapter.getForeignObject('system.repositories', (err, repos) => {
                err && adapter.log.error('May not read "system.repositories"');
                // Check if repositories exists
                let exists = false;
                const active = systemConfig.common.activeRepo;

                // if repo is valid and actual
                if (!err &&
                    repos &&
                    repos.native &&
                    repos.native.repositories &&
                    repos.native.repositories[active] &&
                    Date.now() < repos.ts + adapter.config.autoUpdate * ONE_HOUR_MS) {
                    exists = true;
                }
                if (!exists || isForce) {
                    adapter.log.info('Request actual repository...');
                    // request repo from host
                    adapter.sendToHost(adapter.host, 'getRepository', {
                        repo:   active,
                        update: true
                    }, _repository => {
                        if (_repository === ERROR_PERMISSION) {
                            adapter.log.error('May not read "getRepository"');
                        } else {
                            adapter.log.info('Repository received successfully.');

                            socket && socket.repoUpdated();
                        }

                        // start next cycle
                        if (adapter.config.autoUpdate) {
                            adapter.timerRepo && clearInterval(adapter.timerRepo);
                            adapter.log.debug('Next repo update on ' + new Date(Date.now() + adapter.config.autoUpdate * ONE_HOUR_MS + 1).toLocaleString());
                            adapter.timerRepo = setTimeout(() => updateRegister(), adapter.config.autoUpdate * ONE_HOUR_MS + 1);
                        }
                    });
                } else if (adapter.config.autoUpdate) {
                    const interval = repos.ts + adapter.config.autoUpdate * ONE_HOUR_MS - Date.now() + 1;
                    adapter.log.debug('Next repo update on ' + new Date(Date.now() + interval).toLocaleString());
                    adapter.timerRepo && clearInterval(adapter.timerRepo);
                    adapter.timerRepo = setTimeout(() => updateRegister(), interval);
  "version": "3.6.8",
    "body-parser": "^1.19.0",
    "cookie-parser": "^1.4.4",
    "express": "^4.17.1",
    "express-fileupload": "^1.1.6-alpha.5",
    "express-session": "^1.16.2",
    "request": "^2.88.0",
    "xtend": "^4.0.2",
    "@iobroker/testing": "^1.2.5",
    "@babel/core": "^7.6.3",
    "@babel/plugin-transform-arrow-functions": "^7.2.0",
    "@babel/plugin-transform-block-scoping": "^7.6.3",
    "@babel/plugin-transform-classes": "^7.5.5",
    "@babel/plugin-transform-template-literals": "^7.4.4",
    "chai": "^4.2.0",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-clean-css": "^4.2.0",
    "gulp-htmlmin": "^5.0.1",
    "gulp-less": "^4.0.1",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "^2.6.5",
    "gulp-terser": "^1.2.0",
    "ansi-colors": "^4.1.1",
    "mocha": "^6.2.1"
/* jslint consts: true */
    let what;
    const a = arguments;
    let L = a.length;
    let ax;
let $iframeDialog = null;  // used in adapter settings window
let configNotSaved = null; // used in adapter settings window
let showConfig = null;     // used in adapter settings window

const defaults = {};
const customPostInits = {};
const customPostOnSave = {};
const FORBIDDEN_CHARS = /[\]\[*,;'"`<>\\\s?]/g;
const adapterRedirect = function (redirect, timeout) {
let gMain = null; // for google maps
    const ua = window.navigator.userAgent;
    const msie = ua.indexOf('MSIE ');
    const trident = ua.indexOf('Trident/');
        const rv = ua.indexOf('rv:');
    const edge = ua.indexOf('Edge/');
    let path = location.pathname + 'socket.io';
    let allTabs = {};
    const main = {
                    const cb = $dialogConfirm.data('callback');
                let tButtons = '';
                for (let b = buttons.length - 1; b >= 0; b--) {
                    const cb = $dialogConfirm.data('callback');
            let text = typeof dateObj;
            let v;
            const $wizard = $('#button-wizard');
                        main._lastTimer = Date.now();
                const obj = main.objects[main.currentUser];
                let name = '';
                    const objs = {};
                const groups = [];
                for (let i = 0; i < tabs.users.groups.length; i++) {
                    const group = main.objects[tabs.users.groups[i]];
            let id;
                    const obj = main.objects[id];
        },
                const list = [];
                for (const id in main.objects) {
            const leaf = $tree ? $tree.selectId('getTreeInfo', id) : null;
    const tabs = {
    let stdout;
    let cmdCallback      = null;
    let activeCmdId      = null;
    const $stdout        = $('#stdout');
    const $dialogCommand = $('#dialog-command');
    const $dialogLicense = $('#dialog-license-main');
    const $dialogMessage = $('#dialog-message');
    const $dialogConfirm = $('#dialog-confirm');
    const $dialogCommandProgress = $dialogCommand.find('.progress div');
    const $adminSideMenu = $('#admin_sidemenu_menu');
    const $adminSideMain = $('#admin_sidemenu_main');
    let firstConnect     = true;
			const $versionBtn = $('.button-version');
            const $dialog = $('#admin_sidemenu_dialog');
            const html = $dialog.html();
            const $e = $(event.target);
            const offs = $e.offset();
            let text =
            const $lis = $adminSideMenu;
            for (const tid in allTabs) {
                const name = allTabs[tid];
                const found = $adminSideMenu.find('.admin-sidemenu-items[data-tab="' + tid + '"]').length;
                const id = 'chk-' + tid;
                const id = $(this).attr('id').substr(4);
                    const pos = main.systemConfig.common.tabs.indexOf(id);
                const $input = $(this).prev();


            const $menu = $adminSideMenu;
            const panelSelector = $menu.data('problem-link');
                const $panel = $(panelSelector);
                    const link = $panel.data('src');
                        const $iframe = $panel.find('iframe');
        let text      = '';
        const list    = [];
        const addTabs = [];
        for (let i = 0; i < main.instances.length; i++) {
            const instance = main.instances[i];
            const instanceObj = main.objects[instance];
                let isFound = false;
                const inst1 = instance.replace(/\.(\d+)$/, '.');
                for (let j = 0; j < addTabs.length; j++) {
                    const inst2 = addTabs[j].replace(/\.(\d+)$/, '.');
            const $this = $(this);
            const id = $this.attr('id');
        for (let a = 0; a < addTabs.length; a++) {
            const tab = main.objects[addTabs[a]];
            let name  = 'tab-' + tab.common.name;
            let link  = tab.common.adminTab.link || '/adapter/' + tab.common.name + '/tab.html';
            const parts = addTabs[a].split('.');
            let buttonName;
                let isReplace = false;
                    const div = '<div id="' + name + '" data-name="' + buttonName + '" class="tab-custom ' + (isReplace ? 'link-replace' : '') + '" data-adapter="' + parts[2] + '" data-instance="' + parts[3] + '" data-src="' + link + '">' +
                        let elem = $ (this).contents ().find('body>header');
                        if (!elem || !elem.length) {
                            elem = $(this).contents ().find('head');
                        }
                        if (elem && elem.length) {
                            elem.append('<link rel="stylesheet" type="text/css" href="../../lib/css/iob/selectID.css"/>');
                        }
            let countLink = 0;
            let loadTimeout = setTimeout(function() {
        const $selHosts = $('#host-adapters');
        const lines = [];
        let color;
        let curId;
        for (let i = 0; i < main.tabs.hosts.list.length; i++) {
        const $selBtn = $('#host-adapters-btn').show();
            const val = $(this).data('value');
            const id  = 'system.host.' + val + '.alive';
            const val = $(this).prop('checked');
            const $input = $(this).prev();
                    const major = parseInt(result['Node.js'].split('.').shift().replace('v', ''), 10);
                let obj;
                for (const id in main.objects) {
                for (const dialog in main.dialogs) {
        //const changed = false;
        const oldObj = main.objects[id];
        let action = 'update';
                let ignore = false;
                        const newObj = JSON.parse(JSON.stringify(obj));
        const ts = Date.now();

        main._timer = setTimeout(() => {
        for (const pattern in main.subscribesStates) {
        for (const pattern in main.subscribesObjects) {
            for (let s = 0; s < patterns.length; s++) {
            for (let s = 0; s < patterns.length; s++) {
                    console.debug('Unsubscribe: ' + patterns[s]);
            for (let s = 0; s < patterns.length; s++) {
            for (let s = 0; s < patterns.length; s++) {
        const parts = decodeURI(window.location.hash).split('/');
            const parts   = decodeURI(window.location.hash).split('/');
                const parts  = main.currentHash.split('/');
                let tab      = parts[0].replace(/^#/, '').replace(/^tab-/, '');
                const dialog = parts[1];
                const params = decodeURIComponent(parts[2]);
                const $adminBody = $('.admin-sidemenu-body');
                let $actualTab   = $adminBody.find('.admin-sidemenu-body-content');
                const $panel     = $('#tab-' + tab);
                    let link;
                        const $oldPanel = $('#tab-' + main.currentTab);
                            const $iframe_ = $oldPanel.find('>iframe');
                            const $iframe = $panel.find('>iframe');
                const  $tab = $adminSideMenu.find('.admin-sidemenu-items[data-tab="tab-' + tab + '"]');
        let icon;
        let alt;
        const isCommon = obj && obj.common;
                    let instance;
        let icon     = '';
        let alt      = '';
                const result = getIconHtml(obj);
        let icon     = '';
        let alt      = '';
            const result = getIconHtml(obj);
        const units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
        let u = -1;
        const r = parseInt(hex.slice(0, 2), 16),
    const tabsInfo = {
        'tab-node-red-2':       {order: 62,   icon: 'device_hub'},
        let lines = '';
        const elements = [];
            const id = $(this).attr('id');
            const id = $(this).attr('id');
                let icon;
                    const _id = 'system.adapter.' + id.substring(4);
		            if (main.objects[_id] && main.objects[_id].common.adminTab && main.objects[_id].common.adminTab['fa-icon']) {
        for (let e = 0; e < elements.length; e++) {
            let m = text.match(/^upload \[(\d+)]/);
                const max = $dialogCommand.data('max');
                const value = parseInt(m[1], 10);
            const $backButton = $adminSideMain.find('.button-command');
                let error = $dialogCommand.data('error');
                    const m = error.match(/error: (.*)$/);
                        const vendor = data.native.vendor;
                                for (let r = 0; r < vendor.admin.css.rules.length; r++) {
                        const pos = main.systemConfig.common.tabs.indexOf('tab-log');
                                            let language = (main.systemConfig.common.language || window.navigator.userLanguage || window.navigator.language || '').substring(0, 2);
                                            if (language !== 'en' && language !== 'de' && language !== 'ru') {
                                                language = 'en';
                                            }
                                                const $input = $(this).prev();
                                for (const t in tabs) {

        main.waitForRestart && location.reload();
        main.waitForRestart && location.reload();
    const that = this;
            const tmp = version.split ('.');
                    const node = data.node;
                    const $tdList = $(node.tr).find('>td');
                    const obj = that.data[node.key];
                        for (let c = 0; c < that.tree.length; c++) {
                                let installed = 0;
                                for (let k = 0; k < that.tree[c].children.length; k++) {
                                    if (that.data[that.tree[c].children[k].key].installed) {
                                        installed++;
                                    }
                                let title = '[<span title="' + _('Installed from group') + '">' + installed + '</span> / <span title="' + _('Total count in group') + '">' + that.tree[c].children.length + '</span>]';
                    const idx = obj.desc.indexOf('<div');
                    const desc = idx >= 0 ? obj.desc.substr(0, idx) : obj.desc;
                const $processAdapters = that.$tab.find('.process-adapters');
        let anyVisible = false;
                const $this = $(this);
                const data = {};
                const order = [];
                let url;
                for (let o = 0; o < order.length; o++) {
                    const user = that.urls[order[o]].match(/\.com\/([-_$§A-Za-z0-9]+)\/([-._$§A-Za-z0-9]+)\//);
                    const isCustom = !that.$installDialog.find('a[href="#tabs-install-github"]').hasClass('active');//!!that.$installDialog.find('#tabs-install').tabs('option', 'active');
                    let url;
                    let debug;
                    let adapter;
                        const parts = that.$installDialog.find('#install-github-link').val().split(' ');
                    const $input = $(this).prev();
            if (!that.data[node.key]) return false;
            let title = that.data[node.key].title;
            if (title && typeof title === 'object') {
            }
            let desc = that.data[node.key].desc;
               desc = desc[systemLang] || desc.en;
            if ((that.data[node.key].name     && that.data[node.key].name.toLowerCase().indexOf(that.currentFilter)     !== -1) ||
                (title                        && title.toLowerCase().indexOf(that.currentFilter)                        !== -1) ||
                (that.data[node.key].keywords && that.data[node.key].keywords.toLowerCase().indexOf(that.currentFilter) !== -1) ||
                (desc                         && desc.toLowerCase().indexOf(that.currentFilter)                         !== -1)){
            } else {
                return false;
            }
            this.main.socket.emit('sendToHost', host, 'getRepository', {repo: this.main.systemConfig.common.activeRepo, update: updateRepo}, _repository => {
                this.curRepository = _repository || {};

                if (this.curRepository && that.curInstalled && that.curRunning) {
                    this.curRepoLastUpdate = Date.now();
                    setTimeout(() => {
                        this.curRunning.forEach(cb =>
                            cb(this.curRepository, this.curInstalled));

                        this.curRunning = null;
            this.main.socket.emit('sendToHost', host, 'getInstalled', null, _installed => {
                this.curInstalled = _installed || {};

                if (this.curRepository && that.curInstalled) {
                    this.curRepoLastUpdate = Date.now();
                    setTimeout(() => {
                        this.curRunning.forEach(cb =>
                            cb(this.curRepository, this.curInstalled));

                        this.curRunning = null;
            setTimeout(() => {
                if (this.curRunning) {
                    this.curRunning.forEach(cb =>
                        cb(this.curRepository, this.curInstalled));

                    this.curRunning = null;
                callback && callback(this.curRepository, this.curInstalled);
        let text = '';
            for (const v in adapter.news) {
        let adapters;
            for (let a = 0; a < dependencies.length; a++) {
                for (const b in dependencies[a]) {
        for (const adapter in adapters) {
            //const d1 = that.data[c1.key], d2 = that.data[c1.key];
            const inst1 = c1.data.installed || 0, inst2 = c2.data.installed || 0;
            const ret = inst2 - inst1;
            let t1 = c1.titleLang || c1.title || '';
            let t2 = c2.titleLang || c2.title || '';
        const interval = now.getTime() - time.getTime();
        const days = Math.floor(interval / (24 * 3600000));
            const t  = days % 10;
            const tt = days % 100;
                let obj;
                let version;
                let rawVersion;
                let adapter;
                let adaptersToUpdate = 0;

                const listInstalled = [];
                const listNonInstalled = [];
                const nowObj = new Date();
                const localTexts = {
                    //const span = getVersionSpan(version);
                    const color = getVersionClass(version);
                    const title = color + '\n\r' + (news || '');
                for (const i = 0; i < listInstalled.length; i++) {
                    let installed = '';
                    let rawInstalled = '';
                    let icon = obj.icon;
                    let _instances = 0;
                    let _enabled   = 0;
                        let news = '';
                        let updatable = false;
                        let updatableError = '';

                        for (const z = 0; z < that.main.instances.length; z++) {
                    const group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
                    let desc  = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                    let title = obj.titleLang || obj.title;
                        let iGroup = -1;
                        for (const jj = 0; jj < that.tree.length; jj++) {
                    for (let i = 0; i < listNonInstalled.length; i++) {
                        const group = (obj.type || that.types[adapter] || 'common adapters') + '_group';
                        let desc = (typeof obj.desc === 'object') ? (obj.desc[systemLang] || obj.desc.en) : obj.desc;
                        let title = obj.titleLang || obj.title;
                            let igroup = -1;
                            for (const j = 0; j < that.tree.length; j++){
                    const akeys = Object.keys(that.data);
                    const newData = {};
                    for (let u = 0; u < akeys.length; u++) {
                    let text = '';
                    const types = [];
                    for (const a in that.data) {
                        const ad = that.data[a];
                    for (const b in that.data) {
                    let tTypes = '<li class="main-toolbar-table-types-item" data-type=""><a>' + localTexts['all'] + '</a></li>\n';
                    for (let g = 0; g < types.length; g++) {
                    const $types = that.$tab.find('#main-toolbar-table-types');
                            const text = '<div class="icon-large" style="' +
                            const $big = $(text);
                            const h = parseFloat($big.height());
                            let top = Math.round($(this).position().top - ((h - parseFloat($(this).height())) / 2));
                            const big = $(this).data('big');
                    const classes = [
                        const i = 0;
                let adaptersToUpdate = 0;
                for (const adapter in installedList) {
                    const obj = installedList ? installedList[adapter] : null;
                    let version = '';
                    if (repository[adapter] && repository[adapter].version) {
                        version = repository[adapter].version;
                    }
            const $updates = $('#updates-for-adapters');
        const $dialogAddInstance = $('#dialog-add-instance');
        let text = '';
        for (const h = 0; h < that.main.tabs.hosts.list.length; h++) {
            const host = that.main.tabs.hosts.list[h];
        let min = -1;
        const used = [];
        for (const i = 0; i < that.main.tabs.instances.list.length; i++) {
            const parts = that.main.tabs.instances.list[i].split('.');
                const index = parseInt(parts[parts.length - 1], 10);
        for (const m = 0; m < min; m++) {
        const $dialogLicense = $('#dialog-license');
        let timeout = setTimeout(function () {
            const adapter = $(this).attr('data-adapter-name');
            const desc = $(this).attr('data-adapter-desc');
                    let obj = repo[adapter];
                    obj = obj || installed[adapter];
                    if (!obj) {
                        return;
                    }
                        showLicenseDialog(adapter, isAgree =>
                            isAgree && that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, exitCode =>
                                !exitCode && that._postInit(true)));
                        that.main.cmdExec(null, 'add ' + adapter + ' ' + index + ' --host ' + host, exitCode =>
                            !exitCode && that._postInit(true));
            const name = $(this).attr('data-adapter-name');
            const aName = $(this).attr('data-adapter-name');
            const aName = $(this).attr('data-adapter-name');
        const $button = this.$tab.find('.adapter-update-custom-submit[data-adapter-name="' + adapter + '"]');
            const versions = [];
                const news = that.main.objects['system.adapter.' + adapter].common.news;
                for (const id in news) {
            const menu = '<div class="collection">';
            for (const v = 0; v < versions.length; v++) {
                const nnews = (news[versions[v]] ? news[versions[v]][systemLang] || news[versions[v]].en : '');
            const $adaptersMenu = $('#adapters-menu');
                const adapter = $(this).data('adapter-name');
                const version = $(this).data('version');
                    const $adaptersMenu = $('#adapters-menu');
                    const trigger = $adaptersMenu.data('trigger');
                const j = this.list.indexOf(id);
        let text = '';
        let opened;
            const adapter = id.match(/^system\.adapter\.([\w\d-]+)\.upload$/);
                const $adapter = this.$tab.find('.adapter-upload-progress[data-adapter-name="' + adapter[1] + '"]');
                const text = showUploadProgress(state.val);
            box-sizing: border-box;
        input:focus {
            outline: none;
        }
        input:focus ~ label,
        input.used ~ label {
        input:focus ~ .bar:before,
        input:focus ~ .bar:after {
            width: 50%;
        }
        .button:focus {
            outline: 0;
        }
        .buttonBlue:hover {
            background: #39a1f4;
        }
        <input type="text" name="username" id="username" class="used"><span class="highlight"></span><span class="bar"></span>
        <input type="password" name="password" id="password" class="used"><span class="highlight"></span><span class="bar"></span>
        /*for (var i = 0; i < inputs.length; i++) {
        }*/