documentation-sources/content/xdocs/using/scripting/ecmascript.xml (120 lines of code) (raw):
<?xml version="1.0"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- ========================================================================= -->
<!-- author cjolif@ilog.fr -->
<!-- version $Id$ -->
<!-- ========================================================================= -->
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "http://forrest.apache.org/dtd/document-v20.dtd">
<document>
<header>
<title>Scripting with ECMAScript</title>
</header>
<body>
<p>
This page is a brief introduction to scripting SVG documents with
ECMAScript, and how Batik’s ECMAScript environment can be extended.
</p>
<section id="scriptingBasics">
<title>Scripting basics</title>
<p>
As the ECMAScript language (the standardised version of JavaScript)
is one of the most popular scripting languages, and as the SVG
specification states that an SVG conforming implementation must
support it, SVG documents processed by Batik support scripting
with ECMAScript using Mozilla’s ECMAScript interpreter,
<a href="http://www.mozilla.org/rhino/">Rhino</a>.
</p>
<p>
There are two places in an SVG file where you can put scripts.
</p>
<p>
The first one is in the <code>script</code>
element, where you can place any code, including function
definitions, to be executed just before the document
<code>SVGLoad</code> event is fired.
</p>
<source><![CDATA[<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<script type="text/ecmascript">
// ECMAScript code to be executed
</script>
<!-- Remainder of the document... -->
</svg>]]></source>
<p>
You can also attach script to respond to user or document
events using attributes on SVG elements. As shown in the
previous example, the scripting language must be set on
the <code>script</code> element. However, for event handling
the default language type <code>text/ecmascript</code>
is assumed. If you want to change it you can use
the <code>contentScriptType</code> attribute on the
<code>svg</code> element.
</p>
<p>
The event attribute can contain any script code to execute when the
event reaches the element (as described by the
<a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow">DOM event flow</a>)
in either the bubbling or at-target phases. The following example
will change the <code>rect</code> to be filled in blue when it is
clicked.
</p>
<source><![CDATA[<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<rect x="0" y="0" width="10" height="10"
onclick="evt.target.setAttribute('fill', 'blue')"/>
</svg>]]></source>
<p>
Note that inside the event attribute script, there is a variable
called <code>evt</code> that is a reference to the
<a class="class" href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Event">Event</a>
object that represents the event that is being handled.
</p>
<p>
For more information on using scripting in SVG you can have a look at:
</p>
<ul>
<li>
the <a href="http://www.w3.org/TR/SVG11/script.html">scripting
chapter of the SVG specification</a>, for advanced information on
scripting in SVG, and
</li>
<li>
the <a href="http://www.ecma.ch/ecma1/stand/ecma-262.htm">ECMAScript
specification</a>, for advanced information on the ECMAScript
language.
</li>
</ul>
</section>
<section id="rhinoFeatures">
<title>Using Rhino features</title>
<p>
Rhino has a number of features beyond those supported by standard
ECMAScript interpreters, and these can be used with Batik. One
useful feature is that ECMAScript code can use Java classes and
objects, and not just the standard ECMAScript primitive types and
host objects exposed by Batik.
</p>
<p>
To create an instance of a Java class from ECMAScript, you first need
to import the package in which it resides. This is done using the
<code>importPackage</code> global function that Rhino provides.
For example, to import the <code>javax.swing.JFrame</code> class,
you use:
</p>
<source>importPackage(Packages.javax.swing);</source>
<p>
This then exposes a global property for each class in the
<code>javax.swing</code> package that you can use to create a new
object of this class, similar to a <code>import javax.swing.*;</code>
statement in Java. We can use the exposed <code>JFrame</code>
property to create a new instance of this class:
</p>
<source>var frame = new JFrame("My test frame");</source>
<p>
Note how an ECMAScript string value is passed as the parameter to
<code>JFrame</code>’s constructor. Rhino will attempt to convert
ECMAScript values into appropriate Java primitive types or objects
to make underlying constructor or method calls. In this instance,
the ECMAScript string value is converted into a
<code>java.lang.String</code> object to be passed to the constructor.
</p>
<p>
Now that we have a reference to this Java object, we can call any
method on it as we usually would from Java code. The following
complete example demonstrates this, where clicking the green
circle will pop up a frame:
</p>
<source><![CDATA[<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="50" fill="green" onclick="showFrame()"/>
<script type="text/ecmascript">
importPackage(Packages.javax.swing);
function showFrame() {
var frame = new JFrame("My test frame");
var label = new JLabel("Hello from Java objects created in ECMAScript!");
label.setHorizontalAlignment(SwingConstants.CENTER);
frame.getContentPane().add(label);
frame.setSize(400, 100);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
</script>
</svg>]]></source>
<p>
For more information on scripting Java classes from ECMAScript code,
see Rhino's
<a href="http://www.mozilla.org/rhino/ScriptingJava.html">Scripting
Java</a> document.
</p>
</section>
<section id="customizingRhino">
<title>Customizing the Rhino interpreter</title>
<p>
A useful example of customization of the Rhino interpreter comes from
the fact that the ECMAScript specification doesn’t provide any
predefined I/O facilities to interact with the console. However, it is
very common for ECMAScript compatible languages to provide a function
named <code>print</code> to output messages to the console. We will
describe here an example of customization of the Batik Rhino
interpreter to add such functionality to it.
</p>
<p>
You should first subclass the default Batik ECMAScript interpreter to
add the functionality to it as below.
</p>
<source>import org.apache.batik.script.rhino.RhinoInterpreter;
import java.net.URL;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.PropertyException;
public class ExtendedRhinoInterpreter extends RhinoInterpreter {
public ExtendedRhinoInterpreter(URL documentURL) {
super(documentURL);
// Array of functions to put in the global object.
final String[] names = { "print" }
try {
// Add the functions to the global object.
getGlobalObject().defineFunctionProperties
(names, ExtendedRhinoInterpreter.class,
ScriptableObject.DONTENUM);
} catch (PropertyException e) {
throw new Error(e);
}
}
public static void print(Context cx, Scriptable thisObj,
Object[] args, Function funObj) {
for (int i = 0; i < args.length; i++) {
if (i > 0) {
System.out.print(" ");
}
// Convert the ECMAScript value into a string form.
String s = Context.toString(args[i]);
System.out.print(s);
}
System.out.println();
}
}</source>
<p>
Now, you need to tell to Batik to use this interpreter instead of the
default one. For that, you must first define a factory to create
instances of your interpreter.
</p>
<source>import org.apache.batik.script.Interpreter;
import org.apache.batik.script.rhino.RhinoInterpreterFactory;
public class ExtendedRhinoInterpreterFactory extends RhinoInterpreterFactory {
public Interpreter createInterpreter(URL documentURL, boolean isSVG12) {
return new ExtendedRhinoInterpreter(documentURL);
}
}</source>
<p>
Then, you must build an
<a class="class" href="../../javadoc/org/apache/batik/script/InterpreterPool.html">IntepreterPool</a>
that will use this factory, and then set the pool on the
<a class="class" href="../../javadoc/org/apache/batik/bridge/BridgeContext.html">BridgeContext</a>
of your application.
</p>
<source>org.apache.batik.bridge.BridgeContext ctx = ...;
org.apache.batik.script.InterpreterPool pool =
new org.apache.batik.script.InterpreterPool();
InterpreterFactory f = new ExtendedRhinoInterpreterFactory();
// Register the interpreter factory for all four MIME types that
// Batik normally supports for ECMAScript.
pool.putInterpreterFactory("text/ecmascript", f);
pool.putInterpreterFactory("text/javascript", f);
pool.putInterpreterFactory("application/ecmascript", f);
pool.putInterpreterFactory("application/javascript", f);
ctx.setIntepreterPool(pool);</source>
<p>
For example if you are using the Batik SVG browser application you
should be able to use the previous piece of code on a subclass of the
<a class="class" href="../../javadoc/org/apache/batik/swing/JSVGCanvas.html">JSVGCanvas</a>
class in the <code>createBridgeContext()</code> method.
</p>
<p>
For further information on working with Rhino, consult the
<a href="http://www.mozilla.org/rhino/">Rhino website</a>.
</p>
</section>
</body>
</document>