# RoomleScript Overview

# What is RoomleScript?

RoomleScript is our proprietary language, which is most similar to JavaScript. In the Component object, which you define with the Json component files, the values of the objects' attributes are either constants or RoomleScripts.

To tell, which attribute value is available in RoomleScript, please refer to Roomle Configuration Catalog Structure Format, chapter "JSON Objects". Wherever there is Script<type>, you can use either a constant of that type, or a RoomleScript, which returns the value of that type.

# Roomle Script Basic overview

# Internal Variables

Variables do not have a keyword to declare them. Once a variable is first assigned, it is initialized and declared as well. The data type is assigned automatically, just like in JavaScript. There are following data types available:

  • Boolean
  • Integer
  • Float
  • String (enclosed in 'single quotes')
  • Array of Float

Example:

booleanVariable = true; /* or false, also 1 and 0 work implicitly */
anInteger = 64;
aDecimalOrFloatValue = 3.141;
stringText = 'hello';
anArray = [131.05, 314, -16.64];

It is the responsibility of the scripter, that all used variables are initialized by the time they are used. This is especially important to test during reloading docked configurations, because variables can be initialized from other components as well. In order to be sure that all variables are intialized, we've defined as best practice to use following construct at the beginning of the onUpdate script:

if (ifnull(inited, false) == false) {
    inited = true;
    variablesOrConstants = 564;
    you = false;
    wantTo = 33423.5423;
    initialize = 'empty';
}

The ifnull function returns the false fallback value if the inited variable is not initialized (is null), which it is at the beginning of the component instance life. Setting inited = true makes sure that the inside of the block won't run more than once.

# Comments

There 3 ways commenting inside RoomleScripts:

// single line comment
# equal single line comment
/* multi
line
comment
*/

Attention: using single line comments breaks "Load into Kernel" functionality, therefore it is recommended to use the /* multi line */ style comments. Even the Roomle Component Formatter will convert // style comments to:

/* single line comment */ 

# Operators and Expressions

Precedence Operator Description
1 -a, +a Unary plus and minus
2 !a Logical NOT
3 a++ Suffix/postfix increment
4 a*b, a/b Multiplication and division
5 a+b, a-b, a\|b Addition, subtraction and concatenation
6 a>=b, a<=b, a>b, a<b Relational operators < and ≤ and > and ≥ respectively
7 a==b, a!=b Equality operators = and ≠ respectively
8 a&&b Logical AND
9 a\|\|b Logical OR
10 a?b:c Ternary conditional

The Roomle script does not have a % (modulo) operator, but has the fmod(dividend: float, divisor: float) : float function.

To write an expression inside RoomleScript:

totalHeight = ((hasPlinth) * plinthHeight) + countOfShelves * 400 + (countOfShelves - 1) * woodThickness;

Note: Prior to January 2022, it was recommended to use extensive brackets with the negation operator, making the parsing more robust. This is not necessary anymore.

Example of expressions in the Component definition:

{
    ...
    "parameters": [
        ...
        {
            "key": "width",
            "type": "Decimal",
            "defaultValue": 800,
            "valueObjects": [
                {
                    "value": "800",
                    "condition": "depth < 600"
                },
                {
                    "value": "1200",
                    "condition": true
                },
                {
                    "value": "1600",
                    "condition": "depth == 800;"
                }
            ],
            "enabled": true,
            "visible": "height > 400",
            "visibleInPartList": "height > 400"
        },
        ...
    ],
    "articleNr": "'500.' | widthCode | '.' | materialCode",
    ...
}

# Branching

In RoomleScript, if-else blocks can be nested to mimic if-else-if blocks as in many other popular languages:

if (booleanExpression) {
    ...
} else if (anotherBooleanExpression) {
    ...
} else {
    ...
}

# Return

The return statement terminates the script immediately. This statement can be invoked anywhere within a script. All subsequent commands are skipped and the script ends.

...
    
if (booleanExpression) {
    return;
}
...

# Loops

For-loop is available within RoomleScript, syntax is identical with JavaScript:

for (i = 0; i < loopsCount; i++) {
    ...
}

break and continue statements are available

The break statement terminates a for-loop. If a break statement is inside nested loops, the innermost loop containing the statement is terminated. A break statement that is not inside a loop is ignored and has no effect.

The continue statement ends the current step, triggers the increment and if for-loop condition is still true, enters the next step.

for (i = 0; i < loopsCount; i++) { 
    ...
    if (goNext) {
        continue;
    }
    if (quitTheCycle) {
        break;
    }
    ...
}

# Scope and Context of Variables

Default scope of any internal variable or parameter is the whole current component. Some scripts have read/write access to the component data, some scripts only have a read access. To find out, which scripts can access which context, you can refer to the Script Access Rights chapter.

RoomleScript also provides contexts in several occasions. This is an overview of them:

prefix availability purpose
nothing current component
_. current script Use this for local helper variables. Variable will be set to null after current script finishes.
parameter. parameter.onValueChange script provides userTriggeredChange : boolean getter
other. all scripts in connection (onUpdate, condition, assigmentScripts) access to all parameters and internal vars of the component on the other side of the connection
self. all scripts in connection (onUpdate, condition, assigmentScripts) actually is redundant, but helps to understand the code unambigously and should be used whenever other. is used
connection. all scripts in connection (onUpdate, condition, assigmentScripts) provides index, position, isPreview
There are two connections in one docking/sibling point dock pair, one on each side. You can store and read variables relevant to the connection at the self side
other_connection. all scripts in connection (onUpdate, condition, assigmentScripts) Same as before, but targets the other side of the connection.

Note: For parameter.userTriggeredChange, see the Parameters chapter. For other, self, and connection contexts, see the Docking Logic Basics - Assignments and Docking Logic Basics - Ranges chapters.

# Available functions

For reference on available functions, see Roomle Configuration Catalog Structure Format, chapter RoomleScript.

# Internal values

Some scripts have internal values defined. See the table and their purpose:

identifier context purpose
articleNr articleNr script sets the current component's article number
number subComponent.numberInPartList sets the subcomponent's count of entries in the part list
connection.isPreview docking condition read-only, returns true if in docking preview state
connection.index docking range returns index of the docking point in the range array
connection.position docking range, line returns Vector3f position of the child relative to the parent's coordinate system
condition all conditions sets the result of the condition
label all label scripts Has value from the labels map in current language. Overwrite with value based on the script
language all label scripts Getter for the language ISO code of the current locale the configurator runs with.
parameter.userTriggeredChange parameter.onValueChange script getter to determine if user has just interacted with this parameter

# Custom functions

At the moment, you can not define functions. However, for repetitive code between components, in some cases you can use a subcomponent with assignment and superseding as a workaround. For more information, see the SubComponents chapter

# Arrays

RoomleScript supports arrays of floats. To initialize the array:

array = [0, 0, 0, 0];
emptyArray = [];
for (_.i = 0; _.i < arrayLengthNeeded; _.i++) {
    pushback(emptyArray, 0);
}
arrayFromVars = [someVar, someParameter, 3, 4];

To access values in the array, get(array, index) and set(array, index, value) functions are used (VS Code snippets: getValueFromArray, setValueInArray)

value = get(array, 3);
set(array, 3, 32.146);

Note: RoomleSript does not (yet) support accessing values using index in square brackets, like array[5].

# Calling Order of the Scripts

In order to understand the order in which the configurator scripts are called, please refer to the Update logic chapter.