The full code for this sample is available at https://github.com/jcleblanc/programming-social-applications/blob/master/chapter_7/chapter_final.xml.
Now that we’ve examined data pipelining and templating, one thing should be blatantly obvious: these two features of the OpenSocial specification can significantly reduce the amount of code that you need to write, create a highly modularized environment to promote multiuser development, and generally simplify debugging and data retrieval to allow you to focus on more important aspects of your gadget, such as monetization and social features.
Up until now, we’ve explored the individual specifications for accessing and working with data. Now we will put together a few of the lessons that we have learned into a fully functional example that showcases some of the major uses for templating and data pipelining.
In this example, we’ll look at:
Capturing data sources through data pipelining HTTP Requests
Integrating data pipes into templates
Using expressions
Using loop structures
Using special variables
Our sample code will use the data pipelining specification to pull in two RSS feeds, one from www.reddit.com and the other from the San Francisco local news section of www.craigslist.org. Following that, we’ll create two separate pages that can be switched back and forth by tabs. These pages will use the templating specification to display the title, link, and date from these two feeds as styled, unordered lists. The general visual form that we will use for this application will mimic Figure 7-1.
First, we have to look at the foundation of our gadget XML file and the requirements that we need to include:
<?xml version="1.0" encoding="utf-8s"?> <Module> <ModulePrefs title="Chapter rollup example" title_url="http://www.jcleblanc.com" description="Displays templating and data pipelining specifications" author="Jonathan LeBlanc"> <Require feature="opensocial-0.9"/> <Require feature="opensocial-data" /> <Require feature="opensocial-templates" /> </ModulePrefs>
Since the sample gadget that we are building will integrate functionality from data pipelining as well as templating, we need to require these features in our gadget:
At the very minimum, we should include version 0.9 of the
OpenSocial specification as opensocial-0.9
. (This is the minimum version
that is needed to run both the features from data pipelining and
templating.)
The data pipelining specification as opensocial-data
.
The templating specification as opensocial-templates
.
Now that our JavaScript feature requirements are in place in our
core gadget, we can focus on the Content
section that will house our gadget
functionality:
<Content type="html"> <![CDATA[ <style type="text/css"> #nav{ margin:5px 0 5px 15px; padding-top:10px; } #nav a{ background-color:#e8f0f4; border:1px solid #0c7099; border-bottom:0; color:#000; padding:6px 8px; margin-top:5px; } #nav a{ cursor:pointer; font-weight:bold; } #nav a.navOff{ background-color:#0c7099; color:#fff; } #nav, .page{ font:12px arial,helvetica,sans-serif; } .page{ margin:5px 0; padding:5px; margin:0 10px 15px; background-color:#e8f0f4; border:1px solid #0c7099; } .page li{ list-style-type:none; padding:3px 10px 3px 0; } .textSmall{ font-size:10px; } .hide{ display:none; } </style>
We first need to specify the styles that will be used in the gadget. These styles will define the look of the anchor tags that are used for the page tabs, the layout for the pages, and the formatting of the data pipes as unordered lists.
Following the styles, we define the navigation of the gadget, which will allow us to switch between the two feed pages:
<div id="nav"> <a id="linkCraigslist" onclick="switchNode('pageCraigslist', 'pageReddit', this)"> Craigslist Local</a> <a id="linkReddit" onclick="switchNode('pageReddit', 'pageCraigslist', this)" class="navOff">Reddit</a> </div>
Our navigation system consists of a div
wrapping two anchor tags that are styled to
look like tabs. To visualize the tab’s off
state, we can set a class of navOff
. Other than that, our anchor tags contain
an onclick
event that, when triggered,
will call the switchNode(...)
function
to swap between the current page and the other page.
Our next task is to define the data pipe that will pull in the RSS feeds from www.reddit.com and www.craigslist.org:
<script type="text/os-data" xmlns:os="http://ns.opensocial.org/2008/markup"> <os:HttpRequest key="feeds" href="http://query.yahooapis.com/v1/public/yql? q=select%20*%20from%20yql.query.multi%20where%20queries%3D%22select%20 *%20from%20rss%20where%20url%3D'http%3A%2F%2Fwww.reddit.com%2F.rss'%3B select%20*%20from%20rss%20where%20url%3D'http%3A%2F%2Fsfbay.craigslist. org%2Fvnn%2Findex.rss'%22&format=json&debug=true"/> </script>
As with some of our previous examples, we use the os:HttpRequest
element to pull in our data
feeds, using the Yahoo! Query Language to aggregate the sources. The YQL
URL makes the request to the service, which in turn makes two separate
RESTful requests to the two RSS
sources and then binds the results into a single data pipe in JSON format.
Our pipe is set to a key of feeds
.
Next we will cover the visualization of these data sources through the templating specification:
<script type="text/os-template" require="feeds"> <div id="pageReddit" class="page hide"> <ul> <li repeat="${feeds.content.query.results.results[0].item}"> <b>${Cur.title[0]}</b><br /> <a href="${Cur.link}">${Cur.link}</a><br /> <span class="textSmall">${Cur.pubDate}</a> </li> </ul> </div> <div id="pageCraigslist" class="page"> <ul> <li repeat="${feeds.content.query.results.results[1].item}"> <b>${Cur.title[0]}</b><br /> <a href="${Cur.link}">${Cur.link}</a><br /> <span class="textSmall">${Cur.date}</a> </li> </ul> </div> </script>
Our template script
block
contains the require
attribute stating
that if the feeds
data pipe is not
available, the template should not be rendered with the data.
If feeds
is available, the
template will set up two div
nodes, one
set as visible and the other as hidden. Within these div
nodes, or pages, we set up an unordered
list. Then we use the repeat
attribute
of the templating specification to create an <li>
node in the unordered list for each
object in the associated RSS feed. Within each list item, we create markup
to display the title, link, and current date for the appropriate RSS feed
item.
Our last task is to define our JavaScript functionality to enable us to swap between the two different pages that we have defined:
<script type="text/javascript"> //swap the visibility of one page function switchNode(activate, deactivate, linkObj){ //get page nodes to swap var showNode = document.getElementById(activate); var hideNode = document.getElementById(deactivate); //switch the display properties of the two pages hideNode.className = "page hide"; showNode.className = "page"; //swap link tab classes var linkAlt = (linkObj.id == "linkCraigslist") ? document.getElementById("linkReddit") : document.getElementById("linkCraigslist"); linkObj.className = ""; linkAlt.className = "navOff"; }; </script> ]]> </Content> </Module>
Our JavaScript functionality layer consists of a single function,
switchNode(...)
, which accepts three
parameters:
activate
The ID of the page to be switched to a display status.
deactivate
The ID of the page to be hidden.
linkObj
The DOM node object representing the tab that was clicked. This determines the tab visualization states.
This is a simple function that has two responsibilities: swap the
state of the pages, and swap the state of the tabs. We start out by
swapping the pages, removing the hide
class from the page to be displayed and adding the class to the page to be
hidden. We then determine the tab that was clicked and the one that
wasn’t, and swap the classes for those tabs as well.
When we render, we should be presented with a tabbed interface like Figure 7-2, which contains a basic application that allows you to read up on current news and topics.
18.191.60.249