This appendix describes the use of JavaScript with the Script node. See "Concepts - Scripting" for a general overview of scripting in VRML, and see "Nodes Reference - Script" for a description of the Script node.
Netscape JavaScript was created by Netscape Communications Corporation (http://home.netscape.com). JavaScript is a programmable API that allows cross-platform scripting of events, objects, and actions. A full description of JavaScript can be found at: http://home.netscape.com/comprod/products/navigator/version_2.0/script/script_info/. This appendix describes the use of JavaScript as the scripting language of a Script node.
The url field of the Script node may contain a URL that references JavaScript code:
Script { url "http://foo.com/myScript.js" }
The javascript: protocol allows the script to be placed inline as follows:
Script { url "javascript: function foo() { ... }" }
The url field may contain multiple URLs and thus reference a remote file or in-line code:
Script { url [ "http://foo.com/myScript.js", "javascript: function foo() { ... }" ] }
The file extension for JavaScript source code is .js.
The MIME type for JavaScript source code is defined as follows:
application/x-javascript
Events sent to the Script node are passed to the corresponding JavaScript function in the script. It is necessary to specify the script in the url field of the Script node. The function's name is the same as the eventIn and is passed two arguments, the event value and its timestamp (See "Parameter passing and the EventIn function"). If there isn't a corresponding JavaScript function in the script, the browser's behavior is undefined.
For example, the following Script node has one eventIn field whose name is start:
Script { eventIn SFBool start url "javascript: function start(value, timestamp) { ... }" }
In the above example, when the start eventIn is sent the start() function is executed.
When a Script node receives an eventIn, a corresponding method in the file specified in url field of the Script node is called, which has two arguments. The value of the eventIn is passed as the first argument and timestamp of the eventIn is passed as the second argument. The type of the value is the same as the type of the EventIn and the type of the timestamp is SFTime. See "Mapping between JavaScript types and VRML types" for a description of how VRML types appear in JavaScript.
Authors may define a function named eventsProcessed which will be called after some set of events has been received. Some implementations will call this function after the return from each EventIn function, while others will call it only after processing a number of EventIn functions. In the latter case an author can improve performance by placing lengthy processing algorithms which do not need to execute for every event received into the eventsProcessed function.
The eventsProcessed function takes no parameters. Events generated from it are given the timestamp of the last event processed.
Authors may define a function named initialize which is called when the corresponding Script node has been loaded and before any events are processed. This can be used to prepare for processing before events are received, such as construct geometry or initialize external mechanisms.
The initialize function takes no parameters. Events generated from it are given the timestamp of when the Script node was loaded.
Authors may define a function named shutdown which is called when the corresponding Script node is deleted or the world containing the Script node is unloaded or replaced by another world. This can be used to send events informing external mechanisms that the Script node is being deleted so they can clean up files, etc.
The shutdown function takes no parameters. Events generated from it are given the timestamp of when the Script node was deleted.
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.
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 persistent across function calls. EventOuts defined in the script node can also be read. The value is the last value sent.
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 directOutput TRUE 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.
Assigning to an eventOut sends that event at the completion of the currently executing function. This implies that assigning to the eventOut multiple times during one execution of the function still only sends one event and that event is the last value assigned.
This section lists the functions available in the browser object, which allows scripts to get and set browser information. Return values and parameters are shown typed using VRML data types for clarity. For descriptions of the methods, see the Browser Interface topic of the Scripting section of the spec.
Return value |
Method Name |
SFString | getName() |
SFString | getVersion() |
SFFloat | getCurrentSpeed() |
SFFloat | getCurrentFrameRate() |
SFString | getWorldURL() |
void | replaceWorld(MFNode nodes) |
SFNode | createVrmlFromString(SFString vrmlSyntax) |
SFNode | createVrmlFromURL(MFString url, Node node, SFString event) |
void | addRoute(SFNode fromNode, SFString fromEventOut, SFNode toNode, SFString toEventIn) |
void | deleteRoute(SFNode fromNode, SFString fromEventOut, SFNode toNode, SFString toEventIn) |
void | loadURL(MFString url, MFString parameter) |
void | setDescription(SFString description) |
JavaScript has few native types. It has strings, booleans, a numeric type and objects. Objects have members which can be any of the three simple types, a function, or another object. VRML types are mapped into JavaScript by considering MF field types as objects containing one member for each value in the MF field. These are accessed using array dereferencing operations. For instance getting the third member of an MFFloat field named foo in JavaScript is done like this:
bar = foo[3];
After this operation bar contains a single numeric value. Note that array indexing in JavaScript starts at index 1.
Simple SF field type map directly into JavaScript. SFString becomes a JavaScript string, SFBool becomes a boolean, and SFInt32 and SFFloat become the numeric type. SF fields with more than one numeric value are considered as objects containing the numeric values of the field. For instance an SFVec3f is an object containing 3 numeric values, accessed using array dereferencing. To access the y component of an SFVec3f named foo do this:
bar = foo[2];
After this operation bar contains the y component of vector foo.
Accessing an MF field containing a vector is done using double array dereferencing. If foo is now an MFVec3f, accessing the y component of the third value is done like this:
bar = foo[3][1];
Assigning a JavaScript value to a VRML type (such as when sending an eventOut), performs the appropriate type conversion. Assigning a one dimensional array to an SFField with vector contents (SFVec2f, SFVec3f, SFRotation or SFColor) assigns one element to each component of the vector. If too many elements is passed the trailing values are ignored. If too few are passed the vector is padded with 0's. Assigning a numeric value to an SFInt32 truncates the value.
Assigning a simple value to an MFField converts the single value to a multi-value field with one entry. Assigning an array to an SFField places the first array element into the field. Assigning a one dimensional array to an MFField with vector quantities first translates the array into the the vector quantity then assigns this as a single value to the MFField. For instance if foo is a 4 element array and it is assigned to an MFVec2f, the first 2 elements are converted to an SFVec2f, the last 2 elements are discarded, then the SFVec2f is converted to an MFVec2f with one entry.
Assigning a string value to any numeric type (anything but SFString/MFString) attempts to convert the number to a float then does the assignment. If it does not convert a 0 is assigned. Assigning to an SFTime interprets the value as a double.
Assigning to an SFImage interprets the value as a numeric vector with at least 3 values. The first 2 are the x,y dimensions of the image in pixels, the third value is the number of components in the image (1 for monochrome, 3 for rgb, etc.) and the remaining values are pixel colors as described in " Fields and Events - SFImage".
Here's an example of a Script node which determines whether a given color contains a lot of red. The Script node exposes a color field, an eventIn, and an eventOut:
Script { field SFColor currentColor 0 0 0 eventIn SFColor colorIn eventOut SFBool isRed url "javascript: function colorIn(newColor, ts) { // This method is called when a colorIn event is received currentColor = newColor; } function eventsProcessed() { if (currentColor[0] >= 0.5) // if red is at or above 50% isRed = true; }" }
For details on when the methods defined in ExampleScript are called - see the "Concepts - Execution Model".
DEF Example Script { field SFNode myself USE Example field MFString url "foo.wrl" eventIn MFNode nodesLoaded eventIn SFBool trigger_event url "javascript: function trigger_event(value, ts){ // do something and then fetch values browser.createVRMLFromURL(url, myself, "nodesLoaded"); } function nodesLoaded(value, timestamp){ // do something }" }
DEF Sensor TouchSensor {} DEF Baa Script { field SFNode myself USE Baa field SFNode fromNode USE Sensor eventIn SFBool clicked eventIn SFBool trigger_event url "javascript: function trigger_event(eventIn_value){ // do something and then add routing browser.addRoute(fromNode, "isActive", myself, "clicked"); } function clicked(value){ // do something } }