XMLHttpRequest.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @requires OpenLayers/Request.js
  16. */
  17. (function () {
  18. // Save reference to earlier defined object implementation (if any)
  19. var oXMLHttpRequest = window.XMLHttpRequest;
  20. // Define on browser type
  21. var bGecko = !!window.controllers,
  22. bIE = window.document.all && !window.opera,
  23. bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
  24. // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
  25. function fXMLHttpRequest() {
  26. this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
  27. this._listeners = [];
  28. };
  29. // Constructor
  30. function cXMLHttpRequest() {
  31. return new fXMLHttpRequest;
  32. };
  33. cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
  34. // BUGFIX: Firefox with Firebug installed would break pages if not executed
  35. if (bGecko && oXMLHttpRequest.wrapped)
  36. cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
  37. // Constants
  38. cXMLHttpRequest.UNSENT = 0;
  39. cXMLHttpRequest.OPENED = 1;
  40. cXMLHttpRequest.HEADERS_RECEIVED = 2;
  41. cXMLHttpRequest.LOADING = 3;
  42. cXMLHttpRequest.DONE = 4;
  43. // Public Properties
  44. cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
  45. cXMLHttpRequest.prototype.responseText = '';
  46. cXMLHttpRequest.prototype.responseXML = null;
  47. cXMLHttpRequest.prototype.status = 0;
  48. cXMLHttpRequest.prototype.statusText = '';
  49. // Priority proposal
  50. cXMLHttpRequest.prototype.priority = "NORMAL";
  51. // Instance-level Events Handlers
  52. cXMLHttpRequest.prototype.onreadystatechange = null;
  53. // Class-level Events Handlers
  54. cXMLHttpRequest.onreadystatechange = null;
  55. cXMLHttpRequest.onopen = null;
  56. cXMLHttpRequest.onsend = null;
  57. cXMLHttpRequest.onabort = null;
  58. // Public Methods
  59. cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) {
  60. // Delete headers, required when object is reused
  61. delete this._headers;
  62. // When bAsync parameter value is omitted, use true as default
  63. if (arguments.length < 3)
  64. bAsync = true;
  65. // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
  66. this._async = bAsync;
  67. // Set the onreadystatechange handler
  68. var oRequest = this,
  69. nState = this.readyState,
  70. fOnUnload;
  71. // BUGFIX: IE - memory leak on page unload (inter-page leak)
  72. if (bIE && bAsync) {
  73. fOnUnload = function() {
  74. if (nState != cXMLHttpRequest.DONE) {
  75. fCleanTransport(oRequest);
  76. // Safe to abort here since onreadystatechange handler removed
  77. oRequest.abort();
  78. }
  79. };
  80. window.attachEvent("onunload", fOnUnload);
  81. }
  82. // Add method sniffer
  83. if (cXMLHttpRequest.onopen)
  84. cXMLHttpRequest.onopen.apply(this, arguments);
  85. if (arguments.length > 4)
  86. this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
  87. else
  88. if (arguments.length > 3)
  89. this._object.open(sMethod, sUrl, bAsync, sUser);
  90. else
  91. this._object.open(sMethod, sUrl, bAsync);
  92. this.readyState = cXMLHttpRequest.OPENED;
  93. fReadyStateChange(this);
  94. this._object.onreadystatechange = function() {
  95. if (bGecko && !bAsync)
  96. return;
  97. // Synchronize state
  98. oRequest.readyState = oRequest._object.readyState;
  99. //
  100. fSynchronizeValues(oRequest);
  101. // BUGFIX: Firefox fires unnecessary DONE when aborting
  102. if (oRequest._aborted) {
  103. // Reset readyState to UNSENT
  104. oRequest.readyState = cXMLHttpRequest.UNSENT;
  105. // Return now
  106. return;
  107. }
  108. if (oRequest.readyState == cXMLHttpRequest.DONE) {
  109. // Free up queue
  110. delete oRequest._data;
  111. /* if (bAsync)
  112. fQueue_remove(oRequest);*/
  113. //
  114. fCleanTransport(oRequest);
  115. // Uncomment this block if you need a fix for IE cache
  116. /*
  117. // BUGFIX: IE - cache issue
  118. if (!oRequest._object.getResponseHeader("Date")) {
  119. // Save object to cache
  120. oRequest._cached = oRequest._object;
  121. // Instantiate a new transport object
  122. cXMLHttpRequest.call(oRequest);
  123. // Re-send request
  124. if (sUser) {
  125. if (sPassword)
  126. oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
  127. else
  128. oRequest._object.open(sMethod, sUrl, bAsync, sUser);
  129. }
  130. else
  131. oRequest._object.open(sMethod, sUrl, bAsync);
  132. oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
  133. // Copy headers set
  134. if (oRequest._headers)
  135. for (var sHeader in oRequest._headers)
  136. if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions
  137. oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
  138. oRequest._object.onreadystatechange = function() {
  139. // Synchronize state
  140. oRequest.readyState = oRequest._object.readyState;
  141. if (oRequest._aborted) {
  142. //
  143. oRequest.readyState = cXMLHttpRequest.UNSENT;
  144. // Return
  145. return;
  146. }
  147. if (oRequest.readyState == cXMLHttpRequest.DONE) {
  148. // Clean Object
  149. fCleanTransport(oRequest);
  150. // get cached request
  151. if (oRequest.status == 304)
  152. oRequest._object = oRequest._cached;
  153. //
  154. delete oRequest._cached;
  155. //
  156. fSynchronizeValues(oRequest);
  157. //
  158. fReadyStateChange(oRequest);
  159. // BUGFIX: IE - memory leak in interrupted
  160. if (bIE && bAsync)
  161. window.detachEvent("onunload", fOnUnload);
  162. }
  163. };
  164. oRequest._object.send(null);
  165. // Return now - wait until re-sent request is finished
  166. return;
  167. };
  168. */
  169. // BUGFIX: IE - memory leak in interrupted
  170. if (bIE && bAsync)
  171. window.detachEvent("onunload", fOnUnload);
  172. }
  173. // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
  174. if (nState != oRequest.readyState)
  175. fReadyStateChange(oRequest);
  176. nState = oRequest.readyState;
  177. }
  178. };
  179. function fXMLHttpRequest_send(oRequest) {
  180. oRequest._object.send(oRequest._data);
  181. // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
  182. if (bGecko && !oRequest._async) {
  183. oRequest.readyState = cXMLHttpRequest.OPENED;
  184. // Synchronize state
  185. fSynchronizeValues(oRequest);
  186. // Simulate missing states
  187. while (oRequest.readyState < cXMLHttpRequest.DONE) {
  188. oRequest.readyState++;
  189. fReadyStateChange(oRequest);
  190. // Check if we are aborted
  191. if (oRequest._aborted)
  192. return;
  193. }
  194. }
  195. };
  196. cXMLHttpRequest.prototype.send = function(vData) {
  197. // Add method sniffer
  198. if (cXMLHttpRequest.onsend)
  199. cXMLHttpRequest.onsend.apply(this, arguments);
  200. if (!arguments.length)
  201. vData = null;
  202. // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
  203. // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
  204. // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
  205. if (vData && vData.nodeType) {
  206. vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
  207. if (!oRequest._headers["Content-Type"])
  208. oRequest._object.setRequestHeader("Content-Type", "application/xml");
  209. }
  210. this._data = vData;
  211. /*
  212. // Add to queue
  213. if (this._async)
  214. fQueue_add(this);
  215. else*/
  216. fXMLHttpRequest_send(this);
  217. };
  218. cXMLHttpRequest.prototype.abort = function() {
  219. // Add method sniffer
  220. if (cXMLHttpRequest.onabort)
  221. cXMLHttpRequest.onabort.apply(this, arguments);
  222. // BUGFIX: Gecko - unnecessary DONE when aborting
  223. if (this.readyState > cXMLHttpRequest.UNSENT)
  224. this._aborted = true;
  225. this._object.abort();
  226. // BUGFIX: IE - memory leak
  227. fCleanTransport(this);
  228. this.readyState = cXMLHttpRequest.UNSENT;
  229. delete this._data;
  230. /* if (this._async)
  231. fQueue_remove(this);*/
  232. };
  233. cXMLHttpRequest.prototype.getAllResponseHeaders = function() {
  234. return this._object.getAllResponseHeaders();
  235. };
  236. cXMLHttpRequest.prototype.getResponseHeader = function(sName) {
  237. return this._object.getResponseHeader(sName);
  238. };
  239. cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) {
  240. // BUGFIX: IE - cache issue
  241. if (!this._headers)
  242. this._headers = {};
  243. this._headers[sName] = sValue;
  244. return this._object.setRequestHeader(sName, sValue);
  245. };
  246. // EventTarget interface implementation
  247. cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) {
  248. for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
  249. if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
  250. return;
  251. // Add listener
  252. this._listeners.push([sName, fHandler, bUseCapture]);
  253. };
  254. cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) {
  255. for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
  256. if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
  257. break;
  258. // Remove listener
  259. if (oListener)
  260. this._listeners.splice(nIndex, 1);
  261. };
  262. cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) {
  263. var oEventPseudo = {
  264. 'type': oEvent.type,
  265. 'target': this,
  266. 'currentTarget':this,
  267. 'eventPhase': 2,
  268. 'bubbles': oEvent.bubbles,
  269. 'cancelable': oEvent.cancelable,
  270. 'timeStamp': oEvent.timeStamp,
  271. 'stopPropagation': function() {}, // There is no flow
  272. 'preventDefault': function() {}, // There is no default action
  273. 'initEvent': function() {} // Original event object should be initialized
  274. };
  275. // Execute onreadystatechange
  276. if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
  277. (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
  278. // Execute listeners
  279. for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
  280. if (oListener[0] == oEventPseudo.type && !oListener[2])
  281. (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
  282. };
  283. //
  284. cXMLHttpRequest.prototype.toString = function() {
  285. return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
  286. };
  287. cXMLHttpRequest.toString = function() {
  288. return '[' + "XMLHttpRequest" + ']';
  289. };
  290. // Helper function
  291. function fReadyStateChange(oRequest) {
  292. // Sniffing code
  293. if (cXMLHttpRequest.onreadystatechange)
  294. cXMLHttpRequest.onreadystatechange.apply(oRequest);
  295. // Fake event
  296. oRequest.dispatchEvent({
  297. 'type': "readystatechange",
  298. 'bubbles': false,
  299. 'cancelable': false,
  300. 'timeStamp': new Date + 0
  301. });
  302. };
  303. function fGetDocument(oRequest) {
  304. var oDocument = oRequest.responseXML,
  305. sResponse = oRequest.responseText;
  306. // Try parsing responseText
  307. if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
  308. oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
  309. oDocument.async = false;
  310. oDocument.validateOnParse = false;
  311. oDocument.loadXML(sResponse);
  312. }
  313. // Check if there is no error in document
  314. if (oDocument)
  315. if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
  316. return null;
  317. return oDocument;
  318. };
  319. function fSynchronizeValues(oRequest) {
  320. try { oRequest.responseText = oRequest._object.responseText; } catch (e) {}
  321. try { oRequest.responseXML = fGetDocument(oRequest._object); } catch (e) {}
  322. try { oRequest.status = oRequest._object.status; } catch (e) {}
  323. try { oRequest.statusText = oRequest._object.statusText; } catch (e) {}
  324. };
  325. function fCleanTransport(oRequest) {
  326. // BUGFIX: IE - memory leak (on-page leak)
  327. oRequest._object.onreadystatechange = new window.Function;
  328. };
  329. /*
  330. // Queue manager
  331. var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
  332. aQueueRunning = [];
  333. function fQueue_add(oRequest) {
  334. oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
  335. //
  336. setTimeout(fQueue_process);
  337. };
  338. function fQueue_remove(oRequest) {
  339. for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++)
  340. if (bFound)
  341. aQueueRunning[nIndex - 1] = aQueueRunning[nIndex];
  342. else
  343. if (aQueueRunning[nIndex] == oRequest)
  344. bFound = true;
  345. if (bFound)
  346. aQueueRunning.length--;
  347. //
  348. setTimeout(fQueue_process);
  349. };
  350. function fQueue_process() {
  351. if (aQueueRunning.length < 6) {
  352. for (var sPriority in oQueuePending) {
  353. if (oQueuePending[sPriority].length) {
  354. var oRequest = oQueuePending[sPriority][0];
  355. oQueuePending[sPriority] = oQueuePending[sPriority].slice(1);
  356. //
  357. aQueueRunning.push(oRequest);
  358. // Send request
  359. fXMLHttpRequest_send(oRequest);
  360. break;
  361. }
  362. }
  363. }
  364. };
  365. */
  366. // Internet Explorer 5.0 (missing apply)
  367. if (!window.Function.prototype.apply) {
  368. window.Function.prototype.apply = function(oRequest, oArguments) {
  369. if (!oArguments)
  370. oArguments = [];
  371. oRequest.__func = this;
  372. oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
  373. delete oRequest.__func;
  374. };
  375. };
  376. // Register new object with window
  377. /**
  378. * Class: OpenLayers.Request.XMLHttpRequest
  379. * Standard-compliant (W3C) cross-browser implementation of the
  380. * XMLHttpRequest object. From
  381. * http://code.google.com/p/xmlhttprequest/.
  382. */
  383. OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
  384. })();