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.
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.
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.
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.
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
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:
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.