JavaScript

The plugin can use JavaScript as script language. To enable this, the keyword "enableJavaScript" must be entered in the advanced settings.

When JavaScript is enabled, script files with the extension ".js" will be handled by the JavaScript engine, while all other extensions will be processed by the midiScript engine. In-editor JavaScript is enabled if the first two letters on the first line are "js".

JavaScript scripts are entirely event-driven, and specific function names for events must be used. JavaScript is, by definition, case sensitive, so event functions must be declared exactly as defined.

Unlike midiScripts, JavaScript event handlers are always triggered and must decide for themselves whether to respond to an event. For example, the event for received CC messages (OnControlChangeReceived) will always be triggered when a CC message is received; the OnControlChangeReceived function must decide whether the message data (channel, control, and value) is something it should react to.

Please consider this a draft, very much in beta (or, alpha, rather), and open to feedback. I haven't tested everything. Some parts might be impossible, illogical, missing, or could be improved. Please let me know.

Script example

This is a small example script, just to get a feeling for what it looks like.

const myChannel = 1;
const myControl = 1;

function OnDialRotated(ticks, value)
{
    ui.text(value);
    midi.sendCC(myChannel, myControl, value);
}

function OnControlChangeReceived(channel, control, value)
{
    if ((channel == myChannel) && (control == myControl))
    {
        ui.value(value);
        ui.text(value);
    }
}

Script objects

The scripts use various objects to execute actions. The available objects are:

  • ui - Everything (except layouts) related to editor properties, button and dial states and values.
  • layout - Everything related to dial layouts.
  • midi - Everything related to midi 
  • global - Global variables
  • timer - Timers
  • io - keyboard/file/clipboard things
  • list - dial list things

There is currently no object to access the functions library, since I assume it is not needed. I assume that what the function library does can also be implemented in plain JavaScript, which is why I haven't made the functions library available to JavaScript scripts. If I'm mistaken, please let me know.

Event handlers

OnInit()
OnKeyPressed(state)
OnKeyReleased(state)
OnFaderMoved(value)
OnVpotMoved(value)
OnScreenTouched(x, y)
OnDialRotated(ticks, value)
OnDialPressed()
OnDialReleased()
OnDeepLinkReceived(part1, part2, part3)
OnDeviceDidConnect(name)
OnDeviceDidDisconnect(name)

OnNoteOnSent(channel, key, velocity)
OnNoteOnReceived(channel, key, velocity)
OnNoteOffSent(channel, key, velocity)
OnNoteOffReceived(channel, key, velocity)
OnProgramChangeSent(channel, program)
OnProgramChangeReceived(channel, program)
OnControlChangeSent(channel, control, value)
OnControlChangeReceived(channel, control, value)
OnChannelPressureSent(channel, pressure)
OnChannelPressureReceived(channel, pressure)
OnNRPNSent(channel, control, value)
OnNRPNReceived(channel, control, value)
OnPitchBendSent(channel, value)
OnPitchBendReceived(channel, value)
OnSysExReceived(sysexdata) Sysex needs some improvement, maybe by adding more arguments, such as providing text.

OnTimerElapsed(name, isGlobal, time)
OnGlobalVariableChanged(name, value)

OnSelectedfromlist(index, name)

The ui namespace

In these object and namespace declarations, the C# argument syntax is used, I hope this makes sense to you. Arguments with a value (e.g., bool upper = false) are optional and can be omitted, in which case the default value will be assigned.

ui.text(string value, bool upper = false)
ui.title(string value) 
ui.design(string path)
ui.image(string path)
ui.value(int value, bool forWhilePressed = false) 
ui.vu(int value, string text = null) 
ui.vuscale(string value) 
ui.vpotspan(string value)
ui.minmax(int min, int max, bool forWhilePressed = false) 
ui.speed(int value) 
ui.titlevalueposition(string value) 
ui.iconleft(string path)
ui.iconright(string path)
ui.display(string value, bool forWhilePressed = false)
ui.largeicons(bool value)
ui.vpotsizefull(bool value)
ui.vpotendless(bool value) 
ui.avoidfader(string value) 
ui.step(string algorithm, bool forWhilePressed = false) 
ui.step(string algorithm, int stepsize, bool forWhilePressed = false);
ui.valuedisplay(string value, bool forWhilePressedOrUpper = false) 
ui.icon(string path, int? size = null, int? x = null, int? y = null, bool upper = false)

The vpot and fader subobjects are "convenient" objects that can be used to make scripts clearer by denoting which kind of object is referenced, but they affect the same properties as the non-prefixed settings. Examples:  ui.vpot.endless affect the same value as ui.vpotendless. ui.design, ui.vpot.design and ui.fader.design all affect the "Design" dropdown.

ui.vpot.endless(bool value)
ui.vpot.sizefull(bool value)
ui.vpot.span(string value)
ui.vpot.minmax(int min, int max, bool forWhilePressed = false) 
ui.vpot.speed(int value)
ui.vpot.design(string path)
ui.vpot.step(string algorithm, bool forWhilePressed = false)
ui.vpot.step(string algorithm, int stepsize, bool forWhilePressed = false)
ui.vpot.value(int value, bool forWhilePressed = false)

ui.fader.minmax(int min, int max, bool forWhilePressed = false)
ui.fader.speed(int value)
ui.fader.design(string path)
ui.fader.step(string algorithm, bool forWhilePressed = false)
ui.fader.step(string algorithm, int stepsize, bool forWhilePressed = false)
ui.fader.value(int value, bool forWhilePressed = false)

The layout namespace

layout.load(string path, bool reset = false)
layout._item_._property_(value)
layout._item_._property_ = value
layout._item_.set(_property_, value)

_item_ is the key name for the item, _property_ is the property.

Examples for setting values:

layout.l_toptext_full_width.background = "#FF0000";
layout.l_toptext_full_width.background("#FF0000");
layout.l_toptext_full_width.set("background", "#FF0000");
layout.l_toptext_full_width.set("font", { size: 26, weight: 700 });

Examples to get values:

let text = layout.l_toptext_full_width.value;         // Gets the current text value
let font = layout.l_toptext_full_width.font;          // Gets the current font object
let barColor = layout.l_bar_vol_full_width.bar_fill_c; // Gets the bar fill color

Accessing font properties:

layout.l_toptext_full_width.font.size = 22

let font = layout.l_toptext_full_width.font; // Get the current font object
font.size = 20; // Change the size
font.weight = 800; // Change the weight
layout.l_toptext_full_width.set("font", font); //  Set the updated font object back

The global variables namespace

Global variables, like in the midiScript engine, are variables that are accessible to all scripts. To access a global variable, JavaScript scripts must use the global functions. Local variables (i.e., variables displayed in the Variables window as "local") are variables declared in the root of the JavaScript script. In JavaScript terminology, these variables are "global" (in the sense that they are accessible from all functions in the script), but from the plugin's point of view, they are local to the script. Variables defined in functions are not accessible to the plugin and are not displayed in the Variables window.

var myvar = global.get("myGlobalVar")
global.set("myGlobalVar", value')
global.remove("myGlobalVar")
if (global.exists("myGlobalVar")) {}

The JavaScript engine imposes no restrictions on naming global variables, and global and local variables can share the same names.

The timers namespace

timer.run(string name, int callBackTime, bool? isGlobal = false)
timer.pause(string name, bool? isGlobal = false)
timer.restart(string name, int callBackTime, bool? isGlobal = false)
timer.reset(string name, bool? isGlobal = false)
var elapsed = timer.value(string name, bool? isGlobal = false)

The midi namespace

var isConnected = midi.connected  
midi.sendCC(int channel, int control, int value)
var value = getCC(int channel, int control)
var previousValue = getCC_Prev(int channel, int control)

midi.sendNRPN(int channel, int control, int value)
var value = getNRPN(int channel, int control)
var previousValue = getNRPN_Prev(int channel, int control)

midi.sendPC(int channel, int program) 
var value = getPC(int channel)
var previousValue = getPC_Prev(int channel) 

midi.sendNoteOn(int channel, int key, int velocity = 127)
var value = getNoteOn(int channel, int key)
var previousValue = getNoteOn_Prev(int channel, int key) 

midi.sendNoteOff(int channel, int key, int velocity = 0)
var value = getNoteOff(int channel, int key)

midi.sendCP(int channel, int pressure)
var value = getCP(int channel)
var previousValue = getCP_Prev(int channel)

midi.sendPB(int channel, int value) 
var value = getPB(int channel)
var previousValue = getPB_Prev(int channel)

midi.sendSysEx(byte[] data)  Sysex needs some improvement, maybe by adding more arguments, such as providing text.

The io namespace

io.key(string text) 
io.launch(string path, string parameters = "")
io.requestInput(string id, string part2Label = "", string part3Label = "") 
io.saveToClipboard(string text)
io.saveFile(string action, string path, string text)
var fileContent = io.loadFile(string path, int? line = null)

The list namespace

list.set(string itemsCsv)
list.set(string[] items)
list.show(string itemsCsv = null, string focus = null)
list.hide()
list.focus(string itemOrIndex)
var item = list.current()
var item = list.selectedName()
var item = list.selectedIndex()
list.options() = listoptions...TBD