Mashup Case Study: Extending the Capabilities of an AJAX RSS Reader

Enable the Subscriptions block here!
Mashup

by Paul Sobocinski
February 7, 2007

The main idea behind a mashup application is to create a feature-packed web application quickly and easily, mainly by using code (from multiple sources) that's already been written. The term "mashup" is especially appropriate in most cases, because as we developers know, not all code is created equal. On the one hand, we can have API code that serves the main purpose of scalability, while on the other hand, we can have code that's been written to demonstrate an approach or technique. While the code in the latter case may be shorter and simpler, it is usually less scalable. Using code from various sources is one of the main challenges in developing an effective mashup application.

In this tutorial, we will go through the steps involved in creating a basic mashup. Our mashup will combine a simple client-side RSS reader with the capabilities of the AOL Video Search API; the end result will be a more interesting and feature-rich web application. In addition to displaying the content of an RSS feed, our mashup will also search for videos relevant to each news item and present them together with the RSS feed data. Essentially, we will be enriching the user experience by providing video as well as textual information from the RSS feed.

Overview of the Technologies Used

To accomplish our goal, we will draw on two main technologies: a client-side RSS reader, and the AOL Video Search API. We will use the RSS reader as the basis of our mashup, while the AOL API will be used to build upon it, adding functionality. The article "RSS and AJAX: A Simple News Reader" covers the RSS reader that we will be using, while an introduction to the AOL Video Search API is presented in "Building a Simple AOL Video Search API Application Using Ajax." Once you have a basic understanding of what is covered in these articles, you will find it easy to follow this case study.

In his "Cute Chihuahuas: Customized AOL AJAX Video Search" blog post, Kevin Farnham discusses a neat feature of the AOL API that we will use in our application. Kevin explains the use of the VideoSet.embedTag feature, which presents the results of our video search as an auto-scrolling collection of thumbnails. This is really neat, because it solves the problem of presenting a search result (a group of videos) in a concise and informative way. As we will see, this is quite important, as our mashup will be presenting multiple search results.

General Design Approach

The mashup application will be composed of three main tasks: get the RSS file from our local server, search for and retrieve relevant videos using the AOL Video Search API, and display the results to the user. The diagram below further breaks these three tasks into seven steps, numbered in execution sequence. A brief explanation of each step follows the diagram.

Figure 1
Figure 1

  1. The user selects an RSS feed and submits the form, triggering a DOM onsubmit event.
  2. Use AJAX to get the RSS file from the local server.
  3. The RSS file is retrieved as a JavaScript XML object, and is converted into a custom RSS object.
  4. Get videos relevant to the RSS items.
  5. Send the search request to the AOL server using the AOL Video Search API.
  6. Video results are available; send subsequent search requests if there are any more RSS items.
  7. Display the textual data in the RSS object, as well as the retrieved AOL video results.

Now that we have a general idea of what needs to be done, we can start programming our mashup application.

Mashing It Up

As the diagram suggests, there are two main JavaScript components that we are concerned with: rssajax-m.js and mashup.js. The former is a slightly modified version of rssajax.js, which is explained in detail in the RSS reader article. Most of the code contained in mashup.js will originate from the AOL Video API article. To start, we will cover the changes necessary to rssajax.js.

Modifying Existing Code: rssajax.js

Only minor changes to this component are needed--the two functions that we modify are processRSS() and showRSS(). We show the source code for both functions below (they are explained in more detail in the RSS reader article). The changes to the functions are indicated in bold below:

//processes the received rss xml
function processRSS(rssxml)
{
    RSS = new RSS2Channel(rssxml);
    /*A*/
    //showRSS(RSS);
    startGetVids(RSS); //see mashup.js
}

//shows the RSS content in the browser
function showRSS(RSS)
{
    //default values for html tags used
    var imageTag = "<img id='chan_image'";
    var startItemTag = "<div id='item'>";
    var startTitle = "<div id='item_title'>";
    var startLink = "<div id='item_link'>";
    var startDescription = "<div id='item_description'>";
    /*B*/
    var startVid = "<div id='item_vidthumbs'>";
    var endTag = "</div>";

    //populate channel data
    var properties = new Array("title","link","description","pubDate","copyright");
    for (var i=0; i<properties.length; i++)
    {
        eval("document.getElementById('chan_"+properties[i]+"').innerHTML = ''");
        curProp = eval("RSS."+properties[i]);
        if (curProp != null)
            eval("document.getElementById('chan_"+properties[i]+"').innerHTML = curProp");
    }

    //show the image
    document.getElementById("chan_image_link").innerHTML = "";
    if (RSS.image.src != null)
    {
        document.getElementById("chan_image_link").href = RSS.image.link;
        document.getElementById("chan_image_link").innerHTML = imageTag
            +" alt='"+RSS.image.description
            +"' width='"+RSS.image.width
            +"' height='"+RSS.image.height
            +"' src='"+RSS.image.url
            +"' "+"/>";
    }

    //populate the items
    document.getElementById("chan_items").innerHTML = "";
    for (var i=0; i<RSS.items.length; i++)
    {
        item_html = startItemTag;
        item_html += (RSS.items[i].title == null) ? "" : startTitle + RSS.items[i].title + endTag;
        /*C*/
        item_html += startVid + vidTagsArr[i] + endTag;
        item_html += (RSS.items[i].link == null) ? "" : startLink + RSS.items[i].link + endTag;
        item_html += (RSS.items[i].description == null) ? "" : startDescription + 
                      RSS.items[i].description + endTag;
        item_html += endTag;
        document.getElementById("chan_items").innerHTML += item_html;
    }

    //we're done
    //document.getElementById("chan").style.visibility = "visible";
    return true;
}

Code Segment 1: rssajax-m.js

A: At this point, the RSS reader calls showRSS() to display the contents of the RSS object. We comment out the call to showRSS() and call startGetVids() instead. In effect, we are doing a program execution "detour" of sorts; showRSS() will be called once the videos are ready to be displayed. As you might expect, the startGetVids() function is found in mashup.js. We will explain how this function works in the next section.

B: Here, we define the variable that contains the HTML for the tag that will contain the video result of the RSS item being displayed, just as we have done for each RSS item's title, link, and description.

C: This is where we've added the actual video search result to the RSS item data. The variable vidTagsArr is a globally declared array, containing video search results for each RSS item. We populate this array in mashup.js, which we cover next.

Writing New Code: mashup.js

The source code contained in mashup.js is displayed in its entirety below. If you haven't done so already, I encourage you to study the AOL Video API article mentioned previously, as many of the functions it covers are used below.

/*C*/
function startGetVids(RSS)
{
    RSSObj = RSS;
    currIndex = 0;
    vidTagsArr = new Array();

    if (!searchReady)
    {
        alert("Error: AOL Video Search API has not initialized");
        return false;
    }
    else
        getNextVid();
}

/*D*/
function getNextVid()
{
    searchterms = getSearchTerms(RSSObj.items[currIndex]);
    AOLVS.getVideos(searchterms);
}

/*E*/
function getSearchTerms(RSSItem)
{
    finalTerms = "";
    numTerms = 0;
    termArray = RSSItem.title.split(" ");

    for (var i=0; (i<termArray.length && numTerms<maxSearchTerms); i++)
        if (termArray[i].length >= minSearchTermLength)
        {
            finalTerms += termArray[i] + " ";
            numTerms++;
        }
    return finalTerms;
}

/*F*/
function onVUpdateH()
{
    vidTag = AOLVS.VideoSet.embedTag;
    vidTagsArr.push(vidTag);

    if ((currIndex == maxSearches-1) || (currIndex == RSSObj.items.length-1))
        endGetVids();
    else
    {
        currIndex++;
        getNextVid();
    }
}

/*G*/
function endGetVids()
{
    showRSS(RSSObj); //see rssajax-m.js
}

/*B*/
function onVInitializeH()
{
    searchReady = true;
}

/*A*/
function onLoadH(e)
{
    AOLVS = new AOLVideoSearch(appId);
    AOLVS.attachEvent("onload", "onVInitializeH();");
    AOLVS.attachEvent("onupdate", "onVUpdateH();");
    AOLVS.initialize();
    return true;
}

var RSSObj;
var AOLVS;
var vidTagsArr;
var appId = "a66boz8q0tq7vhm02";

var maxSearches = 5;
var maxSearchTerms = 3;
var minSearchTermLength = 4;

var searchReady = false;
var currIndex;

window.onload = onLoadH;

Code Segment 2: mashup.js

A: This is the event handler for the page load (it runs automatically as soon as the entire HTML page is loaded). It initializes the AOL video search object and assigns the relevant event handlers. For a more detailed explanation of this function, see the AOL API article.

B: The onVInitializeH() function is called when the AOL video search object is ready to handle search requests. It simply sets a global Boolean variable to true to indicate this. Usually, this function runs shortly after AOLVS.initialize() is executed in onLoadH().

C: The startGetVids() function is called from processRSS() in rssajax-m.js (see "A" in Code Segment 1). First, it assigns initial values to the global variables: RSSObj, currIndex, and vidTagsArr. RSSObj stores the entire RSS feed received from the server (passed as a parameter to this function); currIndex stores the index of the current RSS item that we are fetching videos for; and lastly vidTagsArr is an array that stores the video result sets. The indexes of this array are matched up to the indexes of the RSS items in RSSObj. Once we verify that the AOL Video Search object has been properly initialized (by checking the value of searchReady), we move on to fetching the first video result set by calling getNextVid().

D: In the function getNextVid() we tell the AOL video search object to search for videos that match searchterms by passing it to the getVideos() method. The key here is the contents of searchterms--it should contain keywords relevant to the RSS item for which we are currently fetching videos. Unfortunately, this isn't a simple process, as the RSS 2.0 standard does not include a "keywords" tag. This means that we have to devise a way of extracting keywords from an RSS item. We have created the function getSearchTerms() to accomplish this task.

E: There are many ways to implement the getSearchTerms() function. In fact, you should customize this function so it works best with your RSS feed. For example, assume the titles of your RSS feed items are always prefixed with your domain name. If this is the case, you should omit this from searchterms as it is not useful as a search keyword.

We've used a very simple approach here: the function selects certain terms from the title tag, filtering out any words that have less than minSearchTermLength, and capping the total number of words at maxSearchTerms. These constants can be tweaked to work optimally with your RSS feed.

F: Once the requested search result is ready, the function onVUpdateH() is invoked. As we don't want to be concerned with the presentation of the result set, we use the embedTag attribute (see Kevin's blog entry for more details). The contents of this are pushed onto the vidTagsArr--note that the indexes of vidTagsArr[] will automatically correspond to the indexes of RSSObj.items[]. If we have fetched videos for all the RSS items, or we have reached the maximum allowed searches per feed (maxSearches), we have finished fetching videos, and move on to endGetVids(). If not, we increment the RSS item index and send a subsequent search request by calling getNextVid() again.

G: The function endGetVids() is called when we have finished fetching video search results. We can now go onto displaying the information contained in the feed, as well as the video search results. We accomplish this by calling showRSS() For an in-depth discussion, refer to the original showRSS() function found in the RSS reader article, as well as the updated version we use (see Code Segment 1).

Conclusion

We have managed to create a application that applies two different potent technologies with a relatively small time spent on actual coding. It's by no means a textbook example of object-oriented programming. However, it is a fairly good example of implementing a mashup application. On the one hand, we applied the AOL Video Search API--robust and scalable, and we didn't even need to look at the internal workings of the code. On the other hand, we applied the AJAX RSS reader: a simple application programmed mainly to serve an informative purpose. The latter component fit into our mashup application less elegantly; we had to change the source code of the RSS reader, modifying processRSS() and showRSS() (albeit slightly). Such contrasting styles and quality of code is precisely what you can expect when building mashup applications from multiple components.

The files necessary to get this mashup running are available below. In the interests of brevity, I didn't discuss layout and aesthetic considerations, so I'm warning you ahead of time that the mashup itself doesn't "look pretty." You can easily improve on the design by elaborating on the CSS rules at the head of the HTML document, or even define a separate CSS file to store these. Also, the RSS feeds are only sample files that are somewhat outdated, so they don't give optimal video search results. The key to improving the quality of the returned video search results lies in tweaking the getSearchTerms() function (Code Segment 2) so that it works optimally with your RSS feeds.

I hope that this case study has shed some light on the process of developing a mashup application from a collection of different APIs and technologies. Now go have some fun with mashups!

Example Code

References