// // Copyright 2006 Nelson Daza. nelson.daza@gmail.com. All rights reserved. // DOM HTML // ---------------------------------------------------- // // HTTPRequest.js // // version 1.0.5.2 major.minor[.revision[.build]] // // CONSTRUCTOR FUNCTION // function HTTPRequest ( ) { /* PROPERTIES */ this.xmlHttpRequest = null; this.timeout = 0; // Timeout period ( in ms ) until an async request will be aborted, and the ontimeout function will be called. this.cache = false; // ¿A unique numeric value is appended so that the requested URL will not be cached? this.url = null; // The url that the request will be made to, which defaults to the current url of the window. this.method = 'GET'; // The method of the request, either GET (default), POST, or HEAD. this.async = true; // ¿The request will be asynchronous? this.username = null; // The username used to access the URL. this.password = null; // The password used to access the URL. this.parameters = new Object( ); // Hashtable holding name/value pairs which will be added to the url for a GET request or the request content for a POST request. this.requestIndex = HTTPRequest.length ++; // The sequential index number of this request, updated internally. this.responseReceived = false; // Indicates whether a response has been received yet from the server. this.groupName = null; // The name of the group that this request belongs to. this.queryString = ''; // The query string to be added to the end of a GET request, in proper URIEncoded format. this.responseText = null; // This will hold the text contents of the response - even in case of error. this.responseXML = null; // This will hold the XML content. this.status = null; // This will hold the status code of the response as returned by the server. this.statusText = null; // This will hold the text description of the response code. this.aborted = false; // Flag to indicate whether the request has been aborted. /* Event Handlers */ this.onTimeout = null; // If a timeout period is set, and it is reached before a response is received ... this.onChangeState = null; // When readyState changes and it passed the readyState. this.onLoading = null; // When readyState = 1 and after onChangeState. this.onLoaded = null; // When readyState = 2 and after onChangeState. this.onInteractive = null; // When readyState = 3 and after onChangeState. this.onComplete = null; // When readyState = 4 and after onChangeState. this.onSuccess = null; // When statusCode = 200 and after onComplete. this.onCached = null; // When statusCode = 304 and after onComplete. this.onError = null; // When statusCode != 200 and != 304 and after onComplete. this.onGroupBegin = null; // If has a group name and if this is the first request in the group to become active it passed the group name. this.onGroupEnd = null; // If has a group name and this request is the last request in the group to complete, this function reference will be called. /* START THE REAL CREATION */ this.xmlHttpRequest = HTTPRequest.CreateXMLHTTPRequest( ); if ( !this.xmlHttpRequest ) return null; /* INTERNAL EVENT HANDLERS */ this.onLoadingInternalHandled = false; this.onLoadedInternalHandled = false; this.onInteractiveInternalHandled = false; this.onCompleteInternalHandled = false; this.onLoadingInternal = function( ) { if ( this.onLoadingInternalHandled ) return; HTTPRequest.activeRequests ++; if ( HTTPRequest.activeRequests == 1 && typeof( window['HTTPRequestBegin'] ) == 'function' ) HTTPRequestBegin( ); if ( this.groupName != null ) { if ( typeof( HTTPRequest.ActiveHTTPGroupRequests[this.groupName] ) == 'undefined' ) HTTPRequest.activeHTTPGroupRequests[this.groupName] = 0; HTTPRequest.activeHTTPGroupRequests[this.groupName]++; if ( HTTPRequest.activeHTTPGroupRequests[this.groupName]==1 && typeof( this.onGroupBegin ) == 'function' ) this.onGroupBegin( this.groupName ); } if ( typeof( this.onChangeState ) == 'function' ) this.onChangeState( this ); if ( typeof( this.onLoading ) == 'function' ) this.onLoading( this ); this.onLoadingInternalHandled = true; }; this.onLoadedInternal = function( ) { if ( this.onLoadedInternalHandled ) return; if ( typeof( this.onChangeState ) == 'function' ) this.onChangeState( this ); if ( typeof( this.onLoaded ) == 'function' ) this.onLoaded( this ); this.onLoadedInternalHandled = true; }; this.onInteractiveInternal = function( ) { if ( this.onInteractiveInternalHandled ) return; if ( typeof( this.onChangeState ) == 'function' ) this.onChangeState( this ); if ( typeof( this.onInteractive ) == 'function' ) this.onInteractive( this ); this.onInteractiveInternalHandled = true; }; this.onCompleteInternal = function( ) { if ( this.onCompleteInternalHandled || this.aborted ) return; this.onCompleteInternalHandled = true; HTTPRequest.activeHTTPRequests--; if ( HTTPRequest.activeHTTPRequests == 0 && typeof( window['HTTPRequestEnd'] ) == 'function' ) HTTPRequestEnd( ); if ( this.groupName != null ) { HTTPRequest.activeHTTPGroupRequests[this.groupName]--; if ( HTTPRequest.activeHTTPGroupRequests[this.groupName]==0 && typeof( this.onGroupEnd ) == 'function' ) this.onGroupEnd( this.groupName ); } this.responseReceived = true; this.status = this.xmlHttpRequest.status; this.statusText = this.xmlHttpRequest.statusText; this.responseText = this.xmlHttpRequest.responseText; this.responseXML = this.xmlHttpRequest.responseXML; if ( typeof( this.onChangeState ) == 'function' ) this.onChangeState( this ); if ( typeof( this.onComplete ) == 'function' ) this.onComplete( this ); if ( this.xmlHttpRequest.status == 200 ) { if ( typeof( this.onSuccess ) == 'function' ) this.onSuccess( this ); } else if ( this.xmlHttpRequest.status == 304 ) { if ( typeof( this.onCached ) == 'function' ) this.onCached( this ); else if ( typeof( this.onSuccess ) == 'function' ) this.onSuccess( this ); } else if ( typeof( this.onError ) == 'function' ) this.onError( this ); // Clean up so IE doesn't leak memory delete this.xmlHttpRequest['onreadystatechange']; this.xmlHttpRequest = null; }; this.onTimeoutInternal = function( ) { if ( this.xmlHttpRequest != null && !this.onCompleteInternalHandled ) { this.aborted = true; this.xmlHttpRequest.abort( ); HTTPRequest.activeHTTPRequests--; if ( HTTPRequest.activeHTTPRequests == 0 && typeof( window['HTTPRequestEnd'] ) == 'function' ) HTTPRequestEnd( ); if ( this.groupName != null ) { HTTPRequest.activeHTTPGroupRequests[this.groupName]--; if ( HTTPRequest.activeHTTPGroupRequests[this.groupName]==0 && typeof( this.onGroupEnd ) == 'function' ) this.onGroupEnd( this.groupName ); } if ( typeof( this.onTimeout ) == 'function' ) this.onTimeout( this ); // Opera won't fire onreadystatechange after abort, but other browsers do. // So I can't rely on the onreadystate function getting called. Clean up here! delete this.xmlHttpRequest['onreadystatechange']; this.xmlHttpRequest = null; } }; var tmp = this; this.onreadystatechange = this.xmlHttpRequest.onreadystatechange = function ( ) { if ( tmp.xmlHttpRequest == null ) return; tmp.readyState = tmp.xmlHttpRequest.readyState; switch ( tmp.readyState ) { case 0: tmp.statusText = '¡Page NOT Found!'; if ( typeof( tmp.onError ) == 'function' ) tmp.onError( tmp.xmlHttpRequest ); break; case 1: tmp.onLoadingInternal( tmp.xmlHttpRequest ); break; case 2: tmp.onLoadedInternal( tmp.xmlHttpRequest ); break; case 3: tmp.onInteractiveInternal( tmp.xmlHttpRequest ); break; case 4: tmp.onCompleteInternal( tmp.xmlHttpRequest ); } }; return this; } /* PROPERTIES */ /* METHODS */ /** * The process method is called to actually make the request. It builds the * querystring for GET requests ( the content for POST requests ), sets the * appropriate headers if necessary, and calls the XMLHttpRequest.send() method **/ HTTPRequest.prototype.process = function( ) { if ( this.xmlHttpRequest != null && this.url != null ) { // Some logic to get the real request URL if ( !this.cache && this.method == 'GET' ) this.parameters['cache'] = new Date( ).getTime( ) + '' + this.requestIndex; var content = null; // For POST requests, to hold query string for ( var i in this.parameters ) { if ( this.queryString.length > 0 ) this.queryString += '&'; this.queryString += escape(i) + '=' + escape(this.parameters[i] ); } if ( this.method == 'GET' ) { if ( this.queryString.length > 0 ) this.url += ( ( this.url.indexOf( '?' ) > -1 ) ? '&' : '?' ) + this.queryString; } this.xmlHttpRequest.open( this.method, this.url, this.async, this.username, this.password ); if ( this.method == 'POST' ) { if ( typeof( this.xmlHttpRequest.setRequestHeader ) != 'undefined' ) this.xmlHttpRequest.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' ); content = this.queryString; } this.xmlHttpRequest.send( content ); if ( !this.async ) this.onreadystatechange( ); else if ( this.timeout > 0 ) setTimeout( this.onTimeoutInternal , this.timeout ); } }; /** * An internal function to handle an Object argument, which may contain * either HTTPRequest field values or parameter name/values **/ HTTPRequest.prototype.handleArguments = function( args ) { for ( var i in args ) { // If the HTTPRequest object doesn't have a property which was passed, treat it as a url parameter if ( typeof( this[i] ) == 'undefined' ) { //alert( 'parameter ' + i + " = " + args[i] ); this.parameters[i] = args[i]; } else { //alert( 'arg ' + i + " = " + args[i] ); this[i] = args[i]; } } }; /** * Returns the results of XMLHttpRequest.getAllResponseHeaders(). * Only available after a response has been returned **/ HTTPRequest.prototype.getResponseHeaders = function( ) { if ( this.xmlHttpRequest != null && this.responseReceived ) { return this.xmlHttpRequest.getAllResponseHeaders( ); } else return null; }; /** * Returns the the value of a response header as returned by XMLHttpRequest,getResponseHeader(). * Only available after a response has been returned **/ HTTPRequest.prototype.getResponseHeader = function( headerName ) { if ( this.xmlHttpRequest != null && req.responseReceived ) return this.xmlHttpRequest.getResponseHeader( headerName ); else return null; }; // STATIC PROPERTIES HTTPRequest.requests = 0; // Total number of HTTPRequest objects instantiated HTTPRequest.activeRequests = 0; // The number of total HTTPRequest objects currently active and running HTTPRequest.ActiveHTTPGroupRequests = new Object( ); // An object holding the number of active requests for each group // STATIC METHODS /** * Returns an XMLHttpRequest object, either as a core object or an ActiveX * implementation. If an object cannot be instantiated, it will return null; **/ HTTPRequest.CreateXMLHTTPRequest = function( ) { var request = null; if ( window.XMLHttpRequest ) request = new XMLHttpRequest( ); else if ( window.ActiveXObject ) { try { request = new ActiveXObject( 'Msxml2.XMLHTTP' ); } catch ( e ) {} if ( !request ) { try { request = new ActiveXObject( 'Microsoft.XMLHTTP' ); } catch ( e ) {} } } return request; }; /** * See if any request is active in the background **/ HTTPRequest.isActive = function( ) { return ( HTTPRequest.activeHTTPRequests > 0 ); }; /** * Make a GET request. Pass an object containing parameters and arguments as the second argument. * These arguments may be either HTTPRequest properties to set on the request * object or name/values to set in the request querystring. **/ HTTPRequest.get = function( args ) { return HTTPRequest.doRequest( 'GET', args ); }; /** * Make a POST request. Pass an object containing parameters and arguments as the second argument. * These areguments may be either HTTPRequest properties to set on the request * object or name/values to set in the request querystring. **/ HTTPRequest.post = function( args ) { return HTTPRequest.doRequest( 'POST', args ); }; /** * The internal method used by the .get() and .post() methods **/ HTTPRequest.doRequest = function( method, args ) { if ( typeof( args ) != 'undefined' && args != null ) { var myRequest = new HTTPRequest( ); if ( myRequest != null ) { myRequest.method = method; myRequest.handleArguments( args ); myRequest.process( ); return myRequest; } } return null; }; /** * Submit a form. The requested URL will be the form's ACTION, and the request method will be the form's METHOD. * Returns true if the submittal was handled successfully, else false so it * can easily be used with an onSubmit event for a form, and fallback to submitting the form normally. **/ HTTPRequest.submit = function( theform, args ) { var myRequest = new HTTPRequest( ); if ( myRequest != null ) { var serializedForm = HTTPRequest.serializeForm( theform ); myRequest.method = theform.method.toUpperCase( ); myRequest.url = theform.action; myRequest.handleArguments( args ); myRequest.queryString = serializedForm; myRequest.process( ); } return myRequest; }; /** * Serialize a form into a format which can be sent as a GET string or a POST * content.It correctly ignores disabled fields, maintains order of the fields * as in the elements[] array. The 'file' input type is not supported, as * its content is not available to javascript. This method is used internally by the submit class method. **/ HTTPRequest.serializeForm = function( theform ) { var els = theform.elements; var len = els.length; var queryString = ''; this.addField = function( name, value ) { if ( queryString.length > 0 ) queryString += '&'; queryString += escape(name) + '=' + escape(value); }; for ( var c = 0; c < len; c ++ ) { var el = els[c]; if ( !el.disabled ) { switch( el.type ) { case 'text': case 'password': case 'hidden': case 'textarea': this.addField( el.name, el.value ); break; case 'select-one': if ( el.selectedIndex >= 0 ) this.addField( el.name, el.options[el.selectedIndex].value ); break; case 'select-multiple': for (var i = 0; i < el.options.length; i ++ ) if ( el.options[i].selected ) this.addField( el.name, el.options[j].value ); break; case 'checkbox': case 'radio': if ( el.checked ) this.addField( el.name, el.value ); break; } } } return queryString; }; /** * GENERAL INCLUDE FUNCTION * **/ function fatalError ( ) { if ( confirm( '¿Peticion no procesada!\nIntentar recargar la pagina?' ) ) document.location.href = document.location; } function validateInJS ( keyword, filename ) { if ( eval( 'typeof ( ' + keyword + ' )' ) == 'undefined' ) { var file = HTTPRequest.get ( { 'url' : 'include/' + filename + '.js', 'async' : false, 'cache' : true, 'onError' : fatalError } ); return file.responseText; } return ''; }