The Script Section of a Scriptlet

The script portion of a Scriptlet has a conventional structure. We must expose the public interface of the object, as well as include some internal 'helper' routines. The script procedures we write will provide the Scriptlet behavior, and take care of responding to all the Dynamic HTML events that are supported by the Scriptlet itself. Using special naming conventions, we declare some of these routines as public and allow the container script code to access them. Scripting may use a mix of VBScript, JavaScript, JScript, and any other scripting language supported in the browser.

Defining the Public Interface

At the start of this chapter, we saw one way to define the interface of a Scriptlet – with VBScript and the implicit declarations which use the public_ prefix. JavaScript offers a public_description object that works as a class definition module. In a certain sense, using this object is similar to writing a header file for a C++ class. We can use it to assign properties and methods to the Scriptlet by name. For each attribute, we specify the name of the 'helper' script routine that implements it. A container's element will refer to that attribute using an external name, which is determined following a special naming convention.

 

First of all, we have to initialize the public_description object like this:

<script language="Javascript">
public_description = new CreateThisScriptlet();
</script>

CreateThisScriptlet is not a system routine, but just the name we give to the actual function we want to use to create the interface. This is a typical body for it:

function CreateThisScriptlet () {
  this.put_color = put_color;
  this.get_color = get_color;
  this.title = window.document.title;
  this.paint = doPaint;
  this.event_onPainting = "";
}

CreateThisScriptlet defines properties, methods and events for the new object, assigning them dynamically to the this pointer. As we saw earlier in the chapter, to fully expose a property called color we need to write two script procedures called get_color and put_color. They must be assigned to the corresponding elements in the public_description object, and take care of reading and writing the property value. In the get function we usually only need to return the current setting. The put function, however, saves the new value and often applies the changes to the Scriptlet output. Notice that we must use the put and get prefix to qualify it as a read/write property. If we want a read-only or write-only property, we just omit the declaration we don't need.

Defining Methods and Events in JavaScript

Methods don't follow any convention, and the name we assign to the public descriptor is the same one we invoke outside the Scriptlet. In the above sample, the doPaint procedure will execute after any external caller invokes the paint method. There is absolutely no need to implement the paint function with a script procedure called doPaint—though it is preferable for the sake of clarity. Feel free to give it the same name as your dog if you prefer!

Events have no actual code implementation. In an ActiveX control, for instance, you need to declare events just to let the development tool build the proper type library, so that the container knows about these events. This is also true for Scriptlets. A component fires events, but the actual code that runs in response is decided and executed in the container page. In other words, the host and object swap their respective roles. When raising an event, any object (Scriptlets, ActiveX controls) ends up invoking a dynamic method on the host, allowing the host to provide the code that is executed in response to the event.

At first sight, the line

this.event_onPainting = "";

seems to be necessary to make sure that event is properly notified and received. If you want a Visual Basic 5 ActiveX control to raise a given event, you absolutely need to declare it. This ensures that the container will get a pointer to a connection-point interface that includes an entry-point with the name onPainting:

Event onPainting(ByVal data As Long)

This is certainly true for ActiveX controls, but not for Scriptlets. If we want our Scriptlet to raise an event called onPainting, we don't need to declare it explicitly, because the Scriptlet custom events are all routed through the same entry-point: onScriptletEvent. More on this later. For now, just keep in mind that we aren't strictly required to declare events, but it is nice if we do so simply for documentation's sake.

Javascript vs VBScript

In the previous section, we discussed how to define the Scriptlet's public interface using JavaScript . As we saw at the beginning of this chapter, this is not the only approach we can take. By using a special naming convention for all the properties and methods you want to expose, you can make them public by design.

If you're an expert Windows programmer, this should sound familiar, and remind you of the two options you have for exporting functions from within a DLL. You can list all of them in a DEF file, or you can make them public using the special keyword declspec(dllexport) in the prototype.

Look at how to convert the previous sample using the public keyword:

Function public_get_color
Sub public_put_color 
Sub public_paint
' note that there are no declarations for events

This approach, called default interface description, allows us to use VBScript instead of JavaScript. However, bear in mind that the JavaScript approach creates a single niche in our code where the whole of the object's public interface is described. Furthermore, it allows us to have different public and private names for all the methods and properties.

Note that the JavaScript approach always takes priority over the default interface descrition approach if both exist in the same Scriptlet.

Ambient Properties

A Scriptlet runs inside a special container object that offers the following ambient properties.

Property Type Description
scrollbar Boolean If true enables the use of scrollbars. If the Scriptlet content is too large for its actual size, both vertical and horizontal scrollbars will appear automatically. Default is false.
selectableContent Boolean If true allows users to select the Scriptlet's content and drag it or copy it to the clipboard. Otherwise the user can't select anything in the Scriptlet's area. Default is false.
version String Identifies the current version of the Scriptlet container object. At present it is "4.0 Win32".
frozen Boolean Read-only property. If false, indicates that the page hosting the Scriplet is ready to handle events.

These stock attributes are accessible through an object called external, which is a new property of the Dynamic HTML window object. We can use them to control the environment of our Scriptlet to some extent, for example we can enable the use of scrollbars from within a Scriptlet with a line like this:

window.external.scrollbar = true

Accessing the External Object

The external object is available only when the Scriptlet page is actually viewed as a Scriptlet within another page. If we load and view the Scriptlet as an ordinary HTML page (for example, select it up from the File | Open menu) the external object doesn't get initialized. Attempting to use it will then result in a runtime error. Curiously, this situation seems to be handled differently by the two scripting engines that come with IE 4. Consider the following HTML code:

<HTML>
<BODY>

<SCRIPT language="Javascript" for="window" event="onload">
  alert("External.version is: " + window.external.version);
</SCRIPT>

<SCRIPT language="VBScript" for="window" event="onload">
  MsgBox "External.version is: " & window.external.version
</SCRIPT>

</BODY>
</HTML>

Both the scripts catch the onLoad event of the window object, and try to display a message box with the current value of the window.external.version property. The JavaScript procedure traps the exception and returns the string "undefined", while the VBScript procedure produces the error like the one shown here:

Detecting When a Scriptlet is Running – Our Custom InScriplet Variable

A Scriptlet is nothing more than a Web page viewed through a special container. Though it may not always make sense, we might also want it to display properly if viewed directly with a browser, rather then embedded into another page. Unfortunately, this produces a runtime script error if we refer to the window.external object in our code. It would be nice to be able to detect whether the page is being viewed directly in a browser, or as a Scriptlet. In this way, we could only access the ambient properties when appropriate.

To do this, we introduce a custom JavaScript variable named InScriptlet into our Scriptlet code, and set it to true if we are running the page as a Scriptlet, or false if not. This is done using the JavaScript typeOf method, which tells us what kind of value a variable or property is holding:

InScriptlet = (typeOf (window.external.version)=="string");
if( InScriptlet ) {
  window.external.scrollbar = true;
}

JavaScript will never prompt us with a script error. In the above situation, the window.external.version property will be of type string if it actually exists (i.e. we are running a Scriptlet), or undefined if the external object doesn't exist because we are running the page directly.