|
// 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. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
/** |
|
* @requires OpenLayers/Request.js |
|
*/ |
|
|
|
(function () { |
|
|
|
// Save reference to earlier defined object implementation (if any) |
|
var oXMLHttpRequest = window.XMLHttpRequest; |
|
|
|
// Define on browser type |
|
var bGecko = !!window.controllers, |
|
bIE = window.document.all && !window.opera, |
|
bIE7 = bIE && window.navigator.userAgent.match(/MSIE ([\.0-9]+)/) && RegExp.$1 == 7; |
|
|
|
// Constructor |
|
function cXMLHttpRequest() { |
|
this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); |
|
this._listeners = []; |
|
}; |
|
|
|
// 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 = ''; |
|
|
|
// 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, |
|
nState = this.readyState, |
|
fOnUnload; |
|
|
|
// 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); |
|
|
|
if (!bGecko && !bIE) { |
|
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) { |
|
// |
|
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, 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) |
|
for (var sHeader in oRequest._headers) |
|
if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions |
|
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); |
|
|
|
// 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._object.send(vData); |
|
|
|
// BUGFIX: Gecko - missing readystatechange calls in synchronous requests |
|
if (bGecko && !this._async) { |
|
this.readyState = cXMLHttpRequest.OPENED; |
|
|
|
// Synchronize state |
|
fSynchronizeValues(this); |
|
|
|
// Simulate missing states |
|
while (this.readyState < cXMLHttpRequest.DONE) { |
|
this.readyState++; |
|
fReadyStateChange(this); |
|
// Check if we are aborted |
|
if (this._aborted) |
|
return; |
|
} |
|
} |
|
}; |
|
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); |
|
}; |
|
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" + ']'; |
|
}; |
|
|
|
// Helper function |
|
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, |
|
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 = oR |