diff --git a/lib/OpenLayers/Request/XMLHttpRequest.js b/lib/OpenLayers/Request/XMLHttpRequest.js index f9b9899bfa..9bf714f3a3 100644 --- a/lib/OpenLayers/Request/XMLHttpRequest.js +++ b/lib/OpenLayers/Request/XMLHttpRequest.js @@ -1,4 +1,4 @@ -// Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com) +// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,11 +23,13 @@ // Define on browser type var bGecko = !!window.controllers, - bIE = window.document.all && !window.opera; + bIE = window.document.all && !window.opera, + bIE7 = bIE && window.navigator.userAgent.match(/MSIE ([\.0-9]+)/) && RegExp.$1 == 7; // Constructor function cXMLHttpRequest() { - this._object = oXMLHttpRequest ? new oXMLHttpRequest : new window.ActiveXObject('Microsoft.XMLHTTP'); + this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); + this._listeners = []; }; // BUGFIX: Firefox with Firebug installed would break pages if not executed @@ -43,10 +45,10 @@ // Public Properties cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; - cXMLHttpRequest.prototype.responseText = ""; + cXMLHttpRequest.prototype.responseText = ''; cXMLHttpRequest.prototype.responseXML = null; cXMLHttpRequest.prototype.status = 0; - cXMLHttpRequest.prototype.statusText = ""; + cXMLHttpRequest.prototype.statusText = ''; // Instance-level Events Handlers cXMLHttpRequest.prototype.onreadystatechange = null; @@ -59,24 +61,50 @@ // 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, - nState = this.readyState; + nState = this.readyState, + fOnUnload; // BUGFIX: IE - memory leak on page unload (inter-page leak) - if (bIE) { - var fOnUnload = function() { - if (oRequest._object.readyState != cXMLHttpRequest.DONE) + if (bIE && bAsync) { + fOnUnload = function() { + if (nState != cXMLHttpRequest.DONE) { fCleanTransport(oRequest); + // Safe to abort here since onreadystatechange handler removed + oRequest.abort(); + } }; - if (bAsync) 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); + + if (!bGecko && !bIE) { + this.readyState = cXMLHttpRequest.OPENED; + fReadyStateChange(this); + } + this._object.onreadystatechange = function() { if (bGecko && !bAsync) return; @@ -87,7 +115,7 @@ // fSynchronizeValues(oRequest); - // BUGFIX: Firefox fires unneccesary DONE when aborting + // BUGFIX: Firefox fires unnecessary DONE when aborting if (oRequest._aborted) { // Reset readyState to UNSENT oRequest.readyState = cXMLHttpRequest.UNSENT; @@ -110,7 +138,14 @@ 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, sUser); + } + 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) @@ -154,7 +189,7 @@ }; oRequest._object.send(null); - // Return now - wait untill re-sent request is finished + // Return now - wait until re-sent request is finished return; }; */ @@ -168,19 +203,6 @@ fReadyStateChange(oRequest); nState = oRequest.readyState; - }; - - // Add method sniffer - if (cXMLHttpRequest.onopen) - cXMLHttpRequest.onopen.apply(this, arguments); - - this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); - - // BUGFIX: Gecko - missing readystatechange calls in synchronous requests - if (!bAsync && bGecko) { - this.readyState = cXMLHttpRequest.OPENED; - - fReadyStateChange(this); } }; cXMLHttpRequest.prototype.send = function(vData) { @@ -221,7 +243,7 @@ if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments); - // BUGFIX: Gecko - unneccesary DONE when aborting + // BUGFIX: Gecko - unnecessary DONE when aborting if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true; @@ -244,34 +266,86 @@ 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" + ']'; }; // Helper function function fReadyStateChange(oRequest) { - // Execute onreadystatechange - if (oRequest.onreadystatechange) - oRequest.onreadystatechange.apply(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 oDocument = oRequest.responseXML, + sResponse = oRequest.responseText; // Try parsing responseText - if (bIE && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { - oDocument = new ActiveXObject('Microsoft.XMLDOM'); - oDocument.loadXML(oRequest.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.tagName == "parsererror")) + if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) return null; return oDocument; }; @@ -286,9 +360,6 @@ function fCleanTransport(oRequest) { // BUGFIX: IE - memory leak (on-page leak) oRequest._object.onreadystatechange = new window.Function; - - // Delete private properties - delete oRequest._headers; }; // Internet Explorer 5.0 (missing apply)