So you want to build a Yahoo Widget…

Developing the Solar-Viewer was fun. I was surprised at how native the widget environment seemed to be, though there were a few quirks. If you have some knowledge of XML and Javascript, you will be able to build a widget as well. The widget engine is well documented and you will want to take a look at the development tools and references that Yahoo provides. Definitely print out and read the Widget Creation Tutorial, we found it to be an excellent jumping off point into widget world.

How We Did It

We built the Solar-Viewer widget using the Widget Creation Script for Photoshop CS. Our designer Kemper created the look for the widget in Photoshop. He then used the Widget Creation Script(a plugin for Photoshop) to convert the .PSD into a widget. Yep, it actually converts your photoshop file into a working widget. So we took this:

Photoshop Widget Mock Up

And we got this:

Output of Photoshop Widget Creation Script

The really cool thing is that it scrapes your layers and converts them to PNGs and it also creates a .kon file with all of the elements defined. For those new to widgets a .kon contains the main code for the widget. It is an xml file that looks like this:

<?xml version="1.0" encoding="macintosh"?>
<widget version="1.0" minimumVersion="2.0">
<debug>off</debug>
<!--
Solar-Viewer
Written by: Ideum

Generated by Photoshop Widget Generator Script
Copyright (C) 2004 - 2005 Pixoria, Inc. All Rights Reserved.

Any modifications will be lost if the generation script is run again.
-->

<window title="Solar Viewer">
<name>mainWindow</name>
<width>616</width>
<height>404</height>
<visible>1</visible>
<shadow>1</shadow>
</window>

<image xsrc="Resources/aurora.png" mce_src="Resources/aurora.png">
<name>aurora</name>
<hOffset>3</hOffset>
<vOffset>3</vOffset>
<width>609</width>
<height>397</height>
<opacity>100%</opacity>
</image>

Note: A widget is simply a group of files saved as a package. You can easily look at the source code of widgets by either right clicking on them and selecting “Show Package Contents”? (OSX) or by changing the file extension to .zip and then opening the zip file (Windows).

After the conversion, each layer of the photoshop is referenced in the .kon file as an xml element. The script correctly placed and sized the layer in the widget using <hOffset>, <vOffset>, <width>, and <height> properties of the <image> element — again, too cool!

<image xsrc="resources/full_image.png" mce_src="resources/full_image.png">
<name>fullimage</name>
<hOffset>286</hOffset>
<vOffset>58</vOffset>
<width>314</width>
<height>315</height>
<opacity>100%</opacity>
</image>

The source of the image is also defined. The sources for the images were placed in a folder called resources. One thing we learned is to be careful with your naming conventions. The script will name the widget elements just as you have your layers named, so make sure you name your layers in a meaningful manner.

In step one we created the widget from a Photoshop file, so now we need to make the widget dynamic. The images of the sun are loaded dynamically when the widget is launched from their source on the web. The widget was created with placeholders for the images. The placeholders were layers that got exported as image elements. You can reference element properties using JavaScript:

fullimage.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_171_512.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_171_512.jpg";

So all I did was set the source of the placeholder image to the url of the image. I used the “€œonload”€? trigger of the element to trigger the load all of the images. To load the images I simply changed the source of their respected placeholder:

<action trigger="onload">
img01.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_171_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_171_65.jpg";
img02.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_195_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_195_65.jpg";
img03.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_284_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_284_65.jpg";
img04.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_304_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_eit_304_65.jpg";
img05.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_LASCOc2_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_LASCOc2_65.jpg";
img06.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_LASCOc3_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_LASCOc3_65.jpg";
img07.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_trace_171_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_trace_171_65.jpg";
img08.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_white_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_white_65.jpg";
img09.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_halpha_65.jpg" mce_src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_halpha_65.jpg";
</action>

Next, we needed to allow the users to select a thumbnail image and have a larger version of the thumbnail be displayed. At the same time a description of the image needed to be displayed. We used the <onMouseUp> event for the image element to trigger a change in the source of the full size image.

<image xsrc="Resources/img09.png" mce_src="Resources/img09.png" >
<name>img09</name>
<hOffset>198</hOffset>
<vOffset>208</vOffset>
<width>66</width>
<height>66</height>
<opacity>100%</opacity>
<onMouseUp>
fullimage.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_halpha_512.jpg";
description.data = filesystem.readFile("Resources/
image09.txt");
</onMouseUp>
</image>

To display the text we created a <textarea> element and placed it over a graphical placeholder. The data property of the <textarea> element was changed on the same event that triggered the main image change. The data is pulled from text files located in the resources folder using the filesystem.readFile() function. We included the text files with in the widget because the original files had HTML in them and, unless you do some scripting a widget can not render HTML.

Along the same lines we used two other mouse events, <onMouseEnter> and <onMouseExit> to turn on and off an image that creates the rollover state:

<image xsrc="Resources/img09.png" mce_src="Resources/img09.png">
<name>img09</name>
<hOffset>198</hOffset>
<vOffset>208</vOffset>
<width>66</width>
<height>66</height>
<opacity>100%</opacity>
<onMouseEnter>
glow09.visible = true;
</onMouseEnter>
<onMouseUp>
fullimage.src="http://ds9.ssl.berkeley.edu/imageviewer/
images/images2/latest_bbso_halpha_512.jpg";
description.data = filesystem.readFile("Resources/image09.txt");
</onMouseUp>
<onMouseExit>
glow09.visible = false;
</onMouseExit>
</image>

The last major piece of functionality was to have the widget update the images every hour and display when the last update was. We used another element called <timer> to accomplish this. The <timer> was set to run every 3600 seconds (60 minutes) and when it ran, it triggered the <onTimerFired> element nested with the <timer>. On that trigger all of the code was run within the <onTimerFired> element:

<timer name="mainTimer" interval="3600" ticking="true">
<onTimerFired>
theHour = String(theDate.getHours());
time.data = "Reloading images...";
img01.reload();
img02.reload();
img03.reload();
img04.reload();
img05.reload();
img06.reload();
img07.reload();
img08.reload();
img09.reload();
fullimage.reload();
time.data = "Updated today at: " + theHour + ":00";
</onTimerFired>
</timer>

The code reloads the images (you can not just change their source, they have to be reloaded) and sets the data of an additional <text> element that displays our last update message.

I hope this gives you a good overview of how we built the Solar-Viewer widget. We will be making a Mac widget version of this soon and I will write up a description of how we built that as soon as we are done.

Back To Blog

Recent Posts

Image for the post: 'Building an Interactive Video Wall'

Building an Interactive Video Wall

Our most ambitious technical project of 2016 was the DinoStomp 3D interactive video wall that we developed with the Fort Worth Museum of Science and History.  The DinoStomp exhibit consists of a video wall 8’ high and…