From 5b5a71467bdc0e399d697bc16f2a56c390e3af53 Mon Sep 17 00:00:00 2001
From: Christian <git-developer@users.noreply.github.com>
Date: Sun, 26 Jan 2025 12:21:22 +0100
Subject: [PATCH 1/4] refactor(controllers): drop lodash dependency

---
 res/controllers/Behringer BCR2000.midi.xml    |  3 +-
 res/controllers/Behringer DDM4000.midi.xml    |  3 +-
 .../Behringer-Extension-scripts.js            | 84 ++++++++++++++-----
 3 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/res/controllers/Behringer BCR2000.midi.xml b/res/controllers/Behringer BCR2000.midi.xml
index 9f557abd74e..a03ad4935c3 100644
--- a/res/controllers/Behringer BCR2000.midi.xml	
+++ b/res/controllers/Behringer BCR2000.midi.xml	
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<MixxxControllerPreset schemaVersion="1" mixxxVersion="2.2.3+">
+<MixxxControllerPreset schemaVersion="1" mixxxVersion="2.5.0+">
   <info>
     <name>Behringer BCR2000</name>
     <author>Christian</author>
@@ -7,7 +7,6 @@
   </info>
   <controller id="BCR2000">
     <scriptfiles>
-      <file filename="lodash.mixxx.js"/>
       <file filename="midi-components-0.0.js"/>
       <file filename="Behringer-Extension-scripts.js" />
       <file filename="Behringer-BCR2000-preset-scripts.js" />
diff --git a/res/controllers/Behringer DDM4000.midi.xml b/res/controllers/Behringer DDM4000.midi.xml
index 64b92d076cd..873d83e6f25 100644
--- a/res/controllers/Behringer DDM4000.midi.xml	
+++ b/res/controllers/Behringer DDM4000.midi.xml	
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<MixxxControllerPreset schemaVersion="1" mixxxVersion="2.2.3+">
+<MixxxControllerPreset schemaVersion="1" mixxxVersion="2.5.0+">
   <info>
     <name>Behringer DDM4000</name>
     <author>Christian</author>
@@ -7,7 +7,6 @@
   </info>
   <controller id="DDM4000">
     <scriptfiles>
-      <file filename="lodash.mixxx.js" />
       <file filename="midi-components-0.0.js" />
       <file filename="Behringer-Extension-scripts.js" />
       <file filename="Behringer-DDM4000-scripts.js" functionprefix="DDM4000" />
diff --git a/res/controllers/Behringer-Extension-scripts.js b/res/controllers/Behringer-Extension-scripts.js
index cc912e64d3a..17095652404 100644
--- a/res/controllers/Behringer-Extension-scripts.js
+++ b/res/controllers/Behringer-Extension-scripts.js
@@ -9,6 +9,49 @@
     /** @private */
     var engine = global.engine;
 
+    /**
+     * Determines the merge strategy for a value when merging a component container definition.
+     *
+     * @param value anything
+     * @returns {boolean} merge strategy for the value: true for 'assign', false for 'deep merge'
+     * @private
+     */
+    const doAssign = value => {
+        return typeof value !== "object"
+            || value === null
+            || value instanceof components.ComponentContainer
+            || value instanceof components.Component;
+    };
+
+    /**
+     * Merges a list of component container definitions into a target object.
+     *
+     * This is not a generic deep merge algorithm for JS objects. It does not handle circular references.
+     * If a definition contains a reference to an object instance of a component or component container
+     * (e.g. `ShiftButton.target`), the instance is taken over by reference, no deep copy is made.
+     *
+     * @param {object} targetObject Target object for the merged definitions
+     * @param {Array} sources Source definitions
+     * @returns {object} The target object
+     * @see `GenericMidiController` on component container definition
+     * @private
+     */
+    const mergeDefinitions = (targetObject, ...sources) => sources.reduce((target, source) => {
+        for (const [key, value] of Object.entries(source || {})) {
+            if (doAssign(value)) {
+                target[key] = value;
+            } else if (value !== undefined) {
+                if (Array.isArray(value) && !Array.isArray(target[key])) {
+                    target[key] = [];
+                } else if (typeof target[key] !== "object" || target[key] === null) {
+                    target[key] = {};
+                }
+                mergeDefinitions(target[key], value);
+            }
+        };
+        return target;
+    }, targetObject);
+
     /**
      * Contains functions to print a message to the log.
      * `debug` output is suppressed unless the caller owns a truthy property `debug`.
@@ -34,7 +77,7 @@
      * Determine an ID from a component's MIDI address
      *
      * @param {Array<number>} midiAddress MIDI address consisting of two integers
-     * @return {string} ID for the MIDI address; `undefined` on error
+     * @returns {string} ID for the MIDI address; `undefined` on error
      * @private
      */
     var findComponentId = function(midiAddress) {
@@ -51,7 +94,7 @@
      * Create a human-readable string to identify a component.
      *
      * @param {components.Component} component A component
-     * @return {string} A short string that describes the component; `undefined` on error
+     * @returns {string} A short string that describes the component; `undefined` on error
      * @private
      */
     var stringifyComponent = function(component) {
@@ -92,11 +135,11 @@
      *
      * @param {object} parent Constructor of parent whose prototype is used as base
      * @param {object} members Own members that are not inherited
-     * @return {object} A new prototype based on parent with the given members
+     * @returns {object} A new prototype based on parent with the given members
      * @private
      */
     var deriveFrom = function(parent, members) {
-        return _.merge(Object.create(parent.prototype), members || {});
+        return Object.assign(Object.create(parent.prototype), members);
     };
 
     /**
@@ -231,7 +274,7 @@
      * @see https://github.com/mixxxdj/mixxx/wiki/Script-Timers
      */
     var Timer = function(options) {
-        _.assign(this, options);
+        Object.assign(this, options);
         this.disable();
     };
     Timer.prototype = {
@@ -274,7 +317,7 @@
     var Throttler = function(options) {
         options = options || {};
         options.delay = options.delay || 0;
-        _.assign(this, options);
+        Object.assign(this, options);
         this.locked = false;
         this.jobs = [];
         this.unlockTimer = new Timer(
@@ -810,7 +853,7 @@
          * Create a new ComponentContainer within this registry.
          *
          * @param {string} name Name of the ComponentContainer
-         * @return {components.ComponentContainer} The ComponentContainer; `undefined` on error
+         * @returns {components.ComponentContainer} The ComponentContainer; `undefined` on error
          * @public
          */
         createContainer: function(name) {
@@ -827,7 +870,7 @@
          * Retrieve an existing ComponentContainer.
          *
          * @param {string} name Name of an existing ComponentContainer
-         * @return {components.ComponentContainer} The ComponentContainer; `undefined` on error
+         * @returns {components.ComponentContainer} The ComponentContainer; `undefined` on error
          * @public
          */
         getContainer: function(name) {
@@ -884,7 +927,7 @@
          *
          * @param {components.Component} component A component
          * @param {string} containerName Name of a ComponentContainer
-         * @return {string} ID of the stored component; `undefined` on error
+         * @returns {string} ID of the stored component; `undefined` on error
          * @public
          */
         register: function(component, containerName) {
@@ -926,7 +969,7 @@
          *
          * @param {components.Component} component A component
          * @param {string} containerName Name of an existing ComponentContainer
-         * @return {string} ID of the removed component; `undefined` on error
+         * @returns {string} ID of the removed component; `undefined` on error
          * @public
          */
         unregister: function(component, containerName) {
@@ -977,7 +1020,7 @@
         /**
          * Retrieve the Default layer.
          *
-         * @return {object} The Default layer
+         * @returns {object} The Default layer
          * @private
          */
         defaultLayer: function() {
@@ -992,7 +1035,7 @@
         /**
          * Retrieve the Shift layer.
          *
-         * @return {object} The Shift layer
+         * @returns {object} The Shift layer
          * @private
          */
         shiftLayer: function() {
@@ -1004,7 +1047,7 @@
          *
          * @param {number} status First byte of the component's MIDI address
          * @param {number} control Second byte of the component's MIDI address
-         * @return {components.Component} Component on the active layer matching the MIDI address;
+         * @returns {components.Component} Component on the active layer matching the MIDI address;
          *                                undefined on error. When the active layer does not contain
          *                                a matching component, the Default layer is used as
          *                                fallback.
@@ -1035,7 +1078,7 @@
          * @param {LayerManager~registryOperation} The operation to run
          * @param {components.Component} component A component
          * @param {boolean} shift Iff true, use Shift Layer, otherwise use Default Layer
-         * @return Result of the operation
+         * @returns Result of the operation
          * @private
          */
         onRegistry: function(operation, component, shift) {
@@ -1317,7 +1360,7 @@
          * @param {object} deckDefinitions Definition of decks
          * @param {object} effectUnitDefinitions Definition of effect units
          * @param {object} containerDefinitions Definition of additional component containers
-         * @return {object} Layer manager
+         * @returns {object} Layer manager
          * @see `LayerManager`
          * @private
          */
@@ -1388,8 +1431,8 @@
         createDeck: function(deckDefinition, componentStorage) {
             var deck = new components.Deck(deckDefinition.deckNumbers);
             deckDefinition.components.forEach(function(componentDefinition, index) {
-                var options = _.merge({group: deck.currentDeck}, componentDefinition.options);
-                var definition = _.merge(componentDefinition, {options: options});
+                const options = Object.assign({group: deck.currentDeck}, componentDefinition.options);
+                const definition = Object.assign(componentDefinition, {options: options});
                 deck[index] = this.createComponent(definition);
             }, this);
             if (deckDefinition.equalizerUnit) {
@@ -1450,7 +1493,7 @@
          * @param {Array} publisherStorage Storage for publisher components
          * @param {Array<string>} rebindTriggers Names of functions that trigger rebinding a
          *                                       publisher to its source component
-         * @return {components.ComponentContainer} The given component container argument
+         * @returns {components.ComponentContainer} The given component container argument
          * @private
          */
         setupMidi: function(definition, implementation, publisherStorage, rebindTriggers) {
@@ -1540,8 +1583,7 @@
             var container = new containerType(containerDefinition.options);
             if (containerDefinition.components) {
                 containerDefinition.components.forEach(function(componentDefinition, index) {
-                    var definition = _.merge(
-                        {}, containerDefinition.defaultDefinition || {}, componentDefinition);
+                    const definition = mergeDefinitions({}, containerDefinition.defaultDefinition, componentDefinition);
                     container[index] = this.createComponent(definition);
                 }, this);
             }
@@ -1614,5 +1656,5 @@
     exports.Publisher = Publisher;
     exports.LayerManager = LayerManager;
     exports.GenericMidiController = GenericMidiController;
-    global.behringer = _.assign(global.behringer, {extension: exports});
+    global.behringer = Object.assign(global.behringer || {}, {extension: exports});
 })(this);

From 705be3c4bf1eabf1fe82d0ccbbcd6803a7172d3d Mon Sep 17 00:00:00 2001
From: Christian <git-developer@users.noreply.github.com>
Date: Sun, 26 Jan 2025 12:29:12 +0100
Subject: [PATCH 2/4] refactor(controllers): remove deepMerge

---
 res/controllers/common-controller-scripts.js | 52 --------------------
 res/controllers/midi-components-0.0.js       |  6 ++-
 2 files changed, 5 insertions(+), 53 deletions(-)

diff --git a/res/controllers/common-controller-scripts.js b/res/controllers/common-controller-scripts.js
index 4199439026e..5045a28f461 100644
--- a/res/controllers/common-controller-scripts.js
+++ b/res/controllers/common-controller-scripts.js
@@ -140,58 +140,6 @@ var colorCodeToObject = function(colorCode) {
 var script = function() {
 };
 
-/**
- * Discriminates whether an object was created using the `{}` synthax.
- *
- * Returns true when was an object was created using the `{}` synthax.
- * False if the object is an instance of a class like Date or Proxy or an Array.
- *
- * isSimpleObject({}) // true
- * isSimpleObject(null) // false
- * isSimpleObject(undefined) // false
- * isSimpleObject(new Date) // false
- * isSimpleObject(new (class {})()) // false
- * @param {any} obj Object to test
- * @returns {boolean} true if obj was created using the `{}` or `new Object()` synthax, false otherwise
- */
-const isSimpleObject = function(obj) {
-    return obj !== null && typeof obj === "object" && obj.constructor.name === "Object";
-};
-
-script.isSimpleObject = isSimpleObject;
-
-/**
- * Deeply merges 2 objects (Arrays and Objects only, not Map for instance).
- * @param target {object | Array} Object to merge source into
- * @param source {object | Array} Object to merge into source
- */
-const deepMerge = function(target, source) {
-    if (target === source || target === undefined || target === null || source === undefined || source === null) {
-        return;
-    }
-
-    if (Array.isArray(target) && Array.isArray(source)) {
-        const objTarget = target.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {});
-        const objSource = source.reduce((acc, val, idx) => Object.assign(acc, {[idx]: val}), {});
-        deepMerge(objTarget, objSource);
-        target.length = 0;
-        target.push(...Object.values(objTarget));
-    } else if (isSimpleObject(target) && isSimpleObject(source)) {
-        Object.keys(source).forEach(key => {
-            if (
-                Array.isArray(target[key]) && Array.isArray(source[key]) ||
-              isSimpleObject(target[key]) && isSimpleObject(source[key])
-            ) {
-                deepMerge(target[key], source[key]);
-            } else if (source[key] !== undefined && source[key] !== null) {
-                Object.assign(target, {[key]: source[key]});
-            }
-        });
-    }
-};
-
-script.deepMerge = deepMerge;
-
 // ----------------- Mapping constants ---------------------
 
 // Library column value, which can be used to interact with the CO for "[Library] sort_column"
diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js
index 225e5c6e083..bf0990a445d 100644
--- a/res/controllers/midi-components-0.0.js
+++ b/res/controllers/midi-components-0.0.js
@@ -650,6 +650,10 @@
             // Unset isShifted for each ComponentContainer recursively
             this.isShifted = false;
         },
+        /**
+         * @param newLayer Layer to apply to this
+         * @param reconnectComponents Whether components should be reconnected or not
+         */
         applyLayer: function(newLayer, reconnectComponents) {
             if (reconnectComponents !== false) {
                 reconnectComponents = true;
@@ -660,7 +664,7 @@
                 });
             }
 
-            script.deepMerge(this, newLayer);
+            Object.assign(this, newLayer);
 
             if (reconnectComponents === true) {
                 this.forEachComponent(function(component) {

From bf811186b8197cf976e8725c4c45d0516bcb13cf Mon Sep 17 00:00:00 2001
From: Christian <git-developer@users.noreply.github.com>
Date: Sun, 26 Jan 2025 12:22:28 +0100
Subject: [PATCH 3/4] refactor(controllers): remove Qt5 quirk

---
 res/controllers/Behringer-Extension-scripts.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/res/controllers/Behringer-Extension-scripts.js b/res/controllers/Behringer-Extension-scripts.js
index 17095652404..4c3ce199dbd 100644
--- a/res/controllers/Behringer-Extension-scripts.js
+++ b/res/controllers/Behringer-Extension-scripts.js
@@ -282,12 +282,12 @@
         isEnabled: function() { return this.id !== 0; },
         start: function() {
             this.reset();
-            this.id = engine.beginTimer(this.timeout, function() {
+            this.id = engine.beginTimer(this.timeout, () => {
                 if (this.oneShot) {
                     this.disable();
                 }
                 this.action.call(this.owner);
-            }.bind(this), this.oneShot); // .bind(this) is required instead of arrow function for Qt < 6.2.4 due to QTBUG-95677
+            }, this.oneShot);
         },
         reset: function() {
             if (this.isEnabled()) {

From b42dccaefe2ec697932378920b037d75c1ea0a5f Mon Sep 17 00:00:00 2001
From: Christian <git-developer@users.noreply.github.com>
Date: Sun, 26 Jan 2025 13:30:23 +0100
Subject: [PATCH 4/4] style(controllers): prefer const & let over var

---
 .../Behringer-BCR2000-preset-scripts.js       |  56 ++---
 res/controllers/Behringer-BCR2000-scripts.js  |  11 +-
 res/controllers/Behringer-DDM4000-scripts.js  |  61 +++---
 .../Behringer-Extension-scripts.js            | 194 +++++++++---------
 4 files changed, 161 insertions(+), 161 deletions(-)

diff --git a/res/controllers/Behringer-BCR2000-preset-scripts.js b/res/controllers/Behringer-BCR2000-preset-scripts.js
index 14524c14111..d98e6c6cca3 100644
--- a/res/controllers/Behringer-BCR2000-preset-scripts.js
+++ b/res/controllers/Behringer-BCR2000-preset-scripts.js
@@ -4,22 +4,22 @@
 (function(global) {
 
     /* Controller-specific constants */
-    var ROW_SIZE                       = 8;
-    var PUSHENCODERGROUP_COUNT         = 4;
-    var BUTTONROW_COUNT                = 2;
-    var ENCODERROW_COUNT               = 3;
-    var BUTTONBOX_SIZE                 = 4;
-    var PRESET_MIN                     = 1;
-    var PRESET_MAX                     = 32;
-    var STATUS_CONTROL_CHANGE          = 0xB0;
-    var STATUS_PROGRAM_CHANGE          = 0xC0;
+    const ROW_SIZE                       = 8;
+    const PUSHENCODERGROUP_COUNT         = 4;
+    const BUTTONROW_COUNT                = 2;
+    const ENCODERROW_COUNT               = 3;
+    const BUTTONBOX_SIZE                 = 4;
+    const PRESET_MIN                     = 1;
+    const PRESET_MAX                     = 32;
+    const STATUS_CONTROL_CHANGE          = 0xB0;
+    const STATUS_PROGRAM_CHANGE          = 0xC0;
 
     /* Preset-specific constants */
-    var PUSHENCODERGROUP_START         = 0x01;
-    var PUSHENCODERGROUP_BUTTON_OFFSET = 0x20;
-    var BUTTONROW_START                = 0x41;
-    var ENCODERROW_START               = 0x51;
-    var BUTTONBOX_START                = 0x69;
+    const PUSHENCODERGROUP_START         = 0x01;
+    const PUSHENCODERGROUP_BUTTON_OFFSET = 0x20;
+    const BUTTONROW_START                = 0x41;
+    const ENCODERROW_START               = 0x51;
+    const BUTTONBOX_START                = 0x69;
 
     /**
      * Select a preset in the controller.
@@ -27,7 +27,7 @@
      * @param {number} A preset number (integer 1..32)
      * @public
      */
-    var setPreset = function(presetNumber) {
+    const setPreset = function(presetNumber) {
         if (presetNumber) {
             presetNumber = Math.max(PRESET_MIN, presetNumber);
             presetNumber = Math.min(PRESET_MAX, presetNumber);
@@ -43,7 +43,7 @@
      * @return {Array} Array containing values
      * @private
      */
-    var createElements = function(size, elementFactory) {
+    const createElements = function(size, elementFactory) {
         return Object.keys(Array.apply(0, Array(size))).map(
             function(_v, i) { return elementFactory.call(this, i); });
     };
@@ -64,10 +64,10 @@
      * @return {Array<number>} Array of MIDI addresses for the given range
      * @private
      */
-    var calculateRange = function(startAddress, rangeNumber, size) {
+    const calculateRange = function(startAddress, rangeNumber, size) {
         size = size || ROW_SIZE;
-        var rangeOffset = rangeNumber * ROW_SIZE;
-        var rangeStart = startAddress + rangeOffset;
+        const rangeOffset = rangeNumber * ROW_SIZE;
+        const rangeStart = startAddress + rangeOffset;
         return createElements(size, function(i) { return rangeStart + i; });
     };
 
@@ -81,7 +81,7 @@
      * @return {Array<number>} Address range for the encoders in the encoder group
      * @private
      */
-    var createPushEncoderGroup = function(groupNumber) {
+    const createPushEncoderGroup = function(groupNumber) {
         return calculateRange(PUSHENCODERGROUP_START, groupNumber).map(function(encoder) {
             return {"encoder": encoder, "button": encoder + PUSHENCODERGROUP_BUTTON_OFFSET};
         });
@@ -95,7 +95,7 @@
      * @return {Array<number>} Address range for the encoders in the encoder row
      * @private
      */
-    var createEncoderRow = function(rowNumber) {
+    const createEncoderRow = function(rowNumber) {
         return calculateRange(ENCODERROW_START, rowNumber);
     };
 
@@ -107,7 +107,7 @@
      * @return {Array<number>} Address range for the buttons in the button row
      * @private
      */
-    var createButtonRow = function(rowNumber) {
+    const createButtonRow = function(rowNumber) {
         return calculateRange(BUTTONROW_START,  rowNumber);
     };
 
@@ -118,17 +118,17 @@
      * @return {Array<number>} Address range for the buttons in the button box
      * @private
      */
-    var createButtonBox = function() {
+    const createButtonBox = function() {
         return calculateRange(BUTTONBOX_START, 0, BUTTONBOX_SIZE);
     };
 
     /* Definition of MIDI controls */
-    var pushEncoderGroups = createElements(PUSHENCODERGROUP_COUNT, createPushEncoderGroup);
-    var buttonRows = createElements(BUTTONROW_COUNT, createButtonRow);
-    var encoderRows = createElements(ENCODERROW_COUNT, createEncoderRow);
-    var buttonBox = createButtonBox();
+    const pushEncoderGroups = createElements(PUSHENCODERGROUP_COUNT, createPushEncoderGroup);
+    const buttonRows = createElements(BUTTONROW_COUNT, createButtonRow);
+    const encoderRows = createElements(ENCODERROW_COUNT, createEncoderRow);
+    const buttonBox = createButtonBox();
 
-    var exports = {};
+    const exports = {};
     exports.STATUS_CONTROL_CHANGE = STATUS_CONTROL_CHANGE;
     exports.setPreset             = setPreset;
     exports.pushEncoderGroups     = pushEncoderGroups;
diff --git a/res/controllers/Behringer-BCR2000-scripts.js b/res/controllers/Behringer-BCR2000-scripts.js
index 9d151908d35..00d601dd320 100644
--- a/res/controllers/Behringer-BCR2000-scripts.js
+++ b/res/controllers/Behringer-BCR2000-scripts.js
@@ -3,16 +3,17 @@
  */
 
 /* Globally available objects are declared as variables to avoid linter errors */
+// eslint-next-line-disable no-var
 var behringer = behringer, BCR2000Preset = BCR2000Preset;
 
-var BCR2000 = new behringer.extension.GenericMidiController({
+const BCR2000 = new behringer.extension.GenericMidiController({
     configurationProvider: function() {
 
         /* Shortcut variables */
-        var c = components;
-        var e = behringer.extension;
-        var p = BCR2000Preset;
-        var cc = p.STATUS_CONTROL_CHANGE;
+        const c = components;
+        const e = behringer.extension;
+        const p = BCR2000Preset;
+        const cc = p.STATUS_CONTROL_CHANGE;
 
         return {
             init: function() {
diff --git a/res/controllers/Behringer-DDM4000-scripts.js b/res/controllers/Behringer-DDM4000-scripts.js
index bf98175b9f2..dcf50f20cdd 100644
--- a/res/controllers/Behringer-DDM4000-scripts.js
+++ b/res/controllers/Behringer-DDM4000-scripts.js
@@ -3,23 +3,24 @@
  */
 
 /* Globally available objects are declared as variables to avoid linter errors */
+// eslint-next-line-disable no-var
 var behringer = behringer;
 
-var DDM4000 = new behringer.extension.GenericMidiController({
+const DDM4000 = new behringer.extension.GenericMidiController({
     configurationProvider: function() {
 
-        var DEFAULT_LONGPRESS_DURATION = 500;
-        var DEFAULT_BLINK_DURATION = 425;
-        var THROTTLE_DELAY = 40;
+        const DEFAULT_LONGPRESS_DURATION = 500;
+        const DEFAULT_BLINK_DURATION = 425;
+        const THROTTLE_DELAY = 40;
 
         /* Shortcut variables */
-        var c    = components;
-        var e    = behringer.extension;
-        var cc   = 0xB0;
-        var note = 0x90;
-        var toggle = c.Button.prototype.types.toggle;
+        const c    = components;
+        const e    = behringer.extension;
+        const cc   = 0xB0;
+        const note = 0x90;
+        const toggle = c.Button.prototype.types.toggle;
 
-        var CrossfaderAssignLED = function(options) {
+        const CrossfaderAssignLED = function(options) {
             options = options || {};
             options.outKey = options.outKey || "orientation";
             e.CustomButton.call(this, options);
@@ -31,16 +32,16 @@ var DDM4000 = new behringer.extension.GenericMidiController({
                 right: 2
             }
         });
-        var left = CrossfaderAssignLED.prototype.position.left;
-        var center = CrossfaderAssignLED.prototype.position.center;
-        var right = CrossfaderAssignLED.prototype.position.right;
+        const left = CrossfaderAssignLED.prototype.position.left;
+        const center = CrossfaderAssignLED.prototype.position.center;
+        const right = CrossfaderAssignLED.prototype.position.right;
 
-        var CrossfaderUnit = function(options) {
-            var unitOptions = options || {};
+        const CrossfaderUnit = function(options) {
+            const unitOptions = options || {};
             unitOptions.group = unitOptions.group || "[Master]";
             c.ComponentContainer.call(this, unitOptions);
 
-            var Crossfader = function(options) {
+            const Crossfader = function(options) {
                 options = options || {};
                 options.inKey = options.inKey || options.key || "crossfader";
                 options.group = options.group || unitOptions.group;
@@ -56,9 +57,9 @@ var DDM4000 = new behringer.extension.GenericMidiController({
                     engine.setValue("[Master]", "crossfader_set_default", 1);
                 },
             });
-            var crossfader = new Crossfader(options.crossfader);
+            const crossfader = new Crossfader(options.crossfader);
 
-            var CrossfaderToggleButton = function(options) {
+            const CrossfaderToggleButton = function(options) {
                 options = options || {};
                 if (options.type === undefined) {
                     options.type = c.Button.prototype.types.toggle;
@@ -96,7 +97,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
          * @param {number} options Options object
          * @public
          */
-        var CrossfaderReverseTapButton = function(options) {
+        const CrossfaderReverseTapButton = function(options) {
             options = options || {};
             options.inKey = options.inKey || "xFaderReverse";
             c.Button.call(this, options);
@@ -108,7 +109,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
             },
         });
 
-        var Blinker = function(target, blinkDuration, outValueScale) {
+        const Blinker = function(target, blinkDuration, outValueScale) {
             this.target = target;
             this.outValueScale = outValueScale || components.Component.prototype.outValueScale;
 
@@ -125,11 +126,11 @@ var DDM4000 = new behringer.extension.GenericMidiController({
             },
         };
 
-        var SamplerBank = function(bankOptions) {
+        const SamplerBank = function(bankOptions) {
             c.ComponentContainer.call(this);
-            var bank = this;
+            const bank = this;
 
-            var PlayButton = function(options) {
+            const PlayButton = function(options) {
                 options = options || {};
                 options.inKey = options.inKey || "cue_gotoandplay";
                 options.outKey = options.outKey || "track_loaded";
@@ -151,7 +152,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
                 group: bankOptions.group,
             });
 
-            var PlayIndicatorLED = function(options) {
+            const PlayIndicatorLED = function(options) {
                 options = options || {};
                 options.outKey = options.outKey || "play_indicator";
                 this.blinker = new Blinker(this, options.blinkDuration);
@@ -171,7 +172,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
                 blinkDuration: DEFAULT_BLINK_DURATION,
             });
 
-            var ReverseMode = function(options) {
+            const ReverseMode = function(options) {
                 options = options || {};
                 options.key = options.key || "reverse";
                 c.Button.call(this, options);
@@ -179,7 +180,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
             ReverseMode.prototype = e.deriveFrom(c.Button);
             this.reverseMode = new ReverseMode({midi: bankOptions.reverse, group: bankOptions.group});
 
-            var LoopMode = function(options) {
+            const LoopMode = function(options) {
                 options = options || {};
                 options.key = options.inKey || "beatloop_activate";
                 c.Button.call(this, options);
@@ -187,11 +188,11 @@ var DDM4000 = new behringer.extension.GenericMidiController({
             };
             LoopMode.prototype = e.deriveFrom(c.Button, {
                 outValueScale: function(value) {
-                    var button = c.Button.prototype;
+                    const button = c.Button.prototype;
                     bank.playButton.type = value ? button.types.toggle : button.types.push;
                     if (!value) {
-                        var beatloopSize = engine.getValue(this.group, "beatloop_size");
-                        var key = "beatloop_" + beatloopSize;
+                        const beatloopSize = engine.getValue(this.group, "beatloop_size");
+                        const key = `beatloop_${beatloopSize}`;
                         engine.setValue(this.group, key, 0);
                     }
                     return button.outValueScale(value);
@@ -199,7 +200,7 @@ var DDM4000 = new behringer.extension.GenericMidiController({
             });
             this.loopMode = new LoopMode({midi: bankOptions.loop, group: bankOptions.group});
 
-            var ModeButton = function(options) {
+            const ModeButton = function(options) {
                 options = options || {};
                 options.key = options.key || "mode";
                 options.longPressTimeout = options.longPressTimeout || DEFAULT_LONGPRESS_DURATION;
diff --git a/res/controllers/Behringer-Extension-scripts.js b/res/controllers/Behringer-Extension-scripts.js
index 4c3ce199dbd..6bcf8ddfed4 100644
--- a/res/controllers/Behringer-Extension-scripts.js
+++ b/res/controllers/Behringer-Extension-scripts.js
@@ -4,10 +4,10 @@
 (function(global) {
 
     /** @private */
-    var components = global.components;
+    const components = global.components;
 
     /** @private */
-    var engine = global.engine;
+    const engine = global.engine;
 
     /**
      * Determines the merge strategy for a value when merging a component container definition.
@@ -59,7 +59,7 @@
      * @param {string} message Message
      * @private
      */
-    var log = {
+    const log = {
         debug: function(message) {
             if (this.debug) {
                 print("[DEBUG] " + message);
@@ -80,7 +80,7 @@
      * @returns {string} ID for the MIDI address; `undefined` on error
      * @private
      */
-    var findComponentId = function(midiAddress) {
+    const findComponentId = function(midiAddress) {
         if (Array.isArray(midiAddress) && midiAddress.length === 2
                 && typeof midiAddress[0] === "number"  && typeof midiAddress[1] === "number") {
             return "[" + midiAddress.map(function(x) {
@@ -97,14 +97,14 @@
      * @returns {string} A short string that describes the component; `undefined` on error
      * @private
      */
-    var stringifyComponent = function(component) {
+    const stringifyComponent = function(component) {
         if (!component) {
             return;
         }
-        var key = component.inKey || component.outKey;
-        var value = component.group + "," + key;
+        const key = component.inKey || component.outKey;
+        let value = `${component.group},${key}`;
         if (component.midi) {
-            var id = findComponentId(component.midi);
+            const id = findComponentId(component.midi);
             if (id !== undefined) {
                 value = id + ": " + value;
             }
@@ -118,7 +118,7 @@
      * @param {number} value A number between 0 and 1.
      * @private
      */
-    var convertToMidiValue = function(value) {
+    const convertToMidiValue = function(value) {
         /*
          * Math.round() is important to keep input and output in sync.
          * Example:
@@ -138,7 +138,7 @@
      * @returns {object} A new prototype based on parent with the given members
      * @private
      */
-    var deriveFrom = function(parent, members) {
+    const deriveFrom = function(parent, members) {
         return Object.assign(Object.create(parent.prototype), members);
     };
 
@@ -150,7 +150,7 @@
      * @private
      * @see `Throttler`
      */
-    var throttle = function(action, owner) {
+    const throttle = function(action, owner) {
         if (owner.throttler) {
             owner.throttler.schedule(action, owner);
         } else {
@@ -166,7 +166,7 @@
      * @param {object} options Options object
      * @public
      */
-    var ParameterComponent = function(options) {
+    const ParameterComponent = function(options) {
         components.Component.call(this, options);
     };
     ParameterComponent.prototype = deriveFrom(components.Component, {
@@ -188,7 +188,7 @@
      * @param {components.Component|components.ComponentContainer} options.target Target component
      * @public
      */
-    var ShiftButton = function(options) {
+    const ShiftButton = function(options) {
         components.Button.call(this, options);
     };
     ShiftButton.prototype = deriveFrom(components.Button, {
@@ -210,7 +210,7 @@
      * @param {object} options Options object
      * @public
      */
-    var Trigger = function(options) {
+    const Trigger = function(options) {
         components.Component.call(this, options);
     };
     Trigger.prototype = deriveFrom(components.Component, {
@@ -226,7 +226,7 @@
      * @param {number} options.offValue Value for `off`; optional, default: opposite of `onValue`
      * @public
      */
-    var CustomButton = function(options) {
+    const CustomButton = function(options) {
         options = options || {};
         if (options.onValue === undefined) { // do not use '||' to allow 0
             options.onValue = 1;
@@ -273,7 +273,7 @@
      * @public
      * @see https://github.com/mixxxdj/mixxx/wiki/Script-Timers
      */
-    var Timer = function(options) {
+    const Timer = function(options) {
         Object.assign(this, options);
         this.disable();
     };
@@ -314,7 +314,7 @@
      * @param {number} options.delay Minimal delay between two consecutive actions (in ms)
      * @public
      */
-    var Throttler = function(options) {
+    const Throttler = function(options) {
         options = options || {};
         options.delay = options.delay || 0;
         Object.assign(this, options);
@@ -331,14 +331,14 @@
 
         notify: function() {
             if (this.jobs.length > 0 && this.acquireLock()) {
-                var job = this.jobs.shift();
+                const job = this.jobs.shift();
                 job.action.call(job.owner);
                 this.unlockTimer.start();
             }
         },
 
         acquireLock: function() {
-            var unlocked = !this.locked;
+            const unlocked = !this.locked;
             if (unlocked) {
                 this.locked = true;
             }
@@ -359,9 +359,9 @@
      * @param {object} options Options object
      * @public
      */
-    var LongPressButton = function(options) {
+    const LongPressButton = function(options) {
         components.Button.call(this, options);
-        var action = function() {
+        const action = function() {
             this.isLongPressed = true;
             this.onLongPress();
         };
@@ -399,9 +399,9 @@
      * @param {number} options.blinkDuration Blink duration in ms; optional, default: 500
      * @public
      */
-    var BlinkingButton = function(options) {
+    const BlinkingButton = function(options) {
         options = options || {};
-        var blinkAction = function() {
+        const blinkAction = function() {
             this.send(components.Button.prototype.outValueScale.call(
                 this, this.flashing = !this.flashing));
         };
@@ -436,14 +436,14 @@
      * @param {object} options Options object
      * @public
      */
-    var DirectionEncoder = function(options) {
+    const DirectionEncoder = function(options) {
         components.Encoder.call(this, options);
         this.previousValue = this.inGetValue(); // available only after call of Encoder constructor
     };
     DirectionEncoder.prototype = deriveFrom(components.Encoder, {
         min: 0,
         inValueScale: function(value) {
-            var direction = 0;
+            let direction = 0;
             if (!(this.relative && this.isShifted)) {
                 if (value > this.previousValue || value === this.max) {
                     direction = 1;
@@ -471,13 +471,13 @@
      * @param {number} options.bound A positive integer defining the range bounds
      * @public
      */
-    var RangeAwareEncoder = function(options) {
+    const RangeAwareEncoder = function(options) {
         components.Encoder.call(this, options);
     };
     RangeAwareEncoder.prototype = deriveFrom(components.Encoder, {
         outValueScale: function(value) {
             /* -bound..+bound => 0..1 */
-            var normalizedValue = (value + this.bound) / (2 * this.bound);
+            const normalizedValue = (value + this.bound) / (2 * this.bound);
             /* 0..1 => 0..127 */
             return convertToMidiValue.call(this, normalizedValue);
         },
@@ -492,7 +492,7 @@
      * @param {number} options.bound A positive integer defining the range bounds
      * @public
      */
-    var RangeAwarePot = function(options) {
+    const RangeAwarePot = function(options) {
         components.Pot.call(this, options);
     };
     RangeAwarePot.prototype = deriveFrom(components.Pot, {
@@ -512,7 +512,7 @@
      * @param {number} options.maxValue A positive integer defining the maximum enumeration value
      * @public
      */
-    var EnumToggleButton = function(options) {
+    const EnumToggleButton = function(options) {
         options = options || {};
         if (options.maxValue === undefined && options.values === undefined) {
             log.error("An EnumToggleButton requires either `values` or a `maxValue`.");
@@ -526,9 +526,9 @@
     EnumToggleButton.prototype = deriveFrom(components.Button, {
         input: function(channel, control, value, status, _group) {
             if (this.isPress(channel, control, value, status)) {
-                var newValue;
+                let newValue;
                 if (this.values) {
-                    var index = this.values.indexOf(this.inGetValue());
+                    const index = this.values.indexOf(this.inGetValue());
                     newValue = this.values[(index + 1) % this.values.length];
                 } else {
                     newValue = (this.inGetValue() + 1) % (this.maxValue + 1);
@@ -549,7 +549,7 @@
      * @public
      * @see https://github.com/mixxxdj/mixxx/wiki/Midi-Scripting#soft-takeover
      */
-    var EnumEncoder = function(options) {
+    const EnumEncoder = function(options) {
         options = options || {};
         if (options.values === undefined) {
             log.error("EnumEncoder constructor was called without specifying enum values.");
@@ -563,7 +563,7 @@
     };
     EnumEncoder.prototype = deriveFrom(components.Encoder, {
         input: function(_channel, _control, value, _status, _group) {
-            var scaledValue = this.inValueScale(value);
+            const scaledValue = this.inValueScale(value);
             if (!this.softTakeover
                 || this.previousValue === undefined
                 || this.previousValue === this.inGetValue()) {
@@ -572,14 +572,14 @@
             this.previousValue = scaledValue;
         },
         inValueScale: function(value) {
-            var normalizedValue = value / this.max;
-            var index = Math.round(normalizedValue * this.maxIndex);
+            const normalizedValue = value / this.max;
+            const index = Math.round(normalizedValue * this.maxIndex);
             return this.values[index];
         },
         outValueScale: function(value) {
-            var index = this.values.indexOf(value);
+            const index = this.values.indexOf(value);
             if (index !== -1) {
-                var normalizedValue = index / this.maxIndex;
+                const normalizedValue = index / this.maxIndex;
                 return convertToMidiValue.call(this, normalizedValue);
             } else {
                 log.warn("'" + value + "' is not in supported values " + "[" + this.values + "]");
@@ -595,7 +595,7 @@
      * @param {object} options Options object
      * @public
      */
-    var LoopEncoder = function(options) {
+    const LoopEncoder = function(options) {
         options = options || {};
         if (options.values === undefined) {
             /* taken from src/engine/controls/loopingcontrol.cpp */
@@ -620,7 +620,7 @@
      * @param {string} options.sizeControl (optional) Name of a control that contains `size`
      * @public
      */
-    var LoopMoveEncoder = function(options) {
+    const LoopMoveEncoder = function(options) {
         options = options || {};
         options.inKey = options.inKey || "loop_move";
         options.size = options.size || 0.5;
@@ -628,8 +628,8 @@
     };
     LoopMoveEncoder.prototype = deriveFrom(DirectionEncoder, {
         inValueScale: function(value) {
-            var direction = DirectionEncoder.prototype.inValueScale.call(this, value);
-            var beats = this.sizeControl
+            const direction = DirectionEncoder.prototype.inValueScale.call(this, value);
+            const beats = this.sizeControl
                 ? engine.getValue(this.group, this.sizeControl)
                 : this.size;
             return direction * beats;
@@ -644,18 +644,18 @@
      * @param {object} options Options object
      * @public
      */
-    var BackLoopButton = function(options) {
+    const BackLoopButton = function(options) {
         options = options || {};
         options.key = options.key || "loop_enabled";
         components.Button.call(this, options);
     };
     BackLoopButton.prototype = deriveFrom(components.Button, {
         inSetValue: function(value) {
-            var script = global.script;
-            var group = this.group;
+            const script = global.script;
+            const group = this.group;
             if (value) {
-                var loopSize = engine.getValue(group, "beatloop_size");
-                var beatjumpSize = engine.getValue(group, "beatjump_size");
+                const loopSize = engine.getValue(group, "beatloop_size");
+                const beatjumpSize = engine.getValue(group, "beatjump_size");
                 engine.setValue(group, "beatjump_size", loopSize);
                 script.triggerControl(group, "beatjump_backward");
                 script.triggerControl(group, "beatloop_activate");
@@ -675,7 +675,7 @@
      *                              (`0`: additive, `1`: constant)
      * @public
      */
-    var CrossfaderCurvePot = function(options) {
+    const CrossfaderCurvePot = function(options) {
         options = options || {};
         options.group = options.group || "[Mixer Profile]";
         if (options.mode) {
@@ -717,7 +717,7 @@
      *                                              controller
      * @public
      */
-    var Publisher = function(options) {
+    const Publisher = function(options) {
         if (options.source === undefined) {
             log.error("Missing source component");
             return;
@@ -744,16 +744,16 @@
         },
     });
 
-    var EffectUnit = function(rack, deckGroup) {
+    const EffectUnit = function(rack, deckGroup) {
         components.ComponentContainer.call(this);
-        var effectGroup = "[" + rack + "_" + deckGroup + "_Effect1]";
-        var channelGroup = "[" + rack + "_" + deckGroup + "]";
+        const effectGroup = `[${rack}_${deckGroup}_Effect1]`;
+        const channelGroup = `[${rack}_${deckGroup}]`;
 
-        var ParameterKnob = function(parameterNumber) {
+        const ParameterKnob = function(parameterNumber) {
             components.Pot.call(this, {group: effectGroup, key: "parameter" + parameterNumber});
         };
         ParameterKnob.prototype = deriveFrom(components.Pot);
-        var ParameterButton = function(parameterNumber) {
+        const ParameterButton = function(parameterNumber) {
             components.Button.call(this, {group: effectGroup, key: "button_parameter" + parameterNumber});
         };
         ParameterButton.prototype = deriveFrom(
@@ -766,16 +766,14 @@
         this.mix = new components.Pot({group: channelGroup, key: "mix"});
 
         this.parameterKnobs = new components.ComponentContainer();
-        var parameterKnobCount = engine.getValue(effectGroup, "num_parameters");
-        for (var knobIndex = 1; knobIndex <= parameterKnobCount; knobIndex++) {
-            this.parameterKnobs[knobIndex] = new ParameterKnob(knobIndex);
-        }
+        const parameterKnobCount = engine.getValue(effectGroup, "num_parameters");
+        [...Array(parameterKnobCount)].map((x, i) => i+1).forEach(knobIndex =>
+            this.parameterKnobs[knobIndex] = new ParameterKnob(knobIndex));
 
         this.parameterButtons = new components.ComponentContainer();
-        var parameterButtonCount = engine.getValue(effectGroup, "num_button_parameters");
-        for (var buttonIndex = 1; buttonIndex <= parameterButtonCount; buttonIndex++) {
-            this.parameterButtons[buttonIndex] = new ParameterButton(buttonIndex);
-        }
+        const parameterButtonCount = engine.getValue(effectGroup, "num_button_parameters");
+        [...Array(parameterButtonCount)].map((x, i) => i+1).forEach(buttonIndex =>
+            this.parameterButtons[buttonIndex] = new ParameterButton(buttonIndex));
     };
     EffectUnit.prototype = deriveFrom(components.ComponentContainer);
 
@@ -800,7 +798,7 @@
      * @yields {EqualizerUnit}
      * @public
      */
-    var EqualizerUnit = function(deckGroup) {
+    const EqualizerUnit = function(deckGroup) {
         EffectUnit.call(this, "EqualizerRack1", deckGroup);
     };
     EqualizerUnit.prototype = deriveFrom(EffectUnit);
@@ -829,7 +827,7 @@
      * @yields {QuickEffectUnit}
      * @public
      */
-    var QuickEffectUnit = function(deckGroup) {
+    const QuickEffectUnit = function(deckGroup) {
         EffectUnit.call(this, "QuickEffectRack1", deckGroup);
     };
     QuickEffectUnit.prototype = deriveFrom(EffectUnit);
@@ -841,7 +839,7 @@
      * @param {Array<string>} initialContainers Initial container names
      * @public
      */
-    var ComponentRegistry = function(initialContainers) {
+    const ComponentRegistry = function(initialContainers) {
         this.containers = new components.ComponentContainer();
         if (Array.isArray(initialContainers)) {
             initialContainers.forEach(function(name) { this.createContainer(name); }, this);
@@ -940,14 +938,14 @@
                     + stringifyComponent(component) + " without MIDI address");
                 return;
             }
-            var id = findComponentId(component.midi);
+            const id = findComponentId(component.midi);
             if (!Object.prototype.hasOwnProperty.call(this.containers, containerName)) {
                 this.createContainer(containerName);
             }
-            var container = this.getContainer(containerName);
-            var store = true;
+            const container = this.getContainer(containerName);
+            let store = true;
             if (Object.prototype.hasOwnProperty.call(container, id)) {
-                var old = container[id];
+                const old = container[id];
                 if (old !== component) {
                     this.unregister(old, containerName);
                 } else {
@@ -974,8 +972,8 @@
          */
         unregister: function(component, containerName) {
             log.debug(containerName + ": unregister " + stringifyComponent(component));
-            var container = this.getContainer(containerName);
-            var id = findComponentId(component.midi);
+            const container = this.getContainer(containerName);
+            const id = findComponentId(component.midi);
             delete container[id];
             return id;
         },
@@ -1004,7 +1002,7 @@
      * @param {boolean} options.debug Optional flag to emit debug messages to the log
      * @public
      */
-    var LayerManager = function(options) {
+    const LayerManager = function(options) {
         this.componentRegistry = new ComponentRegistry([
             LayerManager.prototype.defaultContainerName,
             LayerManager.prototype.shiftContainerName]);
@@ -1024,7 +1022,7 @@
          * @private
          */
         defaultLayer: function() {
-            var defaultContainer = this.componentRegistry.getContainer(this.defaultContainerName);
+            const defaultContainer = this.componentRegistry.getContainer(this.defaultContainerName);
             return Object.keys(this.shiftLayer()).reduce(
                 function(shiftCounterparts, name) {
                     shiftCounterparts[name] = defaultContainer[name];
@@ -1054,13 +1052,13 @@
          * @private
          */
         findComponent: function(status, control) {
-            var id = findComponentId([status, control]);
+            const id = findComponentId([status, control]);
             if (id === undefined) {
                 return;
             }
-            var component = this.activeLayer[id];
+            let component = this.activeLayer[id];
             if (component === undefined) {
-                var defaultComponents
+                const defaultComponents
                     = this.componentRegistry.getContainer(this.defaultContainerName);
                 component = defaultComponents[id];
             }
@@ -1082,7 +1080,7 @@
          * @private
          */
         onRegistry: function(operation, component, shift) {
-            var layerName = shift === true ? this.shiftContainerName : this.defaultContainerName;
+            const layerName = shift === true ? this.shiftContainerName : this.defaultContainerName;
             return operation.call(this.componentRegistry, component, layerName);
         }
         /**
@@ -1113,7 +1111,7 @@
          * @public
          */
         unregister: function(component, shift) {
-            var id = this.onRegistry(this.componentRegistry.unregister, component, shift);
+            const id = this.onRegistry(this.componentRegistry.unregister, component, shift);
             delete this.activeLayer[id];
         },
 
@@ -1177,7 +1175,7 @@
          * @public
          */
         input: function(channel, control, value, status /* ignored: ,group */) {
-            var component = this.findComponent(status, control);
+            const component = this.findComponent(status, control);
             if (component === undefined) {
                 return;
             }
@@ -1275,7 +1273,7 @@
      * @param {function} components.configurationProvider Mapping configuration provider
      * @public
      */
-    var GenericMidiController = function(options) {
+    const GenericMidiController = function(options) {
         if (!options || typeof options.configurationProvider !== "function") {
             log.error("The required function 'configurationProvider' is missing.");
             return;
@@ -1297,7 +1295,7 @@
             this.controllerId = controllerId;
             this.debug = debug;
 
-            var delay = this.config.throttleDelay;
+            const delay = this.config.throttleDelay;
             if (delay > 0) {
                 log.debug("Component registration is throttled using a delay of " + delay + "ms");
                 this.throttler = new Throttler({delay: delay});
@@ -1367,9 +1365,9 @@
         createLayerManager: function(target,
             deckDefinitions, effectUnitDefinitions, containerDefinitions) {
 
-            var layerManager = new LayerManager({debug: this.debug});
-            var controller = this;
-            var registerComponents = function(definition, implementation) {
+            const layerManager = new LayerManager({debug: this.debug});
+            const controller = this;
+            const registerComponents = function(definition, implementation) {
                 controller.registerComponents(layerManager, definition, implementation);
             };
 
@@ -1405,7 +1403,7 @@
                 if (Array.isArray(context.definitions)) {
                     context.definitions.forEach(function(definition) {
                         throttle(function() {
-                            var implementation = context.factory.call(this, definition, target);
+                            const implementation = context.factory.call(this, definition, target);
                             target.push(implementation);
                             context.register(definition, implementation);
                         }, this);
@@ -1429,7 +1427,7 @@
          * @private
          */
         createDeck: function(deckDefinition, componentStorage) {
-            var deck = new components.Deck(deckDefinition.deckNumbers);
+            const deck = new components.Deck(deckDefinition.deckNumbers);
             deckDefinition.components.forEach(function(componentDefinition, index) {
                 const options = Object.assign({group: deck.currentDeck}, componentDefinition.options);
                 const definition = Object.assign(componentDefinition, {options: options});
@@ -1478,7 +1476,7 @@
          * @private
          */
         createPublisher: function(source, publisherStorage) {
-            var publisher = new Publisher({source: source});
+            const publisher = new Publisher({source: source});
             publisherStorage.push(publisher);
             return publisher;
         },
@@ -1506,14 +1504,14 @@
 
             /* Add publishers for pots */
             if (definition.feedback) {
-                var triggers = rebindTriggers || [];
-                var createPublisher = this.createPublisher; // `this` is bound to implementation
+                const triggers = rebindTriggers || [];
+                const createPublisher = this.createPublisher; // `this` is bound to implementation
                 implementation.forEachComponent(function(effectComponent) {
                     if (effectComponent instanceof components.Pot) {
-                        var publisher = createPublisher(effectComponent, publisherStorage);
-                        var prototype = Object.getPrototypeOf(effectComponent);
+                        const publisher = createPublisher(effectComponent, publisherStorage);
+                        const prototype = Object.getPrototypeOf(effectComponent);
                         triggers.forEach(function(functionName) {
-                            var delegate = prototype[functionName];
+                            const delegate = prototype[functionName];
                             if (typeof delegate === "function") {
                                 prototype[functionName] = function() {
                                     delegate.apply(this, arguments);
@@ -1549,12 +1547,12 @@
          * @private
          */
         createEffectUnit: function(effectUnitDefinition, componentStorage) {
-            var effectUnit = this.setupMidi(
+            const effectUnit = this.setupMidi(
                 effectUnitDefinition,
                 new components.EffectUnit(effectUnitDefinition.unitNumbers, true),
                 componentStorage,
                 ["onFocusChange", "shift", "unshift"]);
-            var shiftType = effectUnitDefinition.sendShiftedFor;
+            const shiftType = effectUnitDefinition.sendShiftedFor;
             /*
              * `shiftType` is expected to be a JS component (e.g. `c.Button` or `c.Component`)
              * which in terms of JS means that it is of type `function`. If something else is given
@@ -1579,8 +1577,8 @@
          * @private
          */
         createComponentContainer: function(containerDefinition) {
-            var containerType = containerDefinition.type || components.ComponentContainer;
-            var container = new containerType(containerDefinition.options);
+            const containerType = containerDefinition.type || components.ComponentContainer;
+            const container = new containerType(containerDefinition.options);
             if (containerDefinition.components) {
                 containerDefinition.components.forEach(function(componentDefinition, index) {
                     const definition = mergeDefinitions({}, containerDefinition.defaultDefinition, componentDefinition);
@@ -1601,7 +1599,7 @@
          * @private
          */
         createComponent: function(definition) {
-            var component = null;
+            let component = null;
             if (definition && definition.type) {
                 if (definition.type.prototype instanceof components.ComponentContainer) {
                     component = this.createComponentContainer(definition);
@@ -1628,14 +1626,14 @@
                 layerManager.register(implementation, definition && definition.shift === true);
             } else if (implementation instanceof components.ComponentContainer) {
                 Object.keys(implementation).forEach(function(name) {
-                    var definitionName = definition ? definition[name] : null;
+                    const definitionName = definition ? definition[name] : null;
                     this.registerComponents(layerManager, definitionName, implementation[name]);
                 }, this);
             }
         },
     });
 
-    var exports = {};
+    const exports = {};
     exports.deriveFrom = deriveFrom;
     exports.ParameterComponent = ParameterComponent;
     exports.ShiftButton = ShiftButton;