Will Your Next Web Application Be a Google Gadget?

ince AJAX first appeared, developer interest in JavaScript has increased exponentially?and toolsets for building functionality in JavaScript have burgeoned as well. One of the latest tools for JavaScript-related development is Google Gadgets. Many web sites already provide gadgets built with the Google tools, and you can, too. This article shows you how to develop a Google Gadget that fetches DevX RSS feeds and displays them to the user.

Anatomy of a Google Gadget
Developing a Google Gadget (simply gadget from now on) is really an easy task. After all, a gadget is made of XML, HTML, JavaScript, and optionally, CSS, used as follows:

  • XML describes the gadget’s structure.
  • HTML and CSS provide the presentation layer.
  • JavaScript supplies the gadget’s logic.

The XML below shows the basic gadget structure:

                                  ]]>         

Here’s a breakdown of the XML elements. The root element is Module. The ModulePrefs element holds information about the gadget (title, height, and so on) and its author?more on this later. The Content element is apt to contain the “real” content?the CSS, HTML, and JavaScript code. As you’ll see later on, there are other elements to consider, but these are the basic elements you’ll use in each and every gadget you develop.

A Simple “Hello, World” Gadget
The easiest way to start learning a new programming language or technology is to dive right in with a simple example. Here’s the code for a gadget that just prints out the traditional “Hello, World” message in a box:

                                      hello, world!          
]]>

Save the preceding XML file using the name hello-world.xml. You won’t use this right away?I’ll show you how to deploy it in the next section?but for now, look at Figure 1, which shows what the HelloWorld gadget looks like when run.

 
Figure 1. HelloWorld Gadget In Action: This is how the hello-world.xml renders in the Google Homepage.

By looking at the previous example you can note the following:

  • You define a gadget completely within an XML file.
  • The tag indicates that this XML file contains a gadget.
  • You specify the gadget’s title using the ModulePrefs attribute.
  • The line indicates that the gadget’s content type is HTML (it may also contain CSS and/or JavaScript code). There are other content types but this one is the most flexible and versatile. You can find more info on content types here.
  • The CDATA section contains the HTML (and optionally CSS and JavaScript) code used to render and activate the gadget. You don’t have to use CSS, you can use simply inline style attributes in your HTML, as the previous example does to specify the red color.

To include a CSS block use a standard

You include JavaScript code within

You'll see more on both CSS and JavaScript later on when developing the DevX RSS reader gadget.

Gadget Deployment
Of course, after developing a gadget you will want to deploy it. For example, to run the "hello, world" gadget you need to follow these steps:

  1. Upload the gadget to your web server.
  2. Go to http://www.google.com/ig.
  3. To add a gadget, you must have a personalized homepage. If you don't already have one, you must create it by clicking the "Get started" link. Then sign in with an existing Google Account, or create a new one.
  4. After you have a personalized homepage, you can add gadgets to it by clicking the "Add stuff" link in the upper right corner. This takes you to the content directory. You can use the content directory to search for gadgets and add them to your homepage.
  5. Click the "Add by URL" link (next to the "Search Homepage Content" button).
  6. In the "Add by URL" field, enter the URL (your server or mine) for the hello-world.xml gadget, and then click the Add button.
  7. Click the "Back to homepage" link, to see the new gadget on your homepage.
Author's Note: I uploaded the examples for this article in my web server so you can try them even if you don't have your own. You can find all the examples under the following path: http://www.alessandrolacava.com/google/gadgets/. For example, the URL related to the "hello, world" gadget will be: http://www.alessandrolacava.com/google/gadgets/hello-world.xml.

You can also publish your gadgets to the Google Content Directory. That link discusses how to publish your gadget to other targets too, such as your own web site through syndication.

Now that you've seen how to write and deploy a simple gadget you're ready to move on to more interesting stuff.

User Preferences
Some gadgets need to give users a way of supplying user-specific information. For example, an RSS-feed-reader gadget might require users to provide the number of items to retrieve or the URL of a feed the user is interested in. The user preferences section in the XML file describes the user input fields that are turned into user interface controls when the gadget runs. To include a user preference in your gadget you need to include a section into your XML file. Going back to the HelloWorld example, you could let users specify the gadget's title through a UserPref element:

                                     hello, world!          
]]>
 
Figure 2. User Preference: At runtime, the gadget renders the "title" user preference as a simple textbox.

Figure 2 shows how the preceding user preference gets rendered?as a simple textbox.

The attributes for the UserPref elements in the preceding example have the following meaning:

  • name is the preference's name. It's through its name that you refer to the preference within your code. This is the only required attribute.
  • display_name is the label displayed beside the rendered object?textbox, checkbox, and so on.
  • default_value is the value selected by default.
  • datatype is the datatype of this user preference. The options are string, bool, enum, hidden, list or location. If you don't specify a datatype value it defaults to string. The preference rendering depends on this attribute. For example, a preference defined with a datatype of bool renders as a checkbox. You'll see more about datatypes later on.

These attributes are the most common, but there are others you might need in your gadget. Refer to the official documentation to find out more on this and other gadget API topics.

Notice how the previous code points to the user preference. It uses a double underscore, followed by "UP," which signifies "User Preference," another underscore, and the preference name, closing with another double underscore:

   __UP_prefName__

The only thing that changes is prefName which must match the name you provided in the name attribute when you defined the preference. Such a variable is called a "substitution variable." For example, you can refer to the title preference using __UP_title__. Later on you'll see how to reference a user preference programmatically within a JavaScript block.

Core JavaScript Library
You manage gadget logic using JavaScript. To include JavaScript code in your gadget use the following template:

                                                   // Your JavaScript code here...                     ]]>         

You'll use the Google Gadget API core JavaScript library frequently. Possibly the most important function in this library is _IG_RegisterOnloadHandler, which is the event-handler function called when the gadget is loaded. It takes a single argument?the function it should invoke when the page loads. Here's an example:

                         function init()            {              alert("hello, world!");            }                  // Call the init function on page load            _IG_RegisterOnloadHandler(init);                     ]]>     

This gadget uses _IG_RegisterOnloadHandler to call the init function, which displays "hello, world!" as soon as the page gets loaded.

Another useful Google Gadget API feature is the _IG_Prefs class, which you use to retrieve user preferences programmatically:

   var prefs = new _IG_Prefs();   var title = prefs.getString("title");   alert(title);

The preceding code retrieves the title user preference and displays it using the JavaScript alert method. In addition to getString, you can use getInt and getBool to retrieve integer and Boolean user preferences, respectively. You can also set preferences as well as retrieve them, for example:

   var prefs = new _IG_Prefs();   prefs.set("title", "The New Title Here");

To use this setter method you need to include the setprefs library in your gadget. You do this through a Require XML element?a child of ModulePrefs, for example:

           

There are many other JavaScript libraries you may find useful. They fall under the name "Feature-Specific JavaScript Libraries." For example, you could include the JavaScript library used to build tabbed applications. You'd include that library using the following code:

           

The preceding code would let you use the _IG_Tabs class and its methods to build very nice tab-based gadgets. I won't go into any more detail about this and other feature-specific JavaScript libraries in this article since that discussion would require an article of its own.

Instead, I'll briefly discuss useful features in the core JavaScript libraries. For example, if you want to retrieve the content at a URL as plain text, HTML or JSON (read more on JSON here) you can use the _IG_FetchContent function as follows:

                   function init()         {         _IG_FetchContent(           "http://www.some-content.to-display.com",            callbackFunc);      }         function callbackFunc(responseText)      {        _gel("mainContainer").innerHTML = responseText;      }            // Call the init function on page load      _IG_RegisterOnloadHandler(init);                          
]]>

As you can see _IG_FetchContent takes two arguments. The first is the URL to fetch the content from. The second is the callback function to call when the content gets retrieved. This is necessary since _IG_FetchContent is asynchronous so it does return immediately after its call. In the previous example the callback function displays the HTML code retrieved from that fictitious URL within the mainContainer div tag.

Of course, you're free to handle the retrieved content any way you'd like. For example, you might use this scenario to display a random funny quote whenever the gadget gets loaded. Note also the use of another core function, _gel, which is just a shorthand "get-element" wrapper around the more verbose document.getElementById JavaScript function. There are other useful wrapper functions. You'll see some of them later on. The others can be inspected by pointing your browser to the Official API Reference.

In addition to _IG_FetchContent, the core API provides two other functions used for similar purposes:

  • _IG_FetchXmlContent(URL, func): Fetches the XML content at the specified URL. When it's ready, calls the function specified in the func parameter. Like _IG_FetchContent(), it is asynchronous.
  • _IG_FetchFeedAsJSON(URL, func, num_entries, get_summaries): Fetches the RSS/Atom feed content at URL asynchronously and returns it as a JSON object. When it's ready, calls func. It fetches, at most, the number of feed entries specified by num_entries (the default is 3, the possible range is 1-100), and optionally fetches summaries for each entry depending on the value of get_summaries?the default is false.

I used the _IG_FetchFeedAsJSON method to develop the DevX RSS feed-reader gadget discussed next.

Build a DevX RSS Feed-Reader Gadget
Listing 1 contains the complete code to build the DevX feed reader. Although at first glance, that might look like a lot of code, it's not that much when you consider that it contains both the presentation and the business logic.

Again, if you don't have a server, you can test this gadget at the URL http://www.alessandrolacava.com/google/gadgets/devx-feeds.xml.

Although Listing 1 is considerably larger than the examples you've seen so far, it is composed of the same elements you've already seen, starting with the Module root element, which contains the following child elements:

  • ModulePrefs
  • A set of UserPref elements.
  • A Content element.

However it's worth explaining some new aspects you haven't seen yet. Here's the ModulePrefs element:

       

As you can see, the ModulePrefs element contains general infoormation about the gadget?author info, the gadget's height and so on. Because the content may exceed the height, the gadget supports scrolling via the scrolling="true" attribute. Notice how I wrote my e-mail address:

   [email protected]

I've done that because of spam. Gmail drops everything after the plus sign (+).

I told you earlier that you can specify other datatypes for a UserPref section. The control rendered for the preference strongly depends on the datatype you choose. If you don't specify a datatype it defaults to string, which gets rendered as a simple textbox. The title preference relies on that default rendering:

   
 
Figure 3. Preferences: The figure shows how the the DevX gadget renders preferences.

Figure 3 shows how the sample gadget renders the title and other preference options.

The DevX gadget uses bool and enum preference datatypes as well as the default string type. As you can see from Figure 3, the gadget renders bool preferences as a checkbox, while the enum preference becomes a combobox (a drop-down list). For example, here's the feedUrl enum preference definition that lets the user choose which of several DevX feeds to fetch.

                     ...   

Note that you can indicate a default value through the default_value attribute of the UserPref element, and that the displayed value can differ from the item's value. You specify a different display value using the display_value attribute. If you don't indicate a specific display value the control displays the value attribute value instead?which wouldn't be as convenient for end users in this case. For completeness, here's a more detailed examination of one of the bool preferences used in the DevX gadget:

   

The previous preference definition lets users choose whether to open feed links in the same page?that is in their Google Homepage?or in a different page. I chose true as the default for this preference, which means that the checkbox will be checked by default and all the feed links will load in a fresh browser instance.

As you have probably guessed, the most important part of a gadget lies in the Content element, which is where you define the presentation (CSS and HTML) and business logic (JavaScript) for the gadget application.

I won't delve into the CSS section because it's just styling, like any other CSS. The HTML part is delimited by the following line of code:

   

The contents of this div element will contain the entire gadget body?namely, the DevX RSS feeds.

That leaves the JavaScript section, which is formed by three functions: fillGlobals, init, and parseFeed?plus a call to the _IG_RegisterOnloadHandler discussed earlier, which registers the init function as the page load handler. Here's the code for the init function:

   function init()    {      fillGlobals();         // Display loading message before fetching feed.      container.innerHTML =          '
Loading...
'; // Fetch feed and return it as a JSON object. // parseFeed is the callback function. _IG_FetchFeedAsJSON(feedUrl, parseFeed, numOfEntries); }

The init function calls fillGlobals, which simply assigns values to some variables used throughout the application. It does this by retrieving the user preferences and getting a handle to the feedContainer div tag that contains the feeds. Here's the code for the fillGlobals function:

   function fillGlobals()   {      var prefs = new _IG_Prefs(__MODULE_ID__);      feedUrl = prefs.getString("feedUrl");      numOfEntries = prefs.getInt("numOfEntries");      container = _gel("feedContainer");      showFeedDate = prefs.getBool("showFeedDate");      openInNewPage = prefs.getBool("newPageTarget");   }

After calling fillGlobals, init displays a "Loading..." message in the main container and then calls the _IG_FetchFeedAsJSON built-in function used to retrieve the RSS feed as a JSON object (see this JSON article for more information. The three parameters passed to _IG_FetchFeedAsJSON are:

  • feedUrl: The URL of the RSS feed to fetch. The fillGlobals method retrieves the specific preferred feed value from the user preferences.
  • parseFeed: This is a callback function fired when the feed has been retrieved. Remember, _IG_FetchFeedAsJSON fetches feeds asynchronously and, when done, passes the JSON object?representing the feeds?to the specified callback function, namely parseFeed.
  • numOfEntries: the maximum number of entries to retrieve. This variable is also initialized in fillGlobals from the user's preferences.

That's all the basic information you need to know to build brilliant Google gadgets. Figure 4 shows a screenshot of the completed gadget in my Google homepage.

 
Figure 4. The DevX Tab in My Google Homepage: Here's my homepage showing both the sample gadgets developed for this article?the HelloWorld gadget and the DevX feed reader gadget.

I won't delve into the details of the parseFeed function because it's just basic JavaScript code that loops through the retrieved feed entries and adds them to the gadget's main container (the feedContainer div). The only thing you might need to know more about is the structure of the JSON object passed to parseFeed. For more information on that you can explore the official documentation.

Security Concerns
Finally, I wish to spend some words about security. In fact, you might wonder: "What impedes my gadget from accessing data that belongs to another gadget on the page?"

From gadgets whose content type is HTML?declared such through the line —you cannot access other gadgets' data. The reason for this is that each HTML-type gadget is rendered into an iframe. The following is an excerpt from the Google Gadget Development Fundamentals page:

"Gadget content is wrapped in an iframe. An iframe is effectively a separate page running within the parent page. The iframe has no knowledge of and no ability to interact with the parent page. This isolation helps protect users from malicious gadgets that might try, for example, to steal or modify cookies. However, iframes do impose certain restrictions by denying gadgets to interact with each other and other components on the page."

You can bypass these restrictions by declaring your gadget as an HTML-inline gadget, which you do by changing its content type setting to . Of course, this type of gadget has other limitations; for example, you can't use HTML-inline gadgets with other Google properties, and HTML-inline gadgets can't be included in the content directory. There are other major drawbacks to inlining, but they are beyond the scope of this article. For more information about inline gadgets I strongly suggest you download and study the related documentation.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Related Posts