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.
|
Creating an Active Desktop Component
Josh Hochman |
Looking for the easiest way to get your content onto people's desktops? It's not as hard as you think to develop a commercial-quality desktop component. |
By now you've probably heard something about Microsoft's brand new batch of smartly acronymed and nifty sounding technologies, right? Do DHTML, CSSP, CDF, JavaScript, VBScript, Java, Active Desktop, databinding, TDC, and ADO sound familiar? Well, as soon as I saw them, I had a sudden urge to build an application that incorporated as many of these technologies as possible just to make my life more interesting and to impress my friends.
Seriously, my employer, Time Inc. New Media (the company responsible for the Pathfinder Network), assigned me the glorious task of creating an Active Desktop component that would draw a real-time chart of five major stock indexes and two stocks of the user's choice. This component would exist as a part of Fortune Online's Active Channel at http://www.pathfinder.com/fortune/channel/channel.cdf. Creating the component turned out to be a great way to get familiar with some new Microsoft® technologies. After an initial investigation, I found that to do what I wanted, I needed to use at least nine of the features available to programmers in the latest release of the Microsoft Internet Explorer browser. These included Cascading Style Sheets and Positioning (CSSP), Dynamic HTML (DHTML), JavaScript 1.2, VBScript, databinding, the Tabular Data Control (TDC), ActiveX® Data Objects (ADO), Channel Definition Format (CDF), and Java.
The Interface
|
It is also important to allow for customization. Any good component should let the user personalize it in some way. After all, it's on their desktop! For the Fortune Personal Stock Chart, the logical choice was to allow the user to select and track two stock tickers.
To get the interface looking the way I wanted, I used a couple of new features in Internet Explorer 4.0: CSSP and DHTML. First, I linked the styles into the main HTML file like this:
<! link in a stylesheet >
<LINK REL=STYLESHEET TYPE="text/css" HREF="styles.html"
TITLE="FPSC styles">
That was easy! Why not keep the main HTML file free of CSS info, and link to it instead? It's much cleaner that way. But remember, this will incur an additional hit on your server. If you are concerned about performance and want to keep round trips to a minimum, keep the styles in the main HTML file.
#indexesDIV {
position: absolute;
left: 4px;
top: 172px;
width: 270px;
height: 10px;
padding: 2px;
background-color: #FF7200;
font-size: 7pt;
font-family: Verdana;
font-weight: bold;
text-align: center;
cursor: hand;
z-index:2;
}
In this case, I used the # notation to define a style that applies to the document object with an ID of indexesDIV. This way, I can define all if the style attributes in one spotthe style sheetwhich keeps the main HTML file easier to manage and read. The complete style sheet is shown in Figure 2.
<DIV id=indexesDIV>
<SPAN id=DJIASPAN
onmouseover="style.color='white'"
onmouseout="if (0 != theChoice) style.color='black'"
onmousedown="if (0 != theChoice) handleMouseDown(this)">DJIA</SPAN>
. . .
</DIV>
In the style sheet, I also use the . notation to define styles that I want to apply to multiple objects in the component. Here is how I defined menu, which will give me orange text with a one-pixel border padded by two pixels on the left and right:
.menu {
position: absolute;
height: 10px;
padding-left: 2px;
padding-right: 2px;
border: 1 solid #FF7200;
color: #FF7200;
font-size: 7pt;
font-family: Verdana;
text-align: left;
cursor: hand;
z-index:3;
}
To use this style in the code for the main HTML file, I just use the class attribute within the tag. I still need to make sure that I set the left, top, and width for the element, since they are not defined in the style. I defined the REFRESH button like this:
<DIV id=refreshMenuDIV class=menu style="left:144px;top:192px;width:55px;"
onmouseover="style.color='white'"
onmouseout="style.color='#FF7200'"
onmousedown="style.color='#FF7200';
if (theState == 'customize'){
event.keyCode = 27;
keydown();
} else {
refresh();
}">
REFRESH
</DIV>
By now, you are probably thinking "OK, it all looks pretty. But where's the JavaScript, DHTML, and rollovers?" Easy. I just code everything into the mouseover, mouseout, and mousedown handlers for each <SPAN> or <DIV>. This behavior could also be written into a scriptlet, but I'll save that for a future article. In the previous code, I use the CSS property color to change the text color to white when the mouse rolls over the button. This lets the user know that this button is active. When the mouse leaves the object, the text color returns to orange#FF7200. (See Figure 4.)Figure 4: Using the CSS Property Color |
As you will see later, the ticker button does double duty. Usually it is the REFRESH button, but when in customization mode, it serves as the CANCEL button. Remember, I'm trying to keep screen real estate to a minimum, so I reuse things as much as possible. Since you cannot refresh the display while in customization mode, why not use the button for something else? It's little tricks like these that keep your component smaller and less obtrusive on the desktop. The code behind the ticker buttons is similar, except that I check against the currently selected ticker symbol to make sure that I don't unhighlight the current symbol if the user happens to pass the mouse over the text: |
|
So far, you've seen three events that the user can fire: keydown, refresh, and handleMouseDown. I'll detail those functions later. First, let's look at how to retrieve and manipulate the ticker data.
The Databinding
|
|
Second, you have to make sure that you have a place in the HTML where the data is actually used or else it will never get attached to the ADO object. In my code I hid it in an invisible <DIV> with the DATASRC attribute like so: |
|
Third, since the data access is asynchronous, I had to keep an internal state variable and use a polling routine to keep checking if the data was completely loaded. The TDC loading function looks like this: |
|
Finally, if your program is in JavaScript, you must use VBScript wrappers to access the data with ADO functions. When I called the functions directly from JavaScript, they didn't work. For example, look at how I defined and called the getValue function: |
|
The full JavaScript code is in Figure 5. Here is a shortened example of how I made this all work together: |
|
There are a few new things in this code, like the Symbol object and the drawTable, loadData, and drawChart functions. I'll detail these functions later. Remember when I said that the TDC loading is asynchronous? I implemented a polling routine with the setTimeout function. It will keep calling itself every half second until the internal state is dataWaiting: |
|
The ondatasetcomplete function just sets the state to data-Waiting so the loadData function will execute fully. When the loadData function is complete, it sets the state to idle, which the drawChart function looks for before executing. The other data that I needed to deal with was the user's customization. Storage of this information was executed completely on the client through the use of JavaScript and cookies. In order for the component to have some cookies to use, the user first needs to have a way to store their customizations. I implemented this by calling a function on the mousedown for the CUSTOMIZE button. This sets the state, changes the names on a few buttons, hides a <DIV>, and shows another <DIV> that contains a form (see the customize function). When the Return key or SAVE button is pressed, I set the document.cookie property to save the user's personal stock tickers (see the keydown function). This can be a very useful technique to save important information without having to deal with server-side cookies or creating personal accounts to save information. It's faster and relieves the server of one more task. Once the customization is saved in the cookie, it can be accessed every time the component is loaded. I wrote a simple parsing function to return the value part of the name/value pair stored in the cookie: |
|
The Push
|
|
This file defines five important pieces of information: the parent channel page, the component's home page, the title, the width and height, and how often to push the content. How does the browser know that the CDF defines a desktop item rather than a channel? It's done with the tag <USAGE VALUE="DesktopComponent">. This tag tells the browser to place this item on the desktop and makes the code a desktop component rather than a plain Web page. I found it was easier to test the component within the browser before I
finalized the CDF file, so I left the <USAGE> element out during testing.
The Program
Conclusion
|
From the May 1998 issue of Microsoft Interactive Developer.