CVE-2019-10765 (published 2019-11-20T16:15Z)

iobroker.admin before 3.6.12 allows attacker to include file contents from outside the `/log/file1/` directory.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
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.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/ioBroker/ioBroker.admin
commit_details: repo_url: https://github.com/ioBroker/ioBroker.admin
author: GermanBluefox
author_date: 2019-10-09 18:29:20+02:00
msg: ### 3.6.8 (2019-10-09)
* (bluefox) Log paths were sanitized
* (bluefox) NPM packages were updated
repository: repo_url: https://github.com/ioBroker/ioBroker.admin
repo_name: ioBroker/ioBroker.admin
description: user interface for configuration and administration
repo_language: TypeScript
owner: ioBroker
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,5 @@
 ![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)
@@ -9,11 +8,9 @@
 
 [![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"
@@ -82,6 +79,10 @@ This project uses some icons 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
 
@@ -276,7 +277,7 @@ This project uses some icons from [Flaticon](https://www.flaticon.com/):
 
 ### 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
 
@@ -287,7 +288,7 @@ This project uses some icons from [Flaticon](https://www.flaticon.com/):
 
 ### 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

programming_language: Markdown
filename: gulpfile.js
change_type: ModificationType.MODIFY
diff: @@ -493,13 +493,13 @@ gulp.task('materializeJS', () => {
     .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'));
 });

programming_language: JavaScript
filename: io-package.json
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@
   "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",
@@ -16,7 +16,18 @@
         "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": "обновить перевод некоторых слов",
@@ -87,7 +98,7 @@
         "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",
@@ -162,7 +173,7 @@
       "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",

programming_language: JavaScript
filename: socket.js
change_type: ModificationType.MODIFY
diff: @@ -9,8 +9,10 @@ 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 = {};
@@ -19,6 +21,7 @@ function IOSocket(server, settings, adapter, objects, states, store) {
     this.subscribes = {};
     let cookieParser;
     let passport;
+    let states = {};
 
     if (settings.auth) {
         cookieParser = require('cookie-parser');
@@ -433,10 +436,13 @@ 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) {
 
@@ -539,7 +545,7 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                     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]++;
@@ -692,7 +698,7 @@ function IOSocket(server, settings, adapter, objects, states, store) {
         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')
                 }
@@ -702,7 +708,11 @@ function IOSocket(server, settings, adapter, objects, states, store) {
         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')
                 }
@@ -711,13 +721,11 @@ function IOSocket(server, settings, adapter, objects, states, store) {
 
         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')
+                }
             }
         });
 
@@ -727,16 +735,22 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                     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);
             }
         });
@@ -965,7 +979,6 @@ function IOSocket(server, settings, adapter, objects, states, store) {
             }
         });
 
-
         socket.on('unsubscribe', function (pattern, callback) {
             if (updateSession(socket) && checkPermissions(socket, 'unsubscribe', callback, pattern)) {
                 if (pattern && typeof pattern === 'object' && pattern instanceof Array) {
@@ -975,10 +988,13 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                 } 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);
             }
         });
 
@@ -1168,7 +1184,7 @@ function IOSocket(server, settings, adapter, objects, states, store) {
             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) {
@@ -1178,11 +1194,14 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                             }
                         }
                     });
-                }
+                }*/
 
                 that.server.sockets.emit('eventsThreshold', false);
                 adapter.unsubscribeForeignStates('system.adapter.*');
-                adapter.subscribeForeignStates('*');
+
+                Object.keys(that.subscribes.stateChange).forEach(pattern =>
+                    adapter.subscribeForeignStates(pattern));
+
             }, 50);
         }
     }
@@ -1196,7 +1215,10 @@ function IOSocket(server, settings, adapter, objects, states, store) {
                 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);
         }
@@ -1240,6 +1262,13 @@ function IOSocket(server, settings, adapter, objects, states, store) {
     };
 
     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) {
@@ -1309,4 +1338,5 @@ function IOSocket(server, settings, adapter, objects, states, store) {
 
     return this;
 }
+
 module.exports = IOSocket;
\ No newline at end of file

programming_language: JavaScript
filename: web.js
change_type: ModificationType.MODIFY
diff: @@ -21,6 +21,55 @@ 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 = {
@@ -231,15 +280,23 @@ function Web(settings, adapter, onReady) {
 
                 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);
                 });
@@ -324,7 +381,7 @@ function Web(settings, adapter, onReady) {
 
                 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');
@@ -340,7 +397,7 @@ 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('/');
@@ -348,19 +405,22 @@ function Web(settings, adapter, onReady) {
                 // 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);
@@ -372,7 +432,7 @@ function Web(settings, adapter, onReady) {
                         }
                     }
                 }
-                res.status(404).send('File ' + filename + ' not found');
+                res.status(404).send('File ' + escapeHtml(filename) + ' not found');
             });
             const appOptions = {};
             if (settings.cache) {
@@ -384,7 +444,7 @@ function Web(settings, adapter, onReady) {
                 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) {
@@ -393,6 +453,7 @@ function Web(settings, adapter, onReady) {
 
                     // 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];
@@ -407,7 +468,7 @@ function Web(settings, adapter, onReady) {
                         // 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!');
                             }
@@ -428,7 +489,6 @@ function Web(settings, adapter, onReady) {
 
             // 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;
 
@@ -470,7 +530,7 @@ function Web(settings, adapter, onReady) {
                 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);

programming_language: JavaScript
filename: main.js
change_type: ModificationType.MODIFY
diff: @@ -14,17 +14,19 @@
 '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) {
@@ -55,23 +57,11 @@ function startAdapter(options) {
         }
 
         // 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) => {
@@ -99,18 +89,14 @@ function startAdapter(options) {
             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);
@@ -320,53 +306,10 @@ function writeUpdateInfo(sources) {
         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', '*');
 }
 
@@ -391,30 +334,30 @@ function main() {
         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) {
@@ -439,25 +382,59 @@ function getData(callback) {
             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);
                 }
             });
         }

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 {
   "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",
@@ -19,41 +19,41 @@
     "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"

programming_language: CoffeeScript
filename: admin.js
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 /* jshint -W097 */
 /* jshint strict:true */
-/* jslint vars: true */
+/* jslint consts: true */
 /* global io:false */
 /* global jQuery:false */
 /* jslint browser:true */
@@ -19,10 +19,10 @@
 //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) {
@@ -51,16 +51,17 @@ async function asyncForEach(array, callback) {
     }
 }
 
-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;
@@ -69,25 +70,25 @@ var adapterRedirect = function (redirect, timeout) {
         }, 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);
@@ -99,14 +100,14 @@ function detectIE() {
 
 (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:    '',
@@ -221,17 +222,17 @@ $(document).ready(function () {
                     '<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'));
                 });
             }
@@ -290,7 +291,7 @@ $(document).ready(function () {
             //    ("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);
@@ -301,7 +302,7 @@ $(document).ready(function () {
             // 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;
@@ -385,7 +386,7 @@ $(document).ready(function () {
             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);
@@ -418,13 +419,13 @@ $(document).ready(function () {
                     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();
@@ -432,16 +433,16 @@ $(document).ready(function () {
                     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)));
                     }
@@ -452,7 +453,7 @@ $(document).ready(function () {
 
         // 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];
@@ -467,7 +468,7 @@ $(document).ready(function () {
                     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);
@@ -488,13 +489,13 @@ $(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);
                     }
@@ -508,7 +509,7 @@ $(document).ready(function () {
             }
         },
         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
@@ -544,7 +545,7 @@ $(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),
@@ -573,21 +574,21 @@ $(document).ready(function () {
         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)) {
@@ -633,15 +634,15 @@ $(document).ready(function () {
 
     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
@@ -652,19 +653,19 @@ $(document).ready(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')) {
@@ -672,7 +673,7 @@ $(document).ready(function () {
                         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>';
@@ -684,11 +685,11 @@ $(document).ready(function () {
             $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);
                     }
@@ -698,7 +699,7 @@ $(document).ready(function () {
             });
             // 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');
                 }
@@ -706,11 +707,11 @@ $(document).ready(function () {
         });
 
         main.updateWizard();
-        
+
         $('#button-logout').on('click', function () {
             window.location.href = '/logout/';
         });
-        
+
         setTimeout(function () {
             main.tabs.info.init();
         }, 5000);
@@ -734,15 +735,15 @@ $(document).ready(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);
@@ -758,20 +759,20 @@ $(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;
@@ -785,24 +786,24 @@ $(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') {
@@ -836,7 +837,7 @@ $(document).ready(function () {
             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) {
@@ -850,16 +851,20 @@ $(document).ready(function () {
 
                 // 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'));
@@ -877,10 +882,10 @@ $(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);
@@ -906,7 +911,7 @@ $(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;
         }
@@ -915,10 +920,10 @@ $(document).ready(function () {
 
         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;
@@ -929,7 +934,7 @@ $(document).ready(function () {
         }
         $selHosts.html(lines);
 
-        var $selBtn = $('#host-adapters-btn').show();
+        const $selBtn = $('#host-adapters-btn').show();
         $selBtn
             .text(_('Host:') + ' ' + main.currentHost)
             .dropdown();
@@ -951,8 +956,8 @@ $(document).ready(function () {
 
         // 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;
@@ -997,7 +1002,7 @@ $(document).ready(function () {
         });
 
         $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();
@@ -1013,7 +1018,7 @@ $(document).ready(function () {
         });
         // 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;
 
@@ -1041,7 +1046,7 @@ $(document).ready(function () {
         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');
                     }
@@ -1065,9 +1070,9 @@ $(document).ready(function () {
             }
 
             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];
@@ -1090,7 +1095,7 @@ $(document).ready(function () {
 
                 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();
                     }
@@ -1144,11 +1149,9 @@ $(document).ready(function () {
     }
 
     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) {
@@ -1205,11 +1208,11 @@ $(document).ready(function () {
                 !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;
@@ -1245,14 +1248,15 @@ $(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);
@@ -1260,7 +1264,7 @@ $(document).ready(function () {
 
     // ---------------------------- 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);
@@ -1269,7 +1273,7 @@ $(document).ready(function () {
     };
 
     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);
             }
@@ -1286,7 +1290,7 @@ $(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) {
@@ -1307,12 +1311,12 @@ $(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]];
                 }
@@ -1332,7 +1336,7 @@ $(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) {
@@ -1351,7 +1355,7 @@ $(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]]--;
                 }
@@ -1407,7 +1411,7 @@ $(document).ready(function () {
     };
 
     main.navigateGetParams = function () {
-        var parts = decodeURI(window.location.hash).split('/');
+        const parts = decodeURI(window.location.hash).split('/');
         return parts[2] ? decodeURIComponent(parts[2]) : null;
     };
 
@@ -1425,7 +1429,7 @@ $(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-/, '');
         }
 
@@ -1444,10 +1448,10 @@ $(document).ready(function () {
                 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 === '!') {
@@ -1461,9 +1465,9 @@ $(document).ready(function () {
                 }
                 // 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();
 
@@ -1473,15 +1477,15 @@ $(document).ready(function () {
 
                 // 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', '');
@@ -1504,7 +1508,7 @@ $(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);
                             }
@@ -1515,7 +1519,7 @@ $(document).ready(function () {
                 }
 
                 // 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');
 
@@ -1553,14 +1557,14 @@ $(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\./)) {
@@ -1593,11 +1597,11 @@ $(document).ready(function () {
     }
 
     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 {
@@ -1625,11 +1629,11 @@ $(document).ready(function () {
     };
 
     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;
         }
@@ -1643,8 +1647,8 @@ $(document).ready(function () {
         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;
@@ -1664,14 +1668,14 @@ $(document).ready(function () {
         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},
@@ -1689,7 +1693,7 @@ $(document).ready(function () {
         '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'},
@@ -1697,11 +1701,11 @@ $(document).ready(function () {
     };
 
     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 + '">' +
@@ -1712,14 +1716,14 @@ $(document).ready(function () {
             }
         });
         $('.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'];
                     }
                 }
@@ -1742,7 +1746,7 @@ $(document).ready(function () {
             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);
@@ -1792,14 +1796,14 @@ $(document).ready(function () {
     });
     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$/);
@@ -1848,7 +1852,7 @@ $(document).ready(function () {
             $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) {
@@ -1860,9 +1864,9 @@ $(document).ready(function () {
                     }, 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];
                     }
@@ -1902,7 +1906,7 @@ $(document).ready(function () {
 
                     // 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);
                         }
@@ -1924,7 +1928,7 @@ $(document).ready(function () {
                             }
                             // 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);
                                 }
                             }
@@ -1936,7 +1940,7 @@ $(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';
                         }
@@ -1959,8 +1963,10 @@ $(document).ready(function () {
 
                                         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;
 
@@ -1993,7 +1999,7 @@ $(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');
                                                 }
@@ -2077,7 +2083,7 @@ $(document).ready(function () {
                                 // 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();
                                     }
@@ -2096,18 +2102,15 @@ $(document).ready(function () {
             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 () {

programming_language: JavaScript
filename: adminAdapters.js
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,7 @@
 function Adapters(main) {
     'use strict';
 
-    var that = this;
+    const that = this;
 
     this.curRepository     = null;
     this.curRepoLastUpdate = null;
@@ -69,7 +69,7 @@ function Adapters(main) {
 
     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') {
@@ -106,9 +106,9 @@ function Adapters(main) {
                 },
                 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>';
@@ -120,18 +120,18 @@ function Adapters(main) {
                         $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;
                             }
@@ -145,8 +145,8 @@ function Adapters(main) {
                         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')
@@ -206,7 +206,7 @@ function Adapters(main) {
             });
 
             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) {
@@ -278,11 +278,11 @@ function Adapters(main) {
     }
 
     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;
@@ -383,9 +383,9 @@ function Adapters(main) {
             .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);
@@ -393,8 +393,8 @@ function Adapters(main) {
                 }
                 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;
@@ -412,16 +412,16 @@ 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];
@@ -440,7 +440,7 @@ 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');
                     }
@@ -578,25 +578,25 @@ function Adapters(main) {
         //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;
         }
@@ -621,40 +621,42 @@ function Adapters(main) {
 
         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);
                 }
             });
@@ -663,14 +665,14 @@ function Adapters(main) {
         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];
@@ -685,9 +687,9 @@ function Adapters(main) {
     };
 
     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;
@@ -702,12 +704,12 @@ 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];
                     }
@@ -717,7 +719,7 @@ function Adapters(main) {
             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]);
@@ -732,15 +734,15 @@ function Adapters(main) {
 
     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;
             }
@@ -760,8 +762,8 @@ function Adapters(main) {
         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;
@@ -775,8 +777,8 @@ function Adapters(main) {
                 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);
@@ -798,16 +800,16 @@ function Adapters(main) {
             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'),
@@ -858,9 +860,9 @@ function Adapters(main) {
                 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">' +
@@ -878,27 +880,28 @@ function Adapters(main) {
                 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
@@ -911,7 +914,7 @@ function Adapters(main) {
                             '<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++;
@@ -956,11 +959,11 @@ function Adapters(main) {
                     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] = {
@@ -996,8 +999,8 @@ function Adapters(main) {
                     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;
@@ -1032,7 +1035,7 @@ 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];
@@ -1046,12 +1049,12 @@ function Adapters(main) {
                             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] = {
@@ -1082,8 +1085,8 @@ function Adapters(main) {
                         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;
@@ -1118,7 +1121,7 @@ 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) {
@@ -1137,8 +1140,8 @@ function Adapters(main) {
                             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;
@@ -1146,11 +1149,11 @@ function Adapters(main) {
 
                 // 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);
                         }
@@ -1227,17 +1230,17 @@ function Adapters(main) {
 
                     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') || '';
@@ -1279,20 +1282,20 @@ function Adapters(main) {
                         }
 
                         $(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);
                         });
@@ -1304,7 +1307,7 @@ function Adapters(main) {
 
                     that.sortTree();
                     that.enableColResize();
-                    var classes = [
+                    const classes = [
                         'tab-adapters-table-name',
                         'tab-adapters-table-description',
                         'tab-adapters-table-keywords',
@@ -1314,7 +1317,7 @@ function Adapters(main) {
                         '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++;
@@ -1342,15 +1345,17 @@ 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++;
@@ -1359,7 +1364,7 @@ function Adapters(main) {
                 that.updateCounter(adaptersToUpdate);
             });
         } else if (counter) {
-            var $updates = $('#updates-for-adapters');
+            const $updates = $('#updates-for-adapters');
             if ($updates.length) {
                 $updates.text(counter);
             } else {
@@ -1409,14 +1414,14 @@ function Adapters(main) {
             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>';
         }
 
@@ -1428,12 +1433,12 @@ function Adapters(main) {
         $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;
@@ -1442,7 +1447,7 @@ function Adapters(main) {
         }
         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>';
         }
@@ -1473,14 +1478,14 @@ function Adapters(main) {
     }
 
     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);
@@ -1544,40 +1549,37 @@ 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) {
@@ -1596,7 +1598,7 @@ function Adapters(main) {
         });
 
         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) {
@@ -1605,19 +1607,19 @@ function Adapters(main) {
         });
 
         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);
                     }
@@ -1625,14 +1627,14 @@ function Adapters(main) {
             } 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">' +
@@ -1649,8 +1651,8 @@ function Adapters(main) {
             $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);
@@ -1660,8 +1662,8 @@ function Adapters(main) {
 
             /*$(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();
@@ -1685,7 +1687,7 @@ function Adapters(main) {
             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);
                 }
@@ -1698,8 +1700,8 @@ 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 + '"';
@@ -1733,10 +1735,10 @@ function Adapters(main) {
 
     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});
             }

programming_language: JavaScript
filename: index.html
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@
 <head>
     <style>
         * {
-            box-sizing:border-box;
+            box-sizing: border-box;
         }
 
         body {
@@ -63,7 +63,9 @@
             border-bottom: 1px solid #757575;
         }
 
-        input:focus { outline: none; }
+        input:focus {
+            outline: none;
+        }
 
         /* Label */
 
@@ -79,8 +81,8 @@
         }
 
         /* active */
-
-        input:focus ~ label, input.used ~ label {
+        input:focus ~ label,
+        input.used ~ label {
             top: -20px;
             transform: scale(.75); left: -2px;
             /* font-size: 14px; */
@@ -111,7 +113,10 @@
 
         /* active */
 
-        input:focus ~ .bar:before, input:focus ~ .bar:after { width: 50%; }
+        input:focus ~ .bar:before,
+        input:focus ~ .bar:after {
+            width: 50%;
+        }
 
 
         /* Highlight */
@@ -163,18 +168,21 @@
             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 */
@@ -268,11 +276,11 @@
 </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=""/>
@@ -355,7 +363,7 @@
 
     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) {
@@ -365,7 +373,7 @@
                     this.className = this.className.replace(/\s?used/, '');
                 }
             });
-        }
+        }*/
         setTimeout(function () {
             if (document.getElementById('username').value) {
                 document.getElementById('password').focus();
@@ -379,6 +387,7 @@
             }
         }, 300);
     }
+
     document.getElementById('username').addEventListener('keydown', function (e) {
         if (e.keyCode === 13) {
             e.preventDefault();

programming_language: CSS