Table of Contents

JavaScript language reference

This chapter describes the JavaScript language of TMS Scripter, for the people who write scripts. It uses the syntax of JavaScript: statements, operators, functions, comments and literals look and work like in JavaScript. The values and the objects, however, are the ones of the TMS Scripter engine, shared with the Pascal and Basic languages, so there are important differences from the JavaScript of web browsers: they are listed in Differences from standard JavaScript. Each language of TMS Scripter has its own reference chapter with the same structure: see also the Pascal and Basic references.

The objects, functions and libraries available to a script depend on the application that runs it. This chapter describes the language and the routines that are always available; the documentation of your application tells which additional objects and libraries it provides.

A first example

import Classes;

function greeting(name) {
  return "Hello, " + name + "!";
}

var names = new TStringList();
try {
  names.Add("Anna");
  names.Add("Bruno");
  for (var i = 0; i < names.Count; i++)
    ShowMessage(greeting(names[i]));
} finally {
  names.Free();
}

Script structure

A script is made of:

  1. import statements, at the beginning of the script, listing the libraries and other scripts the script uses (see Using libraries and other scripts).
  2. Functions and statements, in any order.

The statements outside functions are the main block: they run in order when the script is executed. Functions can be called before they are declared.

sayHello();

function sayHello() {
  ShowMessage("Hello");
}

Statements end with a semicolon or with the end of the line: semicolons are optional. A script can also contain only functions; then the application calls them directly, for example as event handlers.

Script result

A return statement outside functions ends the script and returns its value to the application. Applications use this to evaluate expressions and formulas. If the script does not return a value, the result is Null.

return (10 + 6) / 4;

Lexical elements

Case sensitivity

Unlike standard JavaScript, the TMS Scripter JavaScript is not case-sensitive: if, If and IF are the same keyword, and myVar, MyVar and MYVAR are the same identifier. This is needed to work with the objects of the application, whose names come from Delphi, which is not case-sensitive. String values are case-sensitive.

Comments

// a line comment, up to the end of the line
/* a block comment,
   which can span several lines */

Block comments do not nest. Comment markers inside strings are ordinary characters. Comments starting with //$ or /*$ can be directives (see Declaring forms in script).

Identifiers and reserved words

Identifiers (names of variables, constants, functions and parameters) start with a letter, an underscore or a dollar sign, followed by letters, digits, underscores or dollar signs. Letters are the ASCII letters a to z and A to Z.

total  _temp  item2  $value  first_name     // valid
2items  first-name  my name                 // invalid

These reserved words cannot be used as identifiers: await, break, case, catch, class, const, continue, debugger, default, do, else, enum, export, extends, finally, for, function, if, import, in, instanceof, let, new, return, super, switch, this, throw, try, typeof, var, void, while, with, yield. They are allowed as member names after a dot (obj.default). An identifier can start with a reserved word, like format or newItem.

Numbers

42          // integer
-7          // negative integer
3.25        // floating-point number
1.5e-2      // floating-point number with exponent (0.015)
0xFF        // hexadecimal integer (255)

A number without fraction and exponent is an integer; integers are 64-bit values. A floating-point number needs digits on both sides of the dot: write 0.5, not .5.

Strings

Strings are enclosed in double or single quotes. The other quote is an ordinary character inside a string. A backslash starts an escape sequence:

Escape Character
\", \', \\ Double quote, single quote, backslash
\n, \r, \t Line feed, carriage return, tab
\b, \f, \v, \0 Backspace, form feed, vertical tab, null character
\xHH Character with the hexadecimal code HH
\uHHHH Unicode character with the hexadecimal code HHHH

A backslash at the end of a line continues the string on the next line. A backslash before any other character gives that character.

s = "Hello, world";
s = 'Say "hi"';
s = "It's here";
s = "First line\r\nSecond line";
s = "Euro: €";

Special values

Value Meaning
true, false Boolean values.
null No object. Object variables that refer to no object are null.
undefined The unassigned value (converted to an empty string or zero).
Note

In TMS Scripter, null represents "no object" and is the integer 0, so 0 == null is true. A variable declared without an initial value is not null but the null value of the engine, tested with VarIsNull. To test if a variable refers to an object, use Assigned(obj), which works in both cases.

Variables

Declaring variables

Variables are declared with var or let, optionally with an initial value. Several variables can be declared in one statement.

var count;
var total = 0, name = "Anna";
let items = [1, 2, 3];

var and let are the same: both declare a variable of the whole function (or of the whole main block), not of the block where they are declared, and the variable exists in the whole function, also before the declaration. A declaration in a loop runs its initializer at each iteration.

Variables must be declared before they are used. Some applications relax this rule: then assigning a value to an undeclared name creates the variable.

Scope

Variables declared outside functions are global: they are visible in the main block and in every function. Variables declared inside a function are local to it, and a local variable hides a global variable with the same name in the whole function.

var counter = 0;          // global

function increment() {
  var step = 1;           // local
  counter += step;
}

increment();
increment();
ShowMessage(IntToStr(counter));   // 2

Functions cannot be declared inside other functions.

Constants

Constants are declared with const, globally or inside a function. Their value can be an expression. A constant cannot be changed.

const MAX_ITEMS = 100;
const TITLE = "Report";
const HALF_MAX = MAX_ITEMS / 2;

Values and types

Variables, parameters and function results hold values of any type, and the type of the value can change when a new value is assigned. Scripts work with these kinds of values:

  • integers (64-bit) and floating-point numbers;
  • strings;
  • Boolean values (true, false);
  • dates and times (floating-point numbers: the integer part is the date, the fraction is the time of day);
  • objects (or null);
  • arrays;
  • the null value and undefined.

Values are converted automatically when an operation needs another type, following the rules of the TMS Scripter engine, which are not the ones of standard JavaScript:

  • + between two strings concatenates them: "5" + "5" is "55".
  • + between a string and a number adds them as numbers when the string contains a number: "5" + 5 is 10.
  • A string that does not contain a number cannot be used in arithmetic: "Total: " + 5 is an error. Convert the number to a string first: "Total: " + IntToStr(5), or use VarToStr for any value.
  • Comparisons between strings and numbers compare the numbers: "10" == 10 is true.
  • Conditions (if, while, ?:, !, &&, ||) accept Boolean values and numbers (zero is false). A string in a condition is an error: write s != "" instead of s.

Use the conversion routines (IntToStr, StrToInt, FloatToStr, VarToStr and others, see Built-in routines) to convert values explicitly.

Expressions and operators

Arithmetic operators

Operator Operation Example Result
+ Addition (or string concatenation) 7 + 2 9
- Subtraction 7 - 2 5
* Multiplication 7 * 2 14
/ Division, always a floating-point result 7 / 2 3.5
% Remainder of the integer division 7 % 2 1
** Exponentiation, always a floating-point result 2 ** 3 8.0
- (unary) Negation -x
+ (unary) No operation +x

% works on integers: floating-point operands are rounded to integers first. A remainder by zero is an error. There is no integer division operator: use Trunc(a / b).

Comparison operators

Operator Meaning
==, === Equal
!=, !== Not equal
<, > Less than, greater than
<=, >= Less than or equal, greater than or equal

=== and !== are accepted, and are the same as == and !=: comparisons convert the values as described in Values and types. String comparisons are case-sensitive: "abc" == "ABC" is false. Use CompareText or UpperCase to compare strings without case sensitivity.

Logical operators

Operator Operation
! Logical not: !x is true when x is false or zero
&& Logical and
\|\| Logical or

&& and || are short-circuit operators: when the left operand decides the result, the right operand is not evaluated. Like in JavaScript, the result is the operand that decided it, not necessarily a Boolean value: 0 || 5 is 5, and 3 && 0 is 0.

if (count > 0 && total / count > 10)
  ShowMessage("Average above 10");

var size = customSize || 10;    // 10 when customSize is 0

Bitwise operators

Operator Operation
&, \|, ^ Bitwise and, or, exclusive or
~ Bitwise not
<<, >> Shift left and right
>>> Accepted, the same as >>

With Boolean operands, &, | and ^ give the integer -1 (true) or 0 (false).

Conditional operator

condition ? value1 : value2 gives value1 when the condition is true and value2 otherwise. Only the chosen value is evaluated.

var label = count == 1 ? "item" : "items";

Object operators

Operator Operation
new Creates an object: new TStringList() (see Working with objects)
instanceof Tests if an object is an instance of a class, or of a descendant class: sender instanceof TButton

Operator precedence

Operators with higher precedence are evaluated first. Operators with the same precedence are evaluated from left to right, except ** and ?:, which are evaluated from right to left. Use parentheses to change the order.

Precedence Operators
1 (highest) !, ~, unary - and +
2 **
3 *, /, %
4 +, -
5 <<, >>, >>>
6 <, >, <=, >=, instanceof
7 ==, !=, ===, !==
8 &
9 ^
10 \|
11 &&
12 \|\|
13 (lowest) ?:

Statements

Assignment

The assignment operator is =. The target can be a variable, an object property, an array item or a string character. Assignments are statements, not expressions: a = b = 0 and if (x = 1) are not allowed.

total = price * quantity;
Button1.Caption = "OK";
items[2] = "third";

Compound assignments combine an operation with the assignment: +=, -=, *=, /=, %=, **=, <<=, >>=, >>>=, &=, |= and ^=. x += 2 is the same as x = x + 2.

++ and -- increment and decrement a variable, a property or an array item by one, before or after it (i++, ++i). They are statements, not operators: they cannot be used inside an expression.

count++;
total += price;
items[i] *= 2;

Blocks

Braces group statements where a single statement is expected:

if (total > 100) {
  discount = 10;
  ShowMessage("Discount applied");
}

if statement

if (total > 100)
  discount = 10;

if (total > 100)
  discount = 10;
else
  discount = 0;

if (score >= 90)
  grade = "A";
else if (score >= 80)
  grade = "B";
else
  grade = "C";

An else belongs to the nearest if.

switch statement

switch jumps to the case whose value matches the selector, and runs the statements from there, also the ones of the next cases, until a break (fall-through, like in JavaScript). The default case runs when no value matches; it can be anywhere. The selector and the values can be of any type, including strings, and the values can be expressions.

switch (UpperCase(fruit)) {
  case "LIME":
  case "KIWI":
    color = "green";
    break;
  case "LEMON":
    color = "yellow";
    break;
  default:
    color = "unknown";
}

The selector is evaluated once.

for statement

for (var i = 0; i < 10; i++)
  sum += i;

for (var i = 10; i > 0; i -= 2)
  ShowMessage(IntToStr(i));

for (var i = 0, j = 10; i < j; i++, j--)
  swapItems(i, j);

The three parts are optional: for (;;) repeats until a break, return or error. The condition is evaluated before each iteration. for..in and for..of are not supported.

while statement

while repeats a statement while a condition is true. The condition is tested before each iteration, so the statement may not run at all.

while (i < count) {
  process(i);
  i++;
}

do..while statement

do..while repeats a statement while a condition is true. The condition is tested after each iteration, so the statement runs at least once.

do {
  k = i % j;
  i = j;
  j = k;
} while (j != 0);

break and continue

break leaves the innermost loop or switch. continue skips the rest of the current iteration of the innermost loop and goes to the next one (in a for loop, to the update part). Labels are not supported.

for (var i = 0; i < list.Count; i++) {
  if (list[i] == "")
    continue;
  if (list[i] == "END")
    break;
  process(list[i]);
}

return statement

return leaves the current function, with the value that follows it, if any. The value must start on the same line as return. In the main block, return ends the script.

function max(a, b) {
  if (a > b)
    return a;
  return b;
}

Functions

Declaring functions

Functions are declared with function, at the top level of the script (not inside other functions or blocks). Parameters have no types.

function showTotal(value) {
  ShowMessage("Total: " + FloatToStr(value));
}

function average(total, count) {
  return total / count;
}

Every function can return a value with return. If a function ends without returning a value, the result is the null value.

Parameters by value and by reference

Parameters are passed by value: the function receives a copy, and changing the parameter does not change the variable passed by the caller. Declare a parameter with var to pass it by reference: then the function changes the variable of the caller. This is a TMS Scripter extension to JavaScript, needed for event handlers that change their parameters. Only a variable can be passed to a var parameter (not an expression, an array item or a property).

function swap(var a, var b) {
  var temp = a;
  a = b;
  b = temp;
}

var x = 1, y = 2;
swap(x, y);    // now x is 2 and y is 1

Calling functions

Arguments are given in parentheses, and the number of arguments must match the declaration. A function can be called before its declaration, and functions can call themselves (recursion) and each other.

A function name without parentheses also calls the function: functions are not values that can be stored in variables or passed as arguments.

Error handling

try..catch

The statements of the try block run; if an error occurs, execution continues in the catch block. Errors raised by the script, by the functions it calls and by the application are all handled. catch has no parameter: the information about the error is available in LastExceptionMessage and the other values below.

try {
  value = StrToInt(text);
} catch {
  ShowMessage("Invalid number: " + LastExceptionMessage);
  value = 0;
}

try..finally

The statements of the finally block always run, whether an error occurred or not; the error, if any, continues after the finally block. Use it to release resources. A try statement can have both a catch and a finally block.

var list = new TStringList();
try {
  list.LoadFromFile(fileName);
  process(list);
} finally {
  list.Free();
}

Raising errors

throw raises an error. The value that follows it is the error message; it must start on the same line as throw. The script stops at that point, unless the error is handled by a try..catch statement.

if (quantity <= 0)
  throw "Quantity must be positive";

Error objects do not exist: throw new Error(...) is not supported.

Information about the last error

Inside a catch block, these values describe the error being handled:

Name Description
LastExceptionMessage The error message.
LastExceptionClassName The class name of the error (exception class).
LastExceptionLine, LastExceptionCol The script position where the error occurred.
LastExceptionHelpContext The help context number of the error.

An error that is not handled stops the script, and the application receives it with its message and position.

Arrays

Square brackets create an array from a list of values. Arrays are zero-based, and items can be of any type. Nested arrays give multi-dimensional arrays.

var primes = [2, 3, 5, 7];
var item = primes[1];               // 3
primes[0] = 1;

var matrix = [["a", "b"], ["c", "d"]];
item = matrix[1][0];                // "c"
item = matrix[1, 0];                // also "c"

Arrays have a fixed size and no methods or properties (length, push and so on):

  • Length(a) is the number of items; Low(a) and High(a) are the first and last indexes.
  • VarArrayCreate([0, 9], 12) creates an array of 10 empty items.
  • Accessing an index out of the bounds is an error.

Strings

Strings have no methods or properties (length, toUpperCase and so on): use the string routines (Length, UpperCase, Copy, Pos and others, see Built-in routines). Strings can be indexed to read or change a single character; unlike standard JavaScript, the first character has index 1:

var s = "Hello";
var c = s[1];        // "H"
s[1] = "J";          // s is "Jello"
var n = Length(s);   // 5

Working with objects

The application makes objects available to scripts: the controls of a form, data objects, or any other object of the application. Scripts can also create objects of the classes the application provides, like TStringList. Objects are used with their Delphi names and members.

Properties and methods

Use a dot to access properties and methods. Methods without parameters can be called with or without parentheses.

Edit1.Text = "Hello";
var width = Panel1.Width - 10;
Memo1.Lines.Add("New line");
Memo1.Lines.Clear();

Creating and destroying objects

new creates an object: it calls the Create method of the class, with the arguments given in parentheses. Calling Create directly also works. Free destroys the object. To check if a variable refers to an object, use Assigned(obj).

var list = new TStringList();
var label = TLabel.Create(Form1);
list.Add("one");
list.Free();

Object literals ({ name: "Anna" }) and classes (class) are not supported.

Default and indexed properties

Indexed properties take the index in square brackets. When a class has a default indexed property, the property name can be omitted:

var s = Memo1.Lines.Strings[0];
s = Memo1.Lines[0];            // the same: Strings is the default property

Enumerations and sets

Enumeration values are used by name, like constants. To set a set property, assign an array of values. Where a routine expects a set, convert the array with SetOf:

Panel1.Align = alClient;
Label1.Font.Style = [fsBold, fsItalic];
var answer = MessageDlg("Save changes?", mtConfirmation, SetOf([mbYes, mbNo]), 0);

Events

To handle an event of an object, write a function with the parameters of the event, and assign the function name, as a string, to the event property:

function Button1Click(Sender) {
  ShowMessage("Clicked");
}

function FormCloseQuery(Sender, var CanClose) {
  CanClose = Memo1.Lines.Count > 0;
}

Button1.OnClick = "Button1Click";
Form1.OnCloseQuery = "FormCloseQuery";

Declare with var the event parameters that the handler changes, like CanClose above.

Using libraries and other scripts

import statements, at the beginning of the script, give access to libraries: units of the application (for example Classes, SysUtils, Dialogs or Forms, when the application provides them) and other scripts. JavaScript module syntax (import ... from, export) is not supported.

import Classes, Dialogs, MyLibrary;

The names of a library can be used directly, or qualified with the library name: MyLibrary.calculate(10).

When a script uses another script, the global variables, constants and functions of the other script are available to it:

// Script "MathLib"
var precision;

function roundTo2(value) {
  return Round(value * 100) / 100;
}
// Main script
import MathLib;

precision = 2;
ShowMessage(FloatToStr(roundTo2(3.14159)));

The main block of a used script does not run when another script uses it, so its global variables start without a value, even when their declarations have initial values. Initialize them in a function of the library that the other scripts call.

The application defines where used scripts are found (another script it manages, or a file). A script used as a library can be written in any of the TMS Scripter languages.

Declaring forms in script

A script can declare a form, designed in a form file (.dfm, or .sfm in the TMS Scripter IDE), and be used as a class to create and show instances of that form. The //$FORM directive at the beginning of the script gives the form class name and the form file:

//$FORM TCustomerForm, customerform.dfm

var customerId;      // a property of each form instance

function btSaveClick(Sender) {
  ShowMessage("Saving customer " + IntToStr(customerId) + ": " + edName.Text);
  Close();
}

function Init(id) {
  customerId = id;
  btSave.OnClick = "btSaveClick";
}

The directive can also be written as a block comment: /*$FORM TCustomerForm, customerform.dfm*/.

  • The components of the form file are available by name (edName, btSave), and so are the properties and methods of the form itself (Caption, Close).
  • The global variables of the form script are properties of the form, and its functions are methods. Each instance of the form has its own variable values.
  • The form file must not contain event handlers: assign the events in script, as above.

Another script uses the form script and creates instances of the form class:

import Forms, CustomerFormScript;

var form = new TCustomerForm(Application);
form.Init(42);
form.edName.Text = "John";
form.ShowModal();
form.Free();

Declaring classes in script

A script can declare a class with the //$CLASS directive (or /*$CLASS ...*/). The global variables of the script are the properties of the class, its functions are the methods, and its main block runs when an instance is created (use it to initialize the properties).

//$CLASS TCounter

var count, step;

function increment() {
  count += step;
}

count = 0;
step = 1;
import CounterScript;

var counter = new TCounter();
counter.step = 5;
counter.increment();
ShowMessage(IntToStr(counter.count));   // 5
counter.Free();

Script classes cannot inherit from other classes. Calling Free is optional: objects of script classes that are not freed are destroyed automatically later.

Calling DLL functions

Scripts can declare and call functions of DLLs (Windows only, when the application allows it). The declaration gives the parameter and result types, and the name of the DLL:

function MessageBox(hwnd: pointer, text: string, caption: string, flags: integer): integer
  external "user32.dll" name "MessageBoxW";

MessageBox(null, "Hello from a DLL", "Script", 0);

See Calling DLL functions for the complete syntax.

Built-in routines

The routines available to every script (strings, conversions, math, date and time, dialogs, files) are listed in Built-in routines.

Differences from standard JavaScript

The TMS Scripter JavaScript uses the syntax of JavaScript on the TMS Scripter engine. These are the differences from the JavaScript of web browsers and Node.js:

Values and operators

  • Names are not case-sensitive (keywords, variables, functions, objects and members).
  • + with a string and a number adds numbers when the string contains a number ("5" + 5 is 10) and is an error otherwise: convert numbers with IntToStr, FloatToStr or VarToStr to concatenate them.
  • A number without fraction is an integer, and / always gives a floating-point number. % works on integers.
  • Conditions accept Boolean values and numbers only, there is no "truthiness" of strings, objects or arrays.
  • null is "no object" and equals 0; undefined is the unassigned value.
  • === and !== are the same as == and !=. >>> is the same as >>.
  • String characters are indexed from 1. Strings and arrays have no methods or properties: use the built-in routines.

Statements

  • Assignments, ++ and -- are statements, not expressions.
  • var, let and const declare variables of the whole function, not of the block.
  • catch has no parameter (catch (e) is not supported); use LastExceptionMessage. throw takes the error message; there are no Error objects.
  • for..in, for..of, labels, with, typeof, delete (as an operator), void and in are not supported.

Functions and objects

  • Functions are declared at the top level only. Nested functions, function expressions, arrow functions, closures, default parameter values, rest parameters and arguments are not supported, and functions are not values: a function name without parentheses calls it.
  • Parameters can be passed by reference with var.
  • Objects are the ones of the application, created with new (which calls Create) and destroyed with Free. Object literals, classes, prototypes, this, getters and setters are not supported; forms and classes can be declared in script with directives.
  • import loads libraries; module syntax (import ... from, export) is not supported.
  • There is no standard library (Math, JSON, Date, String methods, regular expressions, template literals, promises, async): use the built-in routines and the libraries provided by the application.