Open House On Demand, Part 2: A Video Open House Powered by OpenAuth, Video Uploads, Truveo, and the AOL Video Player

By John Fronckowiak
February 19, 2008

A real estate salesman had just closed his first deal, only to discover that the piece of land he had sold was completely under water. "That customer's going to come back here pretty mad," he said to his boss. "Should I give him his money back?" "Money back?" roared the boss. "What kind of salesman are you? Get out there and sell him a houseboat!" - Anonymous

About Open House On Demand

The goal of the Open House On Demand project is to provide an online virtual open house, supported by AOL technologies. Open House On Demand provides real estate brokers with a mechanism to upload videos of properties for sale to the AOL Video web site and to specify attributes of those properties--for example, property type, number of bedrooms, or year built. In addition, potential buyers will be able to search the property videos and view a virtual open house right in their web browsers. This article is presented in two parts: part one discusses the implementation of the video upload for property brokers, and in part two, which you are reading now, I discuss how potential buyers can search and view the videos.

Building the Open House Video Search

Although I use numerous AOL technologies to build Open House On Demand, I've kept the design simple so that it will be easy to understand. In this article, I focus on using the Truveo Asynchronous JavaScript and XML (AJAX) API to find open house properties and allow property searchers to locate a property based on varying criteria. As you will recall from part one, when brokers upload property videos, they specify a number of property-description parameters that are then converted to tags. Using the same attributes, the property searchers will determine the types of properties they're interested in, as shown in Figure 1.

Figure 1. The Open House Video Search page

A Truveo video search is launched and the resulting videos are displayed at the bottom of the page, as shown in Figure 2. Click the Search button to launch a search for the specified property. If you click the Cancel button, the search criteria are reset and the search results are cleared. If matching properties are found, an image thumbnail and navigation buttons are displayed. Click the thumbnail to launch the open house video.

Figure 2. Displaying the Open House Video Search results

Truveo APIs and Developer Center

Truveo's rich API is integral to developing the Open House Video Search. Although the Open House Video Search 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 on the AOL Truveo Developer Center web site. Click the My API Account link in the Truveo Video Search APIs section in the left nav bar. Your account will be associated with your AOL screen name, as shown in Figure 3.

Figure 3. Truveo Developer Center

Truveo supports your application for up to 10,000 queries per day. After you agree to the terms and conditions, you will receive 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. The Open House On Demand Video Search does not require user authentication.

Truveo Video Search Object

Before you can access the Truveo AJAX API on your web site, you must first import the AJAX API JavaScript library. Listing 1 shows how to place the <script> tag in the video search 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>

Accessing Truveo through AJAX is made possible through the TruveoVideoSearch (TVS) object. Before you can access Truveo from your application, you must create the TruveoVideoSearch object. This requires the Application ID key you previously created at the Truveo Developer Center. As shown in Listing 2, the load() function is called when the search page is loaded. The TruveoVideoSearch object, TVS, is created and initialized when the page is loaded. After the TruveoVideoSearch object is created, the initialize method is called. This method initializes the current state of the AJAX API.

Before any queries are executed, the TruveoVideoSearch object attributes are initialized. The results attribute is initialized to the constant MAX_RESULTS (which is initialized to 50, the maximum value permitted), thus determining the maximum number of results that are returned when a query is executed.

Finally, it is important to understand that the TruveoVideoSearch object processes asynchronously; that is, when a user makes a request of 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 the Open House on Demand Video Search, 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 Open House On Demand page load function

// *****
// Function: load()
// Purpose: Called by HTML body element's onload event
//
// Author: Fronckowiak
// *****

function load()
{
    // create the Truveo Video Search object using the application Id 
    // created at Truveo.com
    TVS = new TruveoVideoSearch(**REPLACE WITH YOUR TRUVEO KEY**);

    // initialize the Truveo Video Search object
    TVS.initialize();

    // set the maximum number of results returned at one time
    TVS.results = MAX_RESULTS;
	
    // 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);");
}
The Video Search Page

The source for the Open House Video Search page is shown in Listing 3. The load function is called when the search page is loaded. When the user clicks the Search button, the openHouseSearch function is called. I discuss this function in more detail in the following section. You will notice that the search form is virtually identical to the form used to specify the tag attributes when the video was uploaded. At the bottom of the page you will see the result set navigation buttons, and <div> tags to hold the video and property address.

Listing 3. The Open House On Demand Video Search page, search.htm

<body onload="load()">
<p align="center"><span class="style3"><span class="style4">Open House Video Search</span></span><br />
</p>
<label></label>
  <span class="style3">
  <label>Property Type:  
  <select name="proptype" id="proptype">
    <option value="single" selected="selected">Single Family Home</option>
    <option value="condos">Condos & Townhouses</option>
    <option value="multiple">Multiple Unit</option>
    <option value="apartment">Apartment</option>
  </select>
  <br />
  </label>
  <label>Price Range:  
  <select name="price" id="price">
    <option value="1" selected="selected">$1,000-$50,000</option>
    <option value="2">$50,001-$75,000</option>
    <option value="3">$75,001-$100,000</option>
    <option value="4">$100,001-$125,000</option>
    <option value="5">$125,001-$150,000</option>
    <option value="6">$150,001-$175,000</option>
    <option value="7">$175,001-$200,000</option>
    <option value="8">$200,001-$250,000</option>
    <option value="9">$250,001-$300,000</option>
    <option value="10">$300,001-$350,000</option>
    <option value="11">$350,001-$400,000</option>
    <option value="12">$400,001-$450,000</option>
    <option value="13">$450,001-$500,000</option>
    <option value="14">$500,001-$600,000</option>
    <option value="15">$600,001-$700,000</option>
    <option value="16">$700,001-$800,000</option>
    <option value="17">$800,001-$900,000</option>
    <option value="18">$900,001-$1,000,000</option>
    <option value="19">$1,000,000+</option>
  </select>
  <br />
  </label>
  <label>Square Feet:  
  <input name="sqfeet" type="text" id="sqfeet" size="12" maxlength="8" />  
  <br />
  </label>
  <label>Number of Bedrooms:  
  <select name="numbedrooms" id="numbedrooms">
    <option value="1" selected="selected">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10+</option>
  </select>
  <br />
  </label>
  <label>Number of Bathrooms:  
    <select name="numbaths" id="numbaths">
    <option value="1" selected="selected">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10+</option>
  </select><br />
  </label>
  <label>Year Built:  
  <input name="yearbuilt" type="text" id="yearbuilt" size="8" maxlength="4" />
  <br />
  </label>
  <label>Stories:  
  <select name="stories" id="stories">
    <option value="1" selected="selected">1</option>
    <option value="2">2</option>
    <option value="3">3+</option>
  </select>
  <br />
  </label>
  <label>Garage:  
  <select name="garage" id="garage">
    <option value="yes">Yes</option>
    <option value="no">No</option>
  </select>
  <br />
  </label>
  <label>Pool: 
    <select name="pool" id="pool">
    <option value="yes">Yes</option>
    <option value="no">No</option>
  </select>
  <br />
  </label>
  <label>Fireplace:  
  <select name="fireplace" id="fireplace">
    <option value="yes">Yes</option>
    <option value="no">No</option>
  </select>
  <br />
  </label>
  <label>Handicap Accessible:  
  <select name="handicap" id="handicap">
    <option value="yes">Yes</option>
    <option value="no">No</option>
  </select>  
  <br />
  </label>
  <label>Lot Features:  
  <select name="lotfeatures" id="lotfeatures">
    <option selected="selected"> </option>
    <option value="cul-de-sac">Cul-de-sac</option>
    <option value="waterfront">Waterfront</option>
    <option value="waterview">Waterview</option>
    <option value="beach">Beach Access</option>
    <option value="golf">Golf Course Lot</option>
    <option value="public-transport">Near Public Transport</option>
  </select>
  <br />
  </label>
  <label>Acreage:  
  <select name="acreage" id="acreage">
    <option selected="selected"> </option>
    <option value="1-5">1-5</option>
    <option value="5-10">5-10</option>
    <option value="10-25">10-25</option>
    <option value="25-50">25-50</option>
    <option value="over-50">50+</option>
  </select>
  <br />
<br />
  </label>
  </span>
  <p><span class="style3">  </span>
    <input name="search" type="button" id="search" onClick="openHouseSearch(event);" value="Search" />
    <input type="reset" name="cancel" id="cancel" onclick="onCancelClick(event)" value="Cancel" />
</p>
  <p>
<p><div id="results" style="display: none;"></div> <br/>
  <div id="address"></div>
  <p> </p>
  <p>
    <input id="first" name="first" type="button" value="<< First" onclick="firstProperty(event)" style="display: none;">
    <input id ="previous" name="previous" type="button" value="< Previous" onclick="previousProperty(event)" style="display: none;">
    <input id = "next" name="next" type="button" value="Next >" onclick="nextProperty(event)" style="display: none;">
    <input id="last" name="last" type="button" value="Last >>"onclick="lastProperty(event)" style="display: none;">
      </p>
    </p>
</body>
</html>
Building a Property Query

When you click the Search button, the openHouseSearch function is called, as shown in Listing 4. The openHouseSearch method builds a Truveo query string. A set of tags was associated with the video when it was uploaded. You can use the tag: modifier to find videos that have been assigned specific tags. Tags are words or groups of words that typically describe some aspect of the video with which they are associated. As long as an attribute is not blank, a tag is appended to the searchStr. Also, the indices, which track the current video that is being displayed, are reset to 0, and then the getVideos method is called.

Listing 4. The openHouseSearch function

// *****
// Function: openHouseSearch 
// Purpose: When the user clicks the Search button - build the search string
// tag list. The tags are based on the values that were set when the video
// was uploaded. 
// event - The search button click event 
//
// Author: Fronckowiak
// *****
function openHouseSearch(event)
{
	var searchStr;
	
	// build the search string tags based on the attributes specified 
// when the video was uploaded...
	// these are based on the tag values that were set when 
// the video was uploaded!
	if(!$('proptype').value.blank()) {
        searchStr = "tag:proptype=" + $('proptype').value;
	}
	if(!$('price').value.blank()) {
        searchStr += " tag:price=" + $('price').value;
	}
	if(!$('sqfeet').value.blank()) {
        searchStr += " tag:sqfeet=" + $('sqfeet').value;
	}
	if(!$('numbedrooms').value.blank()) {
        searchStr += " tag:numbedrooms=" + $('numbedrooms').value;
	}
	if(!$('numbaths').value.blank()) {
        searchStr += " tag:numbaths=" + $('numbaths').value;
	}
	if(!$('yearbuilt').value.blank()) {
        searchStr += " tag:yearbuilt=" + $('yearbuilt').value;
	}
	if(!$('stories').value.blank()) {
        searchStr += " tag:stories=" + $('stories').value;
	}
	if(!$('garage').value.blank()) {
        searchStr += " tag:garage=" + $('garage').value;
	}
	if(!$('pool').value.blank()) {
        searchStr += " tag:pool=" + $('pool').value;
	}
	if(!$('fireplace').value.blank()) {
        searchStr += " tag:fireplace=" + $('fireplace').value;
	}
	if(!$('handicap').value.blank()) {
        searchStr += " tag:handicap=" + $('handicap').value;
	}
	if(!$('lotfeatures').value.blank()) {
        searchStr += " tag:lotfeatures=" + $('lotfeatures').value;
	}
	if(!$('acreage').value.blank()) {
        searchStr += " tag: acreage=" + $('acreage').value;
	}

   // reset the index counters
   currentAbsIndex = 0;
   currentRelIndex = 0;
   currentPageNum = 0;

   // launch the Truveo video search...
   TVS.getVideos(searchStr);
}

Processing the Property Query Result Set

When the TruveoVideoSearch object was initialized in the load function, the attachEvent method was called to attach a function to the onupdate event of the TruveoVideoSearch object. The update event is called whenever functions such as getVideos asynchronously return with their results. Open House Video Search has attached the TVSQueryResult function to the onupdate event. As shown in Listing 5, the TVSQueryResult function takes a single parameter, methodName. This parameter is the name of the TruveoVideoSearch method that was called to obtain results. In Open House Video Search two query methods are used: getVideos, which is used to get the list matching videos based on the specified tags, and goToPage, which is used to page through the set of open house videos that are returned. As shown in Figure 4, upon any video list query, only MAX_RESULTS (which is set to the maximum of 50) videos are returned at a time, even though there could be more matching properties. The goToPage method is used to page through the result set. The Open House Video Search uses two indices to determine the current video being displayed. The currentAbsIndex tracks the index of a video in the complete set of all matching properties, and currentRelIndex tracks the relative index of the video in the current page that has been retrieved.

Listing 5 - The TVSQueryResult function

// *****
// Function: TVSQueryResult
// Purpose: Called by the Truveo Video Service to return the results
// of a query methodName - The name of the method which was called to 
// return the results this will be either getVideos (when a set of
// videos is requested) or gotToPage (when more videos are requested) 
//
// Author: Fronckowiak
// *****
function TVSQueryResult(methodName) {
	// is this a result of a getVideos or goToPage call?
	if(methodName == "getVideos") {
		// determine the total number of videos in the property set
		maxChannels = parseInt(TVS.VideoSet.totalResultsAvailable);
		// were any videos returned?
		if(maxChannels != 0) {
			// show the image dive and the navigation buttons - 
// if we have results
			$("first").show();
			$("previous").show();
			$("next").show();
			$("last").show();
			$("results").show();
			// clear any previous images
			$("results").innerHTML = "";
			// display the video thumbnail for the first property
			showCurrentVideo();
			// disable the first and previous buttons - 
// since we're already at the beginning...
			$("first").disable();
			$("previous").disable();
		} else {
			// set the error in title flag
			errorInTitle = true;
			// show the error in place of the title...
	$("address").innerText = "No Properties Found - 
Try Again!";
			// hide the navigation buttons and thumbnail...
			$("first").hide();
			$("previous").hide();
			$("next").hide();
			$("last").hide();
			// clear the results div
			$("results").innerHTML = "";
			$("results").hide();
		}
	// is this result of a goToPage call?
	} else if(methodName == "goToPage") {
		// display the thumbnail of the next video in this set
		showCurrentVideo();
	}
}

Figure 4. Indexing of the Video Search result set

Browsing the Result Set

When a valid query set is returned, the showCurrentVideo method, as shown in Listing 6, is used to show the video at the current index. Note that the videoResultEmbedTag attribute of the current video is put in the results <div> tag. This will embed the video player directly in the Open House Video Search page.

Listing 6. The showCurrentVideo function

// *****
// Function: showCurrentVideo 
// Purpose: This will display the property video that the currentAbsIndex 
// points to.
//
// Author: Fronckowiak
// *****
function showCurrentVideo() {
    	// is the current index beyond the beginning?
    if(currentAbsIndex > 0) {
		// reset to the beginning of the list
		currentAbsIndex = 0;
		currentRelIndex = 0;
		// disable the first and previous buttons - 
// since we're at the beginning...
		$("first").disable();
		$("previous").disable();
		return;
	// is the current index beyond the last?
	} else if(currentAbsIndex >= maxChannels) {
		// reset the index to the last video
		currentAbsIndex = maxChannels - 1;
		// reset the current relative index
		currentRelIndex = currentAbsIndex % MAX_RESULTS;
		// disable last and next buttons - since we're at the end...
		$("last").disable();
		$("next").disable();
		return;
	} else {
		// enable all navigation buttons...
		$("first").enable();
		$("previous").enable();
		$("last").enable();
		$("next").enable();		
	}
		
	// determine the page number we need to go to the rounding down 
// randomIndex / maxresults (number of items per page)
  	var pageNum = Math.floor(currentAbsIndex / MAX_RESULTS);	
	if(pageNum != currentPageNum) {
		currentPageNum = pageNum;
		// get the next virtual page of videos
		TVS.goToPage(currentPageNum);
	}
	// set the relative index in this page set
	currentRelIndex = currentAbsIndex % MAX_RESULTS;	
		
	// get the URL of the video from the video set
      videoURL = TVS.VideoSet.Video[currentRelIndex].videoUrl;

    $("results").innerHTML = TVS.VideoSet.Video[currentRelIndex].videoResultEmbedTag;
	$('address').innerHTML = TVS.VideoSet.Video[currentRelIndex].title;

	errorInTitle = false;
}

As shown in Listing 7, the navigation buttons are tied to the firstProperty, nextProperty, previousProperty, and lastProperty functions. These functions modify the index of the current video to display in the returned video set appropriately, and then call showCurrentVideo to display the correct video.

Listing 7. The firstProperty, nextProperty, previousProperty, and lastProperty functions

// *****
// Function: firstProperty 
// Purpose: When the user clicks the next button - display the first
// property - increment the index and show the next video thumbnail
// event - The first button click event
//
// Author: Fronckowiak
// *****
function firstProperty(event)
{
	// set index to first
	currentAbsIndex = 0;
	// show the next video thumbnail
	showCurrentVideo();
	$("first").disable();
	$("previous").disable();
}

// *****
// Function: nextProperty 
// Purpose: When the user clicks the next button - display the next 
// property - increment the index and show the next video thumbnail
// event - The next button click event
//
// Author: Fronckowiak
// *****
function nextProperty(event)
{
	// increment the index
	currentAbsIndex++;
	// show the next video thumbnail
	showCurrentVideo();	
}

// *****
// Function: previousProperty 
// Purpose: When the user clicks the previous button - display the previous 
// property - decrement the index and show the next video thumbnail 
// event - The previous button click event
//
// Author: Fronckowiak
// *****
function previousProperty(event)
{
	// decrement the index
	currentAbsIndex--;
	// show the next video thumbnail
	showCurrentVideo();
}

// *****
// Function: lastProperty 
// Purpose: When the user clicks the previous button - display the last
// property - decrement the index and show the next video thumbnail
// event - The last button click event
//
// Author: Fronckowiak
// *****
function lastProperty(event)
{
	// set index to last
	currentAbsIndex = maxChannels - 1;
	// show the next video thumbnail
	showCurrentVideo();
	$("last").disable();
	$("next").disable();
}
Profiling Your Truveo Applications

As I discussed earlier, you need to create an API account, and obtain an application key that must be built into your Truveo-enabled application. As shown in Figure 5, by logging into the Truveo Developer Center with your API account, you can obtain a rich set of reports that include the API Usage report, the Top Queries report, and the Channel Redirects report. In a blog post on the AOL Developer Network I review and dissect the reports. Read more at http://dev.aol.com/node/623.

Figure 5. Your Truveo account usage report

More...

Please also visit my blog on the AOL Developers Network. I'll be discussing more AOL technologies, the application of those technologies, and much more!

Resources