SVG Tiny 1.2 – 20081118

15 Scripting

Contents

15.1 Specifying the scripting language

15.1.1 Specifying the default scripting language

The 'contentScriptType' attribute on the 'svg' element specifies the default scripting language for the given document fragment.

15.1.2 Local declaration of a scripting language

It is also possible to specify the scripting language for each individual 'script' or 'handler' elements by specifying a 'type' attribute on the 'script' and 'handler' elements.

15.2 The 'script' element

A 'script' element may either contain or point to executable content (e.g., ECMAScript [ECMA-262] or Java [JAVA] JAR file). Executable content can come either in the form of a script (textual code) or in the form of compiled code. If the code is textual, it can either be placed inline in the 'script' element (as character data) or as an external resource, referenced through 'xlink:href' attribute. Compiled code must be an external resource. If a 'script' element has both an 'xlink:href' attribute and child character data, the executable content for the script is retrieved from the IRI of the 'xlink:href' attribute, and the child content is not added to the scripting context.

When the executable content is inlined, it must be processed as described in Processing inline executable content.

Some scripting languages such as ECMAScript have a notion of a "global scope" or a "global object" such that a single global object must be associated with the document (unique for each uDOM Document node). This object is shared by all elements contained in that document. Thus, an ECMAScript function defined within any 'script' element must be in the "global" scope of the entire document to which the script belongs. The global object must implement the SVGGlobal interface. In addition to being implemented on the global ECMAScript object, the SVGGlobal object can also be obtained through the DocumentView::defaultView attribute on the Document object. Event listeners attached through event attributes and 'handler' elements are also evaluated using the global scope of the document in which they are defined.

For compiled languages (such as Java) that don't have a notion of "global scope", each 'script' element, in effect, provides a separate scope object. This scope object must perform an initialization as described in the uDOM chapter and serves as event listener factory for the 'handler' element.

15.2.1 Script processing

Execution of a given 'script' element occurs at most once. There is a conceptual flag associated with each 'script' element (referred to here as the "already processed" flag) that enforces this behavior. When a 'script' element is executed depends on the method by which the element was inserted into the document.

One way for a 'script' element to be inserted into the document is if it was inserted while parsing the document. As mentioned in Progressive rendering, as the document is parsed if a 'script' element is encountered then it will be processed just after its end element event occurs, but before any more of the document is parsed and further nodes inserted into the document. (See below for a description of what it means for a 'script' element to be processed.) Once processed, parsing of the document resumes.

The other way a 'script' element can be inserted into the document is if it was inserted by something other than the parser (such as by other script executing). In this case, as soon as one or more 'script' elements are inserted into the document, they must be processed one by one in document order.

A 'script' element is processed as follows:

  1. If the 'script' element's "already processed" flag is true or if the element is not in the document tree, then no action is performed and these steps are ended.

  2. If the 'script' element references external script content, then the external script content using the current value of the 'xlink:href' attribute is fetched. Further processing of the 'script' element is dependent on the external script content, and will block here until the resource has been fetched or is determined to be an invalid IRI reference.

  3. The 'script' element's "already processed" flag is set to true.

  4. If the script content is inline, or if it is external and was fetched successfully, then the script is executed. Note that at this point, these steps may be re-entrant if the execution of the script results in further 'script' elements being inserted into the document.

Note that a load event is dispatched on a 'script' element once it has been processed, unless it referenced external script content with an invalid IRI reference and 'externalResourcesRequired' was set to 'true'.

Modifying or removing the 'script' element (or content) after the script has started its execution must have no effect on the script execution.

Modifying a 'script' element's 'xlink:href' attribute after its "already processed" flag is set to true will not cause any new script content to be fetched or executed.

What it means to execute some script content depends on the script content type. SVG Tiny 1.2 does not require support for any particular programming language. However, SVG defines the behavior for two specific script types in the case where an implementation supports it:

application/ecmascript

This type of executable content must be source code for the ECMAScript programming language. This code must be executed in the context of this element's owner document's global scope as explained above.

SVG implementations that load external resources through protocols such as HTTP that support content coding must accept external script files that have been encoded using gzip compression (flagged using "Content-Encoding: gzip" for HTTP).

application/java-archive

This type of executable content must be an external resource that contains a Java JAR archive. The manifest file in the JAR archive must have an entry named SVG-Handler-Class. The entry's value must be a fully-qualified Java class name for a class contained in this archive. The user agent must instantiate the class from the JAR file and cast it to the EventListenerInitializer2 interface. Then the initializeEventListeners method must be called with the 'script' element object itself as a parameter. If a class listed in SVG-Handler-Class does not implement EventListenerInitializer2, it is an error.

Note that the user agent may reuse classes loaded from the same URL, so the code must not assume that every 'script' element or every document will create its own separate class object. Thus, one cannot assume, for instance, that static fields in the class are private to a document.

Implementations must also accept the script type 'text/ecmascript' for backwards compatibility with SVG 1.1. However, this type is deprecated and should not be used by content authors.

Other language bindings are encouraged to adopt a similar approach to either of the two described above.

Example 18_01 defines a function circle_click which is called when the 'circle' element is being clicked. The drawing below on the left is the initial image. The drawing below on the right shows the result after clicking on the circle. The example uses the 'handler' element which is described further down in this chapter.

Note that this example demonstrates the use of the click event for explanatory purposes. The example presupposes the presence of an input device with the same behavioral characteristics as a mouse, which will not always be the case. To support the widest range of users, the DOMActivate event should be used instead of the click event.

Example: 18_01.svg
<?xml version="1.0"?>
<svg width="6cm" height="5cm" viewBox="0 0 600 500"
     xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     xmlns:ev="http://www.w3.org/2001/xml-events">
  <desc>Example: invoke an ECMAScript function from an click event
  </desc>
  <!-- ECMAScript to change the radius with each click -->
  <script type="application/ecmascript"> <![CDATA[
    function circle_click(evt) {
      var circle = evt.target;
      var currentRadius = circle.getFloatTrait("r");
      if (currentRadius == 100)
        circle.setFloatTrait("r", currentRadius*2);
      else
        circle.setFloatTrait("r", currentRadius*0.5);
    }
  ]]> </script>
  
  <!-- Outline the drawing area with a blue line -->
  <rect x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>
  <!-- Act on each click event -->
  <circle cx="300" cy="225" r="100" fill="red">
  <handler type="application/ecmascript" ev:event="click"> 
        circle_click(evt);
    </handler>
  </circle>  
  
  <text x="300" y="480" font-family="Verdana" font-size="35" text-anchor="middle">
    Click on circle to change its size
  </text>
</svg>
Example 18_01 - invoke an ECMAScript function from an onclick event - before first click     Example 18_01 - invoke an ECMAScript function from an onclick event - after first click

Here the same script is invoked, this time in an external file.

Example: 18_02.svg
<?xml version="1.0"?>
<svg width="6cm" height="5cm" viewBox="0 0 600 500"
     xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     xmlns:ev="http://www.w3.org/2001/xml-events">
  <desc>Example: invoke an external ECMAScript function from an click event
  </desc>
  <!-- ECMAScript to change the radius with each click -->
  <script type="application/ecmascript" xlink:href="sample.es"/> 
  
  <!-- Outline the drawing area with a blue line -->
  <rect x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>
  <!-- Act on each click event -->
  <circle cx="300" cy="225" r="100" fill="red">
  <handler type="application/ecmascript" ev:event="click"> 
        circle_click(evt);
    </handler>
  </circle>  
  
  <text x="300" y="480" font-family="Verdana" font-size="35" text-anchor="middle">
    Click on circle to change its size
  </text>
</svg>
Schema: script
    <define name='script'>
      <element name='script'>
        <ref name='script.AT'/>
        <ref name='script.ATCM'/>
      </element>
    </define>

    <define name='script.AT' combine='interleave'>
      <ref name='svg.CorePreserve.attr'/>
      <ref name='svg.External.attr'/>
      <ref name='svg.ContentType.attr'/>
    </define>

    <define name='script.ATCM'>
      <interleave>
        <choice>
          <group>
            <ref name='svg.XLinkRequired.attr'/>
          </group>
          <text/>
        </choice>
        <ref name='svg.Desc.group'/>
      </interleave>
    </define>

Attribute definitions:

type = "<content-type>"

Identifies the programming language for the 'script' element. The "<content-type>" value specifies a media type, per Multipurpose Internet Mail Extensions (MIME) Part Two [RFC2046]. If 'type' is not specified, the value of 'contentScriptType' on the 'svg' element shall be used, which in turn has a lacuna value of 'application/ecmascript' [RFC4329]. If a 'script' element is not inside an SVG document fragment, 'type' must default to 'application/ecmascript'. This can happen for example if the 'script' element is a child of some arbitrary non-SVG markup.

Animatable: no.

xlink:href = "<IRI>"

An IRI reference to an external resource containing the script code. If the attribute contains an invalid IRI reference, the 'script' element will not execute any script.

Animatable: no.

15.3 XML Events

XML Events [XML-EVENTS] is an XML syntax for integrating event listeners and handlers with DOM Level 2 Events [DOM2EVENTS]. Declarative event handling in SVG 1.1 was hardwired into the language, in that the developer was required to embed the event handler in the element syntax (e.g. an element with an 'onclick' attribute). SVG Tiny 1.2 does not support the event attributes ('onload', 'onclick', 'onactivate', etc.). Instead SVG Tiny 1.2 uses XML Events, through the inclusion of the 'listener' and 'handler' elements to provide the ability to specify the event listener separately from the graphical content.

The list of events supported by SVG Tiny 1.2 is given in the Interactivity chapter.

There are two ways to place a handler in SVG Tiny 1.2 content. The first method is most suitable for simple cases:

Example: simplehandler.svg
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     version="1.2" baseProfile="tiny">
       
  <rect x="10" y="20" width="10" height="20" fill="red">
    <handler type="application/ecmascript" ev:event="click">
      var theRect = evt.target;
      var width = theRect.getFloatTrait("width");
      theRect.setFloatTrait("width", width + 10);
    </handler>
  </rect>
    
</svg>

In this method the 'handler' element is a child element of the observer ([XML-EVENTS], section 3.1). For instance one can place a 'handler' as a child of a 'rect' element, which becomes the observer. This causes the 'handler' element to be invoked whenever the event that it is interested in (click, in this case) occurs on the 'rect'.

The following is an example of an SVG document using XML Events where the 'handler' element can be reused on several objects. The 'listener' element from XML Events is used to specify the 'observer' and 'handler' for a particular 'event'.

Example: handler.svg
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     xmlns:ev="http://www.w3.org/2001/xml-events">
     
  <desc>An example of the handler element.</desc>

  <rect xml:id="theRect1" x="10" y="20" width="10" height="20" fill="red"/>
  <rect xml:id="theRect2" x="10" y="40" width="10" height="20" fill="green"/>

  <ev:listener event="click" observer="theRect1" handler="#theClickHandler"/>
  <ev:listener event="click" observer="theRect2" handler="#theClickHandler"/>
                 
  <handler xml:id="theClickHandler" type="application/ecmascript">
    var theRect = evt.target;
    var width = theRect.getFloatTrait("width");
    theRect.setFloatTrait("width", (width+10));
  </handler>

</svg>

In the above example, the 'listener' element registers that the "theClickHandler" element should be invoked whenever a click event happens on "theRect1" or "theRect2".

The combination of the XML Events syntax and the new 'handler' element allows event handling to be more easily processed in a compiled language. Below is an example of an event handler using the Java language:

Example: javahandler.svg
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     xmlns:foo="http://www.example.com/foo"
      xmlns:xlink="http://www.w3.org/1999/xlink">
     
  <desc>Example of a Java handler</desc>

  <rect xml:id="theRect" x="10" y="20" width="200" height="300" fill="red"/>

  <!-- reference a jar containing an EventListenerInitializer2 object -->
  <script type="application/java-archive" xml:id="init" xlink:href="http://example.com/theJar.jar"/>
  
  <!-- register a listener for a theRect.click event -->
  <ev:listener event="click" observer="theRect" handler="#theClickHandler" />

  <handler xml:id="theClickHandler" type="application/java-archive" xlink:href="#init" foo:offset="10"/>      

</svg>

In this case, the 'handler' element specifies a reference to the 'script' element that specifies the location of compiled code that conforms to the EventListenerInitializer2 interface. The user agent invokes the createEventListener method within the targeted interface.

In this case, the MyListenerInitializer class referenced by the SVG-Handler-Class entry of the theJar.jar manifest has the following definition:

MyListenerInitializer
package com.example;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.svg.EventListenerInitializer2;

public class MyListenerInitializer implements EventListenerInitializer2 {

   Document document;

    public void initializeEventListeners(Element scriptElement) {
        document = scriptElement.getOwnerDocument();
    }

    public EventListener createEventListener(final Element handlerElement) {
        return new EventListener() {
            public void handleEvent(Event event) {
                Element theRect = document.getElementById("theRect");
                float width = Float.parseFloat(theRect.getAttributeNS(null, "width"));
                float offset = Float.parseFloat(handlerElement.getAttributeNS("http://www.example.com/foo", "offset");
                theRect.setAttributeNS(null, "width", "" + (width + offset));
            }
        };
    }
}

The EventListenerInitializer2 interface is currently defined in the SVG package. Future specifications may move this package though it is guaranteed to always be available in the SVG package.

15.4 The 'listener' element

The 'listener' element from XML Events [XML-EVENTS] must be supported. The definition for the 'listener' element is provided in [XML-EVENTS]. Any additional restrictions from this specification must also apply.

Whenever the attributes of a listener element are modified, the corresponding event listener is removed and a new one is created. When listener elements are added or removed, the event listener is added or removed respectively.

Please note that the 'listener' element must be specified in the XML Events namespace, and that an element in the SVG namespace with "listener" as its local name must not be understood as being the element described in this chapter. Furthermore, the XML Events attributes that are available on other elements only when they are in the XML Events namespace, are only available on this element when they are in no namespace.

Schema: listener
    <define name='listener'>
      <element name='listener'>
        <ref name='listener.AT'/>
        <ref name='listener.CM'/>
      </element>
    </define>

    <define name='listener.AT' combine='interleave'>
      <ref name='svg.Core.attr'/>
      <optional>
        <attribute name='event' svg:animatable='false' svg:inheritable='false'>
          <ref name='XML-NMTOKEN.datatype'/>
        </attribute>
      </optional>
      <optional>
        <attribute name='phase' svg:animatable='false' svg:inheritable='false'>
          <choice>
            <value>default</value>
            <value>capture</value>
          </choice>
        </attribute>
      </optional>
      <optional>
        <attribute name='propagate' svg:animatable='false' svg:inheritable='false'>
          <choice>
            <value>continue</value>
            <value>stop</value>
          </choice>
        </attribute>
      </optional>
      <optional>
        <attribute name='defaultAction' svg:animatable='false' svg:inheritable='false'>
          <choice>
            <value>perform</value>
            <value>cancel</value>
          </choice>
        </attribute>
      </optional>
      <optional>
        <attribute name='observer' svg:animatable='false' svg:inheritable='false'>
          <ref name='IDREF.datatype'/>
        </attribute>
      </optional>
      <optional>
        <attribute name='target' svg:animatable='false' svg:inheritable='false'>
          <ref name='IDREF.datatype'/>
        </attribute>
      </optional>
      <optional>
        <attribute name='handler' svg:animatable='false' svg:inheritable='false'>
          <ref name='IRI.datatype'/>
        </attribute>
      </optional>
    </define>


    <define name='listener.CM'>
      <empty/>
    </define>

Attribute definitions:

event = "<XML-NMTOKEN>"

See the XML Events 'event' attribute definition. The 'event' attribute must be a valid SVG Tiny 1.2 event identifier as defined in the list of supported events.

Animatable: no.

observer = "<IDREF>"

See the XML Events 'observer' attribute definition. Note that if the 'observer' attribute is not present, the observer is the parent of the 'listener' element.

Animatable: no.

target = "<IDREF>"

See the XML Events 'target' attribute definition.

Animatable: no.

handler = "<IRI>"

See the XML Events 'handler' attribute definition. This attribute is an IRI reference. Restrictions specified in this chapter as to which IRIs are acceptable must be enforced.

Animatable: no.

phase = "default"

See the XML Events 'phase' attribute definition. Support for the capture phase is not required in SVG Tiny 1.2, and implementations that do not support it must process this attribute as if it had not been specified.

Animatable: no.

propagate = "stop" | "continue"

See the XML Events 'propagate' attribute definition.

Animatable: no.

defaultAction = "cancel" | "perform"

See the XML Events 'defaultAction' attribute definition.

Animatable: no.

15.5 The 'handler' element

The 'handler' element is similar to the 'script' element: its contents, either included inline or referenced, is code that is to be executed by a scripting engine used by user agent.

However, where the 'script' element executes its contents when it is loaded, the 'handler' element must only execute its contents in response to an event. This means that SVG Tiny 1.2 uses 'handler' to get the functionality equivalent to that provided by SVG Full event attributes ([SVG11], section 18.4).

When the executable content is inlined, it must be processed as described in Processing inline executable content.

For example, consider the following SVG 1.1 document:

Example: nohandler.svg
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect id="theRect" x="10" y="20" width="200" height="300" fill="red"
        onclick="evt.target.width.baseVal.value += 10"/>
</svg>

The above example must be rewritten, as described below, to use the 'handler' element and XML Events as shown:

Example: handler2.svg
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     version="1.2" baseProfile="tiny">

  <desc>handler element example</desc>

  <rect xml:id="theRect" x="10" y="20" width="200" height="300" fill="red">
    <handler type="application/ecmascript" ev:event="click">
      var theRect = evt.target;
      var width = theRect.getFloatTrait("width");
      theRect.setFloatTrait("width", width + 10);
    </handler>
  </rect>

</svg> 

Whenever the 'type' or ev:event attributes of a 'handler' element are modified, the corresponding event listener is removed and a new one is created. When the 'xlink:href' attribute is modified or the content of the 'handler' element is modified, the existing event listener is preserved, but the user agent must execute the updated handler logic. When 'handler' elements are added or removed, the corresponding event listener is added or removed respectively.

In ECMAScript, the contents of the 'handler' element behave conceptually as if they are the contents of a new Function object, created as shown:

function(event) {
    var evt = event;
    //contents of handler
}

The 'event' parameter shown above is an Event object corresponding to the event that has triggered the 'handler'. An 'evt' variable can be used instead of 'event' ('evt' is an alias to 'event').

Schema: handler
    <define name='handler'>
      <element name='handler'>
        <ref name='handler.AT'/>
        <ref name='handler.ATCM'/>
      </element>
    </define>

    <define name='handler.AT' combine='interleave'>
      <ref name='svg.CorePreserve.attr'/>
      <ref name='svg.External.attr'/>
      <optional>
        <attribute name='ev:event' svg:animatable='false' svg:inheritable='false'>
          <ref name='XML-NMTOKEN.datatype'/>
        </attribute>
      </optional>
      <ref name='svg.ContentType.attr'/>
    </define>

    <define name='handler.ATCM'>
      <interleave>
        <choice>
          <group>
            <ref name='svg.XLinkRequired.attr'/>
          </group>
          <text/>
        </choice>
        <ref name='svg.Desc.group'/>
      </interleave>
    </define>

Attribute definitions:

type = "<content-type>"

Identifies the programming language for the 'handler' element. The "<content-type>" value specifies a media type, per Multipurpose Internet Mail Extensions (MIME) Part Two [RFC2046]. If 'type' is not specified, the value of 'contentScriptType' on the 'svg' element shall be used, which in turn has a lacuna value of 'application/ecmascript' [RFC4329].

Animatable: no.

xlink:href = "<IRI>"

If this attribute is present, then the script content of the 'handler' element must be loaded from this resource and what content the 'handler' element may have must not be executed.

Animatable: no.

ev:event = "<XML-NMTOKEN>"

The name of the event to handle. This attribute is in the XML Events namespace, http://www.w3.org/2001/xml-events. See event list for a list of all supported events and the XML Events specification for the definition of the 'ev:event' attribute ([XML-EVENTS], section 3.1).

Animatable: no.

For script handlers in Java, the 'xlink:href' attribute must reference a 'script' element that itself references a JAR archive holding a manifest with an SVG-Handler-Class entry pointing to an EventListenerInitializer2 implementation.

15.5.1 Parameters to 'handler' elements

In many situations, the script author uses the 'handler' as a template for calling other functions, using the content of the 'handler' element to pass parameters. However, for compiled languages the 'handler' element does not have any executable content.

In this case, the author should embed the parameters into the 'handler' as custom content in the form of element children in a foreign namespace, or attributes on the 'handler' element also in foreign namespaces.

Below is an example of using parameters on the 'handler' element:

Example: handlerparam.svg
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:ev="http://www.w3.org/2001/xml-events"
     xmlns:foo="http://www.example.com/foo"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.2" baseProfile="tiny">

  <desc>An example of parameters on the handler element.</desc>

  <rect xml:id="theRect" x="10" y="20" width="200" height="300" fill="red"/>               

  <!-- reference a jar containing an EventListenerInitializer2 object -->
  <script type="application/java-archive" xml:id="init" xlink:href="http://example.com/theJar.jar"/>

  <!-- register a listener for a theRect.click event -->
  <ev:listener event="click" observer="theRect" handler="#theClickHandler" />

  <handler xml:id="theClickHandler" type="application/java-archive" xlink:href="#init">
    <foo:offset value="10"/>
    <foo:person>
     <foo:name>Victor Vector</foo:name>
     <foo:age>42</foo:age>
    </foo:person>
  </handler>

</svg>

In this case, the object referenced by the SVG-Handler-Class entry of the theJar.jar manifest has its createEventListener method called and the returned EventListener registered. Whenever a click event on the "theRect" element is observed, the handleEvent method of the listener is called. The object can then access the 'handler' element in order to obtain its parameters from elements in the http://www.example.com/foo namespace.

15.6 Event handling

Events must cause scripts to execute when either of the following has occurred:

Related sections of the spec:

15.7 Processing inline executable content

When executable content is inlined inside an executable element such as 'script' or 'handler' elements, it must be processed as follows before it is parsed and executed.

If the type of the content, obtained either through the 'type' attribute, the 'contentScriptType' or its lacuna value, attribute, or the default is not known by the user agent, then the user agent must ignore the content and no further processing must be performed.

If the type of the content is an XML media type [RFC3023], then the entire subtree of the executable element must be passed on untouched to the script engine.

Otherwise, the content that the user agent's script engine obtains must be that which is obtained through the following steps:

  1. Remove all descendant elements of the executable element.
  2. Then, use the text content of the executable element (as defined by Node::textContent in DOM Level 3 Core ([DOM3], section 1.4) of the executable element.