A Few Gotchas

Before we go on to discuss how we can host Scriptlets inside desktop applications rather than Web pages, there are a few 'gotchas' to be aware of when developing Scriptlets. Some that almost drove us crazy are:

Resizing the Scriptlet's Site

The HTML code in our Clock component begins with:

<HTML ID=MyPage>

Why would we want an HTML page to have an ID, you might ask? It's because we then can access the frame window of the Scriptlet. This window (as you'll recall from the earlier section "What happens behind the scenes") is the second Internet Explorer_Server window. Once we can get a reference to it, we can change its size:

Set s = MyPage.style
s.pixelHeight = s.pixelHeight + mVertOffset 
s.pixelWidth = s.pixelWidth + mHorzOffset 

To change the height and width of a Scriptlet site, we need to access the style object of the HTML page itself, as shown above. Once we've got a reference to this style object (remember that the page represents the Scriptlet as a whole), it's easy to access and modify the pixelHeight and pixelWidth properties. If we don't define an explicit ID for the page, we're unable to do this.

Canceling Scriptlet Events

Custom Scriptlet events are a wonderful invention, but they don't return a value that can be examined to cancel the event—so you have to do your best to work around it. (For standard bubbled events, however, we can return a value via the event object).

A simple trick is to define a write-only property, say CancelEvent, then set it to true from the host page's script code if we need the Scriptlet to stop executing the rest of its code. Suppose that in the Scriptlet we raise an event before accomplishing a given task:

function put_CancelEvent(b) {
  mCancelEvent = b;
  return 1;
}
...
window.external.raiseEvent "onBeforeSomething", 0
if Not mCancelEvent then
  ...
  ... do something
  ...
end if 
...

The member variable mCancelEvent is the internal buffer that represents the CancelEvent property. The onBeforeSomething event reaches the host environment before the Scriptlet performs the "do something" actions. Here, the host page can cancel the event by just setting the CancelEvent property to true:

<script language="VBScript" for="Scriptlet1" event="onscriptletevent(n,o)>
  If n = "onBeforeSomething" Then
    Scriptlet1.CancelEvent = True
  End If
</script>

Hidden Scriptlets

Scriptlets are code components that resemble ActiveX controls. Consequently, there might be situations where we would use a Scriptlet as a silent and invisible 'server'. For instance, think of the Clock sample we discussed earlier. In some situations, we might want to hide it and just exploit its alarm function. We don't want the Scriptlet to have a user interface, but we still need to use it as a regular object. The easy way out is to set the visibility property of its style object to hidden:

<object id=Scriptlet1 width=1 height=1 type="text/x-scriptlet"
   data="scriptlet.htm" style="visibility:hidden">
</object>

This technique can be used to add an invisible Scriptlet to any HTML page. If you just need a hidden component, that's all you need to do. However, sometimes things get a bit more complicated. For instance, we might want the object to appear and disappear on command. Look at the screenshot below:

You can run or download this page, RunClock.htm, from our Web site at http://rapid.wrox.co.uk/books/0707.

The page shows the existing Stop button, plus three new buttons related to the visibility of the clock. The Hide button removes and inserts the entire Scriptlet in the page, while the other two act to show or hide parts of the clock component in different ways, without affecting the container. We can take one of three possible approaches to hiding a Scriptlet dynamically:

Changing the style.visibility Property

visibility is a property of the style object, and applies to every element that can appear in an HTML page. To make an element visible we assign "visible" to the property, otherwise we hide it using "Hidden". We don't necessarily need to do it in the host page, as shown earlier. We can teach our Scriptlet to change the flag itself. To demonstrate this, we added a new SetVisibility method to the Clock sample, which runs when the Hidden button is clicked:

function SetVisibility(b) {
  if(b) 
    document.all("Table1").style.setAttribute("visibility", "visible");
  else 
    document.all("Table1").style.setAttribute("visibility", "hidden");
  return 1;
}

It works on the table element that actually implements the clock. The table has been assigned an ID of Table1:

<TABLE id="Table1">
...
</TABLE>

The function retrieves the style object of the element and changes the value of the visibility attribute according to the Boolean parameter passed to the routine. The effect of hiding it is shown below:

As you can see, only the table with the clock in it has disappeared. The text label and the rest of the Sciptlet's page remain unchanged. This happens by design, because visibility doesn't free the space occupied by the element it actually hides.

Changing the style.display Property

The display property, on the other hand, does just this. Except for the arguments it accepts, display works the same as visibility. The only difference is that it physically removes the element from the Scriptlet document:

Notice in this screenshot that the label shifts upwards when we remove the clock. The display property tells IE4 whether the element should be rendered or not—it expects the value "None" for removing the element, and an empty string (the default) for rendering it. We've implemented this capability in the SetDisplay routine that runs when the Display button is clicked:

function SetDisplay(b) {
  if (b) 
    document.all("Table1").style.setAttribute("display", "");
  else 
    document.all("Table1").style.setAttribute("display", "none");
  return 1;
}

At this point, you might think that hiding the entire Scriptlet from the host page is an insignificant task. You're wrong. Yes, you can access the display property of the whole Scriptlet like this:

document.body.style.setAttribute("display", "none");

or get the host page to change the Scriptlet's display property, like this:

Scriptlet1.style.setAttribute("display", "none");

Resizing the Scriptlet

Both the methods we saw above make the Scriptlet completely invisible. However, the Scriptlet's area of the host page is not always properly cleared—particularly in the case of the first method. And if we want to do the job from code within the Scriptlet component, this is the method we have to use. If we want the clock to disappear leaving no footprints, we may need to resize its window as well. This causes the container page to redraw itself properly, as if we had instantiated an invisible object:

Here's the code for the Hide button, that implements this technique.:

function Show(b) {
 if (b) {
   MyPage.style.pixelHeight = mHeight;
   MyPage.style.pixelWidth = mWidth;
   document.body.style.setAttribute("display", "");
 }
 else {
   document.body.style.setAttribute("display", "none");
   mHeight = MyPage.style.pixelHeight;
   mWidth = MyPage.style.pixelWidth;
   MyPage.style.pixelHeight = 1;
   MyPage.style.pixelWidth = 1;
 }
 return 1;
}

To access the style object for resizing the Scriptlet, we need to specify the ID of the Scriptlet page (MyPage) as we saw earlier. This code assigns the current height and width to global member variables, then resizes the site to1 x 1 pixels (using zero causes the Scriptlet object to be invalidated). It can be resized back to the original size using these stored values.

Matching in with the Host Page's Environment

A Scriptlet can access its parent (host page) environment, and read—say—the background color of the page in order to insert itself neatly in the host frame. The parent window object is available from the window.parent property. So, in order to adopt the parent's background color, the Scriptlet can just use the following code, probably in the window_onload event:

document.bgColor = window.Parent.document.bgColor

In fact, this technique can also be employed to inherit stylesheet information from the parent, or any other exposed properties of the host document.