Function DoCallBack(eventTarget, eventArgument)
This is where the magic happens. Remember, one of the goals of the CallBackObject is to make asynchronous HTTP requests that can be easily integrated into ASP.NET. ASP.NET coders should recognize the general format of this function, as it closely mimics the .NET doPostBack() JavaScript function that initiates server side events in ASP.NET. Let’s take it slow.
var theData = '';
var theform = document.forms[0];
var thePage = window.location.pathname + window.location.search;
var eName = '';
Here we are declaring some variables to hold all of the form data theData, grabbing a reference to the current form theform, and obtaining the name of the current page.
theData = '__EVENTTARGET=' +
escape(eventTarget.split("$").join(":")) + '&';
This is identical to doPostBack(), we are essentially telling ASP.NET which control is responsible for the Call Back. The escape is necessary to URLEncode any data posted to the server, the split and join return ASP.NET control ID’s to their proper form. It isn’t critical to understand why we are doing this to understand the JavaScript, I’ll cover this in more detail in Part 2 of this article.
theData += '__VIEWSTATE=' +
escape(theform.__VIEWSTATE.value).replace(new
RegExp('\\+', 'g'), '%2b') + '&';
The ViewState is that magical hunk of Base64 encoded text that makes web programmers’ jobs a lot easier. Unfortunately, the JavaScript escape function doesn’t handle the ‘+’ sign, so we have to manually encode it by substituting ‘%2b’ in its place.
theData += 'IsCallBack=true&';
This line lets the server side code know that the current request is a CallBack, initiated by the client side code. Otherwise, the server would assume it was a normal web request and might handle things differently.
for( var i=0; i< i++ i++)
{
eName = theform.elements[i].name;
if( eName && eName != '')
{
if( eName == '__EVENTTARGET' || eName == '__EVENTARGUMENT'
|| eName == '__VIEWSTATE' )
{
}
else
{
theData = theData + escape(eName.split("$").join(":")) + '=' +
theform.elements[i].value;
if( i != theform.elements.length - 1 )
theData = theData + '&';
}
}
}
Finally, we loop through the rest of the form elements (input boxes, check boxes, drop down lists, etc.) and append their names and values to our theData variable.
Now you might be wondering why we did all this. After all that processing theData now contains exactly the same information that is sent to the server whenever the “Submit” button is clicked on a form. We’re ready to make our asynchronous request to the server.
if( this.XmlHttp )
{
if( this.XmlHttp.readyState == 4 || this.XmlHttp.readyState == 0 )
{
var oThis = this;
this.XmlHttp.open('POST', thePage, true);
this.XmlHttp.onreadystatechange = function()
{ oThis.ReadyStateChange(); };
this.XmlHttp.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
this.XmlHttp.send(theData);
}
}
First, we make sure we have a valid XmlHTTPRequest object. Then, we check to make sure that the XmlHTTPRequest object is ready to make a new request.
Then, save a reference to the current object (CallBackObject).
var oThis = this;
Open an asynchronous connection to the current page using the POST method.
this.XmlHttp.open('POST', thePage, true);
As the state of our XmlHTTPRequest object changes, we want to be able to take various actions. We tell the object to call CallBackObject.ReadyStateChange() any time the state changes.
this.XmlHttp.onreadystatechange = function()
{ oThis.ReadyStateChange(); };
Finally, send all the form data along with the request.
this.XmlHttp.send(theData);
That’s it!!! We have successfully made an asynchronous web request using JavaScript… now what? Well, in most cases, you will be expecting some sort of response from the server after making your request. When the response comes back, you can process any data returned from the server. How will you know when the response comes back? That is where the ReadyStateChange() method comes in handy.
Event Handler ReadyStateChange()
The XmlHTTPRequest object has four main states/state codes, Loading-1, Loaded-2, Interactive-3, and Complete-4. To allow the client side developer to respond to each of those states, ReadyStateChange() receives the new state from the XmlHTTPRequest object, then raises the proper event.
CallBackObject.prototype.ReadyStateChange = function()
{
if( this.XmlHttp.readyState == 1 )
{
this.OnLoading();
}
else if( this.XmlHttp.readyState == 2 )
{
this.OnLoaded();
}
else if( this.XmlHttp.readyState == 3 )
{
this.OnInteractive();
}
else if( this.XmlHttp.readyState == 4 )
{
if( this.XmlHttp.status == 0 )
this.OnAbort();
else if( this.XmlHttp.status == 200 &&
this.XmlHttp.statusText == "OK")
this.OnComplete(this.XmlHttp.responseText,
this.XmlHttp.responseXML);
else
this.OnError(this.XmlHttp.status,
this.XmlHttp.statusText,
this.XmlHttp.responseText);
}
}
A state code of 4 means complete but may not mean that things went according to plan. If a request was aborted then the state is changed to Completed-4, but the status is Unkown-0, so we raise an event for that as well. A successful HTTP request should yield a response of status 200-OK, anything else is considered an error and the OnError event is raised.