By now, you should have grasped the idea of what Scriptlets are, and how to write them. It's time to design and code a sample which is a little more complex. What we have in mind is a Clock object that you can put in all your pages as a reminder of time passing. A digital clock is made up of three components: hours, minutes and seconds. We can render it using a simple text string that needs formatting each time it is updated, or we can use a small table that keeps the various logical parts of the time separate. A very simple HTML table can be defined like this:
<TABLE>
<TR>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
</TR>
</TABLE>
All the tags (TABLE
, TR
, TD
) expose lots of attributes that set the formatting and layout of the table, but for our purposes they can all be ignored. However, a better approach would be to make them properties of the Clock Scriptlet. This sounds reasonable—especially for BORDER
and CELLSPACING
attributes.
A clock is a self-initialized component, so it doesn't need specific initialization code to work properly. If you open this Scriptlet in a browser you're able to use it straight away. Once more, this demonstrates that a Scriptlet is an HTML page that runs inside another HTML page:
The final 'extended' version of this page, which you'll meet later in the chapter, can be run or download from our Web site at http://rapid.wrox.co.uk/books/0707
.
Our HTML clock component will exploit the setInterval
and clearInterval
methods of the window
object. We just need to provide a function to stop and restart it, and some events. Here's the complete source code:
<HTML id=MyPage>
<TITLE>Clock Scriptlet</TITLE>
<script language="VBscript" for="window" event="onload">
InitClock
</script>
<script language="VBscript">
Sub InitClock
DoSetColor mBgColor, mFgColor
DoUpdateClock
if InScriptlet then
window.external.selectableContent = True
end if
if mEnabled then
mTimer = window.setInterval("DoUpdateClock", 1000)
mStartedAt = Time
end if
End Sub
Sub DoSetColor(b, f)
Set coll = document.all.tags("TABLE")
coll.item(0).style.backgroundColor = b
coll.item(0).style.Color = f
coll.item(0).style.fontFamily = "Tahoma"
End Sub
Sub DoUpdateClock
t = Time
mHour = Hour(t)
mMins = Minute(t)
mSecs = Second(t)
set coll = document.all.tags("TD")
coll.item(0).innerHTML = Right("00"+CStr(mHour),2) + ":"
coll.item(1).innerHTML = Right("00"+CStr(mMins),2) + ":"
coll.item(2).innerHTML = Right("00"+CStr(mSecs),2)
End Sub
Function DoFormatTime
s = FormatDateTime(Time(), vbLongTime)
DoFormatTime = s
End Function
Function DoGetTime
DoGetTime = Time
End Function
Function DoAlarm
if (InScriptlet And (Not window.external.frozen)) then
window.external.raiseEvent "OnAlarm", window.document
end if
End Function
</script>
<script language="JavaScript">
public_description = New CreateClock(); // declare the interface
var InScriptlet = (typeOf (window.external.version) == "string");
mBgColor = "gray";
mFgColor = "white";
mEnabled = 1;
mTimer = 0;
mStartedAt = 0;
mAlarm = 0;
function CreateClock() {
this.put_Text = put_Text;
this.get_Time = get_Time;
this.get_StartedAt = get_StartedAt;
this.put_ClockBgColor = put_ClockBgColor;
this.put_FgColor = put_FgColor;
this.get_FgColor = get_FgColor;
this.put_BgColor = put_BgColor;
this.get_BgColor = get_BgColor;
this.Enable = Enable;
this.Alarm = Alarm;
this.event_OnStart = "";
this.event_OnStop = "";
this.event_OnAlarm = "";
}
function put_ClockBgColor(color) {
document.bgColor = color;
return 1;
}
function put_Text(sText) {
document.all("Text1").innerHTML = sText;
return 1;
}
function get_Time() {
return DoFormatTime();
}
function get_StartedAt() {
return mStartedAt;
}
function put_ForeColor( color ) {
mFgColor = color;
DoSetColor(mBgColor, mFgColor);
return 1;
}
function get_ForeColor() {
return mFgColor;
}
function put_BackColor(color) {
mBgColor = color;
DoSetColor(mBgColor, mFgColor);
return 1;
}
function get_BackColor() {
return mBgColor;
}
function Enable(b) {
mEnabled = b;
if (b) {
mTimer = window.setInterval("DoUpdateClock", 1000);
if (InScriptlet) and (not window.external.frozen)) {
window.external.raiseEvent("OnStart", 0);
mStartedAt = DoGetTime();
}
}
else {
window.clearInterval(mTimer);
Alarm(0);
if (InScriptlet and (not window.external.frozen)) {
window.external.raiseEvent("OnStop", 0);
}
}
return 1;
}
function Alarm(secs) {
if (secs) {
mSnooze = window.setInterval("DoAlarm", secs);
}
else {
window.clearInterval(mAlarm);
}
return 1;
}
</script>
<BODY>
<TABLE>
<TR>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
<TD VALIGN="TOP" ALIGN="CENTER">0</TD>
</TR>
</TABLE>
</BODY>
</HTML>
The public interface of the Scriptlet exposes the following properties, methods and events:
Name | Description |
FgColor |
Sets and gets the foreground color of the clock, i.e. color of the digits. |
BgColor |
Sets and gets the background color of the clock area. |
ClockBgColor |
Sets the background color of the Scriptlet area. |
Time |
Returns the current time formatted accordingly to the user settings. |
StartedAt |
Returns the last time when the clock Scriptlet was started. |
Text |
Sets the text to be displayed below the clock. |
Enable(state) |
A method that disables and enables the clock causing it to stop and go. |
Alarm(secs) |
A method that sets up an alarm to raise an onAlarm event every given number of milliseconds. |
onStop |
This event is fired each time the clock gets disabled. |
onStart |
This event is fired each time the clock is started. |
onAlarm |
This event signals that the given period is expired. |
The Time
property is read-only, and returns the current time. It makes use of the current user's format settings for the date and time with VBScript's FormatDateTime
function. FgColor
and BgColor
define the color of the text and the clock background—by default, we have white text on a gray background. StartedAt
has a secondary role, and its only purpose is to enrich the Scriptlet's interface. ClockBgColor
is a write-only property assigned to the background color of the entire site area, which is usually darker than the clock itself.
Text
is another write-only property that contains the text string we might want to associate with the clock. This text is the body of a SPAN
tag with an ID
of Text1
. Since this is implemented via the element's innerHTML
property, we can assign it text containing HTML tags as well, and be sure it will be correctly handled. This provides us with the opportunity to format the text, be it in italic or bold font, or to include images, links, etc. For instance:
Clock1.Text = "Current Time offered by <i>WROX Press</i>"
The final property, Enable
, is at the core of the component. It accepts a Boolean value, and starts or stops the clock.
All the updates of the clock are controlled by the setInterval
method of the window
object:
mTimer = window.setInterval("DoUpdateClock", 1000);
The setInterval
method takes a string denoting the script code to be executed each time the given interval expires, and an interval period expressed in milliseconds. The code above causes the DoUpdateClock
procedure to be called once every second. As you might guess, DoUpdateClock
just refreshes the table representing the digital clock. First it obtains the current time in terms of hours, minutes and seconds. Then it sets the innerHTML
properties of the three TD
elements:
Sub DoUpdateClock
t = Time
mHour = Hour(t)
mMins = Minute(t)
mSecs = Second(t)
set coll = document.all.tags("TD")
coll.item(0).innerHTML = Right("00"+CStr(mHour),2) + ":"
coll.item(1).innerHTML = Right("00"+CStr(mMins),2) + ":"
coll.item(2).innerHTML = Right("00"+CStr(mSecs),2)
End Sub
The setInterval
method returns a unique identifier for the timer is starts running, and we saved this in a variable mTimer
. To stop the clock, we just pass this variable to the window
object's clearInterval
method:
window.clearInterval(mTimer);
The onStop
and onStart
events are fired after the clock has been stopped and restarted. The onStart
event isn't raised when the Scriptlet is loading for the first time. As you have seen in our earlier discussions, the event declarations are not really required for the Scriptlet to work. The host page will always receive onScriptletEvent
events, whatever the actual event the component raises with the raiseEvent
method. However, we've included the event declarations simply to document the Scriptlet's interface.
A sample page that hosts our Clock component might look like this:
Here's the code for the complete page, including the <OBJECT>
tags that insert the Clock Scriptlet:
<html>
<title>Test page using Clock</title>
<b>Test page using Clock</b>
<SCRIPT LANGUAGE="VBScript" FOR="window" EVENT="onload">
Clock1.BgColor = "lightblue"
Clock1.FgColor = "blue"
Clock1.Alarm 5000
</SCRIPT>
<SCRIPT LANGUAGE="VBScript" FOR="Clock1" EVENT="onscriptletevent(n,o)">
if n = "onStart" then
MsgBox "Current time is " + Clock1.Time
end if
if n = "onStop" then
MsgBox "Started at " + CStr(Clock1.StartedAt)
end if
if n = "onAlarm" then
CRLF = Chr(13) + Chr(10)
sMsg = "Alarm " + Clock1.Time + CRLF + "Continue?"
i = MsgBox(sMsg, vbYesNo, "Clock")
if i = vbNo then Clock1.Alarm 0
end if
</SCRIPT>
<SCRIPT LANGUAGE="VBScript">
Sub Button1_Click()
if Button1.Caption = "Stop" Then
Clock1.Enable(0)
Button1.Caption = "Start"
else
Clock1.Enable(1)
Button1.Caption = "Stop"
end if
End Sub
</SCRIPT>
<p>
<OBJECT ID="Clock1" WIDTH=100 HEIGHT=70 align="bottom"
type="text/x-scriptlet" DATA="clock.htm">
</OBJECT>
</p>
<OBJECT ID="Button1" WIDTH=96 HEIGHT=32
CLASSID="CLSID:D7053240-CE69-11CD-A777-00DD01143C57">
<PARAM NAME="Caption" VALUE="Stop">
</OBJECT>
</body>
</html>
During the onload
event it assigns the clock colors. By clicking on the button you can stop or restart the clock.
Our clock page as it stands doesn't do very much, so we've added a function that simulates an alarm-clock. We can use several of the other methods and events that our Clock component has built into it. By assigning an interval in milliseconds using the Alarm
method, like this:
Clock1.Alarm 5000
we are notified of a specific onAlarm
event after that period expires (in this case, 5 seconds). Our Clock component provides this custom event via the onScriptletEvent
in the container page. The Alarm
method works by setting an interval:
function Alarm(secs) {
if (secs)
mAlarm = window.setInterval("DoAlarm", secs);
else
window.clearInterval(mAlarm);
return 1;
}
To cancel the alarm, we simply pass zero to the Alarm method, which clears the interval timer. When that interval is up, the DoAlarm
routine in our component is executed. This routine raises an event that can be detected in our page:
Function DoAlarm
if (InScriptlet And (Not window.external.frozen)) then
window.external.raiseEvent "onAlarm", window.document
end if
End Function
Notice that our code checks both that we are actually running the component as a Scriptlet, and that the container page is ready to receive events, first.