The body of a scriptlet might be defined at design time, or created dynamically at run-time through the properties and methods publicly exposed to external callers. If a scriptlet has a predefined body, then it will display if you attempt to see it as a stand-alone page (that is opening the scriptlet's HTML file). Sometimes, however, the user interface must be decided at run-time. For example, an animated button will only know at the last moment which image should be displayed. The scriptlet's body contains an empty <IMG>
tag and if you try to see it as a stand-alone file you'll get a window like the one shown below:
The script code contained in a scriptlet provides the functionality that you require, plus it provides a number of private helper routines that help to give the code a better and more understandable structure.
If you don't need, or don't want, to provide automation capabilities, then a scriptlet is just one page hosted in another. However, this is a short-sighted vision of scriptlets which doesn't really make sense. So finally, we've reached the point and provided a public interface for automation.
When someone examines a scriptlet's property or method the browser passes the control to the scriplet. Such a call is interpreted and then dispatched to the right internal procedure that does the job.
What, however, does "dispatched to the right procedure" actually mean? Simply, each scriptlet holds a public interface structure, which is, in effect, a sort of table of connections. For each externally callable name there is an internal procedure that gets executed. This is quite similar to what occurs with ActiveX controls. The following is the complete source code for a bare-bones scriptlet. The function CreateMyScriptlet
defines the public interface of the component. All the methods and the properties listed there are callable outside the scriptlet.
<html>
<head>
<title>Bare-bones Scriptlet</title>
</head>
<script language="JavaScript">
public_description = new CreateMyScriptlet();
function CreateMyScriptlet () {
this.DoSomething = DoSomething;
this.get_Title = DoGetTitle;
}
function DoSomething () {
alert( "I'm a scriptlet's method." );
return 1;
}
function DoGetTitle () {
return document.title;
}
</script>
<body>
</body>
</html>
The scriptlet exposes a method called DoSomething
and a read-only property called Title
. This read-only property returns the title of the scriptlet's HTML page (that is 'Bare-bones scriptlet'). This file is available on our site as barebone.htm
.
The next page is an example of how to use it.
<html>
<head>
<title>Using a Bare-bones Scriptlet</title>
</head>
<script language=JScript for=document event=onclick>
alert( Scriptlet1.Title );
</script>
<body>
<object id=Scriptlet1 data=barebone.htm width=1 height=1
type="text/x-scriptlet">
</object>
</body>
</html>
This page (available as usebare.htm
) includes the previous scriptlet and assigns it an ID of scriptlet1
. By clicking on the page you cause the onclick
event to be raised and a message box with the title of the scriptlet's page to appear.
Actually, there are two possible approaches that can be taken to define the public interface of a scriptlet. The first one requires the use of JavaScript, while the second is a bit more flexible and allows you to use virtually any scripting language you may know.
In this chapter we will discuss both these solutions. However, as you'll learn later, there are a number of reasons for preferring the pure JavaScript method to the alternative.
With JavaScript you can create a new object called public_description
and assign it the result of a function. This function works as the constructor of the scriptlet object. In one sense, the public_description
variable is like a header file for a C++ class. It defines all the properties and methods that the scriptlet exposes. Here's an example:
<script language="Javascript">
public_description = new CreateOurFirstScriptlet();
</script>
CreateOurFirstScriptlet
is the constructor of the scriptlet. It doesn't matter what name you give it, since it is not a system routine. What's important is what this function actually does. Feel free to consider CreateOurFirstScriptlet
as a placeholder for your own constructor. Inside its body you add properties and methods to the object itself. To identify the object (the scriptlet) you can use the JavaScript keyword this
. What follows is a typical content for a constructor.
function CreateOurFirstScriptlet() {
this.get_ImageName = DoGetImage;
this.put_ImageName = DoPutImage;
this.get_Text = DoGetText;
this.put_Text = DoPutText;
this.Paint = DoPaint;
this.Refresh = DoRefresh;
}
The above function defines a new JavaScript object with two read/write properties—ImageName and Text—and two methods—Paint and Refresh. This object is assigned to the public_description variable.
Let's consider a line such as:
this.get_ImageName = DoGetImage;
On the left side we have a get_ImageName
field belonging to the this
object. On the right, however, there's a name that evaluates to a scriptlet's internal procedure. This means that the object's attribute specified on the left is actually implemented with the procedure, whose name appears on the right side of the assignment. Therefore, the body of the scriptlet's constructor looks like a dispatch table. It is composed of a series of assignments. The values on the left are references to the externally callable names. The values on the right, however, are the names of the internal procedures that actually provide such behaviors.
Unlike a C++ class, a COM object can only have methods. Sometimes, however, in development environments, using properties instead of methods is easier and much more natural. This leads to the definition of a public interface made up of methods, properties and events, plus a private table of functions that always implements them in terms of methods. Consequently, any attribute an object can have—a property or a method—has both a public and a private name. This is a convention that also applies to ActiveX components.
For example, when we call the Title
property of the DHTML document object, we're actually calling one of two different functions exposed by the underlying COM object. Also, none of them are named Title
! Instead, we have two different routines that read the current value and set a new one. This approach makes it much easier to expose read-only or write-only attributes.
To define properties and methods correctly for a scriptlet, we need to take a closer look at the commonly used naming conventions.
Let's now examine the code that forms the body of the CreateOurFirstScriptlet
function more thoroughly.
this.get_ImageName = DoGetImage;
this.put_ImageName = DoPutImage;
The two lines above define a property whose public name is ImageName
. Usually a property is implemented through a pair of Get
/Put
functions. The Get
function takes care of returning the current value of the property, while its Put
counterpart changes it. Scripting languages and Visual Basic shield you from this kind of low-level detail and allow you to call the external name directly. Thus,
Scriptlet1.ImageName = "myImage.gif"
MsgBox Scriptlet1.ImageName
are both legitimate expressions, that evaluate respectively to:
put_Image( "myImage.gif" )
and:
MsgBox get_Image()
Let's consider the following code again:
this.get_ImageName = DoGetImage;
this.put_ImageName = DoPutImage;
The names on the left side of the assignment ought to follow a precise convention. What the scriptlet exposes publicly is what you assign to the this
keyword. If you want to expose a property that can be read and written, then you need to assign the object a couple of functions whose names begin with get_
and put_
, followed by the actual external name of the property.
this.get_Image = <name of the function that actually reads this property>;
this.put_Image = <name of the function that actually writes this property>;
What you assign to the get_ImageName
property will be the name of the internal function that actually returns the current value of the property. What you assign to the put_Image
property will be the name of the procedure that sets a new value for it.
The above code shows how to declare a read/write property called ImageName.
This
property is required to handle the file name of an image needed for the scriptlet behavior. If you want read-only or write-only properties then just omit the unneeded declaration. For example, if we want ImageName
to be read-only we need the following kind of declaration:
function CreateOurFirstScriptlet() {
this.get_ImageName = DoGetImage;
// this.put_ImageName = DoPutImage;
this.get_Text = DoGetText;
this.put_Text = DoPutText;
this.Paint = DoPaint;
this.Refresh = DoRefresh;
}
Note that we commented out the Put function. Similarly, to have a write-only property we must avoid the Get function.
function CreateOurFirstScriptlet() {
// this.get_ImageName = DoGetImage;
this.put_ImageName = DoPutImage;
this.get_Text = DoGetText;
this.put_Text = DoPutText;
this.Paint = DoPaint;
this.Refresh = DoRefresh;
}
What appears on the right side of the assignments is just an internal function name— invisible outside the scriptlet. You can give them any name you like as long as you make sure that the scriptlet actually includes such functions.
A common notation assigns them the same name as the object's functions. In this case they would have been get_ImageName and put_ImageName. If you call them DoGetImage
and DoPutImage
everything will still work fine.
As for methods, there are no particular conventions to follow. You provide the this
object with the external name of the method and assign it the name of the internal function that will implement such a behavior. For example, the following code defines two methods called Paint
and Refresh
that the scriptlet implements through the internal DoPaint
and DoRefresh
procedures.
this.Paint = DoPaint;
this.Refresh = DoRefresh;
You can give these procedures whatever names you prefer without having to follow any rules, as were seen for properties.
Events have deliberately been left out of this discussion of the scriptlet's public interface. Later in the book there will be an entire chapter dedicated to them. In Chapter 6 – Events Handling— we'll be explaining all the details. For now, just a few points to consider and a little advice.
From the syntax's point of view you aren't strictly required to declare events for scriptlets, even if they are regularly fired and handled by the container. A scriptlet can detect and fire two types of events: stock and custom. The first ones are the standard events broadcast by the DHTML event model, such as onclick
, onmouseover
, onmouseout
and so on.
Custom events are non-standard events that the scriptlet triggers during its execution under particular circumstances. Whatever the type of event a scriptlet wants to fire, there's no code that gets executed within the scriptlet itself. Thus, you can raise events when needed without the hassle of declaring them in advance.
The reasons for this will be explained in greater detail in Chapter 6.
Even if you can avoid declaring events, for completeness and the documentation's sake, we suggest you do it anyway—provided your scriptlet makes use of them. In this case, all you have to do is extend the constructor by adding a line for each event fired.
function CreateOurFirstScriptlet() {
this.get_Image = DoGetImage;
this.put_Image = DoPutImage;
this.get_Text = DoGetText;
this.put_Text = DoPutText;
this.Paint = DoPaint;
this.Refresh = DoRefresh;
this.event_OnEraseBkGnd = ""
this.event_OnPaint = ""
}
The suggested syntax is event_
followed by the name of the event you want to raise. In the example above, we've declared two events: OnEraseBkGnd
and OnPaint
. Of course, the right side of the assignment is the empty string, since the events—by design—require no implementation from the server object (in this case, the scriptlet). If you never fire such events, or if you fire other events, the scriptlet will continue to work as before.
So why do we suggest that you declare events anyway? Basically, in fact, for the sake of clarity and completeness. In this way, the whole public interface of the scriptlet will be well defined and consistent.
The first sign of life for a scriptlet is the initialization of the public_description
object. While the constructor can have any name, the object variable that stores that instance must have a fixed one; namely public_description
. If you make an error while typing it, or deliberately use another name, then all that the constructor does will be ignored.
<script language="Javascript">
myScriptlet = new CreateScriptlet();
</script>
<script language="Javascript">
public_Description = new CreateScriptlet();
</script>
For instance, both the above fragments will give you a static scriptlet: unable to respond to any automation call. In other words, your scriptlet will work fine, except that you have no way of invoking its properties or methods. If you do, however, try to call methods and access properties, this is what you will see.
What's going on? Internet Explorer 4.0 expects to find a JavaScript object called public_description
and, through it, access the scriptlet's automation interface. If it fails to find such an object, then the entire public interface remains inaccessible and this causes the message seen above.
Note that when working with the public_description object the case is very important. The object absolutely requires lowercase to work as expected.
The public_description
approach to defining the programming interface of a scriptlet requires you to write some JavaScript code. When you set up the constructor you're ultimately storing in the public_description
the names of the functions to be executed correspondingly. You need such functions to be written in JavaScript. Otherwise you'll get an error message like the one shown above.
This is true only if you're following the public_description approach to defining a scriptlet's public interface.
The JavaScript requirement holds true for at least the outer layer of code. In fact, what really matters is that the name specified in the constructor belongs to a JavaScript procedure. Internally, it might also call directly a VBScript procedure and things will work properly anyway.
For instance, the following code will give you problems:
<script language="JavaScript">
public_description = new CreateMyScriptlet();
function CreateMyScriptlet() {
this.put_Image = DoPutImage; // DoPutImage is a VBScript procedure
this.put_Text = put_Text; // put_Text is a Javascript procedure
}
function put_Text() {
// do something
}
</script>
<script language="VBScript">
Sub DoPutImage
' do something
End Sub
</script>
because the DoPutImage
procedure is a VBScript procedure. If you really want the put_Image
code to be written in VBScript, then you need to do the following:
<script language="JavaScript">
public_description = new CreateMyScriptlet();
function CreateMyScriptlet() {
this.put_Image = put_Image; // Javascript procedure
this.put_Text = put_Text; // Javascript procedure
}
function put_Text() {
// do something
}
function put_Image() {
DoPutImage(); // VBScript procedure
return;
}
</script>
<script language="VBScript">
Sub DoPutImage
' do something
End Sub
</script>
This works because the constructor maintains only references to JavaScript procedures, while you can call VBScript procedures seamlessly from within JavaScript code.
The JavaScript public_description
approach is not the only one you can take to define the public interface of a Scriptlet. Alternatively, you can choose to follow a special naming convention that automatically exports all the functions whose name begins with public_
. In doing so you don't need to use JavaScript, but can use VBScript instead.
If you don't create a valid public_description
object, then all the functions exported are those with a name prefixed by the word public
. For example, to export a read/write property called ImageName
you need to define functions with the following, mandatory, names: public_get_ImageName
and public_put_ImageName
.
<script language="JavaScript">
function public_get_ImageName() {
return mImage; // it is supposed to be the current value of the property
}
function public_put_ImageName( sImage ) {
mImage = sImage;
// probably you need to do something else here…
return;
}
</script>
It's the same for methods. A given Paint
function must be declared as follows:
<script language="JavaScript">
function public_Paint() {
// do something
return;
}
</script>
What about events? Since the default interface description approach requires a function to be self-exportable, events simply don't need to be declared! In fact, as stated before, events are implemented on the container side by design. You can never have a procedure representing an event handler within a scriptlet. Instead, you can have a line of code raising an event. Note that a hypothetical public_event_OnEraseBkGnd
function will be considered as a public method called event_OnEraseBkGnd
.
Let's see for completeness how the previously examined declarations will fit into the default interface description. To make things a bit more open, we'll be using VBScript.
Above we outlined a scriptlet with the following JavaScript public interface:
function CreateOurFirstScriptlet() {
this.get_ImageName = DoGetImage;
this.put_ImageName = DoPutImage;
this.get_Text = DoGetText;
this.put_Text = DoPutText;
this.Paint = DoPaint;
this.Refresh = DoRefresh;
this.event_OnEraseBkGnd = ""
this.event_OnPaint = ""
}
Now let's try to express it using VBScript and the default interface description approach. Properties still need a pair of Get
/Put
functions.
<script language=VBScript>
Function public_get_ImageName()
public_get_ImageName = mImage ' it is supposed to be the current value
End Function
Sub public_put_ImageName( sImage )
mImage = sImage
' do something else if needed
End Sub
Function public_get_Text
public_get_Text = mText ' mText is supposed to be the current value
End Function
Sub public_put_Text( sText )
mText = sText
' do something else if needed
End Sub
</script>
Methods just have their names prefixed with public_
.
<script language=VBScript>
Sub public_Paint()
' do something
End Sub
</script>
Furthermore, no declaration is required for events.
If you choose to follow the default interface description then the language you're using isn't important. You may have methods written in JavaScript and properties in VBScript, but the scriptlet will still make them regularly available to any external caller.
Whatever the topic, each time you have two or more choices, they are rarely exact equivalents. So, at this point, you might be wondering which is the best approach to follow when it comes to defining a scriptlet's public interface. Stay tuned! That's just what we're going to approach next.
After all public_description
is not a keyword—though it is not just an object variable name either. Suppose, for example, you want to use a variable called public_description
in your scriptlet's code and suppose you want to expose the automation interface through the default interface description. In the following code we've declared a generic variable with that name, but never initialized it.
<script language="JavaScript">
var public_description;
function public_DoSomething() {
alert( "I'm a function" );
return 1;
}
Nevertheless, Internet Explorer 4.0 recognizes the presence of a public_description
object and handles it as if it really stores the automation interface of the scriptlet! Thus, it discards the next public method and presumes that the public_description
object is correctly initialized. Since this is not the case (we've just declared the variable) it crashes! The same occurs if you initialize the public_description
variable with a non-object type; say a string. It crashes yet again!
Once you have experienced all this, why not risk a third crash by trying to assign the public_description
a standard object like the Javascript Date?
<script language="JavaScript">
public_description = new Date();
function public_DoSomething() {
alert( "I'm a function" );
return 1;
}
Surprisingly, there are no crashes, but the DoSomething
method still remains inaccessible. Any attempt to call it produces the same error message seen above. What's this all about then? Why doesn't it crash now?
If it doesn't crash, maybe Internet Explorer 4.0 (or rather the component it uses to embed scriptlets in Dynamic HTML pages—more on this later) finds that public_description
now holds a suitable object—a Date object!
Let's try to execute the following code from within a host HTML and see what happens.
<html>
<head>
<title>Use Date</title>
</head>
<body bgcolor="#C0C0C0">
Test page.
<hr>
<p>
<object id="Date1" data="Date.htm"
width="200" height="100" type="text/x-scriptlet">
</object>
</p>
<SCRIPT language=VBScript for=document event=onclick>
MsgBox Date1.getDate() & "/" & (1+Date1.getMonth()) & "/" & Date1.getYear()
</SCRIPT>
</body>
</html>
We have a scriptlet called Date1,
which is implemented in a little file called date.htm
. The full source code for the scriptlet is reproduced below.
<html >
<script language="JavaScript">
public_description = new Date();
</script>
<body>
</body>
</html>
By clicking in the client area of the page (and outside the scriptlet's area) we generate an onclick
event. What happens then is rendered in the following picture.
Our experiment gives an amazing result—we succeeded in cloning the JavaScript Date object.
<script language="JavaScript">
public_description = new Date();
</script>
The few lines above will suffice to make the scriptlet inherit all the methods of the Date object. More cleverly, this technique can be used to obtain real scriptlet inheritance. We will return to this in Chapter 12 – Dynamic Methods and Inheritance.