Web Gadget SDK
CassiniWebServer <physical-path> <port> <virtual-path>CassiniWebServer D:\GadgetsSDK\Samples 80 /Gadgets
<?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>
// 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");
// the simplest Gadget: just change the text displayed by your containing element
p_elSource.innerText = "Hello, world!";
// 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>";
// 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();
}
// 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);


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):

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.
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();
}
<?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>
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
...
}
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();
}
getPreference(key); // returns a JavaScript object or null if the preference is not found
setPreference(key, value);
deletePreference(key);
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");
<?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>
<?xml version="1.0"?>
<rss version="2.0" xmlns:binding="http://www.live.com">
<channel>
...
<binding:options renderInline="true"></binding:options>
...
</channel>
</rss>
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);
}
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.
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);
'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");
{
// 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;
}
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: CompanyName.TeamName[.Feature][.SubFeature]FirsNameLastName.MiddleName[.Feature][.SubFeature]Microsoft.Live.GadgetSDK",
"eBay.Auction.Search", "TimSullivan.George.Weather"