Web Gadget SDK Web Gadget SDK
[Back to Web Gadget SDK Index]
This guide will walk you through the process of creating a complete Windows Live Gadget that users can put onto their Live.com home pages. If you are new to Windows Live Gadget development, we recommend that you start here.
Windows Live Gadget Developer's Guide
Table of Contents
Introduction
Requirements for Gadget Development
In order to develop Gadgets, you should have a web server that you're running locally. The instructions on this page assume that you're using IIS, but other web servers should work equally well for Gadget development. You simply need to be able to serve XML, JavaScript, and css files from http://localhost.

Developing Gadgets requires writing code. While some prefer to edit code files in notepad, it's highly recommended that you use a true development IDE. As you start to develop more sophisticated Gadgets and write more complex JavaScript code, it also becomes very useful to have a debugger. We recommend Visual Web Developer Express which is a free version of Visual Studio that includes everything you need for writing and debugging Windows Live Gadgets.
Download the Samples
Start out by downloading the sample Gadgets and unzipping them to a directory on your local machine. The sample Gadgets range in complexity from a super-simple 'Hello World' example to a more complex 'Rumor Mill' Gadget that downloads XML asynchronously and dynamically modifies the DOM to display content. In the next section we'll get your environment setup so you can have Live.com load the sample Gadgets from your local machine.
Writing a Gadget
Setting Up Your Environment
The purpose of this section is to get you up and running with the sample Gadgets as quickly as possible. There is no "installation" per se with regard to Gadgets, but you'll need to configure a web server on your machine to host your Gadgets. This guide will walk you through the process.

Let's Go!

This guide assumes you aryou've unzipped the samples to a directory on your machine. We'll be pointing an IIS vroot at this directory. The samples assume that the name of your vroot is called "Gadgets". You'll need to change the samples if you pick a different name. These instructions refer to IIS, but using IIS is not a requirement. You can use any web server you wish as long as they can serve .js, .css and .xml files.
  1. Open IIS Manager
  2. Create a new Virtual Directory called "Gadgets"
  3. Point this vdir at the Samples directory in the SDK (e.g. D:\GadgetsSDK\Samples\).
  4. Verify everything is working by trying http://localhost/Gadgets/hello/HelloWorldGadget.xml. You should see the XML for the Hello World sample.
  5. Add http://*.live.com and http://*.start.com to your list of trusted sites. See screenshot below for details.
Alternatively, if you don't have IIS or another web server already installed, you can try using Microsoft's free Cassini Web Server. Follow these steps:
  1. Go to the Cassini homepage and follow the instructions to get Cassini installed.
  2. From a cmd prompt start the cassini web server as follows: CassiniWebServer <physical-path> <port> <virtual-path>
  3. For example: CassiniWebServer D:\GadgetsSDK\Samples 80 /Gadgets
  4. Verify everything is working by trying http://localhost/Gadgets/hello/HelloWorldGadget.xml. You should see the XML for the Hello World sample.
  5. Add http://*.live.com and http://*.start.com to your list of trusted sites. See screenshot below for details.
You will also need to add 'http://*.live.com' and 'http://*.start.com' to the list of Trusted sites in IE. Inside IE, you can do this by clicking on Tools -> Internet Options... select the Security tab and click on Trusted sites. Then, click on the 'Sites...' button. Make sure that the 'Require server verification (https:) for all sites in this zone' is unchecked. Then, add 'http://*.live.com' and 'http://*.start.com' to the list.

Adding 2 URLs to the list of Trusted Sites. 

In order to develop locally hosted Gadgets within IE, ensure that you have set the 'Access data sources across domains' setting to 'prompt'. Inside IE, you can check this through Tools -> Internet Options... Click on the Security tab, select internet zone and click on 'Custom Level'. Under 'Miscellaneous' select 'prompt' for 'Access data sources across domains'.

Setting Access data sources across domains 'to Prompt'. 


Now you are ready to add sample Gadgets from your own machine to Live.com. Add a Gadget with the following steps:
  1. Go to http://www.live.com
  2. Click on 'add stuff, and click on 'Advanced Options'
  3. Enter the url to the manifest XML for one of the sample Gadgets in the 'add Gadget or feed by url' edit box. For example, try http://localhost/Gadgets/hello/HelloWorldGadget.xml.
  4. If successful, you'll be switched to the 'my stuff' view and 'Hello World' should be bolded for 2 seconds.
  5. Clicking on the item should open a pop-up module with your Gadget, which you can then add to your page and rearrange as desired. That's it!
Disable Caching in Internet Explorer
You should also set Internet Explorer not to cache files so that you are always loading the latest copy of your Gadget file while you are developing and debugging. To do this, do the following:
  1. In Internet Explorer, click on Tools > Internet Options...
  2. Under the 'General' tab, click'Settings'
  3. Under 'Check for newer versions of stored pages', choose 'Every visit to the page'.
  4. Click OK and then OK again.
Screen shot of how to set IE not to cache.
Writing Gadgets using ASP.net
You can also use ASP.net to write Gadgets. However, this is not covered by this document. For an overview of that process, check out the ASP.net 'Atlas' website.
Starting with Hello World Sample
The Hello World Gadget that we used for testing your environment setup above is a good starting point for us to learn Gadget development. Now you're ready to modify the .js code to make your Gadget unique!
Anatomy of a Gadget
At the most basic level, a Gadget consists of the following 3 components:
  1. Gadget Manifest - this is an XML file that defines several properties of your Gadget, including the location of your Gadget Script file and Gadget Style Sheet.
  2. Gadget Script file - this is a JavaScript file that contains all the code for creating your Gadget.
  3. Gadget Style Sheet - this is an optional CSS file that you can use to help present the UI for your Gadget.
A Gadget is defined by an XML file, called the Gadget manifest. The purpose of the manifest XML is to define a few of the static properties for the Gadget and to provide urls for the script code and style sheet for the Gadget. Here is an example Gadget manifest:

    <?xml version="1.0"?>
    <rss version="2.0" xmlns:binding="http://www.live.com">
        <channel>
            <!-- Specifies the name of your Gadget -->
            <title>Example Gadget</title>
            <!-- Gives a short description of your Gadget -->
            <description>Write a short description here.</description>
            <!-- Specifies the default locale for your Gadget -->
            <language>en-us</language>
            <!-- Specifies the JavaScript object that defines your Gadget 
                (this object must be defined in the JavaScript linked to below) -->
            <binding:type>CompanyName.TeamName.Feature.ExampleGadget</binding:type>
            <item>
                <!-- Specifies the JavaScript file that contains the code for your Gadget -->
                <link>http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.js</link>
            </item>
            <item>
                <!-- Specifies the css file that contains the styles for your Gadget -->
                <link binding:type="css"> http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.css
    </link>
            </item>
            <icons>
                <!-- Specifies the icon to be displayed in the title bar for your Gadget -->
                <icon height="32" width="32"> http://www.mydomain.com/Gadgets/ExampleGadget/TitleIcon.gif</icon>
            </icons>
        </channel>
    </rss>
        
The JavaScript for your Gadget is expected to define a binding of the name specified in your manifest XML. So what does a binding look like?

    // register your Gadget's namespace
    registerNamespace("CompanyName.TeamName.Feature");

    // define the constructor for your Gadget (this must match the name in the manifest XML)
    CompanyName.TeamName.Feature.ExampleGadget = function(p_elSource, p_args, p_namespace)
    {
        // always call initializeBase before anything else!
        CompanyName.TeamName.Feature.ExampleGadget.initializeBase(this, arguments);

        // setup private member variables
        var m_this = this;
        var m_el = p_elSource;

        // initialize is always called immediately after your object is instantiated
        this.initialize = function(p_objScope)
        {
            // always call the base object's initialize first!
            CompanyName.TeamName.Feature.ExampleGadget.getBaseMethod( 
                this, "initialize", "Web.Bindings.Base").call(this, p_objScope);

            // perform any initialization/setup required for your Gadget
            m_el.innerText = "Hello, world!";
        }

        this.dispose = function(p_blnUnload)
        {
            // detach from all events

            // null out all member variables
            m_this = m_el = null;

            // always call the base object's dispose last!
            CompanyName.TeamName.Feature.ExampleGadget.getBaseMethod( 
                this, "dispose", "Web.Bindings.Base").call(this, p_blnUnload);
		
		CompanyName.TeamName.Feature.ExampleGadget.registerBaseMethod(this, "dispose");
        }
    }
    CompanyName.TeamName.Feature.ExampleGadget.registerClass( 
        "CompanyName.TeamName.Feature.ExampleGadget", "Web.Bindings.Base");
        
        

Here's the resulting Gadget as it appears on the Live.com page.
Resulting Gadget
Defining Content
Gadgets display content by dynamically generating DOM elements via JavaScript. The p_elSource parameter that's passed to the constructor for your Gadget class is the containing div element for your Gadget. All of the DOM elements you create should be appended to this element.

The JavaScript that you use to add content to your page can range from very simple, to very complex. Here are a few examples to get you started:

    // the simplest Gadget: just change the text displayed by your containing element
    p_elSource.innerText = "Hello, world!";
        
Resulting Gadget

    // how about some html: include some basic html by directly setting the innerHTML property on your containing element
    p_elSource.innerHTML =
        "<a href=\"http://www.live.com\">Go to the best home page!</a>"
        + "<br />"
        + "<a href=\"http://ideas.live.com\">Check out the new windows live services!</a>";
        
Resulting Gadget

    // Getting fancier: Download html from inside an XML document and push it into your Gadget

    // First create an XML file called htmlcontent.xml with the following content and store it next to your Gadget
    <?xml version="1.0" encoding="utf-8"?>
    <XmlContentDoc>
        <Content type="html">
            <![CDATA[ 
                <B>Some html!</B> 
             ]]>
        </Content>
        <Content type="html">
            <![CDATA[ 
                <br /><br />
                <p>Best viewed on <a href="http://www.live.com">live.com!</a></p> 
             ]]>
        </Content>
    </XmlContentDoc>

    // Next add the following code in your initialize method
    this.initialize = function(p_objScope)
    {
        // Replace "CompanyName.TeamMane.Feature.HtmlInliner" with your fully qualified Gadget name
        CompanyName.TeamName.Feature.HtmlInliner.getBaseMethod(
            this, "initialize", "Web.Bindings.Base").call(this, p_objScope);

        function HtmlDownloadComplete(response)
        {
            if (response.status == 200)
            {
                if (response.responseXML)
                {
                    var htmlNodes = response.responseXML.selectNodes("XmlContentDoc/Content[@type='html']");
                    for (var i = 0; i < htmlNodes.length; i++)
                    {
                        // Pull the html out of the XML document and stuff it into our containing html element
                        p_elSource.innerHTML += htmlNodes[i].text;
                    }
                }
            }
        }

        // Queue a download of the XML that contains our html fragments
        // You should replace the path below to the path to your XML file
        var req = Web.Network.createRequest(
            Web.Network.Type.XML,
            "Gadgets/htmlinliner/htmlcontent.xml",
            {proxy:"generic", numItems:5},
            HtmlDownloadComplete);

        req.execute();
    }
        
Resulting Gadget

    // more complex: create DOM elements directly

    var div1 = document.createElement("div");
    div1.id = "div1";
    div1.className = "myHeaderText";
    div1.innerText = "placeholder header text";
    p_elSource.appendChild(div1);

    var buttonEl = document.createElement("input");
    // attach the fancybutton binding to this input  
    // button to give it the same look and feel as built-in Live.com buttons
    Web.Bindings.attachElementBindingSync(
        buttonEl,
        Start.FancyButton,
        this);
    buttonEl.value = "a button!";
    p_elSource.appendChild(buttonEl);
        
Resulting Gadget

As far as what content to display, that's entirely up to you. The Atlas framework provides handy utilities for asynchronously pulling down XML from any domain on the web via the Live.com proxies, so you don't need to worry about the typical cross-domain limitations of XmlHttpRequest objects. For more on this, see the section below on consuming XML.

Many Gadget developers in the community have come up with creative content to display in their Gadgets. People have implemented games, built utilities for searching, created mashups for photos, and provided access to music.
Testing & Debugging
To debug Gadgets, we recommend you use Microsoft Visual Studio 2005. If you don't already own a copy of VS, you can download a free version of Visual Web Developer Express. Using VS you can attach to the internet explorer process as a script debugger and set breakpoints within your Gadget's code.

Note that you'll need to enable script debugging in IE's options dialog. Go to Tools -> Options, choose the 'Advanced' tab, and uncheck both 'Disable Script Debugging (Internet Explorer)' and 'Disable Script Debugging (Other)'.

Uncheck Disable Script Debugging



To step through the code of your Gadget, you can do the following:
  1. Start Internet Explorer and go to www.live.com
  2. Open your gadget's .js file inside Visual Studio 2005.
  3. Go to Debug > Attach to Process... The Attach to Process dialog should show up.
  4. Choose the instance of the IEXPLORE.exe process that is running www.live.com under the Available Processes list, then click Select. The Select Code Type dialog should show up (see picture  below).
  5. Make sure Script is checked in the Select Code Type dialog as shown below and click OK.

    Attach To Process

  6. Click Attach on the Attach to Process dialog box.
  7. Set a break point in your .js file. Note that there is a bug in the JavaScript debugger that prevents you from setting a breakpoint on the first line inside a JavaScript function. If you must do this, you can work around this by inserting an extra line of code before the line of code where you want to break. 

Now go back to Internet Explorer to reload the Live.com page that contains your Gadget. When the breakpoint hits, execution will pause and you can step through the code and inspect variables.

If you don't have Visual Studio 2005, you will not be able to use Attach to Process. Another way of debugging your Gadget is to do the following (this works on both Visual Web Developer Express and Visual Studio 2005):

  1. Go to Website > Start Options...
  2. Choose Start Options property page on the left pane.
  3. Under Start action, choose Start URL. Enter "http://gadgets.start.com/gadget.aspx?manifestURL=yourManifestURL

    Startup Options

  4. Click OK.
  5. Open your Gadget JavaScript file and set the breakpoint location.
  6. Hit F5. 

Your web browser should launch and it should run your Gadget. Note that this will show your Gadget by itself and will not show how it looks on Live.com

There are also some additional tools which can be helpful for debugging.

For information on how to use these tools, please see their respective documentation.

Where to Go for Help
The best place to go for help is the Gadget forums on microsoftGadgets.com. The forums are frequented by Gadget experts from the community as well as the Live.com development team.

It can be very helpful to look at the source code of existing Gadgets. Try browsing the Gadget gallery; when you find a Gadget that looks interesting, click the 'preview' button and copy the url to the manifest XML. By looking at the manifest XML, you can find the url to the JavaScript for the Gadget, so you can see how it works.
Advanced Gadget techniques
Consuming XML with the Network Stack
The Gadget API provides robust mechanisms for consuming XML within Gadgets. There is a built-in network proxy that allows Gadget code to access XML regardless of where it is hosted, without worrying about cross-domain access issues.
In the AJAX tradition, these XML requests are made asynchronously, so your Gadget and the Live.com page continue responding while waiting for the XML to be retrieved.

Here is some example code that retrieves data from the Live.com blog.

    function GetLiveComBlogData()
    {
        // Define the callback function for when the asynchronous request is complete
        function RssDownloadComplete(response)
        {
            var feed = Start.Parser.ParseRssResponse(response);
            if (feed.channels && feed.channels.length > 0)
            {
                var alertString = feed.channels[0].title + "\n";
                for (var i = 0; i < feed.channels[0].items.length; i++)
                {
                    alertString += feed.channels[0].items[i].title + " : " + 
                                       feed.channels[0].items[i].description + "\n";
                }
                
                window.alert(alertString);
            }
        }

        var url = "http://spaces.msn.com/livecom/feed.rss";
        
        // Fetch RSS feed
        var req = Web.Network.createRequest(
            Web.Network.Type.XML,
            url,
            {proxy:"rss", numItems:5},
            RssDownloadComplete);

        req.execute();
    }
        
TODO : Add more detail on Web.Network.createRequest, including other values for proxy & for type
Consuming XML as a Source
The previous section explained how to fetch XML data using the network stack. In some cases this may be overkill, so we've added a new feature which makes fetching data even simpler by enabling XML/XHTML to be referenced from the Gadget manifest. For example, if you had a "Quote of the Day" Gadget that fetches its quote data from the same source every time, you can now include the URL in the XML manifest and the data will be available to the Gadget when it is instantiated. If you want more control over things like proxy types or making a POST instead of a GET you'll want to use the network stack functions as shown in the last section. To reference a data source, your Gadget manifest would look something like this:

    <?xml version="1.0"?>
    <rss version="2.0" xmlns:binding="http://www.live.com">
        <channel>
            <title>Quote of the Day</title>
            <description></description>
            <language>en-us</language>
            <binding:type>Gadget.Examples.ExampleGadget</binding:type>
            <item>
                <link>http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.js</link>
            </item>
            <item>
                <link binding:type="css"> http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.css
    </link>
            </item>
            <item>
                <link binding:type="xml" binding:name="quotes"> http://somequotesite.com/quotes.xml </link>
            </item>
        </channel>
    </rss>
        
You would access the data in your Gadget as follows:

    function DisplayQuotes()
    {
        // index the xmlSources array on p_args using the name specified 
        // in the 'binding:name' attribute of the manifest url
        // TODO: you need to write your own ParseQuoteResponse() function to parse the response.
        var o = ParseQuoteResponse(p_args.xmlSources["quotes"]);

        // Display quotes
        ...
    }
        
The data is accessed by retrieving the contents from the xmlSources array. In the Gadget manifest, we used the name "quotes" to refer to the data source. This approach allows you to define multiple XML files to be loaded with your Gadget, each referenced independently by name.
The data source lets us skip the step where we would normally call Web.Network.createRequest(). Thus, the the data source is equivalent to using the network stack as follows:

    function DisplayQuotes()
    {
        function CallbackFn(response)
        {
            var o = ParseQuoteResponse(response);

            // Display quotes
            ...
        }

        // Fetch quote XML data
        var r = Web.Network.createRequest(
            Web.Network.Type.XML,
            "http://somequotesite.com/quotes.xml",
            {proxy:"generic"},
            CallbackFn);

        r.execute();
    }

        
Saving Preferences
Saving preferences for your Gadget is easy. The module object that is passed into your Gadget's constructor provides APIs to get, set, and delete preferences. Preferences are defined as name,value pairs, where the name must be a string, and the value can be any JavaScript object. The function signatures for these APIs are as follows:

    getPreference(key); // returns a JavaScript object or null if the preference is not found
    setPreference(key, value);
    deletePreference(key);
        
It's important to remember that each Gadget has only a limited amount of space for saving preferences. Currently the limit is approximately 1000 chars (including key names, serialized values, and XML glue that we inject), so be careful not to save too much data. In the future we will provide APIs to save larger amounts of data, but those are not yet available.

Here's an example to get you started saving preferences:

    CompanyName.TeamName.Feature.ExampleGadget = function(p_elSource, p_args, p_namespace) 
    {
        CompanyName.TeamName.Feature.ExampleGadget.initializeBase(this, arguments);

        var m_this = this;
        var m_module = p_args.module;
        
        var m_preferenceKey = "prefKey";

        this.initialize = function(p_objScope)
        {
            CompanyName.TeamName.Feature.ExampleGadget.getBaseMethod(
                this, "initialize", "Web.Bindings.Base").call(this, p_objScope);

            // Attempt to retrieve our saved preference
            var prefValue = m_module.getPreference(m_preferenceKey);
            
            if (prefValue == null)
            {
                var prefValue = new Object();
                savedValues = new Array();
                savedValues.push("some value");
                savedValues.push("some other value");

                prefValue.savedValues = savedValues;
                prefValue.arbitraryData = 3.14159;

                // Save our preference (note that we can directly save the JavaScript object
                // regardless of its complexity, we are not limited to saving strings)
                m_module.setPreference(m_preferenceKey, prefValue);
            }
            else
            {
                // Delete the preference
                m_module.deletePreference(m_preferenceKey);
            }
        }
    }
    CompanyName.TeamName.Feature.ExampleGadget.registerClass( "CompanyName.TeamName.Feature.ExampleGadget", "Web.Bindings.Base");
        
Using Titlebar Icons
You can provide a custom icon to be placed in the titlebar of your Gadget by modifying the Gadget's manifest XML to include the 'icons' tag. Here is an example:

    <?xml version="1.0"?>
    <rss version="2.0" xmlns:binding="http://www.live.com">
        <channel>
            <title>Example Gadget</title>
            <description></description>
            <language>en-us</language>
            <pubDate>Sun, 15 Mar 2006 19:21:00 GMT</pubDate>
            <binding:type>Gadget.Examples.ExampleGadget</binding:type>
            <item>
                <link>
                    http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.js
                </link>
            </item>
            <item>
                <link binding:type="css">
                    http://www.mydomain.com/Gadgets/ExampleGadget/ExampleGadget.css
                </link>
            </item>
            
            <icons>
                <icon height="32" width="32">
                    http://www.mydomain.com/Gadgets/ExampleGadget/TitleIcon.gif
                </icon>
            </icons>
        </channel>
    </rss>
        
Note that only GIF files are currently supported. Transparent PNG will not render properly on the Live.com site.
Bypassing the iFrame Security
All 3rd party Gadgets on Live.com run within independent iframes to ensure security. Sometimes while developing it's useful to have Live.com render your Gadget inline without the iframe around it. Turning this feature on is as simple as adding a line to your manifest XML:

    <?xml version="1.0"?>
    <rss version="2.0" xmlns:binding="http://www.live.com">
      <channel>
        ...
        <binding:options renderInline="true"></binding:options>
        ...
      </channel>
    </rss>
        
Note that this option should be removed before publishing your Gadget on the web for general consumption. Otherwise, your Gadget will throw an exception. 
Embedding a Web Page as a Gadget
Too lazy to create a real Gadget? Here's a shortcut that allows you to load any web page within your Live.com page.

    this.initialize = function(p_objScope)
    {
        Gadget.Examples.LazyGadget.getBaseMethod(this, "initialize", "Web.Bindings.Base").call(this, p_objScope);

        var url = "http://www.xbox.com/"

        m_iframe = document.createElement("iframe");
        m_iframe.scrolling = "yes";
        m_iframe.frameBorder = "0";
        m_iframe.src = url;
        m_iframe.width="95%";
        m_iframe.height="285px";
        p_elSource.appendChild(m_iframe);
    }
        
Resulting Gadget
Gadgets for Windows Live Spaces

Windows Live Spaces users can interact with Spaces in two modes – author or viewer. Author mode is when the owner of the Space is editing the Space, and viewer mode is when users (including the author) are just browsing the Space.

Gadgets running on Spaces typically will display an author mode view that allows the author of the Space to set preferences for the gadget. These preferences are then read when the gadget is in viewer mode to display the relevant gadget content.

Use the following code to detect whether Spaces is running the gadget in author mode and show/hide the UI accordingly.  There is a p_args argument outlined in the gadgets SDK and we've added a new method off of that called getMode().  You can do a simple comparison of the value returned from that method call to determine author vs. visitor mode. For example:
          
         foo = function(p_elSource, p_args, p_namespace)
         p_args.module.getMode() == Web.Gadget.Mode.author

You can add a gadget you have developed to your own space using the following Spaces API: 
http://spaces.live.com/spacesapi.aspx?wx_action=create&wx_url=URLEncodedPathToYourH ostedGadgetManifestURL

Switch between "Edit your space" and "View your space" to see how it behaves in both author and visitor modes.  If your manifest file, Javascript, and CSS are hosted anywhere but Windows Live Gallery (gallery.live.com), the gadget can only be added for editing/viewing by the space owner.  It will be hidden to visitors.

Once you are ready to publish your gadget, zip up your manifest file and supporting Javascript/CSS files and submit that gadget package to the Windows Live Gallery so other visitors can add it to their space by going to Customize --> Modules --> "Add gadgets from Windows Live Gallery".  Once it has been verified to work in each service, it will appear in the Gallery for users to add to Live.com or Spaces.  Make sure you use relative URLs (mygadget.css instead of http://foogadgetdeveloper.com/mygadget.css) in your manifest XML to point to the scripts/CSS or the gadget will be rejected during verification.

Bindings Basics
What's a Binding?
A binding is a JavaScript object that has been associated with an HTML element. The Atlas framework provides a more structured way to produce JavaScript classes and object hierarchies including full support for inheritance and interfaces. Bindings build on the OO support provided by the Atlas framework to allow sophisticated interaction with HTML.

Each Windows Live Gadget is itself a binding. The Gadget object itself is passed an HTML element as a parameter when it is created, and the object provides the JavaScript code that interacts with that element to define the contents of the Gadget. Thus, you can look at any Gadget as an example of how to define a binding class.

Instantiating a binding class is very simple. Assuming a binding class of type Examples.ExampleBinding, the following code will attach a new object of that type to the div element that we've just created.

    var newEl = document.createElement("div");
    var bindingParams = null;

    var newBinding = Web.Bindings.attachElementBindingSync(
        newEl,                   // specifies the DOM element to associate with the binding 
        Examples.ExampleBinding, // specifies the binding class to create 
        this,                    // setup the binding as a child of this object, assuring proper disposal 
        bindingParams );         // arbitrary object containing parameters to be passed 
                                 // to the constructor of the new object 

    p_elSource.appendChild(newEl);
        
Working with Bindings
Since all Gadgets are themselves bindings, you will certainly be working with bindings while doing Gadget development. How deeply you interact with bindings, though, is up to you. Most Gadgets implemented to date have taken the simpler approach. Alternatively, the Live.com page has been implemented following the latter approach, using bindings to their greater potential. Depending on the complexity of what you want your Gadget to do, either approach may be the right one for you. If you are inclined to experiment, we encourage you to play around with bindings and see what they have to offer.

The basic code outline for a binding is no different from the code outline for a Gadget. (Recall this example from above.)

If you are familiar with basic object-oriented programming, then working with bindings should be a natural extension of the basic programming tenets that you already understand. In general, it makes sense to create a new binding whenever you have a UI element that fits one of the following criteria:
  1. Used repeatedly within your Gadget,
  2. Requires complex initialization or destruction code that should be encapsulated in its own module,
  3. Must be updated by your code in a dynamic way after it has been added to the page, or
  4. Exposes custom events.
UI elements that are used repeatedly within your Gadget should always behave in the same way, and should be given a standard appearance. Rather than having repeated code throughout your Gadget module, it is easier to define a new binding for this UI element. The binding can take care of setting up the appropriate className for the element and initializing the element's contents in a standard way. Then whenever you need one of these elements within your Gadget, you can simply instantiate the binding and all the rest of the work is done for you.

Often UI elements that contain other HTML elements can be complicated to instantiate or destroy, especially if there is JavaScript code listening for more or more events from the DOM objects. In this case it is always recommended that you encapsulate that code within its own binding to ensure tight control over destruction, and to ensure the code is well contained and more easily maintainable.

When one of your UI elements needs to be updated at runtime by some other portion of your JavaScript code, it is recommended that you wrap the element in its own binding. Then the binding can expose a public method to handle the update, keeping the internal implementation of the UI element separate from the code that needs to force the update to happen. This is especially important if the update may cause one or more DOM elements to be removed from the page, since your code will need to detach from all events on those DOM elements.

Finally, bindings allow the easy creation of custom events, which can be attached to by other JavaScript code within your Gadget. Here's a code example that shows how to publish an event from your binding. The binding defines an event 'onMouseOverLink' that any JavaScript class can attach to. The binding creates two link elements that it owns, and it coalesces the onmouseover events from those two elements into a single event that it fires.

    registerNamespace("Gadget.Examples.ExampleBinding");

    Gadget.Examples.ExampleBinding = function(p_elSource, p_args, p_namespace)
    {
        Gadget.Examples.ExampleBinding.initializeBase(this, arguments);

        var m_el = p_elSource;
        var m_this = this;

        var m_a1 = null;
        var m_a2 = null;

        this.initialize = function(p_objScope)
        {
            Gadget.Examples.ExampleBinding.getBaseMethod(
                this, "initialize", "Web.Bindings.Base").call(this, p_objScope);

            m_a1 = document.createElement("a");
            m_a1.innerText = "link 1";
            m_a1.href = "www.live.com";
            m_a1.attachEvent("onmouseover", OnMouseOverLink);
            m_el.appendChild(m_a1);

            m_a2 = document.createElement("a");
            m_a2.innerText = "link 2";
            m_a2.href = "www.live.com";
            m_a2.attachEvent("onmouseover", OnMouseOverLink);
            m_el.appendChild(m_a2);
        }

        this.dispose = function(p_blnUnload)
        {
            m_a1.detachEvent("onmouseover", OnMouseOverLink);
            m_a2.detachEvent("onmouseover", OnMouseOverLink);

            m_a1 = m_a2 = m_el = null;

            Gadget.Examples.ExampleBinding.getBaseMethod( 
                this, "dispose", "Web.Bindings.Base").call(this, p_blnUnload);
        }

        function OnMouseOverLink()
        {
            // Our base class implements the fire method, which takes care of making calls to all
            // subscribers of the event.
            m_this.fire("onMouseOverLink");
        }
    }
    Gadget.Examples.ExampleBinding.registerClass("Gadget.Examples.ExampleBinding", "Web.Bindings.Base");
    // Define the events for our binding
    Gadget.Examples.ExampleBinding.Events = Web.Enum.create("onMouseOverLink");
        
Any code that wants to create an instance of the binding above and wire up to its events would look like this:

    {
        // Create a new empty div element for our binding
        var linkContainerEl = document.createElement("div");

	p_elSource.appendChild(linkContainerEl);

        // Create the binding
        m_linkContainer = Web.Bindings.attachElementBindingSync(
            linkContainerEl,
            Gadget.Examples.ExampleBinding,
            this);
    
        m_linkContainer.attachEvent("onMouseOverLink", OnMouseOverLink);
    }

    // Note: this function must exist outside the scope of the above function so the event can
    //       be detached during disposal
    function OnMouseOverLink()
    {
        window.alert("someone moused over a link in our link container!");
    }
    
    // Note: we assign the binding above to a member variable m_linkContainer, because we
    //       must be very careful to detach from the event when we are disposed.  The dispose 
    //       for this consumer class must call the following code
    function CleanupLinkContainer()
    {
        m_linkContainer.detachEvent("onMouseOverLink", OnMouseOverLink);
        m_linkContainer = null;
    }
        
Best Practices & Performance
Avoiding Memory Leaks
Memory leaks are the number one contributor to poor performance for AJAX-style websites. Often code that appears to be written correctly will turn out to be the source of a massive memory leak, and these leaks can be very difficult to track down. Luckily, there are a few simple guidelines which can help you avoid most memory leaks. The Live.com developers follow these rules religiously, and recommend that you do the same when implementing Gadgets. There are a number of websites and blog entries that document approaches for identifying and fixing memory leaks in AJAX applications. One such helpful site can be found here.

A great way to see whether your Gadget is leaking memory is to use the following URL to load your Gadget: http://gadgets.start.com/gadget.aspx?manifestUrl=gadgetManifestUrl. Open Task Manager in Windows and monitor the memory usage of the Internet Explorer window. Keep reloading the Gadget in Internet Explorer to see if the memory usage increases over time. If the memory usage increases, it is indicative that your Gadget is leaking memory.
Namespacing Conventions
The namespace you use in the registerNamespace() call for your Gadget is used to uniquely identify your Gadget on the platform. To avoid collision with other Gadgets, you should follow the guidelines below: