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 @@ - + Behringer BCR2000 Christian @@ -7,7 +7,6 @@ - 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 @@ - + Behringer DDM4000 Christian @@ -7,7 +7,6 @@ - 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} 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} 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} 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} 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} 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 cc912e64d3a..6bcf8ddfed4 100644 --- a/res/controllers/Behringer-Extension-scripts.js +++ b/res/controllers/Behringer-Extension-scripts.js @@ -4,10 +4,53 @@ (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. + * + * @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. @@ -16,7 +59,7 @@ * @param {string} message Message * @private */ - var log = { + const log = { debug: function(message) { if (this.debug) { print("[DEBUG] " + message); @@ -34,10 +77,10 @@ * Determine an ID from a component's MIDI address * * @param {Array} 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) { + const findComponentId = function(midiAddress) { if (Array.isArray(midiAddress) && midiAddress.length === 2 && typeof midiAddress[0] === "number" && typeof midiAddress[1] === "number") { return "[" + midiAddress.map(function(x) { @@ -51,17 +94,17 @@ * 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) { + 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; } @@ -75,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: @@ -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 || {}); + const deriveFrom = function(parent, members) { + return Object.assign(Object.create(parent.prototype), members); }; /** @@ -107,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 { @@ -123,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, { @@ -145,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, { @@ -167,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, { @@ -183,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; @@ -230,8 +273,8 @@ * @public * @see https://github.com/mixxxdj/mixxx/wiki/Script-Timers */ - var Timer = function(options) { - _.assign(this, options); + const Timer = function(options) { + Object.assign(this, options); this.disable(); }; Timer.prototype = { @@ -239,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()) { @@ -271,10 +314,10 @@ * @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; - _.assign(this, options); + Object.assign(this, options); this.locked = false; this.jobs = []; this.unlockTimer = new Timer( @@ -288,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; } @@ -316,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(); }; @@ -356,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)); }; @@ -393,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; @@ -428,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); }, @@ -449,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, { @@ -469,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`."); @@ -483,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); @@ -506,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."); @@ -520,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()) { @@ -529,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 + "]"); @@ -552,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 */ @@ -577,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; @@ -585,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; @@ -601,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"); @@ -632,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) { @@ -674,7 +717,7 @@ * controller * @public */ - var Publisher = function(options) { + const Publisher = function(options) { if (options.source === undefined) { log.error("Missing source component"); return; @@ -701,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( @@ -723,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); @@ -757,7 +798,7 @@ * @yields {EqualizerUnit} * @public */ - var EqualizerUnit = function(deckGroup) { + const EqualizerUnit = function(deckGroup) { EffectUnit.call(this, "EqualizerRack1", deckGroup); }; EqualizerUnit.prototype = deriveFrom(EffectUnit); @@ -786,7 +827,7 @@ * @yields {QuickEffectUnit} * @public */ - var QuickEffectUnit = function(deckGroup) { + const QuickEffectUnit = function(deckGroup) { EffectUnit.call(this, "QuickEffectRack1", deckGroup); }; QuickEffectUnit.prototype = deriveFrom(EffectUnit); @@ -798,7 +839,7 @@ * @param {Array} 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); @@ -810,7 +851,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 +868,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 +925,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) { @@ -897,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 { @@ -926,13 +967,13 @@ * * @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) { 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; }, @@ -961,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]); @@ -977,11 +1018,11 @@ /** * Retrieve the Default layer. * - * @return {object} The Default layer + * @returns {object} The Default layer * @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]; @@ -992,7 +1033,7 @@ /** * Retrieve the Shift layer. * - * @return {object} The Shift layer + * @returns {object} The Shift layer * @private */ shiftLayer: function() { @@ -1004,20 +1045,20 @@ * * @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. * @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]; } @@ -1035,11 +1076,11 @@ * @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) { - var layerName = shift === true ? this.shiftContainerName : this.defaultContainerName; + const layerName = shift === true ? this.shiftContainerName : this.defaultContainerName; return operation.call(this.componentRegistry, component, layerName); } /** @@ -1070,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]; }, @@ -1134,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; } @@ -1232,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; @@ -1254,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}); @@ -1317,16 +1358,16 @@ * @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 */ 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); }; @@ -1362,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); @@ -1386,10 +1427,10 @@ * @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) { - 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) { @@ -1435,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; }, @@ -1450,7 +1491,7 @@ * @param {Array} publisherStorage Storage for publisher components * @param {Array} 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) { @@ -1463,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); @@ -1506,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 @@ -1536,12 +1577,11 @@ * @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) { - var definition = _.merge( - {}, containerDefinition.defaultDefinition || {}, componentDefinition); + const definition = mergeDefinitions({}, containerDefinition.defaultDefinition, componentDefinition); container[index] = this.createComponent(definition); }, this); } @@ -1559,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); @@ -1586,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; @@ -1614,5 +1654,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); 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) {