Interactive Dice: Calling JavaFX Script from JavaScript

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. Installation and configuration instructions are available at http://java.sun.com/javase/6/webnotes/6u10/plugin2/index.html. 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 of javafx.scene.Group so 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.fx
  • src/dice/GUI/Faces.fx
  • src/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:

Source Code
...

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.

Source Code
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:

Source Code
...

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:

Source Code
...

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

  1. To package the applet for inclusion in a web page, use the javafxpackager tool:
    cd src
    javafxpackager -src ./dice -appClass dice.Main.Dice -appWidth 465 
    -appHeight 325 -sign -pack200 -draggable
    
    The javafxpackager tool creates a dist directory under the current directory and populates it with a number of files.

  2. Edit dist/Dice.html and add an id to the generated code so that JavaScript will know how to find the applet:
Source Code
<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.

Source Code
<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:

Source Code
<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.

Rate This Article
Discussion

We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of replies—your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.

 

English
日本語
한국어
简体中文
русский
Português do Brasil