Introduction
Scripts in iCL Designer provide a variety of ways to make content interdependent and interlinked.
You are able to change the visibility, perform calculations, or extend questions and answers.
At the same time, scripts are very approachable, as you do not need to have programming skills to use them. This is achieved by using blockly, which is an editor that allows you to define scripts similar to assembling a puzzle.
When to use scriptsβ
There is a broad variety of use cases for blockly scripts. They are often used to create dynamic answers or to change the visibility of specific workbook areas based on input.
Another area for scripts are Content Items, which can receive data from previous inspections as well as update existing data or create new data sets.
Scripts are also used to limit, filter or sort data or lists, doing calculations or providing date and time values to work with.
Blockly script UIβ
All scripts will be created in a seperate window - the blockly editor. By default it is located at the bottom of the user interface.
You are able to access the blockly-editor in the properties window at every puzzle icon by clicking on it.
Now, a script can be created
- using any of the provided blocksThe fallback content to display on prerendering
- or using the value of another field, by dragging its Link handle (the puzzle-piece that appears on the outer edge of a question or answer node when you hover it) and dropping it into the blockly editorThe fallback content to display on prerendering
When you hover a question or answer node in the mindmap, a Link handle (a puzzle-piece) appears on the node's outer edge. Drag this handle into the blockly editor to insert a reference to that field's value. A plain click on the handle only shows a short hint on how to use it β it does not insert anything.
The difference is the following: If you use only a constant value in your script like in the first case (1), this script will be evaluated once (when the field it belongs to is created). Thereafter it will not change anymore. Therefore, the first script creates a default value, that the user can later overwrite.
If you, however, use the value of any other field in your script, like in case (2), your script now depends on that field. That means, that whenever those other fields change (e.g. because the user enters some other value), your script will be re-evaluated.
When creating a new script, the editor will have a red border, which indicates, that the script is missing something or has unconnected elements in the script area. This will happen often when building or changing scripts, and while having a red border, the script will not work and, thus, cannot be saved.
You can close the editor at every point by hitting the X in the top right of the window. When there is still a script error while closing the blockly window, a prompt will reassure, if you want to close the window and lose current unsaved changes of this script.
Blockly window dockingβ
You can change the position of the blockly editor by performing a drag&drop action on its colored headline. Several arrows appear in the corners and in the middle, which you can address via hovering the window over that arrow. The window will be positioned in the indicated grey area.
The new window position will not be saved after closing the workbook.
Scriptable propertiesβ
You can find the blockly editor at the following locations:
| Property | Usable at | Allowed Output | Purpose |
|---|---|---|---|
| Title | Workbook / Checklist / Chapter / Headline / Field | Text, Number | Changes the title of that node. |
| Mandatory | Field | Boolean | If the output is true, this field becomes mandatory. False will disable its mandatory status. |
| Exists | Chapter / Headline / Field | Boolean | If the output is true, the field will exist in the iCL Filler. If it doesn't exist, all related scripts and follow-up-fields will be treated as not existent. |
| Hidden | Chapter / Headline Field | Boolean | If the output is true, you are not able to see this field, but the iCL Filler can still access its data and values. |
| Readonly | Field | Boolean | If the output is true, the field is in an readonly state, where no changes can be made. Be careful when mixing mandatory with readonly parameters. |
| Scripted Answer | Field | Text Number Date Time | This script automatically sets the answer of the field with or without a condition/relation. |
| Minimum/Maximum | Chapter / Headline | Number | Modifies the minimum and maximum occurrence of that node. |
| Query | Chapter / Headline / Field (Databound) | - | Creates a connection between two content types based on a matching entry. |
| Order by | Chapter / Headline | - | Modifies the order of this repeated section. |
| Filter | ContentType / Field (Content Item) | - | Sets a filter for the loaded content items of that field to restrict/limit/filter/ data. |
| Mappings | Field (Content Item) | depends on mapping data type | A Mapping stores data to add/modify Content Item data for Content Types. |
| List | Field (Databound) | List | Provides answers based on a list of data |
To get an understanding of when and why such scripts are evaluated, see here.
Available blocksβ
Text blocksβ
| Block | Function | Allowed Types | |
|---|---|---|---|
| Allows translatable and non-translatable input. | Text, numbers, special characters | ||
| Connects several display elements together. | All | ||
| Allows to compare two text for equality or inequality (if they are the same or not). | Text | ||
| Allows to insert a special character (e.g. new line) | - | ||
| Returns a copy of the text with the whites paces removed from one or both ends. | Text | ||
| Displays the attached text in upper/lower case or title case. | Text | ||
| Returns the position of the first/last occurrence of the first text in the second text. Returns 0 if text is not found. | Text | ||
| Returns the letter at the specified position. #1 is the first item. | First input: text, Second input: number | ||
| Returns a specified portion of the text. | First input: text, Second and third inputs: number | ||
| URL-encodes the given text so it is safe to use in a URL or query string (e.g. a space becomes %20). | Text | ||
| URL-decodes the given text, resolving percent-escapes back to their original characters (e.g. %20 becomes a space). | Text | ||
Creates a new unique id, for example 0198f2c1-4a3b-7d2e-9f10-3c5a7b9e2d4f. It is a UUIDv7, so the first part is a timestamp and newer ids sort after older ones. Note that it returns a different value every time the script is evaluated, so it is not a stable identifier for a field. | - |
Math blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Enables the entry of numbers. | Number | |
| Creates a number from text. | Text | |
| Enables mathematical calculations - sum, minimum- maximum value, average. | Number, number fields | |
| Compares two numbers with each other. | Number | |
| Enables basic mathematical calculations. | Number, number fields | |
| Enables rounding - commercial, up, down. | Number, number fields | |
| Enables rounding with defined decimal places - commercial, up, down. | Number, number fields | |
| Calculates the root of a number. | Number | |
| Calculates the remainder of a division. | Number | |
| Returns a random number between -2,147,483,648 and 2,147,483,648. | ||
| Returns a random number between the given minimum and maximum values. The lower limit is inclusive, while the upper limit is exclusive. This means for example: If you specify a minimum of 0 and a maximum of 3, possible values are: 0,1,2 (so 3 is not part of the result set). | Number, number fields | |
| Returns an array with the given amount of unique/not unique random numbers between the given minimum and maximum values. The lower limit is inclusive, while the upper limit is exclusive. This means for example: If you specify a minimum of 2 and a maximum of 5, possible values are: 2,3,4 (so 5 is not part of the result set). | Number, number fields |
Date & time blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Returns the current date of the device. | Datetime | |
| A date field composed of the individual date components. | Number | |
| Adds a specified number of days/months/years to a date. | First input: number, Second input: date | |
| Returns either the year, month, day or weekday as a number. | Date | |
| Returns the calendar week of a date as a number. The dropdown switches between the European (ISO 8601, weeks start Monday, week 1 contains the first Thursday) and American (weeks start Sunday, week 1 contains January 1st) calculation. | Date | |
| Compares two date fields and returns a boolean value. | Date | |
| Returns given text as formatted date. | Text | |
| Returns given text as custom formatted date. | First input: Text, Second input: Text | |
| Calculates the duration between two points in time. | Timepicker |
When you have to use a text field as input for a date block/field, the text will be interpreted as a valid date in case it uses one of following date formats:
MM/dd/yyyy HH:mm:ss
MM/dd/yyyy HH:mm
MM/dd/yyyy
yyyy-MM-ddTHH:mm:ssZ
The last format is an international standard called "ISO 8601" which is also used in REST interface of iCL Portal.
Logic blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| A branch operator to cover different cases logically. | First input: boolean, Second and third inputs: all | |
| A branch operator to cover different cases logically. Allows to add additional else-if branches. | First input: boolean, Second and third inputs: all | |
| A logical truth operator for true and false. | - | |
| Enables mathematical comparisons. | Boolean | |
| Negates a following logical statement. | Boolean | |
Returns the value null. | - | |
| If the result of the first block is an error, returns the second blocks value, otherwise the result of the first block | any |
Lists blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Filters in a list according to certain criteria. | List | |
| Creates a list with 1 entry, can be extended. | All | |
| Creates a list with 3 entries, can be reduced/extended. | All | |
| Filters the given list using the provided logical block, where the logical block is used for each and every element of the list. | First input: list, Second input: boolean | |
| Returns the length of a list. | List | |
| Takes a list or single value. If it is a single value, wraps it in a list. If it is a list that contains lists, it removes those lists and, instead, moves their items into the main list. | List | |
| If used with text from list, it takes a list and joins it together with the specified delimiter. If used with list from text, it takes a text and splits it using the specified delimiter. | List | |
| Reverses the elements of the given list. | List | |
| Returns distinct elements of the given list. | List | |
| Sorts the elements of the given list ascending/descending. | List | |
| Sorts the elements of the given list ascending/descending by a specified expression. | First input: list, Second input: text, number, boolean, date | |
| Checks whether any/every element in the list fulfills the given boolean expression. Returns a boolean. | First input: list, Second input: boolean | |
| Creates a new list with the results of executing the provided expression on every element in the given list. | First input: list, Second input: all |
Regex blocksβ
| Block | Function | Allowed Types | |
|---|---|---|---|
| Checks whether the text matches the regular expression and returns true or false. | First input: text, Second input: pattern | ||
| Replaces every match of the regular expression with the replacement text. The replacement may refer to capture groups with $1, $2, ... | First input: text, Second input: pattern, Third input: text | ||
| Returns the first match of the regular expression, or nothing if the text does not match. | First input: text, Second input: pattern | ||
| Returns a list with every match of the regular expression. The drop-down selects whether the whole match or one of the capture groups is collected. Returns nothing (not an empty list) if there is no match, so check the result before passing it to a list block. | First input: text, Second input: pattern | ||
| A ready-made regular expression for a common format, selected from a drop-down: natural number, decimal number (with optional or required decimals), e-mail address, IBAN, URL or GUID. | - | ||
| Escapes all special characters of the given text so it is matched literally when used inside a regular expression. Use this whenever a pattern is built from an answer or other free text. | Text |
Working with regular expressionsβ
A regular expression (short: regex) is a small pattern language for describing what a text has to look like β for example "digits only", or "two letters followed by two digits".
Scripts are executed by the iCL Filler script engine, which uses the JavaScript (ECMAScript) regular expression syntax β not the .NET syntax. When you build and test a pattern, use a tester set to the ECMAScript/JavaScript flavour, such as regex101.com (select ECMAScript (JavaScript) on the left) or RegExr.
Avoid .NET-oriented testers such as regexstorm.net: features like named groups, lookbehind or
\p{...} character classes are written differently there, and a pattern that works on such a site
may fail or behave differently in iCL.
A good introduction and a complete syntax reference: MDN β Regular expressions guide and the MDN regex cheat sheet.
The ready-made patternsβ
The pattern block has two drop-downs and reads like a sentence, for example Natural number β anywhere in the text.
The first drop-down picks the format. You can always use a plain text block instead if you need a different expression.
The second one decides how much of the text has to match, and it matters a great deal:
- only if it is the entire text β the expression is anchored (
^β¦$), so the complete text has to have that format. Use this together with matches pattern to check an answer. - anywhere in the text β the expression is left unanchored, so it matches at any position. Use this together with first match of / all matches of to pull a value out of a longer text.
Hover over the block to see the exact regular expression the current selection produces.
An anchored pattern never finds anything inside a longer text: ^\d+$ matches the text 42, but
finds nothing in Order 42. If first match of or all matches of returns nothing, switch the
second drop-down to anywhere in the text.
| Entry | Expression (anywhere in the text) | Matches |
|---|---|---|
| Natural number | \d+ | Digits only, no sign and no decimals: 0, 42 |
| Decimal number (optional) | [+-]?\d+([.,]\d+)? | A number with an optional sign and optional decimals β whole numbers are also accepted: 42, -3.5, 3,5 |
| Decimal number (required) | [+-]?\d+[.,]\d+ | The same, but the decimals are required: -3.5, 3,5 β 42 does not match |
| E-mail address | [^@\s]+@[^@\s]+\.[^@\s]+ | A pragmatic e-mail check: something, an @, a domain with a dot, no spaces |
| IBAN | [A-Z]{2}\d{2}[A-Z0-9]{11,30} | Two uppercase country letters, two check digits, then 11β30 letters or digits β written without spaces |
| URL | (https?\|ftp):\/\/[^\s/$.?#].[^\s]* | An http, https or ftp address |
| GUID | [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12} | A GUID in the usual 8-4-4-4-12 notation β this also matches the ids created by the new unique id block |
In only if it is the entire text mode the same entries are used wrapped in ^β¦$ β so "Natural number" becomes
^\d+$.
These patterns are deliberately kept simple and readable rather than covering every edge case of the respective standard. If you need a stricter check, use your own pattern.
Example: pulling values out of a longer textβ
With first match of, the anywhere in the text scope and this text β¦
Invoice 2024-17: contact office@opti-q.com or visit https://opti-q.com/support .
Transfer 1250,75 EUR to AT611904300234573201 , ref 42, ticket 0198f2c1-4a3b-7d2e-9f10-3c5a7b9e2d4f
β¦ each format returns the first thing it finds:
| Pattern entry | Result |
|---|---|
| Natural number | 2024 |
| Decimal number (required) | 1250,75 |
| E-mail address | office@opti-q.com |
| IBAN | AT611904300234573201 |
| URL | https://opti-q.com/support |
| GUID | 0198f2c1-4a3b-7d2e-9f10-3c5a7b9e2d4f |
Swap first match of for all matches of to get every hit as a list instead of just the first. In only if it is the entire text mode every one of these would return nothing, because the text as a whole is neither a number nor an e-mail address.
Using capture groupsβ
Parentheses in a pattern create a capture group: a part of the match you want to read out separately. Groups are numbered from left to right by their opening parenthesis, starting at 1.
Take the text Order A-42 and B-7 with the pattern ([A-Z])-(\d+):
| Drop-down setting | Result |
|---|---|
| all matches | A-42, B-7 β the complete match each time |
| capture group 1 | A, B β only the part matched by ([A-Z]) |
| capture group 2 | 42, 7 β only the part matched by (\d+) |
So instead of getting the whole match and cutting it apart afterwards, you pick the piece you are interested in directly. If the selected group does not exist in a particular match, that entry of the list is empty.
Why you need "escape for use in a pattern"β
Characters like ., +, (, ), * or ? have a special meaning inside a regular expression.
That is fine while you write the pattern, but it becomes a problem as soon as the pattern is built
from something a user typed β an answer, a content item value, a field name.
The escape for use in a pattern block puts a backslash in front of every such character, so the text is searched for literally:
| Text used as a pattern | Used directly | Wrapped in escape for use in a pattern |
|---|---|---|
a.c | The . means "any character", so it also matches abc and axc β probably not what you wanted | Becomes a\.c and matches only the literal text a.c |
3.5 (mm) | The ( ) are read as a group, so 3.5 (mm) itself is not found | Becomes 3\.5 \(mm\) and is found |
1+1 | + means "one or more of the previous character", so this matches 11 β but not the text 1+1 you were actually looking for | Becomes 1\+1 and matches 1+1 |
Preis (netto | The unclosed ( makes the whole pattern invalid and the script fails with an error | Becomes Preis \(netto and is found |
Rule of thumb: if any part of your pattern comes from an answer or another free-text value, wrap that part in escape for use in a pattern. If you typed the whole pattern yourself, you do not need it.
Checklist field blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Compares two values with each other. Mind that you cannot compare with numbers here. Use a text-block in the 'create list with'-part for comparison or switch to math-blocks if comparing two numbers. | Text | |
| Checks if a field has a value or no value at all. | Fields | |
| Allows to resolve all input fields that match the specified pattern. You can resolve all input fields with it or you can get only specific type of input fields e.g. all text fields, all number fields, etc. | - | |
| Allows to resolve all option fields that match the specified pattern. You can resolve all option fields with it or you can get only specific type of option fields e.g. all checkbox fields, all dropdown fields, etc. | - |
User properties blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Shows the ID of the logged in user. | - | |
| Shows the username of the logged in user. | - | |
| Shows the first name of the logged in user. | - | |
| Shows the last name of the logged in user. | - | |
| Shows the e-mail address of the logged in user. | - |
Formatting blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Returns the given date as formatted text. Provides a dropdown list to select the intended format. | Date | |
| Returns the given time as formatted text. Provides a dropdown list to select the intended format. | Time | |
| Returns the given number as formatted text. Provides a dropdown list to select the intended format. | Number | |
| Returns the given object (can be a number/date/etc.) as formatted text using the specified format string. You can read more about format string in Microsoft Docs or in our documentation. | All |
Task blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Allows to get certain properties of the task of the inspection. Properties: external ID, title, inspector, inspected object(s), description, start date and due date. | - | |
| Returns true if the inspection was started with a task, false otherwise. | - |
Inspection blocksβ
| Block | Function | Allowed Types |
|---|---|---|
| Provides information about the current inspection. Properties: language. | - |