This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND



Cutting Edge
cutting@microsoft.com        Download the code (98KB)
Dino Esposito

Scriptlets

W
ith the introduction of Dynamic HTML in Microsoft® Internet Explorer 4.0, you can access the content of a Web page in an object-oriented fashion and make changes to it on-the-fly. Just as a macro language is the best way to enhance an application, scripting is the natural way to work with Dynamic HTML (DHTML). Originally, script languages were little more than glue between the various elements of an HTML page. Now, Internet Explorer 4.0 and DHTML give script writers the ability to modify the contents of a page programmatically without going through the server. Since the concept of a page now includes both the content and the code used to change it, scripting evolves from a simple way to automate individual components into the more active role of a content producer. DHTML becomes a real development platform for all HTML-based code, including an enhanced Web-based Windows® shell through the Active Desktop™. As with any newly introduced rich object model, coding starts out a bit more difficult. And as usual, when programming becomes more complex, code reusability becomes a central issue.
Figure 1: Scriptlet Wizard in action
Figure 1: Scriptlet Wizard in action

      Scriptlets are a simple, elegant way to wed DHTML scripting with reusability. They allow you to create HTML-based objects that can be inserted in both HTML pages and ActiveX™ host applications. I'll explore scriptlets and how they work in the Internet Explorer 4.0 environment. I'll also discuss how to write them, and illustrate my explanations with a couple of samples. The source code I'll provide even includes a wizard for the Developer Studio™ environment that gives you a bare-bones scriptlet in a snap (see Figure 1).

What is a Scriptlet Anyway?
      Another name for scriptlets might have been ActiveX script controls—ActiveX components consisting of script code instead of binary code. Of course, there are a number of fundamental differences between a standard ActiveX control and a scriptlet, but from the user's standpoint they are much the same. A scriptlet is nothing more than an HTML file with a body and some script code. It's identified by name, not with an unintelligible 128-bit CLSID. A scriptlet requires neither compilation nor registration and is distributable as is. To use scriptlets, you need the release version of Internet Explorer 4.0 (available at http://www.microsoft.com/ie/ie40) or a late release candidate. Builds as late as Platform Preview 2 include neither support for scriptlets nor documentation for them.
      What do standard ActiveX controls and scriptlets have in common? First of all, the interface appears the same to a programmer. Both controls and scriptlets expose properties, methods, and events and may be driven via automation. Furthermore, both can be hosted in an HTML page and any other ActiveX document container.
      ActiveX controls can boast an edge in performance because they're not interpreted, but scriptlets are far easier and quicker to write, as well as being considerably smaller and self-contained. Scriptlets, though, are made of pure HTML text and subject to undesired inspection as well as inadvertent changes. On the other hand, scriptlets are immediately ready for all OSs that host Internet Explorer 4.0.

Why Scriptlets?
      Scriptlets are an easy way to facilitate scripting code reuse, and DHTML allows you to use the content of a Web page as the ordinary user interface of a desktop app. This means you can implement animation effects and functions like drag-and-drop text with relatively simple code. For example, to select and highlight an image when the mouse is over its client site, you just have to change the src attribute of the corresponding <IMG> tag. Changing the src attribute is easy with DHTML, but a code fragment like this can make the task even simpler and can be reused on any page for any image:


 Sub DoChangeImage(i, sImage)
   Set coll = document.images
   coll.item(i).setAttribute "src", sImage
 End Sub
      The next step is quite straightforward. Why not write an extended version of the image element that can change a bitmap when the mouse moves over it? This would be a self-contained object made of pure HTML code and pieces of VBScript or JScript™ code: a scriptlet.
      If you're thinking that this is just another marketing gimmick, consider that the advantages of having reusable script code become more and more evident when you design and code large-scale Web apps. You can write a scriptlet in a few minutes, and the full edit-compile-debug cycle requires only that you save changes and press F5 to refresh Internet Explorer 4.0. Furthermore, since a scriptlet's interface is nearly identical to that of an ActiveX control, you can port it from VBScript to Visual Basic 5.0 (or C++) and create an ActiveX control at your leisure.

How Scriptlets Work
      A scriptlet is an HTML file that can be nested inside another HTML page, which is known as a container. In its host environment (the container), the scriptlet occupies a given site; this client area is indicated as a tag attribute at design time. The output produced by the scriptlet appears only inside that area. The parent page behaves like it includes a viewer for HTML documents. To the scriptlet, the site appears to be the traditional window object provided by the Internet Explorer scripting object model.
      To include a scriptlet in a page, you use an <OBJECT> tag as if you were working with standard ActiveX controls. The only difference is that you must indicate a DATA or URL attribute instead of CLASSID, and you must specify a particular MIME type: text/x-scriptlet. On the Windows platform, Internet Explorer runs scriptlets through a special container object, which exposes its own object model to the scriptlet. In other words, when Internet Explorer detects a scriptlet object in an HTML page (by encountering the scriptlet MIME type), it creates an instance of a specialized container control that accepts a URL and displays its content. This viewer, of course, provides full support for Dynamic HTML features.
Figure 2: Security Settings
Figure 2: Security Settings

      Because of the sophisticated security built into Internet Explorer 4.0, scriptlets download to your computer only if the security level isn't set to High for their server's zone (a setting of Medium or less will work fine). If you use custom security settings, make sure that "Script ActiveX controls marked safe for scripting" is enabled. Also, "Initialize and script ActiveX controls not marked as safe" should be set to either Enable or Prompt (see Figure 2). Since a scriptlet may contain other scriptlets or controls from different zones, these settings ensure all will work fine.

Structure of a Scriptlet
      The inner structure of a scriptlet follows the same layout as a standard ActiveX control. If you've used Visual Basic 5.0, this will be obvious. A Visual Basic 5.0 control is usually made up of constituent or child controls that you arrange and make interact to suit your needs. A scriptlet has an HTML body that may contain controls, images, applets, marquees, lines, or anything else that DHTML can handle. Visual Basic hosts and renders an ActiveX control inside a container of type UserControl. A scriptlet can refer to the DHTML object model as its host environment. Specifically, a scriptlet can call the window or document object to find out about its container or enumerate all its child elements.
      A scriptlet is an HTML page where the body represents the user interface; the body can either be defined at design time or on-the-fly, and can be changed dynamically. The scriptlet source code must also include some scripting code. To write it, you can use and mix VBScript and JScript (or other scripting languages such as Perl, if you have the proper interpreter on your machine).
      The script code implements the functionality you want the scriptlet to provide as well as some internal helper routines. Some of these procedures can be declared as public and made visible to external callers. There are two ways you can define the scriptlet's public interface. The first relies on the JScript public_ description object:


 <script language="JScript">
 public_description = new CreateScriptlet();
CreateScriptlet is a placeholder for the actual routine you'll use. Its typical content resembles the following:

 function CreateScriptlet () {
   this.put_Text = put_Text;
   this.get_Text = get_Text;
   this.Expand = DoExpand;
   this.event_OnExpand = "";
 }
      CreateScriptlet adds properties, methods, and events to the new object. To expose a property called Text you need to write two script procedures: get_Text and put_Text. They must be assigned to the corresponding elements in the public_description object you'll refer with the this keyword. Put_Text and get_Text take care of reading and writing the property's value. While the get side of the property usually requires that you only return the current content, the put method makes the changes effective and may affect the scriptlet's output.
      Methods don't follow any specific convention. The name you assign to the public descriptor is the same one you can invoke outside the scriptlet. You need to provide the name of the script procedure that actually implements that method. In the above sample I've called it DoExpand.
      Events don't have to be explicitly declared, although I suggest you do it anyway for the sake of clarity. By convention, events have no server-side implementation. You declare their names just to let the object's container know about them. As you'll see, this is not necessary with scriptlets. You can remove the this.event_OnExpand line above and the event will still be raised and received properly.
      A second way to define a scriptlet's public interface is by using a special naming convention known as the default interface description, for all the properties and methods you want to expose. The prefix public is the magic word that makes a symbol available outside the scriptlet:

 Function public_get_Text
 Sub public_put_Text 
 Sub public_Expand
 ' no need to declare events
This approach allows you to use VBScript as well as JScript.
      Although you can write scriptlets in any script language (even mixing them by procedure), I suggest that you use JScript at least for defining the object's public interface. There are a couple of reasons I recommend the JScript public_description approach over the default interface description. The first is clarity. You have a single place in your code where the object's public interface is fully described. Second, you get flexibility. You can have different public and private names for all methods and properties. Earlier I defined a method called Expand and implemented it via a DoExpand procedure. If you use the default interface description, public_Expand is at the same time both the declaration and the implementation of the method.
      There are some performance problems you will encounter if you use more than one engine in a Web page. If you use VBScript to define a scriptlet, you shouldn't bother defining a JScript public_description object. Doing so will cause your code to jump from one script engine to the other just to get your method.

Detecting When a Scriptlet is Running
      Since a scriptlet is really nothing more than a Web page with extra interfaces, you should design it so that it'll display properly through a browser. Note that this may result in a runtime script error as shown in Figure 3.

Figure 3: Runtime Script Error
Figure 3: Runtime Script Error

The special container object that hosts scriptlets offers the ambient properties summarized in Figure 4. If you attempt to view the scriptlet as an ordinary Web page, make sure not to access those stock attributes since the main browser window doesn't support them. A good way to tell where you're running is by detecting whether the page is viewed through a browser or a scriptlet:

 InScriptlet = (typeof(window.external.version)=="string");
 if( InScriptlet ) {
   window.external.selectableContent = 1;
 }
      You might be wondering how it's possible to fix the error in Figure 3 with the above code. The answer lies in the script language I used. The code that defines the variable InScriptlet is written in JScript while the procedure that originates the error is in VBScript. Compare the output of the following lines outside the scriptlet site:

 // JScript
 Alert( window.external.version );
 
 ' VBScript
 Alert window.external.version
You'll notice that the first displays a message box with the word "undefined," while the second raises an exception like the one in Figure 3. A sample HTML file that reproduces this situation is ambient.htm, shown in Figure 5.

The Container Object Model
      The ambient properties listed in Figure 4 allow you to turn a client site scrollbar on and off and toggle whether or not the scriptlet's content is selectable. You can also add a context menu that will display with a right-click. BubbleEvent is a method you can use to pass standard DHTML events such as onclick, onmouseover, and onmouseout to the scriptlet's host. RaiseEvent fires custom events like the OnExpand seen previously. To make calls into this object model you need to access a new property, external, that's exposed by the DHTML window object. The following code snippet notifies a document that a click has occurred inside the scriptlet's client area:


 <script language="VBscript" 
   for="image1" event="onclick">
   if InScriptlet then
     window.external.bubbleEvent
   end if 
 </script> 
I recommend that you always make sure you're inside a scriptlet viewer before trying to access the window.external object.

Writing Your First Scriptlet
      I don't know how you feel about the feature in Visual Studio™ and Windows 95 setup programs where the bitmaps are highlighted when the mouse passes over them. But I like it very much and think it would be nice to add a similar effect to the buttons on my homepage. DHTML allows you to do this seamlessly. Scriptlets, by extension, let you write this code once and reuse it forever. Figure 6 shows my HotImage scriptlet in action, and Figure 7 illustrates its source code.

Figure 6: HotImage Scriptlet in action
Figure 6: HotImage Scriptlet in action

      Once you declare the public interface of the scriptlet (HotImage only supports properties), there is one step you need to take: the one-time initialization of the object. Initialization is usually accomplished during the window.onload event:

 <script language="VBscript" for="window" event="onload">
     InitHotImage 
 </script>
 
 Sub InitHotImage
   document.bgColor = mBkgndColor
 End Sub
      Like a Visual Basic-based ActiveX control, each exposed property needs an internal counterpart variable to hold its actual value. For example, mBkgndColor is used to store the current value of the Background color property (see the code in Figure 7).
Figure 8: HotImage Skeleton
Figure 8: HotImage Skeleton
      The HotImage scriptlet standard body is an empty image tag that looks like Figure 8. This skeleton changes dynamically, becoming similar to what you see in Figure 6 once logos are implemented via two HotImage scriptlets. To be aware of mouse movements on the page, I need to listen for two DHTML-fired events: onmouseover and onmouseout. These events would make any programmer using Win32® happy if they were available as Windows messages. Fortunately, Dynamic HTML provides them for free:

 <script language="VBscript" for="image" 
     event="onmouseover">
   if mEnabled <> 0 then
     DoSetImage mHotImageName 
     window.external.bubbleEvent
   end if
 </script>
      If you look at the HotImage listing (see Figure 7) you'll notice that the code for onmouseover and onmouseout is nearly the same. The only difference is the name of the variable passed to DoSetImage, where the core of this scriptlet resides.

 Sub DoSetImage( sImage )
   Set coll = document.images
   coll.item(0).setAttribute "src", sImage
   coll.item(0).setAttribute "alt", mDescription
 End Sub
This procedure retrieves the image collection of the DHTML document and accesses the first element in the list. The scriptlet body contains just a single <IMG> tag (see Figure 8) so I can make sure that coll.item(0) refers to the right element. All that remains is adapting the src and the alt attributes of this tag. Any other attribute can also be adjusted here if you want.
      HotImage also bubbles the onclick event so that the image acts normally when clicked. From the container's point of view it behaves just like an animated button:

 <script language="VBScript" for="HotImage1" event="onclick">
 <!—
  MsgBox "You pick up" + HotImage1.Description—>
 </script>
Hosting a Scriptlet
      A scriptlet is inserted into an HTML page via an <OBJECT> tag. Here's an example:

 <object id="HotImage1"
   data="HotImage.htm" align="absbottom" 
   border="0" width="300" height="100" 
   type="text/x-scriptlet">
 </object>
The data attribute specifies the file name or the URL where the scriptlet will be found. Type is set to the new MIME type text/x-scriptlet, telling Internet Explorer 4.0 that this object is a scriptlet. Figure 9 shows the script code for the HTML page that appeared in Figure 6.
Figure 10: Calendar Scriptlet
Figure 10: Calendar Scriptlet

      Behind the scenes, Internet Explorer 4.0 uses an ActiveX control to host the scriptlet. Since it is installed with the release version of Internet Explorer 4.0, you can exploit it to build your own applications that support scriptlets. Figure 10 presents a calendar scriptlet that's hosted within a Visual Basic form. The Microsoft Scriptlet control has a design-time property called URL that can be set to the actual name of the scriptlet file. Until updated development tools arrive, you'll have to manually insert scriptlets into Web pages. FrontPad and FrontPage® recognize them as ActiveX controls but don't allow you to edit their properties. The same holds true for scriptlet-related code, which must be entered without the help of ScriptWizard.

A More Complex Sample
      Let's look at the more complex sample shown in Figure 11. What I've called ComboText is similar to a Windows-based combobox, an area of text that expands and collapses, showing more details. To modify the state (expanded or collapsed), you click on the scriptlet's button or call the associated method. A ComboText object exposes a series of properties that let you customize the position of the button by how many pixels the control will expand. In addition, you can change font and color attributes, make the content selectable, and set the title (with Text) and the details (with Memo) to display.

Figure ComboText Scriptlet
Figure 11 ComboText Scriptlet

      The body includes a <DIV> tag, with its ID set to "text," that represents the title of the ComboText. When the user sets the Text property, the scriptlet formats a new string and assigns it with the outerHTML property of the element.

 Set e = GetElemById( "DIV", "text" ) 
 e.outerHTML = FormatText
The ComboText component expands and collapses via a mouse click, which is detected through the standard DHTML event model:

 <script language="VBscript" for="document" event="onclick">
 if window.event.srcElement.id = "push" then
   DoInsertText
 else
   if window.event.srcElement.id = "pop" then
   DoRemoveText
   end if
 end if
 </script>
Push and pop are the IDs of the images used for the control's button. When it's time to expand the control, I'll use this line to add new formatted text to the scriptlet document:

 document.body.insertAdjacentHTML "BeforeEnd", FormatMemo
      While formatting the string for the Text and Memo properties, I take into account the state of the control and the specified button position. This is also a good time to enable the scrollbar and resize the scriptlet to include the new content:

 Set o = MyPage.style
 o.pixelHeight = o.pixelHeight + mExpandBy
 window.external.scrollbar = True
MyPage is the ID I've set within the HTML tag (see Figure 12), and is the means by which you can access the frame window of the scriptlet. I found that using the window element like this

 window.ResizeTo w,h
resulted in changing the dimensions of the Internet Explorer 4.0 main window!

Adding Custom Events to Scriptlets
      What makes this sample more complex is its full support for custom events. I defined two events specific to ComboText: OnExpand and OnCollapse. They're fired at the times their names imply. They are declared in the public descriptor of the object, but if you turn them off, the code still mysteriously works. What's that all about?
      To fire an event you would use the window.external.raiseEvent method. The following is a typical call:


 window.external.raiseEvent "OnExpand", window.document
RaiseEvent takes two parameters. The first is the name of the event and the second is the data you want to associate with the event. The syntax and semantics match the RaiseEvent call in Visual Basic 5.0. What changes is the behavior. In fact, the Visual Basic version of RaiseEvent links to a COM connection point where it expects to find an entry point with exactly the name you passed in. The DHTML RaiseEvent always connects to OnScriptletEvent regardless of the event name you fire, resulting in implementation code like this:

 <script language="jscript" for="ComboText1" event="onscriptletevent(n,o)">
 if( n="OnExpand" ) {
     Expand1.Caption = "Collapse1";
 }
 else {
     Expand1.Caption = "Expand1";
 }
 </script>
      Since all the events a scriptlet raises are routed through OnScriptletEvent, you can define events at any time and call them with a string that matches their name. The host environment should implement a Select/Case or a multiple if statement to distinguish among the various events. The code above is excerpted from the page rendered in Figure 11, and causes the caption of the buttons to change from "Expand 1" to "Collapse 1," according to the ComboText state.
Figure 13: Creative ComboText Use
Figure 13: Creative ComboText Use

      On a personal note, I often write slides and give presentations. Most of the time I use PowerPoint®, but sometimes I like to do it with HTML pages that contain hidden paragraphs that open and close at my command. I want to accomplish this without runtime modules or ActiveX controls in the middle of the page. Dynamic HTML gives me an idea of what can be done, and scriptlets are an excellent way to put the idea into practice. Figure 13 shows a tour of a MIND presentation site that uses this technique. Test It!
Figure 14: Scriptlet Wizard Output
Figure 14: Scriptlet Wizard Output

      Since the layout of a scriptlet is always the same, sooner or later you'll start cutting-and-pasting code from a functional sample. Wizards always work well in situations like this, so I wrote one. The results are in Figure 1 (Figure 14 shows the wizard's typical output). The Scriptlet Wizard is a two-step custom AppWizard for the Developer Studio environment that can create a couple of HTML files, though not a compilable project. Discussing its inner details is beyond the scope of this article. If you're interested in this topic, check out the excellent piece by Steve Zimmerman, "Extend Developer Studio 97 with Your Own Add-ins, Macros and Wizards," (MSJ, September 1997).
      The source code provided with this article includes the ComboText and HotImage script controls as well as the entire Visual C++®-based project for the Scriptlet Wizard. You can download the full source code of the wizard from the link at the top of this article. Don't forget to copy the binary file scrptlet.awx to the template subdirectory of Visual Studio 97, and feel free to modify and adapt it to your own needs. In addition, you'll find a small Visual Basic 5.0-based project that shows how to host scriptlets in a desktop application. All the samples require at least the release candidate of Internet Explorer 4.0 to work properly. Have fun!

Read more about Scriptlets in the May 1998 installment of Cutting Edge.


From the January 1998 issue of Microsoft Interactive Developer.