The client side is implemented in JavaScript. It
uses the Rich Client System to bind to the server
object and perform remote method invocations. The
client code is responsible for the following tasks:
- Perform remote object binding to make sure a
proxy is always ready to perform search queries
- Detect user idle time, check user input for the
space character or Enter key
- Notify use of any pending search requests
- Display search results
To import the Rich Client System, the script uses
the external script loading mechanism:
<script type="text/javascript" src="../WebORB.js"></script>
Object binding
The script performs object binding upon loading of
the page. The <body> element of the page registers a 'onload'
event handler:
<body onload="bind()">
In addition to the remote object binding, the
'bind' function performs some additional startup
initialization tasks. The function is implemented as
follows:
function bind()
{
//hide the
animation notifying the user when the search is in
progress
document.getElementById( "animation" ).style.visibility = "hidden";
// if the proxy
object does not exist - create one.
// The arguments for the webORB.bind call are:
// 1. "googlesearch.GoogleSearch" - classname with
namespace of the server side class
// 2. "weborb.aspx" - URL of the WebORB handler.
Designates that requests sent by
// the Rich Client System must be
processed by WebORB
if( googleProxy == null )
googleProxy = webORB.bind( "googlesearch.GoogleSearch",
"weborb.aspx" );
// create new request
stack - will be used as a mechanism to ignore multiple
search calls
requestStack = new Array();
}
Handling user input
The search query text field registers a onkeypress
handler:
<input type=text id="searchtext"
name="searchtext"
size="54"
onkeypress="handleKeyPress( event )">
The handleKeyPress() function checks the conditions
for sending out a search query. The function's
implementation can be found below:
function handleKeyPress(
evt )
{
// get the time
when the user pressed a key and save it in a global
variable.
// the script will use the value to check the for user idle time
lastKS = new Date().getTime();
// make sure we
have a cross-browser compatible event object
evt = (evt) ? evt : ((event) ? event : null);
// get the key code
from the event
var code = evt.charCode ? evt.charCode : evt.keyCode;
// if the user
entered a space character or pressed Enter, do the
search,
// otherwise schedule a timer to call the runSearch function in 300
ms.
if( code == 32 || code == 13)
search();
else
setTimeout( runSearch, 300 );
}
The runSearch function checks if there was any
typing activity since the last keystroke. If no key
has been pressed, the function requests a search to
take place:
function runSearch()
{
if( new Date().getTime() - lastKS > 250 )
search();
}
Remote method invocation
The search() function is where the script sends out
a remote method invocation on an object hosted in
WebORB:
function search()
{
// get the search query
text
var search = document.getElementById( "searchtext" ).value;
// get the user' google
key
var googleKey = document.getElementById(
"gkey" ).value;
// push the request onto the stack.
// the stack is used to
ignore intermediary calls if there are any
requestStack.push( search );
// turn on the animation
to indicate that a search query is in progress
document.getElementById( "animation" ).style.visibility = "visible";
// create an async object
with pointers to the functions where
// the response should be delivered to. one function is for successful
invocations
// and the other is for errors and exceptions
var async = new Async( processSearchResults, processError );
// issue a remote method
invocation
googleProxy.runGoogleSearch( search,
googleKey.length != 0 ? googleKey : null, async );
}
Processing results
The page HTML declares a block level element to
contain the markup for the search results:
<div id="searchResults"></div>
Upon a successful method invocation, the Rich
Client System executes the first function from the
Async object - processSearchResults. The function's
argument is an object returned from the C# code shown
above. The Rich Client System ensures that the data
structure of the object is the same as on the server
side. Since the C# code does a pass through on the
response value, the result has the same fields as
declared in the
Google web service API documentation.
function
processSearchResults ( result )
{
// pop a request from the
stack
requestStack.pop();
// if the stack is not
empty, ignore this response - there is more on the way
if( requestStack.length > 0 )
return;
// get a reference to the
search results block level element
var searchResults = document.getElementById( "searchResults" );
// clear out any existing
search results
searchResults.innerHTML = "";
// start building new
search results markup
var searchContent = "";
// iterate over the
search results in the result object and create a
markup
// that would look similar to the original google search results
for( var i = 0; i < result.resultElements.length; i++ )
{
searchContent += "<a style=\"font-size: 12pt; color: #0000FF;
text-decoration: " +
"underline\" href=\"" + result.resultElements[ i
].URL + "\">" +
result.resultElements[ i ].title + "</a><br>";
if(
result.resultElements[ i ].snippet )
searchContent += result.resultElements[ i
].snippet + "<br>";
searchContent += "<font color=\"#339933\">" +
result.resultElements[ i ].URL +
"</font><br><br>";
}
// hide the animation
document.getElementById( "animation" ).style.visibility = "hidden";
// render the new search
results
searchResults.innerHTML = searchContent;
}
.Error handling
The error handling in the example is limited to
displaying the error to the user:
function processError(
error )
{
// pop a request
from the stack
requestStack.pop();
// use the same
search results element to render the error
var searchResults = document.getElementById( "searchResults" );
// render text of
the error/exception
searchResults.innerHTML = "Server reported an error: <b>" +
error.description +
"</b><br><br>";
// render the
server-side stacktrace
searchResults.innerHTML += error.details;
}.. |