Javascript Wrappers for Java Applets

The term "wrapper" is frequently used to describe one piece of computer code used to deploy a second piece of code. Typically these code segments are written in different languages and thus serve to bridge between them. (If a single language would suffice there would be no need for the wrapper and these "two pieces of code" would simply be parts (e.g., methods) of a single program.

This document describes how javascript is employed as a wrapping language for deploying EarthStones' applets.

This issue arises as a consequence of one of the major tenets EarthStones' approach to web design and programming: All web page elements should transparently scale for the viewer's screen resolution so that any page will appear the same regardless of resolution (ignoring, of course, the jaggedness that appears as resolution diminishes).

EarthStones' Java software employs its own AutoRes technology to transparently scall all java objects created by an applet for rendering an HTML document on the screen. The web designer using the applet (or the java programmer customizing or extending it) has to do nothing further to take advantage of this functionality.

There is, however, one limitation imposed by HTML. All Java applet must reside in an HTML document and are deployed using the <applet> tag (see example). This tag reserves space in the document for the applet through its width and height parameters. And the Java applet cannot modify the size of this reserved space. It is free, of course, to use less than the alloted screen real estate, but this arean cannot be increased and any image larger than that specified in the <applet> tag will be truncated.

This means, of course, that if we wish to size the applet itself (rather than the page elements created within the applet) we cannot do so within the applet itself. Rather, this must be accomplished before the applet is executed. And this is where the javascript wrapper comes in handy.

Two examples are provided below:

Wrapping an Inserted Image

The following javascript code uses the applet JImage to insert an image (with optional caption) into an HTML document. Its job is simply to set up the <applet> tag so it reserves exactly the space necessary to display the image and its caption at the viewer's screen resolution.

<!--
JAVASCRIPT WRAPPER FOR JImage APPLET
-->

<script><!--
// THE CALLING CODE
// This code calls the ImageInsert function and should be inserted into the HTML
// document at the point you want the image to appear

var img = new top.ImageInsert(self, "images/huecot~1.jpg", "Tlaloc Efficy, 1200-1425",
"Split yucca or sotol stalk splints sewn with fibers; found in U-Bar Cave, Hidalgo County, New Mexico (1).",
3, "right", 10, 10, 300, 420, 0.95);
//--></script>


<script><!--
// THE IMPLEMENTING CODE

// This code defines the ImageInsert function and is typically placed within the <head> </head> tags
// of an HTML document which the calling code can reference (e.g., in its top frame in this example).

function ImageInsert(frame, image, title, desc, lines, // required arguments
align, hspace, vspace, // optional arguments
width, height, scale) {
// the variable rf is the scaling factor for the user's screen resolution and, if
// not already initialized, is set by calling the java getResFactor() method of
// the JavaSiteApp applet which is already running in the frame top.main.page.

if (rf == null) rf = top.main.page.document.JavaSiteApp.getWeb().getResFactor();

// set up all parameters

var args = ImageInsert.arguments.length
this.frame = frame
this.image=image
this.title = title
this.desc = desc
this.lines = Math.round(lines/rf);
if (args <= 5) this.align = "left"; else this.align = align
if (args <= 6) this.hspace = 10; else this.hspace = hspace
if (args <= 7) this.vspace = 10; else this.vspace = vspace
if (args <= 8) this.width = 0; else this.width = width
if (args <= 9) this.height = 0; else this.height = height
if (args <= 10) this.scale = 1.0; else this.scale = scale

// scale width and height

var wd
var ht

wd = Math.round(this.width*this.scale*rf)

// include space for image caption

ht = Math.round(this.height*this.scale*rf) + captionHeight(lines);

// set alignment to default (LEFT) if not specified

if ((this.align.toUpperCase() != "LEFT") &
(this.align.toUpperCase() != "RIGHT") &
(this.align.toUpperCase() != "TOP") &
(this.align.toUpperCase() != "TEXTTOP") &
(this.align.toUpperCase() != "MIDDLE") &
(this.align.toUpperCase() != "ABSMIDDLE") &
(this.align.toUpperCase() != "BASELINE") &
(this.align.toUpperCase() != "BOTTOM") &
(this.align.toUpperCase() != "ABSBOTTOM"))
this.align = "LEFT"

// The following code includes vspace, hspace in the image
// rendered by JImage rather than letting HTML page take care
// of it

wd = Math.round(2*hspace*rf) + wd;
ht = Math.round(2vspace*rf) + ht;

// construct the HTML <applet> tag

str = "<APPLET CODE='JImage.class' CODEBASE='java' NAME='JImageApp'" +
" ALT='" + this.title + "' WIDTH=" + wd +
" HEIGHT=" + ht + " hspace=0 vspace =0 align='" + this.align + "'>" +
"<param name='image' value='" + this.image + "'>" +
"<param name='orient' value='" + this.orient.toUpperCase() + "'>" +
"<param name='imgWidth' value=" + this.width + ">" +
"<param name='imgHeight' value=" + this.height + ">" +
"<param name='scale' value=" + this.scale + ">" +
"<param name='title' value='" + this.title + "'>" +
"<param name='desc' value='" + this.desc + "'>" +
"</APPLET>"

// write string to frame document

frame.document.write(str)
}

// The following helper functions supply the caption
// line height at the current screen resolution needed
// by ImageInsert

var captionLineHeights = new resArray(16, 14, 14, 12);

function resArray(r0, r1, r2, r3) {
this.size = 4;
this[0] = r0; this[1] = r1; this[2] = r2; this[3] = r3;
return this;
}

function atRes(val) {

// the variable res is the current user's screen resolution and, if
// not already initialized, is set by calling the java getResolution() method of
// the JavaSiteApp applet which is already running in the frame top.main.page.

if (res == null) res = top.main.page.document.JavaSiteApp.getWeb().getResolution();

if (res == resolutions[0]) if (val.size == null) var ret = resfactors[0]*val;
else var ret = val[0];
for (i=1; i<resolutions.size; i++) {
if (res == resolutions[i]) if (val.size == null) ret = resfactors[i]*val;

else ret = val[i];
}
if (val.size == null) return Math.round(ret)
else return ret
}

//--></script>

Note:

First, the javascript code is shown above as two separate scripts enclosed within the HTML <script> </script> tags. Although it could be written as a single script element, the calling code will typically be used multiple times in several different HTML documents while the implementing code need appear only once.

Furthermore, the two code segments need not appear in the same HTML document or even in the same frame. In the above example, the implementing code is assumed to be located in the top document frame. Then any number of documents in its child frames can reference the function as top.ImageInsert().

Second, to do its job, the javascript code must know about the viewer's screen resolution. Here it uses the variable resolution factor (rf) do do the scaling as well as the screen resolution (res) itself. In the example, if not already set this value is obtained by using javascript to call the Java function getResFactor() in a second Java applet (JavaSiteApp) assumed to already be running in another HTML document (so document.JavaSiteApp must be specified to find the applet in that document) in another frame (here, top.main.page, the frame within which the applet is running, is a child of the frame main, which in turn is a child of the top frame).

See the next example for more about letting javascript know about the user's screen resolution.

Deploying the First Applet: The Bootstrapping Problem

So far we have assumed that there is already a Java applet running to let us know the screen resolution (or the appropriate scaling factor). What happens when we need to use javascript to scale that first applet's reserved space but we don't yet know the screen resolution? (I have not found a way to determine the screen resolution in javascript itself. But one of you might know.)

There is a case in which this problem is a non-issue: If a single java applet is to hog all of the screen real estate, simply set the applet's dimensions to large enough values to take up the entire screen. Then EarthStones AutoRes feature will take it from there. In Version 3 of this website, this happy situation is indeed the case. (It turns out to be convenient to set the applet's dimensions to 1024x610 because the available screen height in the browser window is reduced by the browser's toolbars, etc. AutoRes wants dimensions based upon a 1024x768 screen resolution and then scales them up or down for the user's actual resolution.)

Now, suppose (as was the case in Version 3) we do wish to know the screen resolution in advance in order to set the applet's dimensions to their scaled values in the applet tag. (You may wonder why we go to so much trouble. The answer has to do with the fact that, at lower screen resolutions, there are no extra pixels to spare. For more about this issue see background of the development of this web site.)

The solution to this bootstrapping problem involves some slight-of-hand in which an invisible document is loaded first, running just long enough to detect the user's screen resolution. This initial document then selects a second from list of documents, depending upon the resolution found. And this second document in turn sizes the applets in the manner already described above.

This process will be briefly outlined here:

First, the following code fragment from the site's "home page" file index.html gets the ball rolling:

<html>
<head>
<frameset cols=(*, 0) frameborder=0>
<frame src="SelectRes.htm" name="main" scrolling="no" noresize marginwidth=0 marginheight=0>
<frame src="blank.htm" name="dummy" scrolling="no" noresize marginwidth=0 marginheight=0>
<noframes>
<!--code for no-frames version of page goes here-->
</noframes>
</frameset>
</head>
</html>

Although the dummy frame is just what its name implies--it will never have anything in it but a blank HTML document with its background set to white and no width at all--it is useful for a couple of aesthetic and technical reasons. We could dispense with this frameset entirely, simply load the selectRes.htm document (which would then be called index.html instead), and go from there. But "go from there" will mean that the code in the selectRes document, will in turn load another file and soon we have a parade of documents shuffling into and back out of the browser's little web address window. By using the frameset shown here, all the action will be taking place as documents are loaded into the main frame but it will always appear to the user that everything is happening in the index.html document. An added bonus: Any javascript code referenced by documents in child frames of main can be located in index.html and referenced as top.[identifier].

The reason for the zero-pixel width of the dummy frame is more obscure. It turns out that Netscape Navigator doesn't like a frameset with a single frame although it does accept the addition of a second frame with a zero dimension: hence, the very skinny dummy frame defined above.

Next, the file SelectRes.htm loads and executes the Java applet SelectRes.class:

<html>
<head></head>
<body bgcolor="#FFFFFF" topmargin="10" leftmargin="0">
<applet code="earthstones/SelectRes.class" codebase="java" width="2" height="2">
</applet>
</body>
</html>

By default, if the user's screen resolution is found to be 800x600, the SelctRes applet will look for a file named 800.htm and load it into the same browser frame in which the applet itself is running. SelectRes takes two optional parameters which generalize the situation slightly:

A file attribute can be specified using the following tag:

<param name="file" value="myfile.htm">

In this case the applet will expect to find a file named myfile_800.htm, and so on for the other screen resolutions. Also, a frame attribute can be set in a similar tag; in this case the new file will be loaded into the frame set by the value of the frame attribute rather than into the applet's own frame.

At this point the only thing remarkable about the above code is the fact that SelectRes, an invisible applet whose only mission is to unobtrusively detect the user's screen resolution, has a width and height of 2. It turns out (see Disappearing Applets) that Netscape Navigator 3 ignores applets with dimensions of 0 or 1!

What is important for this discussion is not the applet's straightforward Java code which detects the user's screen resolution but rather the action taken based upon this information: the file (in or example) 800.htm is loaded into the document's main frame.

Third, the file 800.htm executes the following code, calling the javascript wrapper function loadFrames(res):

<HTML>
<HEAD>
<script language="JavaScript" for="window" event="onLoad()"><!--
top.loadFrames("800x600");
//--></script>
</HEAD>
</HTML>

Clearly, this is a very low-class kludge: using the file name itself to pass information about the user's screen resolution to the javascript wrapper. But it works! Although other approaches such as using a cookie were attempted, they didn't seem to work in Internet Explorer. The files for the other screen resolutions are identical except, of course, for the file name and the value passed to loadFrames().

And finally, the wrapper function loadFrames() (located in the document index.html which remains loaded in the browser's top frame) sizes main's child frames and loads the documents which will execute their applets. Code similar to the following was used to load the banner, page, and footer frames in Version 2 of this website:

function loadFrames(theRes) {
// The value for the screen resolution passed to
// this function is saved as the javascript variable res
// and is then used in setting the scale factor rf.
// These data are then available for subsequent javascript
// calls (e.g., top.rf)

res = theRes;
rf = atRes(resfactors);

// construct the HTML <applet> tag

str = "<HTML>" +
"<head>" +
"<title>EarthStones</title>" +
"<frameset rows='" + Math.round(rf*34) + ",*," + Math.round(rf*45) + "' frameborder='0'>" +
"<frame src='Banner.htm' NAME='banner' SCROLLING='no' NORESIZE marginwidth=0 marginheight=0>" +
"<frame src='Page.htm' NAME='page' SCROLLING='no' NORESIZE marginwidth=0 marginheight=0>" +
"<frame src='Footer.htm' name='footer' scrolling='no' noresize marginwidth=0 marginheight=0>" +
"</frameset></head></html>"

// write string to frame document

top.main.document.write(str);
top.main.document.close();
}
//--></script>