blob: 67f2e88a06589e2bcf7eddfd94073ca5564d413b [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="copyright" content="Copyright (c) 2013, 2019 EclipseSource. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page."/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title> Scripting </title>
<link rel="stylesheet" href="../../../PRODUCT_PLUGIN/book.css" type="text/css"/>
</head>
<body>
<h1>Scripting</h1>
<h2>Contents</h2>
<ul>
<li><a href="#overview">Overview</a></li>
<li><a href="#api">API</a><ul>
<li><a href="#ClientListener">ClientListener</a></li>
<li><a href="#widgets">Widgets</a></li>
<li><a href="#events">Event Types</a></li>
<li><a href="#data">Data Transfer</a></li>
<li><a href="#crosswidget">Cross-Widget Scripting</a></li>
<li><a href="#html">Widget HTML</a></li>
</ul></li>
<li><a href="#limitations">Limitations</a></li>
<li><a href="#jshints">JavaScript for Java Developer</a></li>
</ul>
<h2 id="overview">Overview</h2>
<p>
RAP applications are running almost entirely on a Server. All application-relevant events
that occur on the client have to be forwarded to the Server before being
processed. Scenarios where minor delays in the event handling are unacceptable
(e.g. fast typing or mouse movements) would therefore be undesirable.
</p>
<p>
This is where RWT Scripting can help. With scripting developers can handle some of
the events directly on the client, without creating any HTTP-requests. This is ideal to
customize or enhance the behavior of specific widgets, most notably
<em><a href="../reference/api/org/eclipse/swt/widgets/Text.html">Text</a></em>.
</p>
<p>
The scripts themselves have to be written in JavaScript on an SWT-like API. This allows application
developers with SWT-experience to get started right away, and makes porting between SWT and
RWT Scripting fairly easy. Even without much JavaScript-experience, this article should
provide you with all the basics you need for RWT Scripting.
</p>
<h2 id="api">Java and JavaScript API</h2>
<p>
Client event processing works like
<a href="http://help.eclipse.org/indigo/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_events.htm">untyped event handling</a>
in SWT/RWT, with the main difference that
the handler itself has to be written in JavaScript. It also can not not provide out-of-the-box
access to all of the resources and functionality that would be available on the server.
</p>
<h3 id="ClientListener">Creating a ClientListener</h3>
<p>
To attach a
client side listener to a widget, instances of
<em><a href="../reference/api/org/eclipse/rap/rwt/scripting/ClientListener.html">ClientListener</a></em>
are used. Example:
</p>
<pre class="lang-java">
widget.addListener( SWT.Verify, new ClientListener( scriptCode ) );
</pre>
<p>
The JavaScript source code can define any number of named function, either with<br/>
"<code>var myFunction = function(){};</code>" or <br/>"<code>function myFunction(){}</code>".
A function named "<em>handleEvent</em>" is obligatory and will be called in case
an event is fired. It takes one argument, which is the <code>event</code> object.
</p>
<p>Example:</p>
<pre class="lang-javajascript">var handleEvent = function( event ){
event.widget.setText( &quot;Hello World!&quot; );
};</pre>
<p id="other">Other functions within the script
can be called from <em>handleEvent</em> to be used as helper. The order in which the functions
are defined is not relevant.
<a href="https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode">
Strict mode</a> is supported and can be activated for the entire script by writing
<code>&quot;use strict&quot;;</code> in the first line.
</p>
<p>
If your script is longer than a few lines, we recommend to read it from an external file.
You may want to create a helper class to do so. It could look like this:
</p>
<pre class="lang-java" style="height:150px;overflow:scroll">public class ResourceLoaderUtil {
private static final ClassLoader CLASSLOADER = ResourceLoaderUtil.class.getClassLoader();
public static String readTextContent( String resource ) {
try {
return readTextContentChecked( resource );
} catch( IOException e ) {
throw new IllegalArgumentException( "Failed to read: " + resource );
}
}
private static String readTextContentChecked( String resource ) throws IOException {
InputStream stream = CLASSLOADER.getResourceAsStream( resource );
if( stream == null ) {
throw new IllegalArgumentException( "Not found: " + resource );
}
try {
BufferedReader reader = new BufferedReader( new InputStreamReader( stream, "UTF-8" ) );
return readLines( reader );
} finally {
stream.close();
}
}
private static String readLines( BufferedReader reader ) throws IOException {
StringBuilder builder = new StringBuilder();
String line = reader.readLine();
while( line != null ) {
builder.append( line );
builder.append( '\n' );
line = reader.readLine();
}
return builder.toString();
}
}</pre>
<p>
<strong>Note that a ClientListener instance is permanently bound to the UISession.</strong> It is therefore
undesirable to create multiple <em>ClientListener</em> with the same script.
Instead it should be created once and shared within the session, for example
by using the SessionStore or creating a SessionSingleton.
</p>
<p><strong>Bad:</strong></p>
<pre class="lang-java">public static void addCustomBehavior( Control control ) {
String scriptCode
= ResourceLoaderUtil.readTextContent( "MyScript.js" );
ClientListener listener = new ClientListener( scriptCode );
control.addListener( SWT.MouseDown, listener );
}</pre>
<p><strong>Good:</strong></p>
<pre class="lang-java">public static void addCustomBehavior( Control control ) {
ClientListener listener = MyClientListener.getInstance();
control.addListener( SWT.MouseDown, listener );
}</pre>
<pre class="lang-java">public class MyClientListener extends ClientListener {
public static MyClientListener getInstance() {
return SingletonUtil.getSessionInstance( MyClientListener.class );
}
private MyClientListener() {
super( getText() );
}
private static String getText() {
return ResourceLoaderUtil.readTextContent( "MyScript.js" );
}
}</pre>
<h3 id="widgets">Client Widget Objects</h3>
<p>
The widget objects in RWT Scripting are abstract representations of SWT widget instances. They
have a JavaScript-conform <b>subset</b> of the API of the actual SWT widgets they represent.
Client widget objects can be obtained from the
<a href="../reference/jsdoc/symbols/Event.html#widget"><code>event</code></a>
objects given in <em><a href="#ClientListener">handleEvent</a></em>, or
from <a href="../reference/jsdoc/symbols/rap.html#.getObject"><em>rap.getObject</em></a>.
(See also <q><a href="#crosswidget">Cross-Widget Scripting</a></q> below.)
The JavaScript API for all scriptable widgets
is <a href="../reference/jsdoc/index.html">documented the WebClient API Reference</a>.
Not all widgets support scripting. Adding a ClientListener to a widget that does not
support Scripting has no effect.
</p>
<h3 id="events">Supported Event Types</h3>
<p> The following Event types are supported:</p>
<table border="1" cellpadding="3" cellspacing="0">
<tr>
<th align="left">Event Type</th>
<th align="left">Notes</th>
</tr>
<tr>
<td>
<code>SWT.KeyDown</code>
</td>
<td>
Fired once when pressing a key, then repeatedly while holding it down. The <code>doit</code>
flag can be used to prevent the character from beeing inserted.
</td>
</tr>
<tr>
<td>
<code>SWT.KeyUp</code>
</td>
<td>
Fired when releasing a key.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseDown</code>
</td>
<td>
Fired when pressing a mouse button.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseUp</code>
</td>
<td>
Fired when releasing a mouse button.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseMove</code>
</td>
<td>
Fired when moving the mouse within the widget.
This type is not supported by server-side
<a href="../reference/api/org/eclipse/swt/widgets/Listener.html">Listener</a>,
only by
<a href="../reference/api/org/eclipse/rap/rwt/scripting/ClientListener.html">ClientListener</a>.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseWheel</code>
</td>
<td>
Fired when moving the mouse wheel.
This type is not supported by server-side
<a href="../reference/api/org/eclipse/swt/widgets/Listener.html">Listener</a>,
only by
<a href="../reference/api/org/eclipse/rap/rwt/scripting/ClientListener.html">ClientListener</a>.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseEnter</code>
</td>
<td>
Fired when moving the mouse over the widget.
This type is not supported by server-side
<a href="../reference/api/org/eclipse/swt/widgets/Listener.html">Listener</a>,
only by
<a href="../reference/api/org/eclipse/rap/rwt/scripting/ClientListener.html">ClientListener</a>.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseExit</code>
</td>
<td>
Fired when moving the mouse out of the widget.
This type is not supported by server-side
<a href="../reference/api/org/eclipse/swt/widgets/Listener.html">Listener</a>,
only by
<a href="../reference/api/org/eclipse/rap/rwt/scripting/ClientListener.html">ClientListener</a>.
</td>
</tr>
<tr>
<td>
<code>SWT.MouseDoubleClick</code>
</td>
<td>
Fired when clicking twice.
</td>
</tr>
<tr>
<td>
<code>SWT.FocusIn</code>
</td>
<td>
Fired when widget is focused.
</td>
</tr>
<tr>
<td>
<code>SWT.FocusOut</code>
</td>
<td>
Fired when widget is blured.
</td>
</tr>
<tr>
<td>
<code>SWT.Paint</code>
</td>
<td>
Fired when widget appears, is changing size, or when "redraw" is called on the widget either
in java, or in RWT Scripting. Only supported on <code>Canvas</code>.
</td>
</tr>
<tr>
<td>
<code>SWT.Selection</code>
</td>
<td>
Fired on <code>Button</code> and <code>Scale</code> widgets when selection changes.
</td>
</tr>
<tr>
<td>
<code>SWT.Modify</code>
</td>
<td>
Fired then the value of the "text" property of a <code>Text</code>, <code>Combo</code>
or <code>Spinner</code>widget changes.
</td>
</tr>
<tr>
<td>
<code>SWT.Verify</code>
</td>
<td>
Fired then the value of the "text" property of a <code>Text</code>
or <code>Combo</code>widget is changed by the
user. Not supported on other widgets. The <code>doit</code> flag can be used to prevent the
change. The "text" field of the event may be changed to replace the inserted text.
</td>
</tr>
</table>
<h3 id="data">Transferring Widget Data</h3>
<p>
As in SWT the client widget objects provides
<a href="../reference/jsdoc/symbols/Control.html#setData"><code>setData</code></a>
and
<a href="../reference/jsdoc/symbols/Control.html#getData"><code>getData</code></a>
methods. These
allow to attach data to a widget instance without affecting the widget itself.
Unlike SWT any value can be stored with <code>setData</code>, not just objects.
</p>
<p>
Data attached to the Java widget instance can be transferred to the client widget object.
To do so,
the key for that data has to be registered with the method
<em><a href="../reference/api/org/eclipse/rap/rwt/widgets/WidgetUtil.html#registerDataKeys-java.lang.String...-">WidgetUtil.registerDataKeys</a></em>,
like this:
</p>
<pre class="lang-java">WidgetUtil.registerDataKeys( &quot;foo&quot; );
widget.setData( &quot;foo&quot;, &quot;myData&quot; );</pre>
<p>
The key has only to be added once per session, but adding it multiple times has no side
effects. The following types are supported:
</p>
<table style="border:none">
<tr>
<td style="border:none" valign="top">
<ul>
<li>null</li>
<li>String</li>
<li>Byte</li>
<li>Short</li>
<li>Integer</li>
<li>Long</li>
<li>Double</li>
<li>Float</li>
</ul>
</td>
<td style="border:none" valign="top">
<ul>
<li>Boolean</li>
<li>int[]</li>
<li>boolean[]</li>
<li>String[]</li>
<li>Object[]</li>
<li>Map</li>
<li>JsonValue</li>
</ul>
</td>
</tr>
</table>
<p>
Changing the value on the client does not change it on the server.
</p>
<h3 id="crosswidget">Cross-Widget Scripting</h3>
<p>
Each <em>ClientListener</em> script has it's own scope and no direct access
any widget other than the one given by the <em>event</em> object.
Widget references can also <i>not</i> be transferred to the client using
<a href="#data">the setData method</a>, but a widgets protocol id can:
</p>
<p>Java:</p>
<pre class="lang-java">widget.setData( &quot;otherWidget&quot;, WidgetUtil.getId( otherWidget ) );</pre>
<p>
The id can then be used to obtain the matching widget object on the client.
</p>
<p>JavaScript:</p>
<pre class="lang-javascript">var otherWidget = rap.getObject( widget.getData( &quot;otherWidget&quot; ) );</pre>
<h3 id="html">HTML Attributes</h3>
<p>
JavaScript widget objects obtained via <a href="../reference/jsdoc/symbols/rap.html#.getObject"><code>rap.getObject( id )</code></a> have a field
<a href="../reference/jsdoc/symbols/Widget.html#$el"><em>$el</em></a> that allows manipulating their HTML element. Currently it can only be used to set
<a href="../reference/jsdoc/symbols/%24.html#attr">HTML attributes</a> or
<a href="../reference/jsdoc/symbols/%24.html#css">CSS properties</a>
of the element containing the entire widget HTML, and
(in case of <em>Text</em>) the <a href="../reference/jsdoc/symbols/Text.html#$input"><code>&lt;input&gt;</code> element</a>.
</p><p>
This feature can be useful to assign test-id's for easier UI-testing. It can also be used to
add <a href="http://www.w3.org/TR/wai-aria/">ARIA</a> attributes that can be evaluated by screen reader software, thereby providing an
alternative to the unsupported SWT accessibility API. (Please take a look at the RAP FAQ
for more information on <a href="https://wiki.eclipse.org/RAP/FAQ#How_to_create_UI_Tests_for_RAP">UI-testing</a> and <a href="https://wiki.eclipse.org/RAP/FAQ#How_to_make_RAP_applications_Accessible">accessibility</a> in RAP.)
</p>
<p>
To access the <em>$el</em> field from Java code use the JavaScriptExecutor service. If, for
example, you want a method to set test-id's on any given widget, your code may look like this:
</p>
<pre class="lang-java">static void setTestId( Widget widget, String value ) {
if( uiTestsEnabled &amp;&amp; !widget.isDisposed() ) {
String $el = widget instanceof Text ? "$input" : "$el";
String id = WidgetUtil.getId( widget );
exec( "rap.getObject( '", id, "' ).", $el, ".attr( 'test-id', '", value + "' );" );
}
}
private static void exec( String... strings ) {
StringBuilder builder = new StringBuilder();
builder.append( "try{" );
for( String str : strings ) {
builder.append( str );
}
builder.append( "}catch(e){}" );
JavaScriptExecutor executor = RWT.getClient().getService( JavaScriptExecutor.class );
executor.execute( builder.toString() );
}</pre>
<h2 id="limitations">Notable Limitations and discouraged Usage</h2>
<ul>
<li>
It is not (and will likely never be) possible to prevent
<em><a href="../reference/api/org/eclipse/swt/widgets/Text.html">Text</a></em> selection/carret
changes in mouse events (using doit flag). Its also not supported to set the <em>Text</em>
selection property in a
MouseDown event. These are also not working (correctly) in SWT. Changing selection on
<em>Text</em> in a <em>MouseUp</em> event works.
</li>
<li>Verify event:
<ul>
<li>Currently only supported on Text.</li>
<li>
Setting the text or selection property <i>of the target</i> within the Verify event is
not supported, but it doesn't produce useful results in SWT either. The modiy event is
better suited for this.
</li>
<li>
Changing the events "text" field has no effect if its empty to begin with. (I.e.
deleting can not be used to insert text).
</li>
</ul>
</li>
<li>
In SWT, some key constants can be compared with both the keyCode field and the
character field (<em>SWT.DEL, SWT.ESC, SWT.BS, SWT.BS, SWT.CR, SWT.TAB</em>). In
RWT Scripting they can only be compared with the keyCode field. The character field will
either not be set for these keys, or contain a (JavaScript) string, while these constants
are numbers.
</li>
<li>
Accessing the window/document Objects is discouraged for cross-browser/cross-client
compaibility concerns. You can do so at your own risk.
</li>
<li>
Creating global variables from within a ClientListener is heavily discouraged
and can easily happen by accident if a
variable is created without the "var" keyword. Use <a href="#other">strict mode</a>
and <a href="http://github.eclipsesource.com/jshint-eclipse/">jshint</a>
to
prevent this.
</li>
<li>
<b>For security reasons</b> you should be aware that, unlike RAP source code written in
Java, all RWT Scripting functions (JavaScript source code) are currently transferred to
the client completely unaltered, <i>including comments</i>,
and can be read by any user with enough technical expertice.
</li>
</ul>
<h2 id="jshints">JavaScript Hints for Java Developer</h2>
<p>
Developers experienced with Java programming and less familiar with (or completely new to)
JavaScript might find the following hints useful in regard to RWT Scripting:
</p>
<h3>Debugging</h3>
<p>
Most modern browser have built-in developer tools, including a debugger. However,
since the script of a <em>ClientListener</em> is created using an <q>eval</q> statement,
the scripts do not simply appear in the "Scripts" or "Resources" tab of the developer tools,
and so it is not possible to set a break point. There are two ways to solve this, though browser support may vary:
</p>
<p>
1. To make the Script appear in the developer tools, add the line<br/>
&quot;<code>//@ sourceURL=NameOfYourScript.js</code>&quot; to your script. Newer browser
may also recognize
<br/>&quot;<code>//# sourceURL=NameOfYourScript.js</code>&quot;.
</p>
<p>
2. To set a break point programmatically, write <code>debugger;</code> in your script
where you wish to start debugging.
</p>
<h3>Noteable differences between Java and JavaScript</h3>
<ul><li><b>JavaScript variables are dynamically typed and have function scope</b><br/>
All local variables in JavaScript are delcared with "var" and can contain any type (undefined,
null, number, boolean, string, object). It is not relevant at all where in the function it is
declared, its scope is always the entire function.
</li></ul>
<ul><li><b>Strings are not objects</b></li></ul>
<p>
Strings are primitives in JavaScript, and are compared with "<code>==</code>" (or
"<code>===</code>"), not "<code>.equals</code>". However, string primitives can be coerced
into a string object, on which several useful methods are
<a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">available</a>.
</p>
<ul><li><b>A number is not always a number</b></li></ul>
<p>
When calculating a numeric value in JavaScript, the result might not always be a number, even
if it is of the <i>type</i> number. The two cases are <code>infinity</code> (e.g. "<code>10/0
== infinity</code>") and <code>NaN</code> (not a number, e.g. "<code>Math.sqrt( -1 )</code>").
<code>NaN</code> can be detected only by using <code>isNaN</code> (e.g. "
<code>isNaN( Math.sqrt( -1 ) ) == true</code>"). If a number is neither <code>NaN</code> nor
<code>infinity</code>, it can do most things Javas <code>int</code> or <code>double</code>
can, including bitwise operations.
</p>
<ul><li><b>Arrays have dynamic length</b></li></ul>
<p>
Even though their syntax is very similar, JavaScript Arrays behave more like Java Lists than
Java Arrays. They can store different types in different slots, and can change their length as
needed. They also have differently named, but similarly working
<a href="http://www.w3schools.com/jsref/jsref_obj_array.asp">methods</a>.
</p>
<h3>Noteable similarities between Java and JavaScript</h3>
<ul><li><b>Objects and Maps</b></li></ul>
<p>
JavaScript Objects (created with a literal "<code>{}</code>") can be used like Java Maps. A
"<code>map.put( "key", value )</code>" would be "<code>map[ key ] = value</code>", and a
"<code> map.get( "key" )</code>" would be "<code>map[ "key" ] </code>". In JavaScript,
<code>value</code> could be of any type.
</p>
<ul><li><b>System.out.println and console.log</b></li></ul>
<p>
All modern desktop browser have some form of javascript console. They all have at least one
function in common that can be used like <code>System.out.println</code>, which is
<code>console.log</code>. Some browser also have <code>console.trace</code>. Browser not
supporting <code>console.log</code> (or in case of InternetExplorer, not having it activated),
will crash when calling that method, so remember removing all occurrences of console from your
JavaScript code after debugging.
</p>
<ul><li><b>Math and Math</b></li></ul>
<p>
The Java <code>Math</code> class and the JavaScript <code>Math</code> object have almost
identical <a href="http://www.w3schools.com/jsref/jsref_obj_math.asp">API</a>.
</p>
<ul><li><b>Date and Date</b></li></ul>
<p>
The JavaScript constructor
<code><a href="http://www.w3schools.com/jsref/jsref_obj_date.asp">Date</a></code>
creates objects almost identical in API to instances of Javas <code>Date</code> class.
</p>
<ul><li><b>Regular Expressions and RegExp</b></li></ul>
<p>
JavaScript also supports
<a href="http://www.w3schools.com/jsref/jsref_obj_regexp.asp">regular expressions</a>.
</p>
<ul><li><b>char and string</b></li></ul>
<p>
JavaScript has no <code>char</code> type. For RWT Scripting, a string with a length of one
character is used instead. This allows for comparison like
"<code>event.character == "A"</code>", but not "<code>event.character &gt;= 65</code>".
To do that use <code>charCodeAt</code>.
Example: "<code>event.character.charCodeAt( 0 ) &gt;= 65</code>".
</p>
</body>
</html>