Project

General

Profile

Download (17.2 KB) Statistics
| Branch: | Tag: | Revision:
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
})();
    (1-1/1)