This document describes concepts and tasks that are fundamental to the gadget development process. For an introduction to using the gadgets API, see Getting Started.
Choosing a Content Type
One of the first decisions you make when you're developing a gadget is which content type to use. For example:
<Content type="html">
The content type determines:
- Which API features are available to you as a gadget author.
- How your gadget is rendered.
- Where you can deploy your gadget.
The following table describes the available content types and when you should use them:
Content Type | Description | When to Use |
---|---|---|
html |
With an html content
type, all of the content typically resides in
the gadget spec. A type="html" gadget contains
HTML, possibly with embedded JavaScript, Flash,
ActiveX, or other browser objects. This is the
default type. |
The most flexible, versatile content type is
html . When in doubt, choose the html content
type. |
url |
With a url content
type, the gadget content lives on a remote web
page referenced by a URL in the gadget spec.
The remote web page is where all of the HTML
markup and JavaScript resides. You can NOT put
any HTML markup or JavaScript code in the gadget
spec itself. |
The type="url" content type is currently not fully supported by the gadgets.* or
OpenSocial APIs. |
HTML
With the html
content type,
all of the code typically resides in the gadget spec.
This includes the gadget XML, and any HTML markup and
JavaScript. Almost all of the examples in this developers
guide use the html
content
type. It is the most flexible, versatile type, and it
should usually be your choice unless you are writing
a gadget that has particular requirements.
The following example is a gadget implementation of ROT13. ROT13 encrypts text by replacing each letter with the letter 13 positions to the the right in the alphabet. Then when you reapply ROT13, it rotates each letter again, which restores the original text.
This is the gadget spec:
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Magic Decoder"/> <Content type="html"> <![CDATA[ <script type="text/javascript"> // The gadget version of ROT13. // Encodes/decodes text strings by replacing each letter with the letter // 13 positions to the right in the alphabet. function decodeMessage (form) { var alpha = "abcdefghijklmnopqrstuvwxyz"; var input = form.inputbox.value; var aChar; var message = ""; for (var i = 0; i <input.length; i++) { aChar = input.charAt(i); var index = alpha.indexOf(aChar.toLowerCase()); // if a non-alphabetic character, just append to string if (index==-1) { message += aChar; } // if you have to wrap around the end of the alphabet else if(index > 12) { // compensate for 0-based index index = 25 - index; // last item in array is at [25] index = 12 - index; // because array starts with 0 aChar = alpha.charAt(index); message += aChar; } // if you don't have to wrap else { aChar = alpha.charAt(index+13); message += aChar; } } document.getElementById('content_div').innerHTML = "<b>Your message: </b>" + message; } </script> <FORM NAME="myform" ACTION="" METHOD="GET">Message: <BR>
<INPUT TYPE="text" NAME="inputbox" VALUE=""><P>
<INPUT TYPE="button" NAME="button" Value="Transform" onClick="decodeMessage(this.form)"> </FORM> <div id="content_div"></div> ]]> </Content> </Module>
The rules for a type="html"
gadget
are as follows:
- A
type="html"
gadget must include a CDATA section, and any HTML must go within that section:
<Content type="html"> <![CDATA[ HTML here... ]]>
CDATA sections are used to escape blocks of text containing characters that would otherwise be regarded as markup. The only delimiter that is recognized in a CDATA section is the "]]>" string that ends the CDATA section.
- You cannot use the
<html>, <head>,
or<body>
tags. Gadgets are generated with their own<html>, <head>,
and<body>
tags. Just include the content that would normally go inside the<body>
tag.
A gadget with an html
content
type can also reference an external JavaScript file:
<Module> <ModulePrefs ... /> <Content type="html"><![CDATA[ <script src="http://www.example.com/gadgets/clock/clock.js" type="text/javascript"></script> ]]></Content> </Module>
URL
When a gadget has a type="url"
content
type, the href=
attribute
provides a URL, and any other content in the
gadget spec is ignored. With a url
content
type, the assumption is that all information relating
to the gadget's user interface and programmatic logic
resides in the file referenced by the URL. You do not
put any HTML markup or JavaScript within the gadget itself. For
example:
<Module> <ModulePrefs ... /> <Content type="url" href="http://www/cgi-bin/example/gadgets/mystats.cgi" /> </Module>
Turning an Existing Web Page or Application into a Gadget
You can turn an existing web page or application into a gadget by following these guidelines:
- Strip the
<html>
,<head>
, and<body>
tags (in other words, just provide the HTML content itself). This guideline only applies totype="html"
gadgets. It does not apply totype="url"
gadgets. - For onload events, use
gadgets.util.registerOnLoadHandler()
. - If the gadget requires login, use a URL content type. See Writing Gadgets that Require Login or Cookies for possible gotchas. Note that HTTPS gadgets issue "mixed content" warnings under Internet Explorer, which can be annoying for users.
- Make any user interface changes necessary to fit
your page or application into the small gadget space.
For hobbyists and professionals building prototypes,
makeRequest()
can be used to proxy the content. For commercially developed gadgets, we recommend simply creating a new, small page and usingtype="url"
to point to it.
Working with Userpref Data Types
Within the gadget spec, every user
preference has a data
type. The datatype
is an
optional string that specifies the data type of the attribute.
The possible values for datatype
are string
, bool
, enum
, hidden
(a
non-visible string that is not user editable), and list
.
The default data type is string
.
See the reference for detailed information about user preference data types.
This section describes one of the more specialized data
types: list
.
You can find examples of how to use the the other data types
throughout the documentation (for example, enum
,
hidden
, and bool
).
Using the list Data Type
A user preference with the list data type is an array of values that are dynamically supplied by users at run time. As users type values into the user preferences edit box, these values are added to the list. The list can be programmatically accessed by the gadget at run-time, just like any other userpref. You can use the list data type any time you want to allow users to dynamically supply an arbitrary list of values. For example, a weather gadget might allow users to enter a list of postal codes.
You declare that a userpref has the list data type by using datatype="list"
. For example:
<UserPref name="mylist" display_name="Add Search Terms" datatype="list" required="true"/>
The gadget accesses the values in the list using the Prefs
function getArray(),
for example:
var search_terms = prefs.getArray("mylist");
Within the array, items are stored as a pipe-delimited list. You can use the Prefs
function getString()
to return this list as a single string in which the values are separated by the pipe (|) character, for example:
prefs.getString("mylist");
You can also use a pipe-delimited string to set default values for a list type:
<UserPref name="mylist" display_name="Add Search Terms" datatype="list" default_value="zdnet|pc|Apple Insider"/>
You can use the Prefs function setArray(name, val)
to programmatically add values to the list. To use this function, your gadget must include <Require feature="setprefs"/>
under <ModulePrefs>
. For example, the following excerpt adds the values "Nokia" and "CNET" to the list:
... <ModulePrefs title="Feed Searcher" scrolling="true"> <Require feature="setprefs" />
</ModulePrefs> ... prefs.setArray("mylist", ["Nokia","CNET"]);
Here is a simple example that outputs the list items that users enter in the Edit box:
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="List Data Type Example" scrolling="true"/> <UserPref name="mylist" display_name="Add Terms" datatype="list" /> <Content type="html"> <![CDATA[ <div id=content_div></div> <script type="text/javascript"> // Get userprefs var prefs = new gadgets.Prefs(); // Get the array of search terms entered by the user var terms = prefs.getArray("mylist"); var html = ""; // If the user has not added any terms yet, display message. if (terms.length == 0) { html += "Edit the userprefs to add terms."; } else { html += "Your terms are:<br /><br />"; for (var i = 0; i < terms.length ; i++) { var term = (terms[i]); html += term + "<br />"; } } document.getElementById("content_div").innerHTML = html; </script> ]]> </Content> </Module>
Specifying a Geographical Location
To write a gadget that lets users specify a geographical location, you use the Google Maps API geocoder.
Note: Previously, gadget developers used the location
userpref data
type to let users specify a geographical location. This data type has been deprecated.
For example, the following gadget displays a map for a user-specified address. It includes a user preference named loc
that has the default data type "string." When the gadget runs, the user enters an address for loc
, which is passed as a string to the GClientGeocoder
getLatLng
method. The getLatLng
method sends a request to Google servers to geocode the specified address, and invokes the specified callback function (in this example, showMap
) with a GLatLng point:
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Map of __UP_loc__" height="300" author="Jane Smith" author_email="xxx@google.com" /> <UserPref name="loc" display_name="Location" required="true" /> <Content type="html"> <![CDATA[ <script src="http://maps.google.com/maps?file=js" type="text/javascript"></script> <div id="map" style="width: 100%; height: 100%;"></div> <script type="text/javascript"> var prefs = new gadgets.Prefs(); var map = new GMap(document.getElementById("map")); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl()); var geocoder = new GClientGeocoder(); geocoder.getLatLng(prefs.getString('loc'), showMap) function showMap(point) { if (point!=null) { map.centerAndZoom(point, 6); } }; </script> ]]> </Content> </Module>
Saving State
It's common to let users set their user preferences explicitly, using the edit box. But sometimes it's useful to set the values for user preferences programmatically, without the user's direct participation. For example, for a game gadget, you might want to persistently store the user's highest score. You could do this by programmatically setting the value of a "high_score" user preference.
To use the setprefs
feature,
your gadget should include the following:
- A
<Require feature="setprefs"/>
tag (under<ModulePrefs>
) to tell the gadget to load thesetprefs
library. - A userpref whose value you want to set programmatically
and store persistently. Typically you give this userpref
a datatype of
hidden
. - A call to the JavaScript function
set()
for the userpref whose value you want to save.
Note that preference size is currently constrained by URL limitations, which is 2K.
The following sample gadget consists of two buttons: one
that increments the value of a counter, and another that
resets the value of the counter to 0. In this example, "counter" is
a user preference. It has a datatype of hidden
,
meaning that users are not permitted to directly modify
its value.
This is the gadget spec:
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Setprefs New"> <Require feature="opensocial-0.8"/> <Require feature="setprefs" /> </ModulePrefs> <UserPref name="counter" default_value="0" datatype="hidden"/> <Content type="html"> <![CDATA[ <div id="content_div" style="height: 100px;"></div> <script type="text/javascript"> // Get user preferences var prefs = new gadgets.Prefs(); var html = ""; var div = document.getElementById('content_div'); // Increment value of "counter" user preference function incrementCounter() { var count = prefs.getInt("counter"); div.innerHTML = "The count is " + count + "."; // Increment "counter" userpref prefs.set("counter", count + 1); } // Reset value of "counter" userpref to 0 function resetCounter(){ prefs.set("counter", 0); div.innerHTML = "Count reset to " + prefs.getInt("counter") + "."; } </script> <input type=button value="Count" name="count" onClick="incrementCounter()"> <input type=button value="Reset" name="reset" onClick="resetCounter()"> ]]> </Content> </Module>
Note: If you need to store multiple values, we recommend saving the values into a JSON string.
Escaping Special Characters
Within the XML attributes in a gadget spec, you must escape certain special characters. Note that only ASCII entities can be used in the gadget spec. For example, you cannot use ISO 8859-1 symbol entities. Here is a list of the special characters that are supported:
Character | Escape Code |
---|---|
& |
& |
< |
< |
> |
> |
" |
" |
' |
' |
For example:
- BAD:
href="http://www.foo.com/bar?x=a&y=b"
- GOOD:
href="http://www.foo.com/bar?x=a&y=b"
- BAD:
description="this is a "sexy" gadget"
- GOOD:
description="this is a "sexy" gadget"
Note that this type of escaping is not required in the CDATA block - escaping in a CDATA block will produce incorrect output.
Within your JavaScript code, you can use the _hesc(str)
function
to return the HTML string str
with
the the following characters escaped: <>'".
Writing Gadgets that Require Login or Cookies
The default privacy policies of Microsoft Internet Explorer and Apple Safari do not permit third-party sites to set cookies. Hence some gadgets may not work correctly. In particular, sites that uses cookies for logins may not work correctly inside an iframe from the iGoogle page. Here are possible solutions:
- Pass parameters such as authentication credentials in the URL instead of cookies. One warning: if you choose this approach, be careful not to leak these URL parameters to other web sites in HTTP referral fields.
- Use OAuth. The gadgets API supports a feature called the OAuth proxy that allows gadgets to securely connect back to another website which stores private data associated with a user's account. OAuth is a standard to allow the account owner of the private data to tell the website to allow another application to access the data. The OAuth proxy is designed to make it easier for gadgets to use the OAuth standard.
- Write P3P headers. Depending on your site's privacy policy, you may be able to write P3P headers that allow Internet Explorer (but not Safari) to read third-party cookies from your site. If you are using PHP and you want to set the headers, you can use this code snippet at the top of your PHP page:
<?php header("P3P: CP=\"CAO PSA OUR\""); ?>This must be called before any output is displayed on the page.
YOU WILL NEED TO CHECK YOUR PRIVACY POLICY CAREFULLY TO SEE WHAT HEADERS CAN BE USED FOR YOUR SITE. WE ENCOURAGE YOU TO CHECK WITH YOUR ATTORNEY.
- Instruct users via JavaScript. You can also integrate instructions with your authentication logic. If you detect that cookies are being blocked, instruct users to lower their privacy setting or try a different web browser. For example, you could display a message such as the following:
Your browser is incompatible with this site as configured. If you are using Microsoft Internet Explorer, you can change your security settings by choosing Tools > Internet Options. Open the Privacy tab, click Advanced, and then check Override automatic cookie handling. Under Third-party Cookies, click Accept. Alternatively, you can try another web browser, such as Firefox.
- If your gadget accesses sensitive private user data, we suggest that you use the locked-domain feature.