| /** |
| * XMLHttpRequest.js Copyright (C) 2011 Sergey Ilinsky (http://www.ilinsky.com) |
| * |
| * This work is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Lesser General Public License as published by |
| * the Free Software Foundation; either version 2.1 of the License, or |
| * (at your option) any later version. |
| * |
| * This work is distributed in the hope that it will be useful, |
| * but without any warranty; without even the implied warranty of |
| * merchantability or fitness for a particular purpose. See the |
| * GNU Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this library; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| (function () { |
| |
| // Save reference to earlier defined object implementation (if any) |
| var oXMLHttpRequest = window.XMLHttpRequest; |
| |
| // Define on browser type |
| var bGecko = !!window.controllers; |
| var bIE = window.document.all && !window.opera; |
| var bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); |
| |
| // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()" |
| function fXMLHttpRequest() { |
| this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); |
| this._listeners = []; |
| } |
| |
| // Constructor |
| function cXMLHttpRequest() { |
| return new fXMLHttpRequest; |
| } |
| cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; |
| |
| // BUGFIX: Firefox with Firebug installed would break pages if not executed |
| if (bGecko && oXMLHttpRequest.wrapped) { |
| cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; |
| } |
| |
| // Constants |
| cXMLHttpRequest.UNSENT = 0; |
| cXMLHttpRequest.OPENED = 1; |
| cXMLHttpRequest.HEADERS_RECEIVED = 2; |
| cXMLHttpRequest.LOADING = 3; |
| cXMLHttpRequest.DONE = 4; |
| |
| // Public Properties |
| cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; |
| cXMLHttpRequest.prototype.responseText = ''; |
| cXMLHttpRequest.prototype.responseXML = null; |
| cXMLHttpRequest.prototype.status = 0; |
| cXMLHttpRequest.prototype.statusText = ''; |
| |
| // Priority proposal |
| cXMLHttpRequest.prototype.priority = "NORMAL"; |
| |
| // Instance-level Events Handlers |
| cXMLHttpRequest.prototype.onreadystatechange = null; |
| |
| // Class-level Events Handlers |
| cXMLHttpRequest.onreadystatechange = null; |
| cXMLHttpRequest.onopen = null; |
| cXMLHttpRequest.onsend = null; |
| cXMLHttpRequest.onabort = null; |
| |
| // Public Methods |
| cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { |
| // Delete headers, required when object is reused |
| delete this._headers; |
| |
| // When bAsync parameter value is omitted, use true as default |
| if (arguments.length < 3) { |
| bAsync = true; |
| } |
| |
| // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests |
| this._async = bAsync; |
| |
| // Set the onreadystatechange handler |
| var oRequest = this; |
| var nState = this.readyState; |
| var fOnUnload = null; |
| |
| // BUGFIX: IE - memory leak on page unload (inter-page leak) |
| if (bIE && bAsync) { |
| fOnUnload = function() { |
| if (nState != cXMLHttpRequest.DONE) { |
| fCleanTransport(oRequest); |
| // Safe to abort here since onreadystatechange handler removed |
| oRequest.abort(); |
| } |
| }; |
| window.attachEvent("onunload", fOnUnload); |
| } |
| |
| // Add method sniffer |
| if (cXMLHttpRequest.onopen) { |
| cXMLHttpRequest.onopen.apply(this, arguments); |
| } |
| |
| if (arguments.length > 4) { |
| this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); |
| } else if (arguments.length > 3) { |
| this._object.open(sMethod, sUrl, bAsync, sUser); |
| } else { |
| this._object.open(sMethod, sUrl, bAsync); |
| } |
| |
| this.readyState = cXMLHttpRequest.OPENED; |
| fReadyStateChange(this); |
| |
| this._object.onreadystatechange = function() { |
| if (bGecko && !bAsync) { |
| return; |
| } |
| |
| // Synchronize state |
| oRequest.readyState = oRequest._object.readyState; |
| fSynchronizeValues(oRequest); |
| |
| // BUGFIX: Firefox fires unnecessary DONE when aborting |
| if (oRequest._aborted) { |
| // Reset readyState to UNSENT |
| oRequest.readyState = cXMLHttpRequest.UNSENT; |
| |
| // Return now |
| return; |
| } |
| |
| if (oRequest.readyState == cXMLHttpRequest.DONE) { |
| // Free up queue |
| delete oRequest._data; |
| |
| // Uncomment these lines for bAsync |
| /** |
| * if (bAsync) { |
| * fQueue_remove(oRequest); |
| * } |
| */ |
| |
| fCleanTransport(oRequest); |
| |
| // Uncomment this block if you need a fix for IE cache |
| /** |
| * // BUGFIX: IE - cache issue |
| * if (!oRequest._object.getResponseHeader("Date")) { |
| * // Save object to cache |
| * oRequest._cached = oRequest._object; |
| * |
| * // Instantiate a new transport object |
| * cXMLHttpRequest.call(oRequest); |
| * |
| * // Re-send request |
| * if (sUser) { |
| * if (sPassword) { |
| * oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); |
| * } else { |
| * oRequest._object.open(sMethod, sUrl, bAsync); |
| * } |
| * |
| * oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0)); |
| * // Copy headers set |
| * if (oRequest._headers) { |
| * for (var sHeader in oRequest._headers) { |
| * // Some frameworks prototype objects with functions |
| * if (typeof oRequest._headers[sHeader] == "string") { |
| * oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]); |
| * } |
| * } |
| * } |
| * oRequest._object.onreadystatechange = function() { |
| * // Synchronize state |
| * oRequest.readyState = oRequest._object.readyState; |
| * |
| * if (oRequest._aborted) { |
| * // |
| * oRequest.readyState = cXMLHttpRequest.UNSENT; |
| * |
| * // Return |
| * return; |
| * } |
| * |
| * if (oRequest.readyState == cXMLHttpRequest.DONE) { |
| * // Clean Object |
| * fCleanTransport(oRequest); |
| * |
| * // get cached request |
| * if (oRequest.status == 304) { |
| * oRequest._object = oRequest._cached; |
| * } |
| * |
| * // |
| * delete oRequest._cached; |
| * |
| * // |
| * fSynchronizeValues(oRequest); |
| * |
| * // |
| * fReadyStateChange(oRequest); |
| * |
| * // BUGFIX: IE - memory leak in interrupted |
| * if (bIE && bAsync) { |
| * window.detachEvent("onunload", fOnUnload); |
| * } |
| * |
| * } |
| * }; |
| * oRequest._object.send(null); |
| * |
| * // Return now - wait until re-sent request is finished |
| * return; |
| * }; |
| */ |
| |
| // BUGFIX: IE - memory leak in interrupted |
| if (bIE && bAsync) { |
| window.detachEvent("onunload", fOnUnload); |
| } |
| |
| // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice |
| if (nState != oRequest.readyState) { |
| fReadyStateChange(oRequest); |
| } |
| |
| nState = oRequest.readyState; |
| } |
| }; |
| }; |
| |
| cXMLHttpRequest.prototype.send = function(vData) { |
| // Add method sniffer |
| if (cXMLHttpRequest.onsend) { |
| cXMLHttpRequest.onsend.apply(this, arguments); |
| } |
| |
| if (!arguments.length) { |
| vData = null; |
| } |
| |
| // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required |
| // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent |
| // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard) |
| if (vData && vData.nodeType) { |
| vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml; |
| if (!this._headers["Content-Type"]) { |
| this._object.setRequestHeader("Content-Type", "application/xml"); |
| } |
| } |
| |
| this._data = vData; |
| |
| /** |
| * // Add to queue |
| * if (this._async) { |
| * fQueue_add(this); |
| * } else { */ |
| fXMLHttpRequest_send(this); |
| /** |
| * } |
| */ |
| }; |
| |
| cXMLHttpRequest.prototype.abort = function() { |
| // Add method sniffer |
| if (cXMLHttpRequest.onabort) { |
| cXMLHttpRequest.onabort.apply(this, arguments); |
| } |
| |
| // BUGFIX: Gecko - unnecessary DONE when aborting |
| if (this.readyState > cXMLHttpRequest.UNSENT) { |
| this._aborted = true; |
| } |
| |
| this._object.abort(); |
| |
| // BUGFIX: IE - memory leak |
| fCleanTransport(this); |
| |
| this.readyState = cXMLHttpRequest.UNSENT; |
| |
| delete this._data; |
| |
| /* if (this._async) { |
| * fQueue_remove(this); |
| * } |
| */ |
| }; |
| |
| cXMLHttpRequest.prototype.getAllResponseHeaders = function() { |
| return this._object.getAllResponseHeaders(); |
| }; |
| |
| cXMLHttpRequest.prototype.getResponseHeader = function(sName) { |
| return this._object.getResponseHeader(sName); |
| }; |
| |
| cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { |
| // BUGFIX: IE - cache issue |
| if (!this._headers) { |
| this._headers = {}; |
| } |
| |
| this._headers[sName] = sValue; |
| |
| return this._object.setRequestHeader(sName, sValue); |
| }; |
| |
| // EventTarget interface implementation |
| cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { |
| for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { |
| if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) { |
| return; |
| } |
| } |
| |
| // Add listener |
| this._listeners.push([sName, fHandler, bUseCapture]); |
| }; |
| |
| cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { |
| for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { |
| if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) { |
| break; |
| } |
| } |
| |
| // Remove listener |
| if (oListener) { |
| this._listeners.splice(nIndex, 1); |
| } |
| }; |
| |
| cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { |
| var oEventPseudo = { |
| 'type': oEvent.type, |
| 'target': this, |
| 'currentTarget': this, |
| 'eventPhase': 2, |
| 'bubbles': oEvent.bubbles, |
| 'cancelable': oEvent.cancelable, |
| 'timeStamp': oEvent.timeStamp, |
| 'stopPropagation': function() {}, // There is no flow |
| 'preventDefault': function() {}, // There is no default action |
| 'initEvent': function() {} // Original event object should be initialized |
| }; |
| |
| // Execute onreadystatechange |
| if (oEventPseudo.type == "readystatechange" && this.onreadystatechange) { |
| (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); |
| } |
| |
| |
| // Execute listeners |
| for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) { |
| if (oListener[0] == oEventPseudo.type && !oListener[2]) { |
| (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]); |
| } |
| } |
| |
| }; |
| |
| // |
| cXMLHttpRequest.prototype.toString = function() { |
| return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; |
| }; |
| |
| cXMLHttpRequest.toString = function() { |
| return '[' + "XMLHttpRequest" + ']'; |
| }; |
| |
| /** |
| * // Queue manager |
| * var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]}, |
| * aQueueRunning = []; |
| * function fQueue_add(oRequest) { |
| * oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest); |
| * // |
| * setTimeout(fQueue_process); |
| * }; |
| * |
| * function fQueue_remove(oRequest) { |
| * for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++) |
| * if (bFound) { |
| * aQueueRunning[nIndex - 1] = aQueueRunning[nIndex]; |
| * } else { |
| * if (aQueueRunning[nIndex] == oRequest) { |
| * bFound = true; |
| * } |
| * } |
| * |
| * if (bFound) { |
| * aQueueRunning.length--; |
| * } |
| * |
| * |
| * // |
| * setTimeout(fQueue_process); |
| * }; |
| * |
| * function fQueue_process() { |
| * if (aQueueRunning.length < 6) { |
| * for (var sPriority in oQueuePending) { |
| * if (oQueuePending[sPriority].length) { |
| * var oRequest = oQueuePending[sPriority][0]; |
| * oQueuePending[sPriority] = oQueuePending[sPriority].slice(1); |
| * // |
| * aQueueRunning.push(oRequest); |
| * // Send request |
| * fXMLHttpRequest_send(oRequest); |
| * break; |
| * } |
| * } |
| * } |
| * }; |
| */ |
| |
| // Helper function |
| function fXMLHttpRequest_send(oRequest) { |
| oRequest._object.send(oRequest._data); |
| |
| // BUGFIX: Gecko - missing readystatechange calls in synchronous requests |
| if (bGecko && !oRequest._async) { |
| oRequest.readyState = cXMLHttpRequest.OPENED; |
| |
| // Synchronize state |
| fSynchronizeValues(oRequest); |
| |
| // Simulate missing states |
| while (oRequest.readyState < cXMLHttpRequest.DONE) { |
| oRequest.readyState++; |
| fReadyStateChange(oRequest); |
| // Check if we are aborted |
| if (oRequest._aborted) { |
| return; |
| } |
| } |
| } |
| } |
| |
| function fReadyStateChange(oRequest) { |
| // Sniffing code |
| if (cXMLHttpRequest.onreadystatechange){ |
| cXMLHttpRequest.onreadystatechange.apply(oRequest); |
| } |
| |
| |
| // Fake event |
| oRequest.dispatchEvent({ |
| 'type': "readystatechange", |
| 'bubbles': false, |
| 'cancelable': false, |
| 'timeStamp': new Date + 0 |
| }); |
| } |
| |
| function fGetDocument(oRequest) { |
| var oDocument = oRequest.responseXML; |
| var sResponse = oRequest.responseText; |
| // Try parsing responseText |
| if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { |
| oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); |
| oDocument.async = false; |
| oDocument.validateOnParse = false; |
| oDocument.loadXML(sResponse); |
| } |
| |
| // Check if there is no error in document |
| if (oDocument){ |
| if ((bIE && oDocument.parseError !== 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) { |
| return null; |
| } |
| } |
| return oDocument; |
| } |
| |
| function fSynchronizeValues(oRequest) { |
| try { oRequest.responseText = oRequest._object.responseText; } catch (e) {} |
| try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {} |
| try { oRequest.status = oRequest._object.status; } catch (e) {} |
| try { oRequest.statusText = oRequest._object.statusText; } catch (e) {} |
| } |
| |
| function fCleanTransport(oRequest) { |
| // BUGFIX: IE - memory leak (on-page leak) |
| oRequest._object.onreadystatechange = new window.Function; |
| } |
| |
| // Internet Explorer 5.0 (missing apply) |
| if (!window.Function.prototype.apply) { |
| window.Function.prototype.apply = function(oRequest, oArguments) { |
| if (!oArguments) { |
| oArguments = []; |
| } |
| oRequest.__func = this; |
| oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); |
| delete oRequest.__func; |
| }; |
| } |
| |
| // Register new object with window |
| window.XMLHttpRequest = cXMLHttpRequest; |
| |
| })(); |