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