The Google Gadget API
December 27th, 2007What it offers:
- Save state
- External content (RSS, XML, existing websites)
- Tabs
- Localized settings (internationalization)
- Drag and drop content
Why we chose the gadget API:
- Needed a way to deliver the content with out having to install software
- Didn’t want to deal with different operating systems, firewalls, etc…
- Makes rolling out versions much easier
Scope of the project:
Budget wanted a program similar to the Southwest Airlines ‘Ding’ application to deliver special deals on car rentals to customers. Users would choose their preferred rental locations (up to 3 locations max) and specials at those locations would be displayed (3 specials for each location). The user could select a particular special and choose their pick up and drop off dates. The Budget Google gadget would take them into the reservation booking flow on budget.com to complete their car rental. If there aren’t any specials for a chosen location, a message of no specials available will be displayed. If there are no specials for all chosen locations, then the user will be taken to a page where they can enter one of the following three options: city & state, zip code, or airport that they wish to rent from. They can also choose their pickup and drop off dates & times for their rental location. The users are then taken to budget.com where they can choose their rate and complete their car rental.
All of this meant that we would need 4 pages: A preferences page for the user to select their rental locations, a specials page to display the specials for the locations, a chosen deal page where the user can select their pickup and drop off dates/times, and finally a no specials page for users to enter the city & state, zip code , or airport and pickup/drop off date and time. We also needed an RSS feed for users to add their chosen locations to a feed reader.
Issues with the Google Gadget API:
This is not meant to be a bashing of the Google Gadget API. It is simply my experience with using it. There maybe ways to get the API functions to work but since I was on a truncated time schedule, I had to make the project work. Perhaps we were pushing the technology beyond what it was intended to do? All of the Google gadgets that I have seen thus far have been very simple in what they do. Our Budget Google gadget seemed so simple at its inception…
Google Gadget Tabs
The Google gadget API tabs are so frustrating! You have no control over where it places the tab elements. It simply drops the div and table tags on top of any content you have on your page. What we wanted to do is have some content above the tabs:
I created the <div> and added the following content to it; Budget, deal alert, and the links to change locations and view more.

But as soon as I created the tabs, this happened:
It completely stomped on the content that was already there! Viewing the source I can clearly see that the content is there, but Google’s Tabs code simply adds itself to the top of the page. So I did some hacking and discovered that it creates a table with the tabs in it. So after figuring out what the name of the table is (tl_0_navTable), I used document.getElementById(’tl_0_navTable’).createTHead() to add a header to the table. I also added the content that I wanted to be above the tabs into this <thead> tag.
Now this (below) is what I was hoping to see. But it only worked in Firefox, so back to the drawing board.

After digging for a while in the API docs, I found out that you can add the tabs to a specific div. Substituting “var tabs = new _IG_Tabs()” for “var tabs = new _IG_Tabs(”tab_0″, “content_div”)” should add the tabs to the div with the id of “content_div”. This is correct but for whatever reason it moves the tabs content to the top of the page again despite the fact that the <div> I added it to is declared after the header div.
After all of this, I just gave up on using the Google Gadget API’s tabs. I believe that there is a way to make it work but after wasting 4 days on this, I decide to use Yahoo’s tabs instead.
Remote Content
When this project started, the plan was to have the gadget get all of the content from a RSS feed and display the data. I started off implementing the code entirely inside the gadget xml file with HTML and JavaScript. Immediately I ran into issues with this approach. The first problem I ran into is the fact that with the Google Gadget API, the only nodes that you can get from a RSS feed are ErrorMsg, URL, Title, Description, Link, Author, and Entry. Each entry has a Title, Link, Summary, and Date. This was a problem because I needed to deliver a lot of data in each entry and the only place I could put it all in was the entry’s summary node. I needed to pass data like the location the special was at, its rate type (weekly or daily), its normal price, discounted price, savings, date offer ends, an image of the car, and a link to the terms and conditions page (see below to get an idea of what I was trying to achieve).

Because of the limitations of the _IG_FetchFeedAsJSON() API function, I tried to pass the pre-formatted HTML in the Summary node via the RSS feed. Note: in the image below, I formatted the RSS <item> code to make it more readable.
Needless to say that it did not work correctly. With the <!CDATA[[ tag, it would not render the HTML correctly. Without this tag, the HTML came through correctly and rendered but the _IG_FetchFeedAsJSON() call stripped out a few necessary items. It removes all HTML id and class attributes, making styling difficult. It also removes all JavaScript in HTML <a> tags:
<a href=”javascript:void(0)” onclick=”openTerms(’11/05/07′, ‘11/18/07′, ‘Weekly’”);”> Terms and Conditions</a>
The Google Gadget API turns the above code into this:
<a> Terms and Conditions </a>
Here is some code with id and class for styling:
<span id=”std_rate” class=”standard_rate_text”>$330.99</span>
The code above becomes
<span>$330.99</span>
and the style for this span (which is a line through the text) is not applied because the class is stripped from the code.


I got around this issue by adding a style attribute to the HTML tags.
<span style=”font-size: 12px; text-decoration: line-through;”>$330.99</span>
This fixed the styling issues but made it difficult for our Creative team to style the pages.
Next, I had to deal with the issue of it stripping out the JavaScript. There wasn’t much that I could do about this issue. I could have added JavaScript code to get the <a> tag by name (the function removed all ids and left the name attributes) and add an onclick event for it. At this point I stepped back from the code and looked at all that I had. I realized that the hundreds of lines of JavaScript and HTML packed into one file would become a maintenance nightmare. I would need our Creative team to adjust the layout and style it. I doubt that they would want to hack through all of the js code to find out where it is generating the HTML and css to adjust it.
It was at this point that I decided, for many reasons, that it would be best to have the Google Gadget xml file point to a webpage that I create. I changed the xml file to have a content type of “URL” and now it acts as an iframe that displays our gadget.
Saving State
The Google Gadget API has the ability to remember user chosen options (user preferences). So for example, the user wants to see specials for Denver, CO all that needs to be done is to create the preference object and save the value:
var user_prefs = new _IG_Prefs(__MODULE_ID__);
user_prefs.set(”location1″, “Denver, CO”);
and to get the setting back at any time, use:
user_prefs.getString(”location1″)
This call will return “Denver, CO”. This all works fine and dandy when your code is entirely within the gadget xml file. But change the content type to “URL” and point it to your external website and it no longer works. In the API docs it clearly states that it will pass the required JavaScript libraries on the URL to your external site. This is so that you can include the js libs in your pages.

The problem is that now the code that creates the user preference object and gets/sets the users selected locations is generated by our server. The code still executes client side but it does not work. I am calling user_prefs.set(…….) and when the page is refreshed, the preferences are empty. The call to the set function is working but the value never gets stored. The API leads you to believe that you can have a gadget point to your web page and everything will work but it doesn’t. In the API examples http://code.google.com/apis/gadgets/docs/fundamentals.html#URL it talks about how to get the required JavaScript libraries and add them to your page so that you can call the functions in the API. But what I noticed in these examples, none of them actually call the API functions. Here is a source code snippet from the preferences page as rendered in the Google gadget on my home page. It shows that I am including the JavaScript files needed to call the preferences functions.
<script src=”http://www.google.com/ig/f/HCYu6mstKNU/lib/libcore.js”></script>
<script src=”http://www.google.com/ig/f/c64nWSZKWFg/lib/libsetprefs.js“></script>
Highlighted in red is the preference library js file. If you click on this URL http://www.google.com/ig/f/c64nWSZKWFg/lib/libsetprefs.js, it will prompt you to save the file (using IE). This proves that the path is correct and any calls to the functions in it should work. There are no JavaScript errors in the budget Google gadget code when rendered in the browser, so it is working. Yet it never sets the preferences. I added debugging code to see what was going on. What I discovered is that when the library is included in a web page outside of the Google gadget XML file, it doesn’t work. You call the set function and it stores the value. Then immediately call the get function and you get the value that you stored (as expected). However, if you refresh the page the stored value is gone. If the content type of your Google gadget is HTML instead of URL, the API calls to the preferences functions will work correctly.
As proof of this issue and to make this as simple as possible, I created three files. One complete gadget XML file (taken straight from the API examples), a gadget XML file that points to an external page, and the external web page. The complete XML file had all of the JavaScript and HTML within it. It is the magic decoder example in the API docs. I added a couple simple lines of code to it to store and retrieve the encoded string that the user enters into the gadget. Adding this complete gadget XML file to my Google home page, I see that it get and sets the encoded message string. When I refresh the page, I get back the stored preference. So everything is working correctly.
| Page Load | Refresh |
| Page Load | Refresh |
I then created a gadget XML file that points to an external web page that contains the same JavaScript and HTML as in the complete gadget XML file. I added the XML file to my Google home page and proceeded to test it. I entered my string and click ‘Transform’. I added alter prompts in the code so that I can verify that it is setting and retrieving the string. With the alerts, I could clearly see that it was storing the message and getting it back. But when I refreshed the page, the alert prompts showed that the stored value wasn’t stored. See the images above. The are screen shots of the alert prompts displayed at page load and at preference set/get.
The width of you Google Gadget
In the API docs, it states that your gadget can be as small as 250 pixels and as large as 600 pixels. Basically it takes 32% of the users screen resolution to set the width of your gadget. When our creative team first designed the CSS for the Budget gadget, the width was 380 pixels. This meant that it would only fit entirely in the container on Google.com if the users’ screen resolution was 1280×1024 or larger. Budget came back to us and requested that we support at least 1024×768. 32% of 1024 is 327.68 pixels. We re-adjusted the CSS and made the width of it 320 pixels. I uploaded the code to test it on Google.com and it was too wide. So, our creative team adjusted the CSS again, making the width 300 pixels. Again, it was too wide. After adjusting the CSS multiple times, we discovered that it could not be wider than 290 pixels at 1024×768 for it to fit! What is worse is that there is a known issue with Google.com and IE. In Firefox, our Budget gadget fits within the window at 290px. With IE, it has to scroll slightly.
In IE, the widths of Google Gadgets end up being around 10 pixels wider due to a 10px margin being added by Google to the gadgets: http://groups.google.com/group/Google-Gadgets-API/web/known-issues
Issue: Incorrect Width for Webpage Gadgets in IE
Description: 8-10px right margin added to webpage gadgets in IE. http://groups.google.com/group/Google-Gadgets-API/browse_thread/thread/101c59bf325f879d?hl=en
Reportee: abowman
Updated: 2007/09/21
Status:
Issue: There is no way to know when prefs.set() has finished.
Description: Setting a pref is not necessarily completed when the call returns. For this reason, a delay is required before taking some actions, such as reloading the gadget after setting a preference. Also, setting multiple preferences in rapid succession can result in unpredictably set preferences. For a description and possible workaround, see post: http://groups.google.com/group/Google-Gadgets-API/msg/94a3953251f2490a
Reportee:
Updated: 2007/06/29
Issue: Dynamic height doesn’t shrink gadgets
Description: Calling _IG_AdjustIFrameHeight() doesn’t work when the gadget needs to shrink. It only works when resizing to a larger height.
Reportee:
Updated: 2007/08/01
Issues with Google.com
Throughout the development cycle of this project I ran into numerous issues with google.com. Issues such as, when trying to add the gadget to my home page and it displaying an error message of “module not found”. This error message is completely misleading because I could browse straight to the gadget xml file with my browser. It is an issue with Google.com but the error message makes you think its an issue with your server. Another issue is that when you add your gadget to your home page, it will be cached by Google. So if you then make changes to the XML file, those changes will not show up. You have to add the “My Gadgets” Google gadget to your home page and uncheck the “cached” check box for your gadget in order to get it to update.
There are times that Google was working on their site and even though I had successfully added the gadget to my home page, it would display messages such as “temporarily unavailable” for my gadget and for other ones that I had added. The final issue I ran into is that even though I successfully published the budget gadget to Google’s content directory, I could never find it. After multiple tries, I read the fine print on the publish page (see screen shot below). It clearly states that it is their sole discretion as to if your gadget will ever be added to the content directory.
Conclusions
The final working product contains not a single line of Google Gadget API code. I think that says it all. The gadget API tabs don’t work as expected. Saving user preferences only works if all of the code is in the xml file. The width of your gadget is very important as when it is added to Google.com, the containing window will be 32% of the user’s screen resolution. Don’t forget about all of the padding Google adds on. If you are planning on using the RSS functionality, your hands will be tied to the hand full of nodes that the API allows you to access. All custom tags in the RSS file will be ignored. Finally, you are not guaranteed that your gadget will show up in the content directory after publishing it.
If your gadget is simple; by this I mean that all of the content exists within the xml file and you are not trying to display many different pages, then the Google Gadget API should work. Otherwise, create the web page(s) using whatever technology (Java/struts, .Net, php, etc…) and point the gadget XML file to your website.

















