Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Connection.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-data-Connection'>/**
19 </span> * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
20  * to a configured URL, or to a URL specified at request time.
21  *
22  * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
23  * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
24  * in the request options object, or an {@link #requestcomplete event listener}.
25  *
26  * # File Uploads
27  *
28  * File uploads are not performed using normal &quot;Ajax&quot; techniques, that is they are not performed using XMLHttpRequests.
29  * Instead the form is submitted in the standard manner with the DOM &amp;lt;form&amp;gt; element temporarily modified to have its
30  * target set to refer to a dynamically generated, hidden &amp;lt;iframe&amp;gt; which is inserted into the document but removed
31  * after the return data has been gathered.
32  *
33  * The server response is parsed by the browser to create the document for the IFRAME. If the server is using JSON to
34  * send the return object, then the Content-Type header must be set to &quot;text/html&quot; in order to tell the browser to
35  * insert the text unchanged into the document body.
36  *
37  * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `&lt;` as `&amp;lt;`, `&amp;` as
38  * `&amp;amp;` etc.
39  *
40  * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
41  * responseText property in order to conform to the requirements of event handlers and callbacks.
42  *
43  * Be aware that file upload packets are sent with the content type multipart/form and some server technologies
44  * (notably JEE) may require some custom processing in order to retrieve parameter names and parameter values from the
45  * packet content.
46  *
47  * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
48  */
49 Ext.define('Ext.data.Connection', {
50     mixins: {
51         observable: 'Ext.util.Observable'
52     },
53
54     statics: {
55         requestId: 0
56     },
57
58     url: null,
59     async: true,
60     method: null,
61     username: '',
62     password: '',
63
64 <span id='Ext-data-Connection-cfg-disableCaching'>    /**
65 </span>     * @cfg {Boolean} disableCaching
66      * True to add a unique cache-buster param to GET requests.
67      */
68     disableCaching: true,
69
70 <span id='Ext-data-Connection-cfg-withCredentials'>    /**
71 </span>     * @cfg {Boolean} withCredentials
72      * True to set `withCredentials = true` on the XHR object
73      */
74     withCredentials: false,
75
76 <span id='Ext-data-Connection-cfg-cors'>    /**
77 </span>     * @cfg {Boolean} cors
78      * True to enable CORS support on the XHR object. Currently the only effect of this option
79      * is to use the XDomainRequest object instead of XMLHttpRequest if the browser is IE8 or above.
80      */
81     cors: false,
82
83 <span id='Ext-data-Connection-cfg-disableCachingParam'>    /**
84 </span>     * @cfg {String} disableCachingParam
85      * Change the parameter which is sent went disabling caching through a cache buster.
86      */
87     disableCachingParam: '_dc',
88
89 <span id='Ext-data-Connection-cfg-timeout'>    /**
90 </span>     * @cfg {Number} timeout
91      * The timeout in milliseconds to be used for requests.
92      */
93     timeout : 30000,
94
95 <span id='Ext-data-Connection-cfg-extraParams'>    /**
96 </span>     * @cfg {Object} extraParams
97      * Any parameters to be appended to the request.
98      */
99
100     useDefaultHeader : true,
101     defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
102     useDefaultXhrHeader : true,
103     defaultXhrHeader : 'XMLHttpRequest',
104
105     constructor : function(config) {
106         config = config || {};
107         Ext.apply(this, config);
108
109         this.addEvents(
110 <span id='Ext-data-Connection-event-beforerequest'>            /**
111 </span>             * @event beforerequest
112              * Fires before a network request is made to retrieve a data object.
113              * @param {Ext.data.Connection} conn This Connection object.
114              * @param {Object} options The options config object passed to the {@link #request} method.
115              */
116             'beforerequest',
117 <span id='Ext-data-Connection-event-requestcomplete'>            /**
118 </span>             * @event requestcomplete
119              * Fires if the request was successfully completed.
120              * @param {Ext.data.Connection} conn This Connection object.
121              * @param {Object} response The XHR object containing the response data.
122              * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
123              * @param {Object} options The options config object passed to the {@link #request} method.
124              */
125             'requestcomplete',
126 <span id='Ext-data-Connection-event-requestexception'>            /**
127 </span>             * @event requestexception
128              * Fires if an error HTTP status was returned from the server.
129              * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
130              * for details of HTTP status codes.
131              * @param {Ext.data.Connection} conn This Connection object.
132              * @param {Object} response The XHR object containing the response data.
133              * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
134              * @param {Object} options The options config object passed to the {@link #request} method.
135              */
136             'requestexception'
137         );
138         this.requests = {};
139         this.mixins.observable.constructor.call(this);
140     },
141
142 <span id='Ext-data-Connection-method-request'>    /**
143 </span>     * Sends an HTTP request to a remote server.
144      *
145      * **Important:** Ajax server requests are asynchronous, and this call will
146      * return before the response has been received. Process any returned data
147      * in a callback function.
148      *
149      *     Ext.Ajax.request({
150      *         url: 'ajax_demo/sample.json',
151      *         success: function(response, opts) {
152      *             var obj = Ext.decode(response.responseText);
153      *             console.dir(obj);
154      *         },
155      *         failure: function(response, opts) {
156      *             console.log('server-side failure with status code ' + response.status);
157      *         }
158      *     });
159      *
160      * To execute a callback function in the correct scope, use the `scope` option.
161      *
162      * @param {Object} options An object which may contain the following properties:
163      *
164      * (The options object may also contain any other property which might be needed to perform
165      * postprocessing in a callback because it is passed to callback functions.)
166      *
167      * @param {String/Function} options.url The URL to which to send the request, or a function
168      * to call which returns a URL string. The scope of the function is specified by the `scope` option.
169      * Defaults to the configured `url`.
170      *
171      * @param {Object/String/Function} options.params An object containing properties which are
172      * used as parameters to the request, a url encoded string or a function to call to get either. The scope
173      * of the function is specified by the `scope` option.
174      *
175      * @param {String} options.method The HTTP method to use
176      * for the request. Defaults to the configured method, or if no method was configured,
177      * &quot;GET&quot; if no parameters are being sent, and &quot;POST&quot; if parameters are being sent.  Note that
178      * the method name is case-sensitive and should be all caps.
179      *
180      * @param {Function} options.callback The function to be called upon receipt of the HTTP response.
181      * The callback is called regardless of success or failure and is passed the following parameters:
182      * @param {Object} options.callback.options The parameter to the request call.
183      * @param {Boolean} options.callback.success True if the request succeeded.
184      * @param {Object} options.callback.response The XMLHttpRequest object containing the response data.
185      * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about
186      * accessing elements of the response.
187      *
188      * @param {Function} options.success The function to be called upon success of the request.
189      * The callback is passed the following parameters:
190      * @param {Object} options.success.response The XMLHttpRequest object containing the response data.
191      * @param {Object} options.success.options The parameter to the request call.
192      *
193      * @param {Function} options.failure The function to be called upon success of the request.
194      * The callback is passed the following parameters:
195      * @param {Object} options.failure.response The XMLHttpRequest object containing the response data.
196      * @param {Object} options.failure.options The parameter to the request call.
197      *
198      * @param {Object} options.scope The scope in which to execute the callbacks: The &quot;this&quot; object for
199      * the callback function. If the `url`, or `params` options were specified as functions from which to
200      * draw values, then this also serves as the scope for those function calls. Defaults to the browser
201      * window.
202      *
203      * @param {Number} options.timeout The timeout in milliseconds to be used for this request.
204      * Defaults to 30 seconds.
205      *
206      * @param {Ext.Element/HTMLElement/String} options.form The `&lt;form&gt;` Element or the id of the `&lt;form&gt;`
207      * to pull parameters from.
208      *
209      * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.**
210      *
211      * True if the form object is a file upload (will be set automatically if the form was configured
212      * with **`enctype`** `&quot;multipart/form-data&quot;`).
213      *
214      * File uploads are not performed using normal &quot;Ajax&quot; techniques, that is they are **not**
215      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
216      * DOM `&lt;form&gt;` element temporarily modified to have its [target][] set to refer to a dynamically
217      * generated, hidden `&lt;iframe&gt;` which is inserted into the document but removed after the return data
218      * has been gathered.
219      *
220      * The server response is parsed by the browser to create the document for the IFRAME. If the
221      * server is using JSON to send the return object, then the [Content-Type][] header must be set to
222      * &quot;text/html&quot; in order to tell the browser to insert the text unchanged into the document body.
223      *
224      * The response text is retrieved from the document, and a fake XMLHttpRequest object is created
225      * containing a `responseText` property in order to conform to the requirements of event handlers
226      * and callbacks.
227      *
228      * Be aware that file upload packets are sent with the content type [multipart/form][] and some server
229      * technologies (notably JEE) may require some custom processing in order to retrieve parameter names
230      * and parameter values from the packet content.
231      *
232      * [target]: http://www.w3.org/TR/REC-html40/present/frames.html#adef-target
233      * [Content-Type]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
234      * [multipart/form]: http://www.faqs.org/rfcs/rfc2388.html
235      *
236      * @param {Object} options.headers Request headers to set for the request.
237      *
238      * @param {Object} options.xmlData XML document to use for the post. Note: This will be used instead
239      * of params for the post data. Any params will be appended to the URL.
240      *
241      * @param {Object/String} options.jsonData JSON data to use as the post. Note: This will be used
242      * instead of params for the post data. Any params will be appended to the URL.
243      *
244      * @param {Boolean} options.disableCaching True to add a unique cache-buster param to GET requests.
245      *
246      * @param {Boolean} options.withCredentials True to add the withCredentials property to the XHR object
247      *
248      * @return {Object} The request object. This may be used to cancel the request.
249      */
250     request : function(options) {
251         options = options || {};
252         var me = this,
253             scope = options.scope || window,
254             username = options.username || me.username,
255             password = options.password || me.password || '',
256             async,
257             requestOptions,
258             request,
259             headers,
260             xhr;
261
262         if (me.fireEvent('beforerequest', me, options) !== false) {
263
264             requestOptions = me.setOptions(options, scope);
265
266             if (this.isFormUpload(options) === true) {
267                 this.upload(options.form, requestOptions.url, requestOptions.data, options);
268                 return null;
269             }
270
271             // if autoabort is set, cancel the current transactions
272             if (options.autoAbort === true || me.autoAbort) {
273                 me.abort();
274             }
275
276             // create a connection object
277
278             if ((options.cors === true || me.cors === true) &amp;&amp; Ext.isIE &amp;&amp; Ext.ieVersion &gt;= 8) {
279                 xhr = new XDomainRequest();
280             } else {
281                 xhr = this.getXhrInstance();
282             }
283
284             async = options.async !== false ? (options.async || me.async) : false;
285
286             // open the request
287             if (username) {
288                 xhr.open(requestOptions.method, requestOptions.url, async, username, password);
289             } else {
290                 xhr.open(requestOptions.method, requestOptions.url, async);
291             }
292
293             if (options.withCredentials === true || me.withCredentials === true) {
294                 xhr.withCredentials = true;
295             }
296
297             headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
298
299             // create the transaction object
300             request = {
301                 id: ++Ext.data.Connection.requestId,
302                 xhr: xhr,
303                 headers: headers,
304                 options: options,
305                 async: async,
306                 timeout: setTimeout(function() {
307                     request.timedout = true;
308                     me.abort(request);
309                 }, options.timeout || me.timeout)
310             };
311             me.requests[request.id] = request;
312             me.latestId = request.id;
313             // bind our statechange listener
314             if (async) {
315                 xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
316             }
317
318             if ((options.cors === true || me.cors === true) &amp;&amp; Ext.isIE &amp;&amp; Ext.ieVersion &gt;= 8) {
319                 xhr.onload = function() {
320                     me.onComplete(request);
321                 }
322             }
323
324             // start the request!
325             xhr.send(requestOptions.data);
326             if (!async) {
327                 return this.onComplete(request);
328             }
329             return request;
330         } else {
331             Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
332             return null;
333         }
334     },
335
336 <span id='Ext-data-Connection-method-upload'>    /**
337 </span>     * Uploads a form using a hidden iframe.
338      * @param {String/HTMLElement/Ext.Element} form The form to upload
339      * @param {String} url The url to post to
340      * @param {String} params Any extra parameters to pass
341      * @param {Object} options The initial options
342      */
343     upload: function(form, url, params, options) {
344         form = Ext.getDom(form);
345         options = options || {};
346
347         var id = Ext.id(),
348                 frame = document.createElement('iframe'),
349                 hiddens = [],
350                 encoding = 'multipart/form-data',
351                 buf = {
352                     target: form.target,
353                     method: form.method,
354                     encoding: form.encoding,
355                     enctype: form.enctype,
356                     action: form.action
357                 }, hiddenItem;
358
359         /*
360          * Originally this behaviour was modified for Opera 10 to apply the secure URL after
361          * the frame had been added to the document. It seems this has since been corrected in
362          * Opera so the behaviour has been reverted, the URL will be set before being added.
363          */
364         Ext.fly(frame).set({
365             id: id,
366             name: id,
367             cls: Ext.baseCSSPrefix + 'hide-display',
368             src: Ext.SSL_SECURE_URL
369         });
370
371         document.body.appendChild(frame);
372
373         // This is required so that IE doesn't pop the response up in a new window.
374         if (document.frames) {
375            document.frames[id].name = id;
376         }
377
378         Ext.fly(form).set({
379             target: id,
380             method: 'POST',
381             enctype: encoding,
382             encoding: encoding,
383             action: url || buf.action
384         });
385
386         // add dynamic params
387         if (params) {
388             Ext.iterate(Ext.Object.fromQueryString(params), function(name, value){
389                 hiddenItem = document.createElement('input');
390                 Ext.fly(hiddenItem).set({
391                     type: 'hidden',
392                     value: value,
393                     name: name
394                 });
395                 form.appendChild(hiddenItem);
396                 hiddens.push(hiddenItem);
397             });
398         }
399
400         Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
401         form.submit();
402
403         Ext.fly(form).set(buf);
404         Ext.each(hiddens, function(h) {
405             Ext.removeNode(h);
406         });
407     },
408
409 <span id='Ext-data-Connection-method-onUploadComplete'>    /**
410 </span>     * @private
411      * Callback handler for the upload function. After we've submitted the form via the iframe this creates a bogus
412      * response object to simulate an XHR and populates its responseText from the now-loaded iframe's document body
413      * (or a textarea inside the body). We then clean up by removing the iframe
414      */
415     onUploadComplete: function(frame, options) {
416         var me = this,
417             // bogus response object
418             response = {
419                 responseText: '',
420                 responseXML: null
421             }, doc, firstChild;
422
423         try {
424             doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
425             if (doc) {
426                 if (doc.body) {
427                     if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea
428                         response.responseText = firstChild.value;
429                     } else {
430                         response.responseText = doc.body.innerHTML;
431                     }
432                 }
433                 //in IE the document may still have a body even if returns XML.
434                 response.responseXML = doc.XMLDocument || doc;
435             }
436         } catch (e) {
437         }
438
439         me.fireEvent('requestcomplete', me, response, options);
440
441         Ext.callback(options.success, options.scope, [response, options]);
442         Ext.callback(options.callback, options.scope, [options, true, response]);
443
444         setTimeout(function(){
445             Ext.removeNode(frame);
446         }, 100);
447     },
448
449 <span id='Ext-data-Connection-method-isFormUpload'>    /**
450 </span>     * Detects whether the form is intended to be used for an upload.
451      * @private
452      */
453     isFormUpload: function(options){
454         var form = this.getForm(options);
455         if (form) {
456             return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
457         }
458         return false;
459     },
460
461 <span id='Ext-data-Connection-method-getForm'>    /**
462 </span>     * Gets the form object from options.
463      * @private
464      * @param {Object} options The request options
465      * @return {HTMLElement} The form, null if not passed
466      */
467     getForm: function(options){
468         return Ext.getDom(options.form) || null;
469     },
470
471 <span id='Ext-data-Connection-method-setOptions'>    /**
472 </span>     * Sets various options such as the url, params for the request
473      * @param {Object} options The initial options
474      * @param {Object} scope The scope to execute in
475      * @return {Object} The params for the request
476      */
477     setOptions: function(options, scope){
478         var me =  this,
479             params = options.params || {},
480             extraParams = me.extraParams,
481             urlParams = options.urlParams,
482             url = options.url || me.url,
483             jsonData = options.jsonData,
484             method,
485             disableCache,
486             data;
487
488
489         // allow params to be a method that returns the params object
490         if (Ext.isFunction(params)) {
491             params = params.call(scope, options);
492         }
493
494         // allow url to be a method that returns the actual url
495         if (Ext.isFunction(url)) {
496             url = url.call(scope, options);
497         }
498
499         url = this.setupUrl(options, url);
500
501         //&lt;debug&gt;
502         if (!url) {
503             Ext.Error.raise({
504                 options: options,
505                 msg: 'No URL specified'
506             });
507         }
508         //&lt;/debug&gt;
509
510         // check for xml or json data, and make sure json data is encoded
511         data = options.rawData || options.xmlData || jsonData || null;
512         if (jsonData &amp;&amp; !Ext.isPrimitive(jsonData)) {
513             data = Ext.encode(data);
514         }
515
516         // make sure params are a url encoded string and include any extraParams if specified
517         if (Ext.isObject(params)) {
518             params = Ext.Object.toQueryString(params);
519         }
520
521         if (Ext.isObject(extraParams)) {
522             extraParams = Ext.Object.toQueryString(extraParams);
523         }
524
525         params = params + ((extraParams) ? ((params) ? '&amp;' : '') + extraParams : '');
526
527         urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;
528
529         params = this.setupParams(options, params);
530
531         // decide the proper method for this request
532         method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
533         this.setupMethod(options, method);
534
535
536         disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
537         // if the method is get append date to prevent caching
538         if (method === 'GET' &amp;&amp; disableCache) {
539             url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
540         }
541
542         // if the method is get or there is json/xml data append the params to the url
543         if ((method == 'GET' || data) &amp;&amp; params) {
544             url = Ext.urlAppend(url, params);
545             params = null;
546         }
547
548         // allow params to be forced into the url
549         if (urlParams) {
550             url = Ext.urlAppend(url, urlParams);
551         }
552
553         return {
554             url: url,
555             method: method,
556             data: data || params || null
557         };
558     },
559
560 <span id='Ext-data-Connection-method-setupUrl'>    /**
561 </span>     * Template method for overriding url
562      * @template
563      * @private
564      * @param {Object} options
565      * @param {String} url
566      * @return {String} The modified url
567      */
568     setupUrl: function(options, url){
569         var form = this.getForm(options);
570         if (form) {
571             url = url || form.action;
572         }
573         return url;
574     },
575
576
577 <span id='Ext-data-Connection-method-setupParams'>    /**
578 </span>     * Template method for overriding params
579      * @template
580      * @private
581      * @param {Object} options
582      * @param {String} params
583      * @return {String} The modified params
584      */
585     setupParams: function(options, params) {
586         var form = this.getForm(options),
587             serializedForm;
588         if (form &amp;&amp; !this.isFormUpload(options)) {
589             serializedForm = Ext.Element.serializeForm(form);
590             params = params ? (params + '&amp;' + serializedForm) : serializedForm;
591         }
592         return params;
593     },
594
595 <span id='Ext-data-Connection-method-setupMethod'>    /**
596 </span>     * Template method for overriding method
597      * @template
598      * @private
599      * @param {Object} options
600      * @param {String} method
601      * @return {String} The modified method
602      */
603     setupMethod: function(options, method){
604         if (this.isFormUpload(options)) {
605             return 'POST';
606         }
607         return method;
608     },
609
610 <span id='Ext-data-Connection-method-setupHeaders'>    /**
611 </span>     * Setup all the headers for the request
612      * @private
613      * @param {Object} xhr The xhr object
614      * @param {Object} options The options for the request
615      * @param {Object} data The data for the request
616      * @param {Object} params The params for the request
617      */
618     setupHeaders: function(xhr, options, data, params){
619         var me = this,
620             headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
621             contentType = me.defaultPostHeader,
622             jsonData = options.jsonData,
623             xmlData = options.xmlData,
624             key,
625             header;
626
627         if (!headers['Content-Type'] &amp;&amp; (data || params)) {
628             if (data) {
629                 if (options.rawData) {
630                     contentType = 'text/plain';
631                 } else {
632                     if (xmlData &amp;&amp; Ext.isDefined(xmlData)) {
633                         contentType = 'text/xml';
634                     } else if (jsonData &amp;&amp; Ext.isDefined(jsonData)) {
635                         contentType = 'application/json';
636                     }
637                 }
638             }
639             headers['Content-Type'] = contentType;
640         }
641
642         if (me.useDefaultXhrHeader &amp;&amp; !headers['X-Requested-With']) {
643             headers['X-Requested-With'] = me.defaultXhrHeader;
644         }
645         // set up all the request headers on the xhr object
646         try{
647             for (key in headers) {
648                 if (headers.hasOwnProperty(key)) {
649                     header = headers[key];
650                     xhr.setRequestHeader(key, header);
651                 }
652
653             }
654         } catch(e) {
655             me.fireEvent('exception', key, header);
656         }
657         return headers;
658     },
659
660 <span id='Ext-data-Connection-property-getXhrInstance'>    /**
661 </span>     * Creates the appropriate XHR transport for the browser.
662      * @private
663      */
664     getXhrInstance: (function(){
665         var options = [function(){
666             return new XMLHttpRequest();
667         }, function(){
668             return new ActiveXObject('MSXML2.XMLHTTP.3.0');
669         }, function(){
670             return new ActiveXObject('MSXML2.XMLHTTP');
671         }, function(){
672             return new ActiveXObject('Microsoft.XMLHTTP');
673         }], i = 0,
674             len = options.length,
675             xhr;
676
677         for(; i &lt; len; ++i) {
678             try{
679                 xhr = options[i];
680                 xhr();
681                 break;
682             }catch(e){}
683         }
684         return xhr;
685     })(),
686
687 <span id='Ext-data-Connection-method-isLoading'>    /**
688 </span>     * Determines whether this object has a request outstanding.
689      * @param {Object} [request] Defaults to the last transaction
690      * @return {Boolean} True if there is an outstanding request.
691      */
692     isLoading : function(request) {
693         if (!request) {
694             request = this.getLatest();
695         }
696         if (!(request &amp;&amp; request.xhr)) {
697             return false;
698         }
699         // if there is a connection and readyState is not 0 or 4
700         var state = request.xhr.readyState;
701         return !(state === 0 || state == 4);
702     },
703
704 <span id='Ext-data-Connection-method-abort'>    /**
705 </span>     * Aborts an active request.
706      * @param {Object} [request] Defaults to the last request
707      */
708     abort : function(request) {
709         var me = this;
710
711         if (!request) {
712             request = me.getLatest();
713         }
714
715         if (request &amp;&amp; me.isLoading(request)) {
716             /*
717              * Clear out the onreadystatechange here, this allows us
718              * greater control, the browser may/may not fire the function
719              * depending on a series of conditions.
720              */
721             request.xhr.onreadystatechange = null;
722             request.xhr.abort();
723             me.clearTimeout(request);
724             if (!request.timedout) {
725                 request.aborted = true;
726             }
727             me.onComplete(request);
728             me.cleanup(request);
729         }
730     },
731
732 <span id='Ext-data-Connection-method-abortAll'>    /**
733 </span>     * Aborts all active requests
734      */
735     abortAll: function(){
736         var requests = this.requests,
737             id;
738
739         for (id in requests) {
740             if (requests.hasOwnProperty(id)) {
741                 this.abort(requests[id]);
742             }
743         }
744     },
745
746 <span id='Ext-data-Connection-method-getLatest'>    /**
747 </span>     * Gets the most recent request
748      * @private
749      * @return {Object} The request. Null if there is no recent request
750      */
751     getLatest: function(){
752         var id = this.latestId,
753             request;
754
755         if (id) {
756             request = this.requests[id];
757         }
758         return request || null;
759     },
760
761 <span id='Ext-data-Connection-method-onStateChange'>    /**
762 </span>     * Fires when the state of the xhr changes
763      * @private
764      * @param {Object} request The request
765      */
766     onStateChange : function(request) {
767         if (request.xhr.readyState == 4) {
768             this.clearTimeout(request);
769             this.onComplete(request);
770             this.cleanup(request);
771         }
772     },
773
774 <span id='Ext-data-Connection-method-clearTimeout'>    /**
775 </span>     * Clears the timeout on the request
776      * @private
777      * @param {Object} The request
778      */
779     clearTimeout: function(request){
780         clearTimeout(request.timeout);
781         delete request.timeout;
782     },
783
784 <span id='Ext-data-Connection-method-cleanup'>    /**
785 </span>     * Cleans up any left over information from the request
786      * @private
787      * @param {Object} The request
788      */
789     cleanup: function(request){
790         request.xhr = null;
791         delete request.xhr;
792     },
793
794 <span id='Ext-data-Connection-method-onComplete'>    /**
795 </span>     * To be called when the request has come back from the server
796      * @private
797      * @param {Object} request
798      * @return {Object} The response
799      */
800     onComplete : function(request) {
801         var me = this,
802             options = request.options,
803             result,
804             success,
805             response;
806
807         try {
808             result = me.parseStatus(request.xhr.status);
809         } catch (e) {
810             // in some browsers we can't access the status if the readyState is not 4, so the request has failed
811             result = {
812                 success : false,
813                 isException : false
814             };
815         }
816         success = result.success;
817
818         if (success) {
819             response = me.createResponse(request);
820             me.fireEvent('requestcomplete', me, response, options);
821             Ext.callback(options.success, options.scope, [response, options]);
822         } else {
823             if (result.isException || request.aborted || request.timedout) {
824                 response = me.createException(request);
825             } else {
826                 response = me.createResponse(request);
827             }
828             me.fireEvent('requestexception', me, response, options);
829             Ext.callback(options.failure, options.scope, [response, options]);
830         }
831         Ext.callback(options.callback, options.scope, [options, success, response]);
832         delete me.requests[request.id];
833         return response;
834     },
835
836 <span id='Ext-data-Connection-method-parseStatus'>    /**
837 </span>     * Checks if the response status was successful
838      * @param {Number} status The status code
839      * @return {Object} An object containing success/status state
840      */
841     parseStatus: function(status) {
842         // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223
843         status = status == 1223 ? 204 : status;
844
845         var success = (status &gt;= 200 &amp;&amp; status &lt; 300) || status == 304,
846             isException = false;
847
848         if (!success) {
849             switch (status) {
850                 case 12002:
851                 case 12029:
852                 case 12030:
853                 case 12031:
854                 case 12152:
855                 case 13030:
856                     isException = true;
857                     break;
858             }
859         }
860         return {
861             success: success,
862             isException: isException
863         };
864     },
865
866 <span id='Ext-data-Connection-method-createResponse'>    /**
867 </span>     * Creates the response object
868      * @private
869      * @param {Object} request
870      */
871     createResponse : function(request) {
872         var xhr = request.xhr,
873             headers = {},
874             lines = xhr.getAllResponseHeaders ? xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n') : [],
875             count = lines.length,
876             line, index, key, value, response;
877
878         while (count--) {
879             line = lines[count];
880             index = line.indexOf(':');
881             if(index &gt;= 0) {
882                 key = line.substr(0, index).toLowerCase();
883                 if (line.charAt(index + 1) == ' ') {
884                     ++index;
885                 }
886                 headers[key] = line.substr(index + 1);
887             }
888         }
889
890         request.xhr = null;
891         delete request.xhr;
892
893         response = {
894             request: request,
895             requestId : request.id,
896             status : xhr.status,
897             statusText : xhr.statusText,
898             getResponseHeader : function(header){ return headers[header.toLowerCase()]; },
899             getAllResponseHeaders : function(){ return headers; },
900             responseText : xhr.responseText,
901             responseXML : xhr.responseXML
902         };
903
904         // If we don't explicitly tear down the xhr reference, IE6/IE7 will hold this in the closure of the
905         // functions created with getResponseHeader/getAllResponseHeaders
906         xhr = null;
907         return response;
908     },
909
910 <span id='Ext-data-Connection-method-createException'>    /**
911 </span>     * Creates the exception object
912      * @private
913      * @param {Object} request
914      */
915     createException : function(request) {
916         return {
917             request : request,
918             requestId : request.id,
919             status : request.aborted ? -1 : 0,
920             statusText : request.aborted ? 'transaction aborted' : 'communication failure',
921             aborted: request.aborted,
922             timedout: request.timedout
923         };
924     }
925 });
926 </pre>
927 </body>
928 </html>