SilverAjax: A(nother) Cross-Browser XMLHttpRequest Wrapper
About
No, I'm not living in a cave, I know they are plenty of XMLHttpRequest wrappers out there already. No, I am not trying to re-invent the wheel! I decided to put one together mostly as an exercise in JavaScript. Truth is, I've been working with MooTools for so long that I barely ever get to do any "real JavaScript", that is, without the help of a framework.
I decided it was time to do something in Javascript without the help of Mootools or Prototype or <insert your favorite JS framework here>… and, having never really played directly with the XMLHttpRequest object, I decided it would be an interesting project.
It turned out to be much easier than I expected (the XMLHttpRequest object that is, Javascript is still, and will always be, a bit of a mystery for me; Prototypal Inheritance, Cross-Browser issues etc..)… So anyway, here is a first draft of what I came up with, the documentation etc. etc.. will follow soon enough…in the meantime, there are some comments…
Brief Documentation
See the separate article: SilverAjax Documentation
The code
You can download it here: http://silverscripting.wikidot.com/local--files/silverajax/silverajax.js
This is the full source (with sweet syntax-highlighting)
// Silverajax by Jean-Nicolas Jolivet // A simple cross-browser XHMLHttpRequest wrapper. // Licensed under the MIT License // IE 4 & 5 don't implement Function.apply( ). // This workaround is based on code by Aaron Boodman. if (!Function.prototype.apply) { // Invoke this function as a method of the specified object, // passing the specified parameters. We have to use eval( ) to do this Function.prototype.apply = function (object, parameters) { var f = this; // The function to invoke var o = object || window; // The object to invoke it on var args = parameters || []; // The arguments to pass o._$_apply_$_ = f; var stringArgs = []; for(var i = 0; i < args.length; i++) stringArgs[i] = "args[" + i + "]"; var arglist = stringArgs.join(","); var methodcall = "o._$_apply_$_(" + arglist + ");"; var result = eval(methodcall); delete o._$_apply_$_; return result; }; } // Silverajax constructor // Arguments: // url: Where to send that request // options: a bunch of useful options var Silverajax = function (url, options) { // define the default options this.options = { timeout: 10000, // The request timeout in milliseconds expectJson: false, // Wheter or not to eval the json response expectJavascript: false, // Whether or not to eval the response method: 'GET', // The request method ('POST' or 'GET') update: '', // An element to update with the responseText encoding: 'utf-8', // The encoding to use for POST requests headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' } }; this.url = url; // Browser detection if (window.XMLHttpRequest) { this.request = new XMLHttpRequest(); // nice, a decent browser } else if (window.ActiveXObject) { // Some version of IE... var ua = navigator.userAgent.toLowerCase(); if (ua.indexOf('msie 5') === -1) { this.request = new ActiveXObject("Msxml2.XMLHTTP"); } else { this.request = new ActiveXObject("Microsoft.XMLHTTP"); } } this.options = this.mergeObjects(this.options, options); // Make sure the method is eighter POST or GET, else, default to GET this.options.method = (this.options.method === 'POST' || this.options.method === 'GET') ? this.options.method : 'GET'; }; // Method: initOptions // Merges this.options with the options passed in the arguments Silverajax.prototype.mergeObjects = function (orig, ext) { var name; // I searched for a better way to do this, (i.e. merge objects) // couldn't find anything so I decided to do it that way... it // might not be the best way but it works well... for (name in ext || {}) { if (typeof name !== "function") { orig[name] = ext[name]; } } return orig; }; // Method: handleRequest // The callback for onreadystatechange Silverajax.prototype.handleRequest = function () { if (this.request.readyState === 4) { // If the request is finished clearTimeout(this.requestTimeout); // Clear our timeout this.completed(); // Fire onComplete } }; // Method: requestTimedOut // Called when the request has timed-out Silverajax.prototype.requestTimedOut = function () { this.abort(); this.onTimeout(); }; // Method: onComplete // Called when the XMLHttpRequest is complete Silverajax.prototype.completed = function () { this.response = { status: this.request.status, statusText: this.request.statusText, responseText: this.request.responseText, responseXml: this.request.responseXML, responseJson: {} }; if (typeof this.onComplete === 'function') { this.onComplete(this.response);} if (this.request.status === 200) { if(this.options.update) { var el = typeof this.options.update === 'string' ? document.getElementById(this.options.update) : this.options.update; el.innerHTML = this.request.responseText; } if(this.options.expectJson) { this.response.responseJson = !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(this.request.responseText.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + this.request.responseText + ')'); } if(this.options.expectJavascript) { eval(this.request.responseText); } if (typeof this.onSuccess === 'function') {this.onSuccess(this.response);} } else { if (typeof this.onFailure === 'function') {this.onFailure(this.response);} } }; // Method: send // Will send the XMLHttpRequest Silverajax.prototype.send = function (sendData) { var that = this; var data = sendData || null; var headers = {}; // Add the callback for the onreadystatechange event this.request.onreadystatechange = function () { that.handleRequest.apply(that); }; // Initialize the request based on our options... this.url = this.options.method === "GET" && data ? this.url + "?" + data : this.url; this.request.open(this.options.method, this.url, true); if(this.options.method == 'POST' && data) { headers = { "Content-type": "application/x-www-form-urlencoded; charset=" + this.options.encoding, "Content-length": data.length, "Connection": "close" }; } // Merge the options'headers with the local headers object and set them headers = this.mergeObjects(this.options.headers, headers); this.setHeaders(headers); // Send the request // Set the request timeout based on the options this.requestTimeout = setTimeout(this.requestTimedOut, this.options.timeout); this.request.send(data); }; // Method: addHeader(key, value) // Adds a new header to the current headers Silverajax.prototype.addHeader = function (key, value) { this.options.headers = this.mergeObjects(this.options.headers, {key: value}); }; // Method: deleteHeader(key) // Deletes the header at [key] Silverajax.prototype.deleteHeader = function (key) { delete this.options.headers[key]; }; // Method: setHeaders // Used internally to set the request Headers.. Silverajax.prototype.setHeaders = function (headers) { var name; for(name in headers) { if(typeof headers[name] !== 'function') { this.request.setRequestHeader(name, headers[name]); } } }; // Method: abort // Aborts the current XMLHttpRequest Silverajax.prototype.abort = function () { this.request.abort(); };





