Writing Google Gadgets – A Tutorial – Part 04
- Samisa Abeysinghe
- VP, Training - WSO2
Writing Google Gadgets – A Tutorial – Part 04
So far, we have looked into various key concepts, of Google gadget development, using a Web content retrieval and data visualization example.
In this final part of the tutorial, we will look into the code in detail, and also explore a bit about the versatility of the gadget and discuss some tips and tricks.
Writing Google Gadgets - A tutorial - Part 01
Basic Gadget Example - Hello Gadget
Pulling Information from the Web into a Gadget
Pulling Information into a Gadget with makeRequest
Dynamic Height with Google Gadgets
Writing Google Gadgets – A tutorial – Part 02
Processing Fetched HTML with JavaScript
Using TEXT vs DOM Content Type when Working with HTML
Using External JavaScript Libraries
Writing Google Gadgets - A tutorial - Part 03
Using enum Data Type as an Options List
Integer Settings with String Data Type
Writing Google Gadgets – A tutorial – Part 04
Versatility of Google Gadgets as a Presentation Instrument
Versatility of Google Gadgets as a Presentation Instrument
In the past three parts of this tutorial, we developed a gadget incrementally, to fetch HTML from a URL, extract data and present those in an appealing manner.
Let’s have a look at a dashboard, built using this single gadget. The following is a screen shot of such a dashboard, created using WSO2 Gadget Server.
As it can be seen, the single gadget is duplicated six times and present data on six different mailing lists.
Moreover we have six themes used for the six graphs, so that mapping of the visual elements on the dashboard is naturally fed into the user’s mind.
Furthermore, if you look at the kind of data being presented, we have architecture list, developer lists, code commit message list and issue
reporting lists all presented side by side on the same dashboard using a single gadget. So we can see how design activities, developer discussions,
code development, issue reporting and fixing reflected on this dashboard. For example, you can easily spot the release efforts in sync with the peaks
in svn commits and issue related activities.
The moral of the story is that, a carefully designed gadget can help you present and visualize wealth of knowledge.
A gadget is worth a thousand pages.
The Complete Google Gadget Code
Following the final complete code of the gadget we developed so far. On left hand side, you see the code and on right hand side, you see the description. If you want more details on a line of code on the left, see the corresponding line number on the right pane for the description.
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="WSO2 Open Source Development Data from MarkMail"> <Require feature="dynamic-height" /> <Require feature="setprefs"/> <Require feature="views" /> </ModulePrefs> <UserPref name="ShowGraph" display_name="Show Graph" datatype="bool" default_value="true"> </UserPref> <UserPref name="MailingList" display_name="Mailing List" datatype="enum" default_value="3"> <EnumValue value="0" display_value="WSO2 Architecture List - https://markmail.org/browse/org.wso2.architecture" /> <EnumValue value="1" display_value="WSO2 Carbon Dev List - https://markmail.org/browse/org.wso2.carbon-dev" /> <EnumValue value="2" display_value="WSO2 Stratos Dev List - https://markmail.org/browse/org.wso2.stratos-dev" /> <EnumValue value="3" display_value="WSO2 SVN Commits - https://markmail.org/browse/org.wso2.carbon-commits" /> <EnumValue value="4" display_value="WSO2 Carbon Jira Activity - https://markmail.org/browse/org.wso2.carbon-jira" /> <EnumValue value="5" display_value="WSO2 Stratos Jira Activity - https://markmail.org/browse/org.wso2.stratos-jira" /> </UserPref> <UserPref name="GraphWidth" display_name="Graph Width" default_value="550"> </UserPref> <UserPref name="GraphHeight" display_name="Graph Height" default_value="625"> </UserPref> <UserPref name="GraphTheme" display_name="Graph Theme" datatype="enum" default_value="2"> <EnumValue value="0" display_value="Keynote" /> <EnumValue value="1" display_value="37signals" /> <EnumValue value="2" display_value="Rails Keynote" /> <EnumValue value="3" display_value="Odeo" /> <EnumValue value="4" display_value="Pastel" /> <EnumValue value="5" display_value="Greyscale" /> </UserPref> <Content type="html"> <![CDATA[ <style> #wrapperDiv { position:relative; } #contentDiv { float: left; margin: 20px; } #graphDiv { float: left; margin: 20px; } </style> <a href="https://markmail.org/"><img src="https://markmail.org/images/logo_white.gif" alt="MarkMail"/></a> <div id="wrapperDiv"> <div id="contentDiv"></div> <span id="hiddenSpan" style="display:none">Hidden</span> <div id="graphDiv"> <canvas id="graph" width="600" height="625">Graph</canvas> Grapth Theme: <select id="graphThemeSelect" onchange="if (this.selectedIndex >=0 ) updateTheme(this);"> <option value="0">Keynote</option> <option value="1">37signals</option> <option value="2">Rails Keynote</option> <option value="3">Odeo</option> <option value="4">Pastel</option> <option value="5">Greyscale</option> </select> </div> </div> <script type="text/javascript" src="js/js-class.js"></script> <script type="text/javascript" src="js/excanvas.js"></script> <script type="text/javascript" src="js/bluff-min.js"></script> <script type="text/javascript"> var prefs = new gadgets.Prefs(); var urls = new Array("https://markmail.org/browse/org.wso2.architecture", "https://markmail.org/browse/org.wso2.carbon-dev", "https://markmail.org/browse/org.wso2.stratos-dev", "https://markmail.org/browse/org.wso2.carbon-commits", "https://markmail.org/browse/org.wso2.carbon-jira", "https://markmail.org/browse/org.wso2.stratos-jira"); var titles = new Array("WSO2 Architecture List Discussions", "WSO2 Carbon Dev List Discussions", "WSO2 Stratos Dev List Duscussions", "WSO2 Carbon and Stratos SVN Commits", "WSO2 Carbon Jira Activity", "WSO2 Stratos Jira Activity"); var columns = new Array("Discussions", "Discussions", "Discussions", "Commits", "Activity", "Activity"); function getHtml() { var parameters = {}; parameters[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT; var listID = prefs.getString("MailingList"); gadgets.io.makeRequest(urls[listID], processResponse, parameters); }; function processResponse(response) { var textData = response.text; textData = textData.replace(/,/g, ''); textData = textData.replace(/td><a/gi, 'th span="row"><a'); textData = textData.replace(/\/a><\/td/gi, '/a></th'); document.getElementById('hiddenSpan').innerHTML = textData; var listID = prefs.getString("MailingList"); document.getElementById('contentDiv').innerHTML = "<table id='data'><caption>" + titles[listID] + "</caption><thead><tr><th></th><th scope='col'>" + columns[listID] + "</th></tr></thead>" + document.getElementById('browse').getElementsByTagName('table')[0].innerHTML + "</table>"; if (gadgets.views.getCurrentView().getName() == "canvas" ) { drawGraph(); document.getElementById('contentDiv').style.display = "block"; document.getElementById('graphDiv').style.display = "block"; } else if (prefs.getBool("ShowGraph") == true) { drawGraph(); document.getElementById('contentDiv').style.display = "none"; document.getElementById('graphDiv').style.display = "block"; } else { document.getElementById('graphDiv').style.display = "none"; document.getElementById('contentDiv').style.display = "block"; } gadgets.window.adjustHeight(); }; function drawGraph() { var g = new Bluff.SideBar('graph', prefs.getInt("GraphWidth") + 'x' + prefs.getInt("GraphHeight")); switch (prefs.getInt("GraphTheme")) { case 0: g.theme_keynote(); break; case 1: g.theme_37signals(); break; case 2: g.theme_rails_keynote(); break; case 3: g.theme_odeo(); break; case 4: g.theme_pastel(); break; case 5: g.theme_greyscale(); } g.tooltips = true; g.data_from_table('data'); g.draw(); }; function updateTheme(theme) { prefs.set("GraphTheme", theme.value); drawGraph(); } gadgets.util.registerOnLoadHandler(getHtml); </script> ]]> </Content> </Module>
<- Start Module <- Start ModulePrefs <- Require dynamic height feature <- Require set user preferences feature <- Require views feature <- End ModulePrefs <- User preference Show Graph Type boolean and defaults to true So we will show the graph by default, if false the table <- User preference Mailing List Type enum and defaults to WSO2 SVN Commits mailing list Values go from 0 to 5 <- User preference Graph Width Type string and defaults to 550 <- User preference Graph Height Type string and defaults to 625 < User preference Graph Theme Type enum and defaults to Keynote theme <- Start HTML Content < - Start CDATA section containing main gadget logic <- Start style section for content and graph divs Wrapper div containing content and graph divs Will allow relative positioning Content div will float to the left Graph div too will float to the left but after content div, if it is visible So we will see both the table and graph in canvas view, side by side <- End style <- Link to MarkMail home page <- Wrapper div that contains the content div and graph div <- Content div to hold HTML table with raw data <- Hidden span to hold data before processing. Used to convert text response into document DOM. <- Graph div to hold the graph canvas and theme options input <- Graph canvas to draw the graph using Bluff library <- Graph themes select control. When a theme is selected, updateTheme function is called <- Bluff JavaScript library files used to draw the graph. Since we have these loaded to the same location as the gadget location in registry, we can use relative links from within the gadget. <- Start of main JavaScript logic of the gadget Gadget Prefs object to deal with user preferences Array of mailing list URLs. Array indexes map to the MailingList user preference enum values. Array of graph titles. Array indexes map to the MailingList user preference enum values. Array of graph column names. Array indexes map to the MailingList user preference enum values. JavaScript function getHtml Set content type to TEXT Get the user selected mailing list ID, based on user preferences Make request with the selected mailing list's URL and set processResponse as the callback JavaScript function processResponse. Called when response from the fetched URL is received. Retrieve text data from response Remove all ',' characters from numbers. e.g. convert 1,256 into 1256. This is required to let the graphs pick numbers right from HTML table. Convert td elements into th in the months column to treat them as Y axis on the graph Set hidden span's inner HTML to processed text data Pick the user selected mailing list ID to select the corresponding table title and column names Set content div elemen's inner HTML to processed table data along with matching table title and matching column name If we are in the canvas view Draw the graph and make both content div and graph view visible If it is not the canvas view and show graph user option is set to true Draw the graph and make content div hidden and and graph view visible Else show graph user option is set to false, hence make content div and graph view hidden Adjust the window height to make sure that what is marked as visible is properly seen on the gadget JavaScript function drawGraph Create a Bluff SideBar graph with user set width and height Switch based on user set Graph Theme option and set the theme Set tooltips option to true Set the table to pick data from to be the table with ID 'data' Draw the graph JavaScript function updateTheme Set the user selected theme from select control as a user preference Draw the graphs to update the theme Register getHtml function as the on load handler. getHtml will be triggered when the gadget is loaded. <-End of main JavaScript logic >- End CDATA section <- End HTML Content <- End Module
Here are some tips and tricks to keep in mind, when developing gadgets
When developing this example gadget, we did not pay much attention to error handling. For example, what would happen,
if we could not fetch the content from URL, due to network outage? As of now, we would display an empty gadget.
However it would be nice to display a meaningful error to the user.
This is relatively simple to deal with. All that you need to do is to use a try/catch block.
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="WSO2 Open Source Development Data from MarkMail"> <Require feature="dynamic-height" /> </ModulePrefs> <Content type="html"> <![CDATA[ <a href="https://markmail.org/"><img src="https://markmail.org/images/logo_white.gif" alt="MarkMail"/></a> <div id="contentDiv"></div> <span id="hiddenSpan" style="display:none;"/> <script type="text/javascript"> var url = "https://markmail.org/browse/org.wso2.carbon-commits"; function getHtml() { try { var parameters = {}; parameters[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT; gadgets.io.makeRequest(url, processResponse, parameters); } catch (err) { document.getElementById('contentDiv').innerHTML = "<stong> error connecting to URL <a href='" + url +"'>" + url + "</a> </strong>"; } }; function processResponse(response) { try { document.getElementById('hiddenSpan').innerHTML = response.text; document.getElementById('contentDiv').innerHTML = "<table>" + document.getElementById('browse').getElementsByTagName('table')[0].innerHTML + "</table>"; } catch (err) { document.getElementById('contentDiv').innerHTML = "<stong> error processing data from URL <a href='" + url +"'>" + url + "</a> </strong>"; } gadgets.window.adjustHeight(); }; gadgets.util.registerOnLoadHandler(getHtml); </script> ]]> </Content> </Module>
Gadgets are simple to implement for simple cases, but are complex for complex cases. Like we did in this tutorial,
it is a good practice to start small and incrementally keep on building the gadget, step by step, testing along the way.
Incremental testing is key to success, as when things go wrong, we need to back track and recollect on what went wrong and fix those.
If we are to take big steps, it would be harder to pinpoint problem cases.
When things are not working, the best way to debug is to use developer tools offered by the browsers. Both with Chrome and Internet Explorer,
the function key, F12, pops up the developer tools.
When you expect things to happen and cannot seem to figure out why things are not the way they supposed to be with the gadget,
the console of the developer tools can lead you to the errors and error locations.
In addition to developer tools, the "Inspect element" option you get on the right click pop-up on Chrome Web browser is quite handy,
when you know where in the gadget things supposed to appear and yet does not.
Internet Explorer (IE) is often notorious for "weird" issues with Web developers.
But on the other hand, if you can make it to work with IE, the chances are that, it will work with others most of the time, though not always.
Since you are developing for wider audience, it is important consider cross browser aspects. If you plan to open up the gadgets for wider audience,
it is important to use IE as part of the incremental test efforts. In other words, test with IE, while you are developing, rather than trying to
test the finished gadget with IE.
I used Eclipse to develop the sample gadget. However, due to the use of CDATA element to wrap the main logic, syntax highlighting is not that great.
But even the simple things like indentation can help a great deal, when you are developing a fairly complex gadget.
The syntax highlighting problem can be worked around by developing the main logic using an html file, and then copying over to the gadget XML CDATA element, when using Eclipse.
This brings us to the end of this four part tutorial. Please feel free to provide us the feedback, and help us improve the content.
Please refer to the table of contents section to find out about the other parts of this tutorial.