Extensible 3D (X3D)
Part 1: Architecture and base components
29 Scripting component
The name of this component is "Scripting". This name shall be used when referring to this component in the COMPONENT statement (see 7.2.5.4 Component statement).
This clause describes the scripting component of this part of ISO/IEC 19775. This includes how Script nodes are used to effect changes in X3D worlds. Table 29.1 provides links to the major topics in this clause.
Authors often require that X3D worlds change dynamically in response to user inputs, external events, and the current state of the world. The proposition "if the vault is currently closed AND the correct combination is entered, open the vault" illustrates the type of problem which may need addressing. These kinds of decisions are expressed programmatically using the Scene Access Interface (SAI) specified in Part 2 of ISO/IEC 19775. The programmatic elements are provided internally from Script nodes (see 29.4.1 Script) or externally from other application programs. These application programs are called scripting environments. In both cases, the scripting environment can receive events, process them, and send new events. Scripting environments can keep track of information between subsequent executions (i.e., retaining internal state over time).
This clause describes the general mechanisms and semantics of all scripting access. [I19775-2] defines a set of abstract scripting services and specific languages bound to those services. For internal scripting, event processing is performed by a program or script contained in (or referenced by) the Script node's url field. This program or script may be written in any programming language that the browser supports.
A Script node is activated when it receives an event. The browser shall then execute the program in the Script node's url field (passing the program to an external interpreter if necessary). The program can perform a wide variety of actions including sending out events (and thereby changing the scene), performing calculations, and communicating with servers elsewhere on the Internet. A detailed description of the ordering of event processing is contained 4.4.8 Event model.
Script nodes may also be executed at initialization and shutdown as specified in 29.2.3 initialize() and shutdown(). Some scripting languages may allow the creation of separate processes from scripts, resulting in continuous execution (see 29.2.7 Asynchronous scripts).
Script nodes receive events in timestamp order. Any events generated as a result of processing an event are given timestamps corresponding to the event that generated them. Conceptually, it takes no time for a Script node to receive and process an event, even though in practice it does take some amount of time to execute a Script.
When a set_url event is received by a Script node that contains a script that has been previously initialized for a different URL, the shutdown() service of the current script is called (see 29.2.3 initialize() and shutdown(). Until the new script becomes available, the script shall behave as though it has no executable content. When the new script becomes available, the Initialize() service is invoked. The limiting case is when the URL contains inline code that can be immediately executed upon receipt of the set_url event (EXAMPLE ecmascript: protocol). In this case, it can be assumed that the old code is unloaded and the new code loaded instantaneously, after any dynamic route requests have been performed.
The scripting language binding may define an initialize() method. This method shall be invoked before the browser presents the world to the user and before any events are processed by any nodes in the same X3D file as the Script node containing this script. Events generated by the initialize() method shall have timestamps less than any other events generated by the Script node. This allows script initialization tasks to be performed prior to the user interacting with the world.
Likewise, the scripting language binding may define a shutdown() method. This method shall be invoked when the corresponding Script node is deleted or the world containing the Script node is unloaded or replaced by another world. This method may be used as a clean-up operation, such as informing external mechanisms to remove temporary files. No other methods of the script may be invoked after the shutdown() method has completed, though the shutdown() method may invoke methods or send events while shutting down. Events generated by the shutdown() method that are routed to nodes that are being deleted by the same action that caused the shutdown() method to execute will not be delivered. The deletion of the Script node containing the shutdown() method is not complete until the execution of its shutdown() method is complete.
The scripting language binding may define an eventsProcessed() method that is called after one or more events are received. This method allows scripts that do not rely on the order of events received to generate fewer events than an equivalent script that generates events whenever events are received. If it is used in some other time-dependent way, eventsProcessed() may be nondeterministic, since different browser implementations may call eventsProcessed() at different times.
For a single event cascade, a given Script node's eventsProcessed() method shall be called at most once. Events generated from an eventsProcessed() method are given the timestamp of the last event processed.
The scripting language binding may define a prepareEvents() method that is called only once per timestamp. prepareEvents() is called before any ROUTE processing and allows a Script to collect any asynchronously generated data, such as input from a network queue or the results of calling field listeners, and generate events to be handled by the browser's normal event processing sequence as if it were a built-in sensor node.
Script nodes that have access to other nodes (via SFNode and MFNode fields) and that have their directOutput field set to TRUE may directly post events to those nodes. They may also read the last value sent from any of the node's fields.
When setting a value in another node, implementations shall set values in other nodes by sending input events to the corresponding fields. These events shall be part of the current event cascade (see 4.4.8.3 Execution model).
Some languages supported by X3D browsers may allow Script nodes to spontaneously generate events, allowing users to create Script nodes that function like new X3DSensorNode nodes. In these cases, the Script is generating the initial events that causes the event cascade, and the scripting language and/or the browser shall determine an appropriate timestamp for that initial event. Such events are then sorted into the event stream and processed like any other event, following all of the same rules including those for looping.
The Script node's url field shall allow for both inline scripting and script reference via a URL. The MIME-type of the returned data defines the language type. Additionally, instructions can be included in-line using scripting language protocols as defined in 9.2.3 Scripting language protocols for the specific language (from which the language type is inferred).
EXAMPLE The following Script node has one field named start and three different URL values specified in the url field: Java, ECMAScript, and inline ECMAScript:
Script { field SFBool start url [ "https://foo.com/fooBar.class", "https://foo.com/fooBar.js", "ecmascript:function start(value, timestamp) { ... }" ] }
When a start event is received by the Script node, one of the scripts found in the url field is executed. The Java platform bytecode is the first choice, the ECMAScript code is the second choice, and the inline ECMAScript code the third choice.
A description of order of preference for multiple valued URL fields may be found in 9.3.2 X3DUrlObject.
Events received by the Script node are passed to the appropriate scripting language method in the script. The method's name depends on the language type used. In some cases, it is identical to the name of the field; in others, it is a general callback method for all events (see the scripting language annexes for details). The method is passed two arguments: the event value and the event timestamp.
The fields of a Script node are accessible from scripting language methods. Events can be routed to fields of Script nodes and the fields of Script nodes can be routed to fields of other nodes. Another Script node with access to this node can access the fields just like any other node (see 29.2.6 Scripts with direct outputs).
It is recommended that user-defined field names defined in Script nodes follow the naming conventions described in [I19775-2]
The field values can be read or written and are persistent across method call, and changes to a field can notify the node through its update method. See 5 Field type reference for more information on field types.
X3DScriptNode : X3DChildNode, X3DURLObject { SFTime [in,out] autoRefresh 0.0 [0,∞) SFTime [in,out] autoRefreshTimeLimit 3600.0 [0,∞) SFString [in,out] description "" SFBool [in,out] load TRUE SFNode [in,out] metadata NULL [X3DMetadataObject] SFTime [in,out] refresh 0.0 [0,∞) MFString [in,out] url [] [URI] }
This abstract node type is the base type for all scripting nodes.
If the autoRefresh field results in a new script node getting loaded or the prior script getting reloaded, then all fields are re-initialized to their initially defined values, and the initialize() method is invoked, if provided, as defined in 29.2.3 initialize() and shutdown().
WARNING Automatically reloading content can have security considerations and needs to be considered carefully.
Script : X3DScriptNode { SFTime [in,out] autoRefresh 0.0 [0,∞) SFTime [in,out] autoRefreshTimeLimit 3600.0 [0,∞) SFString [in,out] description "" SFBool [in,out] load TRUE SFNode [in,out] metadata NULL [X3DMetadataObject] SFTime [in,out] refresh 0.0 [0,∞) MFString [in,out] url [] [URI] SFBool [] directOutput FALSE SFBool [] mustEvaluate FALSE # And any number of: fieldType [in] fieldName fieldType [in,out] fieldName initialValue fieldType [out] fieldName fieldType [] fieldName initialValue }
The Script node is used to program behaviour in a scene. Script nodes typically:
Each Script node has associated programming language code, referenced by the url field, that is executed to carry out the Script node's function. That code is referred to as the "script" in the rest of this description. Details on the url field can be found in 9.2.1 URLs, URNs and URIs.
Browsers are not required to support any specific language. Detailed information on scripting languages is described in 29.2 Concepts. Browsers supporting a scripting language for which a language binding is specified shall adhere to that language binding (see ISO/IEC 19777).
Sometime before a script receives the first event it shall be initialized (any language-dependent or user-defined initialize() is performed). The script is able to receive and process events that are sent to it. Each event that can be received shall be declared in the Script node using the same syntax as is used in a prototype definition:
inputOnly type name
The type can be any of the standard X3D fields (as defined in 5 Field type reference). Name shall be an identifier that is unique for this Script node.
The Script node is able to generate events in response to the incoming events. Each event that may be generated shall be declared in the Script node using the following syntax:
outputOnly type name
If the Script node's mustEvaluate field is FALSE, the browser may delay sending input events to the script until its outputs are needed by the browser. If the mustEvaluate field is TRUE, the browser shall send input events to the script as soon as possible, regardless of whether the outputs are needed. The mustEvaluate field shall be set to TRUE only if the Script node has effects that are not known to the browser (such as sending information across the network). Otherwise, poor performance may result.
Once the script has access to a X3D node (via an SFNode or MFNode value either in one of the Script node's fields or passed in as an attribute), the script is able to read the contents of that node's fields. If the Script node's directOutput field is TRUE, the script may also send events directly to any node to which it has access, and may dynamically establish or break routes. If directOutput is FALSE (the default), the script may only affect the rest of the world via events sent through its fields. The results are undefined if directOutput is FALSE and the script sends events directly to a node to which it has access.
A script is able to communicate directly with the X3D browser to get information such as the current time and the current world URL. This is strictly defined generally by the SAI services (see Part 2 of ISO/IEC 19775) and by the language bindings of the SAI (see ISO/IEC 19777) for the specific scripting language being used.
The location of the Script node in the scene graph has no affect on its operation.
EXAMPLE If a parent of a Script node is a Switch node with whichChoice set to "−1" (i.e., ignore its children), the Script node continues to operate as specified (i.e., it receives and sends events).
The Scripting component provides one level of support as specified in Table 29.2.