Interactive Dice: Calling JavaFX Script from JavaScript
- Skill Level Intermediate
- Supported Versions JavaFX 1.2
- Key Features Language Interoperability
- Last Updated May 2009
In his article on the JavaFX - JavaScript Bridge, Sun engineer Ken Russell described how an applet written in the JavaFX Script programming language can interact with JavaScript code in the web page that contains it. This article re-visits that topic to elaborate on one-way communication from JavaScript into the applet.
Introduction
If your browser is running JRE 6u10 software at minimum, you should see a pair of dice rendered below. Click the following HTML buttons to set their values. If JavaScript exceptions are displayed, you must update your JRE implementation release software version.
Note: JRE6u10 software ships with two versions of the Java Plug-In software (classic and next generation). The applet in this page requires functionality only available in the "next generation" version. If your browser is configured to run the classic version, you will encounter exceptions when setting the values of the dice. Mac users will not be able to run this applet because that version of the plug-in is not yet available.
SET D1
SET BOTH
SET D2
You can program the dice for this applet in many different ways. One particular implementation follows, but remember, the overall goal is to learn how JavaFX Script functions can be exposed to JavaScript. So don't worry about learning every line of code. You'll be alerted to the important parts that are necessary for JavaScript and JavaFX Script interaction.
Package Organization
The code for this applet resides in two packages: dice.GUI, for the graphical user interface elements, and dice.Main, for the main application code.
dice.GUI.DimensionData
Calculates the appropriate coordinates and sizes for the shapes that appear on the dice. This helper file reduces clutter in the other files by separately handling these calculations.dice.GUI.Faces
Defines the GUI code for the six faces of the dice. This file defines six subclasses ofjavafx.scene.Groupso that each rendered face can be treated as its own discrete object.dice.Main.Dice
Provides the application's main entry point, and exposes public script-level functions for setting the values of the dice.
For this discussion, assume that your source files are located under a directory called src. Extracting the contents of dice-demo.zip into src results in the following:
src/dice/GUI/DimensionData.fxsrc/dice/GUI/Faces.fxsrc/dice/Main/Dice.fx
Note that while the final product will be delivered as an applet on a web page, dice.Main.Dice can also be run from the command line as a standalone application.
Source Code Analysis
The dice.Main.Dice script defines a run function that serves as the application's main entry point. The script instantiates the dice, which are subclasses of javafx.scene.Group:
...
var d1: Group;
var d2: Group;
public function run(args : String[]) {
d1 = Faces.Face1{};
d1.translateX = 20;
d1.translateY = 20;
d2 = Faces.Face2{};
d2.translateX = 240;
d2.translateY = 20;
...
Note how the two dice (d1 and d2) only have script-level access. This type of access prevents the outside world from directly setting dice values. Instead, callers, including JavaScript on a web page, must invoke the public script-level functions (setD1 and setD2). These functions provide the necessary checks to ensure that the dice are being set to a value of 1-6.
public function setD1(v: Integer) { ... }
public function setD2(v: Integer) { ... }
The GUI automatically updates when the functions are invoked because the dice are bound to the content of the scene:
...
Stage {
title: "Dice"
width: 480
height: 350
visible: true
scene: Scene {
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
proportional: true
stops: [
Stop {offset: 0.0 color: Color.GREY},
Stop {offset: 1.0 color: Color.BLACK}
]
}
content: bind [d1,d2]
}
}
...
The remaining source code is specific to this particular implementation of the dice. Such details are not necessary for understanding the JavaScript interaction. Just remember the setD1 and setD2 functions. You will see how JavaScript code in a web page invokes these functions later in this article.
Testing From the Command Line
To test this code from the command line, compile and run it as a stand-alone application:
cd src javafxc dice/GUI/*.fx dice/Main/*.fx javafx dice.Main.Dice
You will need to manually invoke these functions each time before recompiling:
...
public function run(args : String[]) {
d1 = Faces.Face1{};
d1.translateX = 20;
d1.translateY = 20;
setD1(5);
d2 = Faces.Face2{};
d2.translateX = 240;
d2.translateY = 20;
setD2(6);
...
If the dice now show 5 and 6 for values, you know that the FX code is working as expected.
Packaging the Applet
- To package the applet for inclusion in a web page, use the
javafxpackagertool:
cd src javafxpackager -src ./dice -appClass dice.Main.Dice -appWidth 465 -appHeight 325 -sign -pack200 -draggable
Thejavafxpackagertool creates adistdirectory under the current directory and populates it with a number of files. - Edit
dist/Dice.htmland add anidto the generated code so that JavaScript will know how to find the applet:
<script src="http://dl.javafx.com/1.2/dtfx.js"></script>
<script>
javafx(
{
archive: "Dice.jar",
draggable: true,
width: 465,
height: 325,
code: "dice.Main.Dice",
name: "Dice",
id: "app"
}
);
</script>
Be sure to add a comma after the name entry on the previous line. It is required and can be easy to miss! You may also need to remove the codebase attribute from files dist/Dice_browser.jnlp and dist/Dice.jnlp if it is present.
Adding the JavaScript Code
Viewing the source of this web page shows you the complete JavaScript listing for this demo. But a summary of the main points should help explain what's going on. In the <head> section (on your system, in the generated dist/Dice.html), two JavaScript functions have been defined that pass off their arguments to the applet. The names setD1 and setD2 have been reused, and a setBoth function has been added for convenience.
<html>
<head>
<script language="javascript">
function setD1(v) {
try {
var myApp = document.getElementById("app");
myApp.script.setD1(v);
} catch (e) {
reportException(e);
}
}
function setD2(v) {
try {
var myApp = document.getElementById("app");
myApp.script.setD2(v);
} catch (e) {
reportException(e);
}
}
function setBoth(v) {
try {
var myApp = document.getElementById("app");
myApp.script.setD1(v);
myApp.script.setD2(v);
} catch (e) {
reportException(e);
}
}
...
</script>
Later, in the body of the HTML, you create the actual buttons:
<STRONG>SET D1</STRONG><BR> <FORM NAME="myform1" ACTION="" METHOD="GET"> <INPUT TYPE="button" NAME="button1" Value="1" onClick="setD1(1)"> <INPUT TYPE="button" NAME="button2" Value="2" onClick="setD1(2)"> <INPUT TYPE="button" NAME="button3" Value="3" onClick="setD1(3)"> <INPUT TYPE="button" NAME="button4" Value="4" onClick="setD1(4)"> <INPUT TYPE="button" NAME="button5" Value="5" onClick="setD1(5)"> <INPUT TYPE="button" NAME="button6" Value="6" onClick="setD1(6)"> </FORM> <STRONG>SET BOTH</STRONG><BR> <FORM NAME="myform2" ACTION="" METHOD="GET"> <INPUT TYPE="button" NAME="button1" Value="1" onClick="setBoth(1)"> <INPUT TYPE="button" NAME="button2" Value="2" onClick="setBoth(2)"> <INPUT TYPE="button" NAME="button3" Value="3" onClick="setBoth(3)"> <INPUT TYPE="button" NAME="button4" Value="4" onClick="setBoth(4)"> <INPUT TYPE="button" NAME="button5" Value="5" onClick="setBoth(5)"> <INPUT TYPE="button" NAME="button6" Value="6" onClick="setBoth(6)"> </FORM> <STRONG>SET D2</STRONG><BR> <FORM NAME="myform3" ACTION="" METHOD="GET"> <INPUT TYPE="button" NAME="button1" Value="1" onClick="setD2(1)"> <INPUT TYPE="button" NAME="button2" Value="2" onClick="setD2(2)"> <INPUT TYPE="button" NAME="button3" Value="3" onClick="setD2(3)"> <INPUT TYPE="button" NAME="button4" Value="4" onClick="setD2(4)"> <INPUT TYPE="button" NAME="button5" Value="5" onClick="setD2(5)"> <INPUT TYPE="button" NAME="button6" Value="6" onClick="setD2(6)"> </FORM>
When the user clicks a button, its value is passed to a JavaScript function, which in turn invokes an applet function. The applet function in turn updates the dice. You can use this same technique in your own FX applets to bring a new level of interactivity to your web pages.
Scott Hommel
Senior Technical Writer, Oracle