Elephant's Pajamas: Building a Truveo Video Joke-of-the-Day Mac OS X Dashboard Widget
By John Fronckowiak
September 26, 2007
Introduction
One morning I shot an elephant in my pajamas. How he got into my pajamas I'll never know. – Groucho Marx
Elephant's Pajamas is a Mac OS X Dashboard widget, powered by AOL Truveo, that provides access to funny joke videos. Truveo is a one-stop destination for searching video resources available all over the Internet, including AOL Music, AOL Video, YouTube, CNN, NBC, Fox News, and much more. Elephant's Pajamas uses the Asynchronous JavaScript and XML (AJAX) API provided by Truveo to search through all the funny joke videos available from the resources aggregated by Truveo, and then randomly select one video, which the user can view in their Web browser. This article shows how I developed Elephant's Pajamas, using the technologies that only Truveo provides.
Elephant's Pajamas and Mac OS X Dashboard Widgets
Apple introduced Dashboard and widgets to the Mac world with the release of Mac OS X 10.4 Tiger. Dashboard widgets are actually mini Web 2.0 applications. Essentially, Dashboard widgets are comprised of an HTML page that describes the layout of the front and back of the widget, and a JavaScript file that contains the code. It's important to realize that widgets aren't full-blown applications; rather, they are small and focused in purpose. Even though Elephant's Pajamas is built as a Mac OS X Dashboard widget, the development techniques I review here can be applied to any Web 2.0 application development that uses AJAX.
The Elephant's Pajamas widget, shown in Figure 1, uses the AJAX API provided by Truveo.
Figure 1. Elephant's Pajamas front view: configuration
By default, Elephant's Pajamas searches for videos that have the keywords "comedy AND jokes AND funny," and from the entire set of videos that is returned, a single video is randomly selected. A thumbnail of the video is displayed in the widget interface. When the user moves the cursor over the thumbnail the video title is displayed. A click on the thumbnail launches the Web browser to play the video. In addition, the user can click on the elephant's "goggly eye" to randomly select another video. As shown in Figure 2, on the back of the widget the user can specify information that directs the search. These parameters will be discussed in the sections “Building Your Query” and “Modifiers and Filters” later in this article.
Figure 2. Elephant's Pajamas back view: configuration
Figure 3 shows an Elephant's Pajamas video in action on Mac OS X Dashboard.
Figure 3. An Elephant's Pajamas video in action
Truveo APIs and Developer Center
Truveo's rich API was integral to developing Elephant's Pajamas. Although Elephant's Pajamas uses Truveo's AJAX interface, Truveo also provides developers with interfaces for XML, Flash, and Ruby.
The Truveo AJAX API provides direct access to the Truveo search engine using JavaScript and Dynamic HTML (DHTML). The AJAX API can work with most current Web browsers: Internet Explorer 6.0 and later, AOL Explorer 1.0 and later, Firefox 1.0 and later, Opera 8 and later, Mozilla 1.7 and later, Netscape 7.0 and later, and Safari 2.0 and later.
Before you can integrate Truveo APIs into your application, you must create a Video Search API account, from the AOL Developer Center Web page, shown in Figure 4.
Figure 4. Truveo API account sign-up page
Truveo provides your application with up to 10,000 queries per day. Agreeing to the terms and conditions provides you with two keys for your application. The first key is the Application ID, which is a unique key that provides access to the Truveo APIs. The second key is the Shared secret, which is required for methods that require user authentication. Elephant's Pajamas currently does not implement user authentication.
When you have deployed your application you can visit your My API Account page at the Truveo Developer Center to get usage reports for your applications, shown in Figure 5. The reports include information about the number of queries and clicks, and median response time. This is essential to keep up on your application’s usage and its performance.
Figure 5. Truveo API usage report
Dissecting the Elephant's Pajamas Interface
Before reviewing the TruveoVideoSearch object, which provides direct access to the Truveo search engine, let's first dissect the important interface components of the Elephant’s Pajamas widget. The front of the widget is shown in Figure 6. There are three primary areas to understand. In the front of the widget, there is an HTML <div> tag that covers the elephant's goggly eye. Recall that you use <div> tags to create logical groups in a Web page. When a user clicks the eye <div>, a random video is selected. The associated thumbnail is loaded in the preview <div>. The title <div>, which is located on top of the preview <div>, is used to display the title information of the selected video. When the user moves the mouse over the title <div>, the opacity of the title <div> is changed and the video title is visible.
Figure 6. The front of the Elephant's Pajamas widget dissected
The back of the widget, which is typically used for configuration, is shown in Figure 7. Clicking the Config button displays the configbox <div>. From here the user can specify the query string that is used to search Truveo. Truveo searches can use basic Boolean operators. For more information about query construction, see Using Basic Search Queries and Using Advanced Search Queries at the Truveo Developer Center.
In addition, Truveo provides the ability to apply modifiers and filters to a query. I will discuss how I applied these in Elephant's Pajamas in more detail later. Users can specify the maximum video runtime—in minutes, the maximum age of a video in days, and the formats of available videos (Windows Media Player, Real Media Player, Apple QuickTime, Adobe Flash, and AOL Hi-Q formats). Users can also direct Truveo to include or exclude adult content. In addition, results can be limited to free videos—those that don't require user registration to view.
Figure 7. The back of the Elephant's Pajamas widget dissected
In addition to the Dashboard widget package, you can download the complete HTML and JavaScript source code for Elephant's Pajamas at http://widgets.idcc.net.
Truveo Video Search Object
Before you can access the Truveo AJAX API in your Web page, you must import the AJAX API JavaScript library. Listing 1 shows how to place the <script> tag in the widget’s HTML page.
Listing 1. Adding the Truveo AJAX API JavaScript library
<!-- Include the Truveo Video Search Javascript Library --> <script type="text/javascript" src="http://xml.truveo.com/TruveoVideoSearchAPIv3.js"></script>
The cornerstone of Truveo access through AJAX is the TruveoVideoSearch object. Before you can access Truveo from your application, you must create the TruveoVideoSearch object. This requires the Application ID key you created at the Truveo Developer Center. As shown in Listing 2, the TruveoVideoSearch object, TVS, is declared as a global variable that is created and initialized when the widget is loaded. After the TruveoVideoSearch object is created, the initialize method is called. The initialize method initializes the current state of the AJAX API.
Before any queries are executed, the TruveoVideoSearch object attributes are initialized based on the widget's saved preferences. The results attribute is initialized to the constant maxresults (which is initialized to 50, the maximum value permitted); this determines the maximum number of results that are returned when a query is executed. The showAdult attribute is set to 0 or 1, again based on the widget's saved preferences. If it is set to 1, adult content is included in the search results.
Finally, it is important to understand that the TruveoVideoSearch object processes asynchronously; that is, when a user makes a request to Truveo it is processed in the background, and only when the results become available is the application notified. Request completion is signaled though an event. You must specify which application functions will process these events. In Elephant's Pajamas two events are registered for the TruveoVideoSearch object: onerror and onupdate. Events are attached using the attachEvent method of the TruveoVideoSearch object. The TVSError method receives two parameters, errorCode and errorMessage. These are strings that specify information about the error that occurred. The TVSQueryResult method receives one parameter, methodName, which is a string that contains the name of the method that initiated the query request.
Listing 2. The widget load function
// *****
// Function: load()
// Purpose: Called by HTML body element's onload event when the widget is ready to start
// *****
function load() {
// setup the widget components
setupParts();
// check current preference version...
// if they don't exist or are new, recreate them...
if (getPref("version") != "1.1") {
// the current version of elephants pajamas
createPref("version", "1.1");
// sound switch
createPref("sounds", "true");
// Truveo query
createPref("query", "comedy AND jokes AND funny");
// show adult video flags
createPref("showAdult", "0");
// show only free videos flag
createPref("showFree", "true");
// maximum video runtime for search items
createPref("maxRuntime", "5");
// maximum number of days old for search items
createPref("maxDays", "30");
// show windows compatible videos
createPref("showWin", "true");
// show real compatible videos
createPref("showReal", "true");
// show quicktime compatible videos
createPref("showQT", "true");
// show flash compatible videos
createPref("showFlash", "true");
// show AOL Hi-Q compatible videos
createPref("showHiQ", "true");
}
// create the Truveo Video Search object using the application Id
// created at Truveo.com
TVS = new TruveoVideoSearch("2fdb77329ed2706ca");
// initialize the Truveo Video Search object
TVS.initialize();
// set the maximum number of results returned at one time
TVS.results = maxresults;
// set the show Adult videos preference... 0=omit adult videos,
// 1=include adult videos
TVS.showAdult = getIntPref("showAdult");
// include related search items in the results
TVS.showRelatedItems = 1;
// attach an error handling event handler - called whenever
// Truveo Video Search throws an error
TVS.attachEvent("onerror", "TVSError(errorCode, errorMessage);");
// attach an update event handler - called when a query returns results
TVS.attachEvent("onupdate", "TVSQueryResult(methodName);");
// run the search query...
runQuery();
}
Building Your Query
Queries are sent using the getVideos method of the TruveoVideoSearch object. The getVideos method accepts a single parameter—a string that contains the query and any filter or modifier directives. In Elephant's Pajamas there are two functions essential to processing a query: runQuery and buildQuery, as shown in Listings 3 and 4. The runQuery function is called in two places in Elephant's Pajamas: when the widget is first loaded, to display an initial selection, and whenever a user clicks the elephant's goggly eye.
The runQuery method displays an animated .gif in the preview <div> to indicate that a search is in progress. The getVideos method of TruveoVideoSearch is called, using the string built by the buildQuery function. Once again, it's important to note that the call to getVideos is asynchronous. It will return immediately after it is called, and then, when results are available, the TVSQueryResult method will be called automatically, because it is attached to the onupdate event of the TruveoVideoSearch object.
Listing 3. The runQuery function: executing the Truveo query
// *****
// Function: runQuery
// Purpose: Run the query...the getVideos method runs asynchronously...
// display a processing image on the title...
// *****
function runQuery() {
// specify a search has started
inSearch = true;
// hide the title div
makeTransparent("title");
// show the processing image
document.getElementById("preview").innerHTML = "<img src='Images/wait20trans.gif'
height='20px' width='20px' style='position: absolute; left:58px; top:41px;' />";
// findVideo flag...
findVideoPage = true;
// call the Truveo Video Search
TVS.getVideos(buildQuery());
}
Modifiers and Filters
Truveo supports a rich mechanism for modifying and filtering your query. Many modifier and filter mechanisms are integrated into Elephant's Pajamas. In the widget preferences the user can specify that only free videos are part of the result set, the maximum runtime in minutes, the age of the video, and the format type of the results. As shown in Listing 4, the buildQuery function assembles and returns the query string. This string contains the query specified in the widget configuration, and then appends the free modifier if it has been specified, the maximum runtime, how many days old, and then restricts the results to the specified formats.
Listing 4. The buildQuery function: creating the search query string
// *****
// Function: buildQuery
// Purpose: Make the document item, specified by the id, visible
// *****
function buildQuery() {
// start with the query text...
var queryStr = getPref("query");
// add the only free video filter...
if (getBoolPref("showFree")) {
queryStr += " type:free";
}
// specify the maximum runtime
queryStr += " runtime:<" + getPref("maxRuntime");
// specify the maximum age
queryStr += " days_old:<" + getPref("maxDays");
// add the filters for video types...
if (getBoolPref("showWin")) {
queryStr += "format:win ";
}
if (getBoolPref("showReal")) {
queryStr += "format:real ";
}
if (getBoolPref("showQT")) {
queryStr += "format:qt ";
}
if (getBoolPref("showFlash")) {
queryStr += "format:flash ";
}
if (getBoolPref("showHiQ")) {
queryStr += "format:hi-q ";
}
// return the built query string...
return queryStr;
}
Asynchronous Query Result Handling
When the results are asynchronously returned from the TruveoVideoSearch object, the onupdate event is triggered. This event was previously tied to the TVSQueryResult function, shown in Listing 5. When the query returns the results, the findVideoPage method will be true; findVideoPage is a global variable, which is initialized to true when the query is initiated (using runQuery). The VideoSet attribute of the TruveoVideoSearch object contains the search results. The totalResultsAvailable attribute of VideoSet determines the total number of videos available in the query result set. As previously specified, only 50 of those videos will be returned at a time. A random index of the complete results set is generated.
Because the total number of results returned at a time is limited, to find a specific video we must determine the page that contains the randomly selected video; each page contains 50 videos. We call the goToPage method of the TruveoVideoSearch object to return the videos contained on the relevant page. The findVideoPage variable is set to false, because the goToPage method also returns the results asynchronously; the TVSQueryResult method is called again when the requested page results are available. When the page is returned, videoUrl is saved, the thumbnail image is placed in the preview <div>, and the title <div> text is updated with the title of the video. It is not possible to access the embedded video through the Truveo API; developers only have access to the URL of the page that contains the video.
Listing 5. The TVSQueryResult method: processing the query results
// *****
// Function: TVSQueryResult
// Purpose: Called by the Truveo Video Service to return the results of a query
//
// event: onUpdate method of Truveo Video Service object
// *****
function TVSQueryResult(methodName) {
// the first time we are here...we need to determine what page in the
// result set to go to
if(findVideoPage) {
var pagenum = 0;
// generate a random number between 0 and the maximum number of
// items returned by the query
randomIndex = getRandomInt(TVS.VideoSet.totalResultsAvailable);
// determine the page number we need to go to the rounding
// down randomIndex / maxresults (number of items per page)
pagenum = Math.floor(randomIndex / maxresults);
// when we get to the page determine which item to display
randomIndex = randomIndex % maxresults;
// were going to return to TVSQueryResult when we call goToPage...
// make sure we display the correct video next time
findVideoPage = false;
TVS.goToPage(pagenum);
} else {
// get the URL of the video from the video set
videoURL = TVS.VideoSet.Video[randomIndex].videoUrl;
// display the thumbnail in the preview div
document.getElementById("preview").innerHTML = "<img src=\"" + TVS.VideoSet.Video[randomIndex].thumbnailUrl
+ "\" width=\"139px\" height=\"103px\"/>";
// store the title as the text of the title div
document.getElementById("title").innerText = TVS.VideoSet.Video[randomIndex].title;
// done with our searching ... activate other events
inSearch = false;
}
}
Error Handling
Error handling is an essential part of any application. Errors can be thrown when calling the methods of the TruveoVideoSearch object. In the initialization process the onerror event of the TruveoVideoSearch object was assigned to the TVSError function. The two parameters passed indicate the error code and the error message returned. As shown in Listing 6, the TVSError message displays the returned error message in the title <div>, which is made visible.
Listing 6. The TVSError function: error event handler
// *****
// Function: TVSError
// Purpose: Called by the Truveo Video Service object when an error is thrown
//
// event: onError method of Truveo Video Service object
// *****
function TVSError(errorCode, errorMessage) {
// sound the alarms!
PlaySound("alarm");
// show the error in place of the title...
document.getElementById("title").innerText = "Truveo Error: " + errorMessage;
// make the title div visible...
makeOpaque("title");
}
More...
Please also visit my blog on the AOL Developers Network http://dev.aol.com/blog/25105 - I'll be discussing more about AOL technologies, applications of those technologies on the Mac, and much more!
Resources
- Truveo Developer Center
- Truveo AJAX API
- Mac OS X Dashcode
- Elephant's Pajamas Widget, HTML, and JavaScript Source
- Mozilla JavaScript Reference
- Document Object Model Reference
- Apple Guide to Dashboard
Acknowledgements
Special thanks to the late great Groucho Marx and his movie Animal Crackers for providing the inspiration for Elephant's Pajamas; and Becky Straka of Straka-Digital for essential help with the elephant design and graphics.

nice idea : )
Bst Rgds,
Michael B.