All Scriptlets runs in an HTML page by the means of an embedded container. This container recreates for the Scriptlet the same habitat as IE 4, and makes available the ambient properties listed earlier. In addition, it provides the following methods:
Method | Description |
bubbleEvent |
Passes the current event down to the host environment, whether an HTML page or a Visual Basic form. |
raiseEvent |
Fires a custom event for the Scriptlet. Each event is identified by name but routed through the single onScriptletEvent of the container. |
setContextMenu |
Allows us to associate a pop-up menu with the Scriptlet. The menu will appear after a right-click on the Scriptlet's area of the page. |
A Scriptlet receives notification of all the standard Dynamic HTML events for the window
and document
objects, and any other elements it contains. bubbleEvent
is the method we use to pass (or bubble) any events we trap in the Scriptlet back up to the container. If we don't pass these events explicitly, event bubbling stops at the container and the host HTML page (or VB form., etc.) will never know that an event occurred.
If we want to handle an onclick
event, for example, then bubble it back up to the container page, we could use the following code within our Scriptlet:
<script language="VBscript"
for="document" event="onclick">
if (InScriptlet) And Not (window.external.frozen) Then
window.external.bubbleEvent
end if
</script>
Aside from just checking to see if the Scriplet page is being run as a Scriptlet (indicated by the InScriptlet
variable) we also have to check to make sure that the hosting container is ready to receive events. This is done by making sure that the window.external.frozen
property is false
.
Once the event gets back up to the container page, we can handle it there. We do this using the container window's event
object, and examine it to get information about the event. So code to handle a bubbled event might look like this:
<script language="jscript" for="Scriptlet1" event="onclick">
// inspect event object to see which button was clicked
if (window.event.button = 2)
alert ("Right-Button Clicked");
</script>
If the Scriptlet is hosted in a non-Web browser environment, such as a C++, Visual Basic, or Office97 application, we have to use the event
object that hangs of the Scriptlet control, rather than the integral window
object's event
object. We'll look at hosting Scriptlets in these kinds of applications towards the end of this chapter.
The raiseEvent
method fires custom events, such as the onPainting
event we discussed earlier. Its prototype is
RaiseEvent(name, data)
The following is a typical call to the raiseEvent
method:
window.external.raiseEvent "onPainting", window.document
The first argument of raiseEvent
is the name of the event we want to raise. The second is the data we want to associate with the event, and notify to the receiver. Whatever name we assign to the event, it will always be detected and handled in the container through the onScriptletEvent
– which is the counterpart of raiseEvent
. Because of this, there is no need to declare our events in a Scriptlet, as they all arrive through the built-in onScriptletEvent
event.
When the client is notified of a custom event (i.e. one that is not a standard DHTML event), it can distinguish which of the Scriptlet's event this is with multiple if..then
statements, or with a select..case
or switch
statement—based on the name
argument. For example:
<script language="jscript" for="Scriptlet1" event="onscriptletevent(eventname, eventdata)">
if(eventname == "onPainting") {
alert("Start painting");
}
else {
if(eventname == "onEndPainting") {
alert("End painting");
}
}
</script>
The Scriptlet would raise these events to the container using:
window.external.raiseEvent "onPainting", window.document
window.external.raiseEvent "onEndPainting", window.document
The window.document
parameter simply represents the data we want to pass to the container's event code. This, of course, will change according to the actual requirements of your code. As previously discussed, the window.external.frozen
property should be checked before firing events.
All the events a Scriptlet raises are perceived by the container as occurrences of the same event, onScriptletEvent
, with different parameters. This also means that we can define events at any time, and qualify them with a string.
The Scriptlet container object also allows us to create and assign a pop-up context menu to a Scriptlet. To do this we must use either VBScript or JavaScript, as these are the only script languages guaranteed to create arrays compatible with the expectations of the setContextMenu
method.
To create a context menu that shows n items, we define an array of 2*n elements. For each pair of elements in the array, the first item is the caption that will be shown on the menu, while the second is the name of the function that will executed when the user selects that item on the menu (notice that these procedures cannot take parameters). If we want an item separator, just add a couple of empty items. Finally, we pass the array to the setContextMenu
method of the window.external
object:
<SCRIPT language="VBScript" for="window" event="onload">
dim menuItems(6)
menuItems(0) = "&One"
menuItems(1) = "One"
menuItems(2) = "&Two"
menuItems(3) = "Two"
menuItems(4) = "&Three"
menuItems(5) = "Three"
window.external.setContextMenu(menuItems)
</SCRIPT>
Once we have assigned the context menu to the external
object, we're done. We have to do nothing more to set up the menu, and have no need to investigate which button the user presses. The container takes care of handling the right-click event, and running the appropriate code, automatically. Here's the result:
The best place to create the context menu is the window
.onLoad
event, as we did in the previous example. If we use a different event, we have to wait for that event to occur before the context menu is initialized and displayed. The context menu can be changed in code during Scriptlet execution, but all the items displayed at any moment in time must occupy consecutive locations in the menu items array. The following example produces the same menu as in the previous example the first time it is clicked. From the second time onwards, the menu changes to include a separator and a fourth menu item:
<script language="VBscript">
dim menuItems(10)
dim bFirstTime
Sub InitMyScriptlet
if InScriptlet then
window.external.selectableContent = mSelectable
menuItems(0) = "&One"
menuItems(1) = "One"
menuItems(2) = "&Two"
menuItems(3) = "Two"
menuItems(4) = "&Three"
menuItems(5) = "Three"
menuItems(6) = "xxx" ' stub the next items
menuItems(7) = "" ' stub the next items
window.external.setContextMenu(menuItems)
bFirstTime = 1
end if
End Sub
Sub document_onmousedown
if bFirstTime = 0 then
menuItems(6) = "" ' separator
menuItems(7) = "" ' separator
menuItems(8) = "&Four"
menuItems(9) = "Four"
window.external.setContextMenu(menuItems)
else
bFirstTime = 0
end if
End Sub
Sub One
MsgBox "One"
End Sub
Sub Two
MsgBox "Two"
End Sub
Sub Three
MsgBox "Three"
End Sub
Sub Four
MsgBox "Four"
End Sub
</script>
The context menu gets initialized first in window.onload
event, then is updated in the document_onmousedown
event. In any case, before the menu is displayed, our Scriptlet will receive an onmousedown
event, so we have the opportunity to initialize it, or make changes to it, there. Here's the result—you can run or download this page, MenuDemo.htm
, from our Web site at http://rapid.wrox.co.uk/books/0707
:
If you aren't using different arrays for different menus, you probably declared an oversized array to make room for new items. In this case, we recommend you use:
menuItems(6) = "xxx" ' stub the next items
menuItems(7) = "" ' stub the next items
to suppress the unnecessary items, by assigning a non-empty caption and an empty or non-existing procedure. Otherwise you risk being flooded with item separators from the multiple empty strings.