Windows 7 gadgets are small applications that are integrated in the Windows desktop. These gadgets are basically webpages displayed in a browser environment that hardly differs from a regular environment. It is therefore entirely possible to turn a gadget into a web application that retrieves data from a remote server with AJAX. In this article we explore some of the possibilities and see how we may use jQuery inside a gadget.
Anatomy of a Windows 7 gadget
There is actually some pretty decent documentation on gadgets available on the web (for example on msdn or here). The trick is to get an example gadget running with as little ballast as possible.
A gadget file is basically a zip file that contains a file gadget.html
plus a number of additional files, the most important one, gadget.xml
. This zip file does have a .gadget
extension instead of a .zip
extension. So these simple steps are needed to create a bare bones gadget:
- Create a directory with a descriptive name, e.g. mygadget
- In this directory create a file
gadget.html
- a subdirectory named
css
with a filemygadget.css
- a file
gadget.xml
- and finally a file
icon.png
- pack the contents of this directory into a file
mygadget.zip
(i.e. not the toplevel directory itself) - rename this file to
mygadget.gadget
(although 7zip for example can pack to a file with any extension directly) - double click this file and follow through the install dialog
With the following gadget.html the result will look like the screenshot
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My Gadget</title> <meta http-equiv="Content-Type" content="text/html; charset=unicode" /> <link href="css/mygadget.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="main_image"> <p>42</p> </div> </body> </html>Not really impressive, I admit, but it shows how simple it is to create a gadget. The necessary
gadget.xml
mainly describes were to find the actual html code and what image to use in the gadget selector:
<?xml version="1.0" encoding="utf-8" ?> <gadget> <name>Mygadget</name> <namespace><!--_locComment_text="{Locked}"-->StartSmall.Gadgets</namespace> <version><!--_locComment_text="{Locked}"-->1.0</version> <author name="Michel Anders"> <info url="http://michelanders.blogspot.com" text="Start Small" /> <logo src="icon.png" /> </author> <copyright><!--_locComment_text="{Locked}"-->© 2011</copyright> <description>Basic Gadget</description> <icons> <icon height="48" width="48" src="icon.png" /> </icons> <hosts> <host name="sidebar"> <base type="HTML" apiVersion="1.0.0" src="gadget.html" /> <permissions> <!--_locComment_text="{Locked}"-->Full </permissions> <platform minPlatformVersion="1.0" /> </host> </hosts> </gadget>
Using jQuery in a Windows 7 gadget
Displaying static information is not much fun at all so let's see what options there are to create a more dynamic gadget:
- Refer to external content like images that get refreshed
- Refresh the content ourselves using JavaScript, possibly even interacting with the user
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My Gadget</title> <meta http-equiv="Content-Type" content="text/html; charset=unicode" /> <link href="css/mygadget.css" rel="stylesheet" type="text/css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script> </head> <body> <div id="main_image"> <p>42</p> </div> <script type="text/javascript">$("#main_image p").append('<span> 43 44 45 </span>');</script> </body> </html>As you can see this is surprisingly simple. The screenshot proves that the final lines of code are actually executed and change the contents of our basic gadget: Our next step is to check if we can retrieve data from a server.
JSONP in a Windows 7 gadget
Due to the same origin policy it is not entirely straight-forward to retrieve data from a server different from the server we get our webpage from. In the gadget environment every server is considered a different server because the gadget.html file originates from a file system. This means that even if we access a web application server on the same pc, we will be denied access.
Fortunately there is a workaround available in the form of JSONP and jQuery makes it very simple for use to use this. Consider the following gadget.html:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My Gadget</title> <meta http-equiv="Content-Type" content="text/html; charset=unicode" /> <link href="css/mygadget.css" rel="stylesheet" type="text/css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script> </head> <body> <div id="main_image"> <p>42</p> </div> <script type="text/javascript"> function Refresh() { $.ajax('http://127.0.0.1:8088/value', {dataType:'jsonp',scriptCharset: "utf-8", success:function(data, textStatus, jqXHR){ $("#main_image").empty().append('<p>'+String(data)+'</p>'); }}); } window.setInterval("Refresh()", 5000); </script> </body> </html>
It will contact an application server every 5 seconds to retrieve a number and will insert this number into the content div. That's all there is to it. The application server that serves these request is equally simple and can be build for example with the applicationserver module from a previous article:
from http.server import HTTPServer, BaseHTTPRequestHandler from json import dumps from applicationserver import IsExposed,ApplicationRequestHandler class Application: def value(self,callback:str,_:int=0) -> IsExposed: r = "%s(%s);"%(callback,dumps(_ % 17)) return r class MyAppHandler(ApplicationRequestHandler): application=Application() appserver = HTTPServer(('',8088),MyAppHandler) appserver.serve_forever()
The only trick here is that JSONP requests pass an additional paramter (usually called callback
that will hold a string (the name of a JavaScript function in the client) and that we have to use this parameter to contruct a return value that looks like a call to this function with the JSON encoded data as its argument (line 8). The _
parameter holds a random number added by JQuery to prevent caching by the browser. So if _
happened to be 123
and callback
would be jquery456_123
the value we woudl return would be the string jquery456_123(4);
The data we return here is merely a random number but of course this could be anything and we could style it to be presented in a more readable form.