The Virtual Reality Modeling Language Specification

E. JavaScript Scripting Reference

Version 2.0, Official Draft #3, ISO/IEC 14772

July 15, 1996


*** IMPORTANT NOTE ***

This section needs to be tightened up. The name VRMLScript has been changed to JavaScript. However, the current text of this section does not describe the full JavaScript implementation or refer to any formal JavaScript documents.


VRML 2.0 does NOT require any scripting language. However, if a browser chooses to implement JavaScript, it shall adhere to the specifications in this annex. This section describes integrating JavaScript with VRML 2.0. It provides functions called when events come into the Script, access to fields within the script, logic to operate on the fields and the ability to send events out from the script. The Reference includes the following sections:

E.1 Script Syntax and Structure

E.1.1 BNF of script syntax
E.1.2 Supported Protocol in the Script Node

E.2 EventIn Handling

Parameter passing
EventsProcessed function

E.3 Accessing Fields

Data Types
Accessing Fields and EventOuts of the Script
Accessing Fields and EventOuts of Other Nodes
Sending EventOuts

E.4 Statements

Conditional Statements
Looping Statements
Expression Statements
Return Statement

E.5 Expressions

Assignment Expressions
Logical Expressions
Arithmetic Expressions

E.6 Built-In Objects

Browser Object
Math Object
Date Object

E.1 Script Syntax and Structure

The script syntax is based on JavaScript. Currently no BNF for JavaScript is available so the BNF for the language is presented here. Currently differences include:

E.1.1 BNF of script syntax

script :
functions
NULL

functions:
functions function
function

function:
function beginFunction ( args ) statementBlock

beginFunction:
identifier

args:
args , identifier
identifier
NULL

stmntblk:
{ statements }
{ }
statement

statements :
statements statement
statement

statement :
ifStatement
forStatement
exprStatement ;
returnStatement ;

returnStatement :
return expr
return

if :
if ( expr ) statementBlock
if ( expr ) statementBlock else statementBlock

forStatement :
for ( optionalExpr ; optionalExpr ; optionalExpr ) statementBlock

optionalExpr:
expr
NULL

expr : ( expr )
- expr
! expr
variable = expr
expr == expr
expr != expr
expr < expr
expr <= expr
expr >= expr
expr > expr
expr + expr
expr - expr
expr * expr
expr / expr
expr % expr
string
number
variable

variable :
identifier

string:
' utf8 '

number:
0{0-7}+
... ANSI C floating point number ...
0X{ 0-9 }+
0x{ 0-9 }+

identifier:
utf8Character { utf8 }*

utf8Character:
... any legal UTF8 character except 0-9 ...

utf8:
utf8Character
0-9

E.1.2 Supported Protocol in the Script Node

The url field of the Script node contains a URL referencing JavaScript code. The javascript: protocol allows the script to be placed inline as follows:

    Script { 
        url "javascript: 
                function foo() { ... }"
    }

The url field can also contain a URL to a file containing the JavaScript:

    Script { 
        url [ "http://foo.com/myScript.javascript",
              "javascript: 
                  function foo() { ... }",
    }

E.2 EventIn Handling

Events to the Script node are passed to the corresponding JavaScript function in the script:

    Script { 
           eventIn SFBool start
               url "... function start() { ... perform some operation ... }"
    }

In the above example, when the start eventIn is sent the start() function is executed.

E.2.1 Parameter passing

Each eventIn is passed a corresponding data value. In the above example this would be an SFBool type. Also, the time each eventIn was received is available as an SFTime value. These are passed as parameters to the JavaScript function:

    url "javascript:function start(value, timestamp) { ... }"

The parameters can have any name. The function can have no parameters, just the value or the value and timestamp. If the function has more than two parameters the extra parameters are not filled in. To JavaScript the value is numeric with 0 being false and 1 being true. The timestamp is a floating point value containing the number of seconds since midnight, Jan. 1, 1970.

EventsProcessed function

Some implementations of the Script node may choose to defer processing of incoming events until a later time. This could be done as an optimization to skip executing scripts that do not affect visible parts of the scene, or because rendering has taken enough time to allow several events to be delivered before execution can be done. In this case the events are processed sequentially, in timestamp order. After the last eventIn is processed, the eventsProcessed function is called. This allows lengthy operations to be performed or status checks to be made once rather than in each eventIn function. Any eventOut generated during the execution of this function have the timestamp of the last eventIn processed.

E.3 Accessing Fields

The fields, eventIns and eventOuts of a Script node are accessible from its JavaScript functions. As in all other nodes the fields are accessible only within the Script. The Script's eventIns can be routed to and its eventOuts can be routed from. Another Script node with a pointer to this node can access its eventIns and eventOuts just like any other node.

E.3.1 Data Types

All VRML data types have an equivalent object in JavaScript. All MFFields can be dereferenced into their corresponding SFField using the JavaScript indexing mechanism. If a is an MFVec3f and you perform the operation "b = a[3]" then b contains an SFVec3f which is the 3rd element of a. The scalar quantities (SFInt32, SFBool, SFFloat, ...) become numeric values, SFString becomes a JavaScript String object, and the vector quantities (SFRotation, SFVec3f, ...) allow access to their individual scalar components using the JavaScript indexing mechanism. In the above example, after the operation "c = b[1]", c would contain element 1 (the Y component) of b.

E.3.2 Accessing Fields and EventOuts of the Script

Fields defined in the Script node are available to the script by using its name. It's value can be read or written. This value is persistant across function calls. EventOuts defined in the script node can also be read. The value is the last value sent. assigning to an eventOut sends that event at the end of event execution. This implies that assigning to the eventOut multiple time during one execution of the function still only sends one event and that event is the last value assigned.

E.3.3 Accessing Fields and EventOuts of Other Nodes

The script can access any exposedField, eventIn or eventOut of any node to which it has a pointer:

    DEF SomeNode Transform { }
    Script {
        field SFNode node USE SomeNode
        eventIn SFVec3f pos
        url "... 
            function pos(value) { 
                node.set_translation = value; 
            }"
    }

This sends a set_translation eventIn to the Transform node. An eventIn on a passed node can appear only on the left side of the assignment. An eventOut in the passed node can appear only on the right side, which reads the last value sent out. Fields in the passed node cannot be accessed, but exposedFields can either send an event to the "set_..." eventIn, or read the current value of the "..._changed" eventOut. This follows the routing model of the rest of VRML.

E.3.4 Sending EventOuts

<TBD>

E.4 Statements

JavaScript statements are block scoped the same as other C-like languages. A statement can appear alone in the body of an if or for statement. A body with multiple statements, or compound statement, must be placed between '{' and '}' characters. This constitutes a new block and all variables defined in this block go out of scope at the end of the block. Statements of a compound statement much be separated by the ';' character.

Example:

if (a < b)
    c = d;      // simple statement

else {          // compound statement
    e = f;      // e is local to this block
    c = h + 1;
}               // e is no longer defined here

E.4.1 Conditional Statements

The if statement evaluates an expression, and selects one of two statements for execution. A simple if statement executes the statement following the condition if the result of the result of the expression evaluation is not 0. The if...else statement additionally executes the statement following the else clause if the result of the expression evaluation is 0.

Example

if (a < 0)  // simple if statement
    <statement>

if (b > 5)  // if...else statement
    <statement>
else
    <statement>

E.4.2 Looping Statements

The for statement contains 3 expressions which control the looping behavior, followed by a statement to which the loop is applied. It executes its first expression once before loop execution. It then evaluates its second expression before each loop and, if the expression evaluates to 0, exits the loop. It then executes the statement, followed by evaluation of the third expression. The loop then repeats, until looping is terminated, either by the second expression evaluating to 0. In typical use, the first expression initialized a loop counter, the second evaluates it, and the third increments it.

Example:

for (i = 0; i < 10; ++i)
    <statement>

E.4.3 Expression Statements

Any valid expression in JavaScript can be a statement. The 2 most common expressions are the function call and the assignment expression (see below).

E.4.4 Return Statement

The return statement does an immediate return from the function regardless of its nesting level in the block structure. If given its expression is evaluated and the result returned to the calling function.

Example:

if (a == 0) {
    d = 1;
    return 5 + d;
}

E.5 Expressions

Expressions are the basic instructions of each function. Each expression requires one or 2 values. Resultant values can be used in further expressions building up compound expressions. Precedence rules are used to order evaluation. The default rules can be overridden with the use of the '(' and ')' characters to bracket higher precedence operations. Default rules are:

<TBD>

E.5.1 Assignment Expressions

An expression of the form expression = expression assigns the result of the right-hand expression to the expression on the left-hand side. The left-hand expression must result in a variable into which a value may be stored. This includes simple identifiers, subscripting operators, and hte return value of a function call.

Examples:

a = 5;          // simple assignment
a[3] = 4;       // subscripted assignment
foo()[2] = 3;   // function returning an array

E.5.2 Logical Expressions

Logical expressions include logical and ('&&'), logical or ('||'), logical not ('!'), and the comparison operators ('<', '<=', '==', '!=', '>=', '>'). Logical not is unary, the rest are binary. Each evaluates to either 0 (false) or 1 (true). The constants true and false can also be used.

Examples:

a < 5
b > 0 && c > 1
!((a > 4) || (b < 6))

E.5.3 Arithmetic Expressions

Arithmetic expressions include and ('&'), or ('|'), exclusive or ('^'), not ('~'), negation ('-'), and the operators ('+', '-', '*', '/', '%'). Not and negation are unary, the rest are binary.

Examples:

5 + b
(c + 5) * 7
(-b / 4) % 6
(c & 0xFF) | 256

E.6 Built-In Objects

Some, but not all of the built-in objects from JavaScript are supported. In particular, none of the Window objects are supported, but the String, Date and Math objects are. Additionally, JavaScript has a Browser object which contains several VRML specific methods.

E.6.1 Browser Object

The Browser object gives access to several aspects of the VRML browser. The function of each method is as described in the "Concepts - Scripting" section. Since JavaScript directly supports all VRML field types the parameters passed and the values returned are as described there. The methods on the Browser object are:

getName() Get a string with the name of the VRML browser.
getVersion() Get a string containing the version of the VRML browser.
getCurrentSpeed() Get the floating point current rate at which the user is traveling in the scene.
getCurrentFrameRate() Get the floating point current instantaneous frame rate of the scene rendering, in frames per second.
getWorldURL() Get a string containing the URL of the currently loaded world.
loadWorld(url) Load the passed URL as the new world. This may not return.
replaceWorld(nodes) Replace the current world with the passed list of nodes.
createVRMLFromURL(url, node, event) Parse the passed URL into a VRML scene. When complete send the passed event to the passed node. The event is a string with the name of an MFNode eventIn in the passed node.
createVRMLFromString(str) Parse the passed string into a VRML scene and return a the list of root nodes from the resulting scene.
addRoute(fromNode, fromEventOut, toNode, toEventIn) Add a route from the passed eventOut to the passed eventIn.
deleteRoute(fromNode, fromEventOut, toNode, toEventIn) Remove the route between the passed eventOut and passed eventIn, if one exists.

E.6.2 Math Object

The math object is taken from JavaScript. It consists of the keyword math dereferenced with the functions supported by the package. Supported functions are:

<TBD>

Examples:

a = math.sin(0.78);
dist = math.sqrt(a*a + b*b);

E.6.3 Date Object

The date object is taken from JavaScript. It consists of the keyword date dereferenced with the functions supported by the package. Supported functions are:

<TBD>

 Contact rikk@best.com , cmarrin@sgi.com, or gavin@acm.org with questions or comments.
This URL: http://vrml.sgi.com/moving-worlds/spec/part1/javascript.html.