Introduction
This article presents a Vista sidebar gadget that accesses Bloglines' web services with AJAX to show your feeds in the sidebar. It will present some of the high points of the gadget's code, along with some tips on gadget development and debugging.
What is it?
From the Bloglines website:
Bloglines is a FREE online service for searching, subscribing, creating and sharing news feeds, blogs and rich web content. With Bloglines, there is no software to download or install -- simply register as a new user and you can instantly begin accessing your account any time, from any computer or mobile device.
I use Bloglines because it's easy to use, it remembers what I've read, and most importantly I can switch computers and not have to worry about keeping anything in sync. The gadget integrates seamlessly with Bloglines, marking articles as read and showing the latest information.
Just Looking?
If you don't have a Bloglines account, but would like to check out how the gadget works, just enter "test" as a login id on the options dialog, with any password. This will cause the gadget to load static XML files from a website - the AJAX functionality will work the same for debugging purposes. Feeds won't be marked as read, because the XML file is static - but this should be the only difference.
Background
Windows Vista introduces a new feature, the sidebar. It is docked to the left or right of the screen, and users can add various gadgets to the sidebar. Vista comes with a few gadgets, and many more can be downloaded from Microsoft's Windows Live Gallery.
Sidebar gadgets can provide all sorts of fun and useful information. They consist of HTML and script, with some additional APIs. A gadget can do essentially anything a web page can, and they also have access to many aspects of the local system through the APIs.
In the past, add-ins for many Microsoft applications have required a knowledge of C++ programming or Windows API. By relying on script and HTML, the Vista sidebar puts gadget development within reach of a wider group of developers.
Bloglines Gadget Description
This gadget uses AJAX to access the Bloglines web services sync API. In the main Window, it shows a list of the feeds to which you are subscribed, along with the count of unread items for each feed.
Bloglines allows feeds to be grouped into folders; the gadget shows these as a folder tree. Folders can be expanded and collapsed with a click, and the total of all unread items in the folder is displayed after the name. If the list is taller than the window, it can be scrolled by moving the mouse to the top or bottom edge, or with the scroll wheel - but you'll need to click on the gadget to give it focus in order to use the wheel.
Clicking a link opens the gadget's flyout Window, as shown in the screenshot at the top of this article. The Window is smallish, but big enough to review the items. Clicking on the blog title or a post title will open the respective links in Internet Explorer.
The gadget's header shows the total number of unread posts, and contains a link which will take you directly to your Bloglines account page.
General Gadget Design
A little general background about sidebar gadgets is in order before we get into the specifics of this one.
The simplest gadget would consist of two files:
- gadget.xml - an XML manifest with various options; it must have this name
- an HTML file - this is specified in the manifest and is displayed as the main Window in the sidebar
A gadget will typically also provide an HTML file for a settings dialog. Depending on what it does, it may also have a flyout, which also consists of an HTML file. This is the larger Window in the screenshot above. These gadget Windows can be thought of as special tiny instances of Explorer; the HTML files can link to stylesheets and script include files, and have all the script and display functionality of a browser, with a few minor exceptions. There are some differences in how the Windows communicate with one another, and how settings are stored, which will be touched on later.
I won't do a step-by-step "getting started" article here - I'll just try to point out a few things that I learned along the way, that weren't obviously or readily found on the Internet. If you're interested in creating your first gadget, Microsoft has a great tutorial that I followed.
You can also look at the code for default Vista gadgets or for gadgets you've downloaded and installed. You'll find these in the following locations:
- C:\Program Files\Windows Sidebar\Gadgets
- C:\Users\[Your Login]\AppData\Local\Microsoft\Windows Sidebar\Gadgets
Since the files are just HTML and JavaScript includes, you have full access to the code if you want to see how other authors accomplished something.
Points of Interest
Gadget Images
Some images for the gadget are specified in the manifest. This is mine - MSDN has complete documentation for this format.
="1.0"="utf-8"
<gadget>
<name>Bloglines Feeds</name>
<namespace>JimRogers</namespace>
<version>1.0</version>
<author name="Jim Rogers">
<info url="www.JimandKatrin.com" />
<logo src="images/jk.png" />
</author>
<copyright>© 2007</copyright>
<description>Keep track of your Bloglines feeds</description>
<icons>
<icon src="images/bloglines.png" />
</icons>
<hosts>
<host name="sidebar">
<base type="HTML" apiVersion="1.0.0" src="bloglines.html" />
<permissions>full</permissions>
<platform minPlatformVersion="1.0" />
<defaultImage src="images/bloglines_drag.png" />
</host>
</hosts>
</gadget>
There are three tags for images:
<icon>
- 64x64 icon for the gadget, displayed in Vista's gadget gallery (click the plus sign at the top of the sidebar) <logo>
- 48x48 image displayed next to name, copyright, and URL in the details section of the gallery<defaultImage>
- this is displayed when dragging the gadget from the gallery to the sidebar
Each of these images may contain transparent or partially transparent regions. They may be any size, but the icon and logo will be reduced to fit within the above sizes, if they are too big. Vista's image interpolation is great, so feel free to try an oversize image - my logo looks just fine after shrinking.
Sidebar Graphics Objects
The sidebar has three special graphics, exposed in the form of HTML tags: gbackground
, gimage
, and gtext
. gbackground
is a reliable way to set the gadget's background image; doing it with CSS gave me some trouble.
<g:background src="Images/bloglines_bg_170.png"
style="position:absolute;top:0px;left:0px;z-index:-1"></g:background>
</body>
</html>
These graphics objects can be manipulated in interesting ways; gimage
has properties for blur, brightness, and opacity, among others. MSDN has the details.
Gadget Events
The gadget
API exposes a number of events; here are some of the more important ones:
System.Gadget.onSettingsClosed
System.Gadget.onShowSettings
System.Gadget.onSettingsClosing
System.Gadget.Flyout.onShow
These events are a convenient way to communicate between the main Window, flyout, and settings Windows. For instance, in bloglines.js, which is including in the main Window, you'll find this code:
System.Gadget.onSettingsClosed = SettingsClosed;
function SettingsClosed(event)
{
if (event.closeAction == event.Action.commit)
{
Initialize();
}
}
The SettingsClosed
event checks whether the user pressed OK, then reloads the main Window with the updated settings.
Undocking
Another set of System.Gadget
events you might want to implement is onDock
and onUndock
. Within the event handler, the style, size, or contents of the gadget can be modified. The gadget can take advantage of a little more screen real estate when undocked to present more information or just a bigger, easier to use interface. In the case of this gadget, a resize would be accomplished by modifying the body style:
if(System.Gadget.docked == true)
{
document.body.style.width = 130;
document.body.style.height = 170;
}
else
{
document.body.style.width = 170;
document.body.style.height = 240;
}
There would be a lot more to do - changing the background image, font sizes, and the sizes of several other elements. Something for version 1.1.
Settings Retention
Gadgets remember their settings when the computer is rebooted, but the settings are lost if the gadget is removed from the sidebar. Presumably the designers felt that removing from the sidebar constituted an "uninstall" of sorts, and justifies deleting the settings. However, actually uninstalling the gadget from your computer is a separate action.
I hardcoded the username and password settings during development, so that I wouldn't have to enter them into the options dialog every time I reloaded the gadget - which was often.
XMLHttp Caching
Internet Explorer aggressively caches AJAX requests - I almost missed this, thinking that I was seeing the same "new posts" because they had been modified. One popular way to avoid caching is to append a unique string (typically Date.getTime()
) to the URL. This seemed like a kluge; I searched around and found the header that controls this from the client side:
req.open("GET", BLOGLINES_LISTSUBS, true,
System.Gadget.Settings.read("BloglinesId"),
System.Gadget.Settings.read("BloglinesPW"));
req.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.send(null);
Note also the Id
and password
arguments for the open()
method; this is for basic authentication.
HTTP 304 Status Code
Bloglines returns a status code of 304 if the getitems
API is called for a feed that has no unread items. At first I thought this was an error, but it is in fact a "not modified" status code. Bloglines uses this to indicate that there are no unread items, and no body is returned with the response; just the 304 header.
I don't think this is what was intended in the WC3 specification; 304s are for caching, and should only be returned if the client does a "conditional get," specifying that the file should only be returned if it has been modified since a particular time - for which time the client has a cached copy.
The end result of this is when loading the flyout for blogs with no unread items, Bloglines didn't provide any data. The blog title and URL must be passed down to the flyout methods. The blog description is saved as a setting the first time that getitems
actually returns data. It is then loaded from the setting in the case of a 304. So you won't see a description in the flyout for a blog until the first time you've loaded unread items for that blog.
Javascript Objects
One of the things I learned along the way was how to construct objects on the fly in JavaScript; This is used in the code to pass by reference, something that can't be done in JavaScript with parameters. Passing by reference was essential for the recursive function that populates the tree view.
var subCount = { count:0 };
...
flyoutInfo = { subId:subId, url:url, title:title };
Files
- bloglines.html - gadget contents
- settings.html - settings dialog
- flyout.html - flyout window
- bloglines.js - methods for accessing Bloglines' web services and rendering the main Window
- ui.js - user interface methods
- rss.js - parser for returned RSS documents
- bloglines.css, flyout.css - stylesheets for corresponding HTML files
Development Tips
Visual Studio 2005
A gadget can be developed with any editor, but of course Visual Studio is convenient for syntax highlighting and formatting - and integrated source control if you have that available. This gadget project was started as a Visual C# ASP.NET Web Application; the default files were deleted. It resides on disk such that the files are in the gadget folder; to reload the gadget I just save the files, close the gadget, and add it back to the sidebar from the gadget gallery.
If you download the project, be sure to unzip it to your gadget folder so you can do the same.
Debugging
It's hard to use some of the quick-and-easy debugging techniques that one might normally use in a browser. Alerts don't work at all, and the gadget Window is too small to output raw data after the content, just to see it.
Fortunately, script debugging works just fine. The easiest way to invoke it is to use the debugger
keyword. Place it at the top of a function that you want to debug, or put it in your initialization function to start debugging right away.
function Initialize()
{
debugger;
var username = System.Gadget.Settings.read("BloglinesId");
The familiar just-in-time debugger dialog will appear with its list of programs, including Visual Studio and Microsoft Script Editor. Remember to enable script debugging in Internet Explorer.
If your gadget is very simple, or if you wrap calls to the system APIs in try
blocks, you can open some or all of your gadget in Explorer. Or, you can write a similar but separate HTML page as a testbed for your JavaScript. This can be useful for testing complex scripts, like the AJAX and XML parsing code used in this gadget. In IE you can take advantage of alerts and better error reporting; I used a big textarea to view the raw data coming back from web service calls.
History