Contents
Introduction
Ever since I was a wee lad, I've been interested in astronomy. I've also been a long-time reader of the Astronomy
Picture of the Day site (APOD), which has hundreds of interesting and informative pages about various subjects
in astronomy. (To see the full archive going back to June 1995, visit the
index page.) However, APOD doesn't have an RSS feed, and the index page is text-only, so it isn't easy to browse
through APOD to find an interesting picture. That's where the gadget comes in! The sidebar gadget presented in
this article lets you easily browse through APOD pages, and shows a thumbnail of each day's picture.
Installing and Using the Gadget
When you download the source code from the link at the top of the article, you'll see the APODViewer.gadget
file in the root of the zip file. Double-click this file and click Install at the prompt to install the
gadget. This will automatically add the APOD gadget to the Sidebar, however there is one manual step you need to
do.
The gadget installation procedure cannot register DLLs. The APOD viewer uses an ActiveX control in a DLL, so
the gadget won't work until the DLL is registered. Follow these steps to register the DLL:
- Remove the APOD Viewer gadget from the Sidebar so it gets unloaded.
- Open an elevated command prompt.
- Navigate to this directory:
%USERPROFILE%\AppData\Local\Microsoft\Windows Sidebar\Gadgets\APODViewer.gadget\bin
- Run the command
regsvr32 apodhelper.dll
to register the DLL.
Once the DLL is registered, the gadget will be fully functional. You can add it back to the Sidebar using the
Add Gadgets dialog. In this screen shot, the APOD gadget is in the top-left:
The gadget will then run and show the APOD page for the current date. Here is the gadget showing the APOD picture
for January 29:
The four links at the top of the window change the APOD page. The left-arrow and right-arrow buttons move one
day backward and forward, respectively. Click the Today link to return to the APOD page for the current
date, and click Random to jump to a random APOD page. Clicking the date below the links brings up a date
picker, where you can jump to any date between 1995 and the present:
A new feature for version 1.1 is the search field. You can enter keywords in the edit box, and click the OK
button to search the APOD site for those words. When you click OK, the flyout window closes and the gadget
opens a new browser window that shows the search results.
The bottom part of the gadget shows the title of the APOD page, along with a thumbnail of that day's picture.
Click the title to open the APOD page in a new browser window, and click the thumbnail to jump directly to the
full-sized picture.
If you browse to a date for which there is no APOD, the gadget will display an error message saying that no
APOD page is available.
Another new feature in version 1.1 is a slide show mode, where the gadget changes to a random APOD page every
so often. To turn on slide show mode, click the options button next to the gadget window (the one that looks like
a little wrench) to open the options dialog:
Check the View random APOD pages box to enable slide show mode. You can also change the interval at which
the picture changes; the choices range from 30 seconds to 5 minutes.
How the Gadget Works
Initialization
Since the APOD site doesn't have an RSS feed, the gadget has to scrape the web pages and extract the information
that it's interested in. Since I'm more proficient in C++ than JavaScript, much of the interesting work is done
in C++ and packaged in an ActiveX control. The ActiveX control is initialized in the gadget_init()
function in APODViewer.js, which is called when the gadget is loaded:
var g_helper;
function gadget_init()
{
g_helper = new ActiveXObject("MDunn.APODViewer.Helper");
if ( g_helper )
{
g_helper.SetGadgetPath ( System.Gadget.path );
OnTodayButton();
}
}
As one of the initialization steps, gadget_init()
tells the ActiveX object what directory it can
create temporary files in by passing the predefined variable System.Gadget.path
to the object's SetGadgetPath()
method.
Getting APOD Info
When you change the date in the gadget, the gadget tells the ActiveX object what the new date is, then calls
the object's GetApodInfo()
method. For example, clicking the Today button runs this script:
function OnTodayButton()
{
if ( g_helper )
{
g_helper.GoToToday();
UpdateApodInfo();
}
}
function UpdateApodInfo()
{
datepicker.innerText = g_helper.CurrDateString;
if ( g_helper.GetApodInfo() )
{
apodtitle.href = g_helper.ApodPageURL;
apodtitle.innerText = g_helper.ApodPageTitle;
apodimg.src = g_helper.SmallImgURL;
apodimglink.href = g_helper.LargeImgURL;
}
}
GetApodInfo()
is responsible for downloading the APOD web page and caching the data that will be
shown in the gadget. It begins by instantiating an HTML parser object and downloading the APOD page for the new
date. The HTML is fed into the parser, and then the code iterates through all the links in the page. We're interested
in the thumbnail, which is the first <a>
tag that has a child <img>
tag.
If such a link is found, the code saves the href
of the <a>
tag (the URL to the
full-size picture), and the src
of the <img>
tag (the smaller version of the picture).
If those URLs are found, the code then reads the <title>
from the HTML, which is shown in the
gadget as the caption above the thumbnail.
If GetApodInfo()
succeeds, the gadget reads the URLs and page title from properties exposed by
the ActiveX object. For example, the LargeImgURL
property is the URL to the full-size picture. These
strings are assigned to the corresponding HTML elements in the gadget UI.
Technical Notes
The gadget relies on two details of the APOD site, both of which have been consistent over the years (and hopefully
will remain that way!):
- The URL for a given date must be:
http://antwrp.gsfc.nasa.gov/apod/apYYMMDD.html
.
- The first link in an APOD page that has an
<img>
child tag must be the link to the full-size
picture, and the child <img>
must be the thumbnail.
Files in the Gadget Directory
If you browse to the APODViewer.gadget directory, you'll see all the files there unpacked from the original
.gadget file. This section lists the contents of each directory, so you'll know what files are where if
you want to look at the inner workings of the gadget.
- root directory
- APODViewer.js: Script that controls the main gadget UI
- flyout.js: Script that controls the flyout page
- options.js: Script that controls the options dialog
- APODViewer.css: CSS file used by the main gadget UI
- bin directory
- APODHelper.dll: The ActiveX control used by the gadget
- en-US directory
- gadget.xml: This XML file describes the gadget, along with other info like the version number
- APODViewer.html: The web page for the main gadget UI
- flyout.html: The web page for the flyout
- options.html: The web page for the options dialog
- strings.js: This script file contains localizable strings
- images directory
- Various icons and images used in the gadget's web pages and the Add Gadgets dialog
You might notice that the en-US directory is never referenced in the other files. This directory contains
localizable resources, and Vista automatically searches it when looking for files. For example, this section of
gadget.xml lists the web page for the main UI:
<gadget>
...
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="APODViewer.html" />
<permissions>Full</permissions>
<platform minPlatformVersion="1.0" />
</host>
</hosts>
</gadget>
The src
attribute in the <base>
tag doesn't have a directory name, so Vista
first looks in the gadget's root directory. Since APODViewer.html is not there, Vista then looks in en-US,
and finds the file there. To localize the gadget into a new language, all that's required is to add a new language
directory alongside en-US, and put the translated files in that directory.
Building and Packaging the Gadget
A .gadget file can be either a ZIP or a CAB file that contains all the files for the gadget. I chose
to use a CAB for the demo download, since CABs can be digitally signed. This section lists the steps necessary
to package the gadget into a CAB and sign it.
- Set up all the gadget files in the APODViewer.gadget directory.
- Build the DLL that contains the ActiveX control. Copy the DLL into the APODViewer.gadget\bin directory
and register it there.
- Open a command prompt and navigate to the APODViewer.gadget directory.
- Run
cabarc -p -r -s 6144 n APODViewer.cab *.*
(The -s
switch reserves space in the
CAB for a digital signature, you should omit this switch if you don't plan on signing the CAB.)
- If you have a signing certificate, sign the CAB file.
- Rename the CAB to have a .gadget extension.
Then distribute the .gadget file! If you sign the file, the prompt will show the gadget name and the
name of the company as listed in your signing certificate. The gadget file in the demo download is signed with
my company's certificate, so our name appears in the prompt:
References
See the resources section in the Gadgets Competition page
at CodeProject.
"A Console Application HTML Parser Using Microsoft's
XML Technologies" by Jeffrey Walton.
Copyright and License
This article is copyrighted material, ©2007 by Michael Dunn. I realize this isn't going to stop people
from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation
of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation,
I would just like to be aware of the translation so I can post a link to it here.
The demo code that accompanies this article is released to the public domain. I release it this way so that
the code can benefit everyone. (I don't make the article itself public domain because having the article available
only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own
application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are
benefitting from my code) but is not required. Attribution in your own source code is also appreciated but not
required.
Revision History
January 17, 2007: Article first published.
January 29, 2007: Version 1.1 released, added search and random page viewing features.