Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / data / Connection.js
index bb43f6f..1e27240 100644 (file)
@@ -1,5 +1,18 @@
+/*
+
+This file is part of Ext JS 4
+
+Copyright (c) 2011 Sencha Inc
+
+Contact:  http://www.sencha.com/contact
+
+GNU General Public License Usage
+This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
+
+*/
 /**
- * @class Ext.data.Connection
  * The Connection class encapsulates a connection to the page's originating domain, allowing requests to be made either
  * to a configured URL, or to a URL specified at request time.
  *
@@ -7,7 +20,7 @@
  * to the statement immediately following the {@link #request} call. To process returned data, use a success callback
  * in the request options object, or an {@link #requestcomplete event listener}.
  *
- * <p><u>File Uploads</u></p>
+ * # File Uploads
  *
  * File uploads are not performed using normal "Ajax" techniques, that is they are not performed using XMLHttpRequests.
  * Instead the form is submitted in the standard manner with the DOM &lt;form&gt; element temporarily modified to have its
@@ -18,8 +31,8 @@
  * send the return object, then the Content-Type header must be set to "text/html" in order to tell the browser to
  * insert the text unchanged into the document body.
  *
- * Characters which are significant to an HTML parser must be sent as HTML entities, so encode "&lt;" as "&amp;lt;", "&amp;" as
- * "&amp;amp;" etc.
+ * Characters which are significant to an HTML parser must be sent as HTML entities, so encode `<` as `&lt;`, `&` as
+ * `&amp;` etc.
  *
  * The response text is retrieved from the document, and a fake XMLHttpRequest object is created containing a
  * responseText property in order to conform to the requirements of event handlers and callbacks.
@@ -46,23 +59,39 @@ Ext.define('Ext.data.Connection', {
     password: '',
 
     /**
-     * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+     * @cfg {Boolean} disableCaching
+     * True to add a unique cache-buster param to GET requests.
      */
     disableCaching: true,
 
     /**
-     * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
-     * through a cache buster. Defaults to '_dc'
+     * @cfg {Boolean} withCredentials
+     * True to set `withCredentials = true` on the XHR object
+     */
+    withCredentials: false,
+
+    /**
+     * @cfg {Boolean} cors
+     * True to enable CORS support on the XHR object. Currently the only effect of this option
+     * is to use the XDomainRequest object instead of XMLHttpRequest if the browser is IE8 or above.
+     */
+    cors: false,
+
+    /**
+     * @cfg {String} disableCachingParam
+     * Change the parameter which is sent went disabling caching through a cache buster.
      */
     disableCachingParam: '_dc',
 
     /**
-     * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+     * @cfg {Number} timeout
+     * The timeout in milliseconds to be used for requests.
      */
     timeout : 30000,
 
     /**
-     * @cfg {Object} extraParams (Optional) Any parameters to be appended to the request.
+     * @cfg {Object} extraParams
+     * Any parameters to be appended to the request.
      */
 
     useDefaultHeader : true,
@@ -78,29 +107,27 @@ Ext.define('Ext.data.Connection', {
             /**
              * @event beforerequest
              * Fires before a network request is made to retrieve a data object.
-             * @param {Connection} conn This Connection object.
+             * @param {Ext.data.Connection} conn This Connection object.
              * @param {Object} options The options config object passed to the {@link #request} method.
              */
             'beforerequest',
             /**
              * @event requestcomplete
              * Fires if the request was successfully completed.
-             * @param {Connection} conn This Connection object.
+             * @param {Ext.data.Connection} conn This Connection object.
              * @param {Object} response The XHR object containing the response data.
-             * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
-             * for details.
+             * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
              * @param {Object} options The options config object passed to the {@link #request} method.
              */
             'requestcomplete',
             /**
              * @event requestexception
              * Fires if an error HTTP status was returned from the server.
-             * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
+             * See [HTTP Status Code Definitions](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
              * for details of HTTP status codes.
-             * @param {Connection} conn This Connection object.
+             * @param {Ext.data.Connection} conn This Connection object.
              * @param {Object} response The XHR object containing the response data.
-             * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
-             * for details.
+             * See [The XMLHttpRequest Object](http://www.w3.org/TR/XMLHttpRequest/) for details.
              * @param {Object} options The options config object passed to the {@link #request} method.
              */
             'requestexception'
@@ -110,101 +137,112 @@ Ext.define('Ext.data.Connection', {
     },
 
     /**
-     * <p>Sends an HTTP request to a remote server.</p>
-     * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
+     * Sends an HTTP request to a remote server.
+     *
+     * **Important:** Ajax server requests are asynchronous, and this call will
      * return before the response has been received. Process any returned data
-     * in a callback function.</p>
-     * <pre><code>
-Ext.Ajax.request({
-url: 'ajax_demo/sample.json',
-success: function(response, opts) {
-  var obj = Ext.decode(response.responseText);
-  console.dir(obj);
-},
-failure: function(response, opts) {
-  console.log('server-side failure with status code ' + response.status);
-}
-});
-     * </code></pre>
-     * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
-     * @param {Object} options An object which may contain the following properties:<ul>
-     * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
-     * which to send the request, or a function to call which returns a URL string. The scope of the
-     * function is specified by the <tt>scope</tt> option. Defaults to the configured
-     * <tt>{@link #url}</tt>.</div></li>
-     * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
-     * An object containing properties which are used as parameters to the
-     * request, a url encoded string or a function to call to get either. The scope of the function
-     * is specified by the <tt>scope</tt> option.</div></li>
-     * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
+     * in a callback function.
+     *
+     *     Ext.Ajax.request({
+     *         url: 'ajax_demo/sample.json',
+     *         success: function(response, opts) {
+     *             var obj = Ext.decode(response.responseText);
+     *             console.dir(obj);
+     *         },
+     *         failure: function(response, opts) {
+     *             console.log('server-side failure with status code ' + response.status);
+     *         }
+     *     });
+     *
+     * To execute a callback function in the correct scope, use the `scope` option.
+     *
+     * @param {Object} options An object which may contain the following properties:
+     *
+     * (The options object may also contain any other property which might be needed to perform
+     * postprocessing in a callback because it is passed to callback functions.)
+     *
+     * @param {String/Function} options.url The URL to which to send the request, or a function
+     * to call which returns a URL string. The scope of the function is specified by the `scope` option.
+     * Defaults to the configured `url`.
+     *
+     * @param {Object/String/Function} options.params An object containing properties which are
+     * used as parameters to the request, a url encoded string or a function to call to get either. The scope
+     * of the function is specified by the `scope` option.
+     *
+     * @param {String} options.method The HTTP method to use
      * for the request. Defaults to the configured method, or if no method was configured,
      * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
-     * the method name is case-sensitive and should be all caps.</div></li>
-     * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
-     * function to be called upon receipt of the HTTP response. The callback is
-     * called regardless of success or failure and is passed the following
-     * parameters:<ul>
-     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
-     * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
-     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
-     * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
-     * accessing elements of the response.</div></li>
-     * </ul></div></li>
-     * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
-     * to be called upon success of the request. The callback is passed the following
-     * parameters:<ul>
-     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
-     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
-     * </ul></div></li>
-     * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
-     * to be called upon failure of the request. The callback is passed the
-     * following parameters:<ul>
-     * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
-     * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
-     * </ul></div></li>
-     * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
-     * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
-     * specified as functions from which to draw values, then this also serves as the scope for those function calls.
-     * Defaults to the browser window.</div></li>
-     * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
-     * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
-     * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
-     * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
-     * with the <tt>form</tt> option</b>.
-     * <p>True if the form object is a file upload (will be set automatically if the form was
-     * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
-     * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
+     * the method name is case-sensitive and should be all caps.
+     *
+     * @param {Function} options.callback The function to be called upon receipt of the HTTP response.
+     * The callback is called regardless of success or failure and is passed the following parameters:
+     * @param {Object} options.callback.options The parameter to the request call.
+     * @param {Boolean} options.callback.success True if the request succeeded.
+     * @param {Object} options.callback.response The XMLHttpRequest object containing the response data.
+     * See [www.w3.org/TR/XMLHttpRequest/](http://www.w3.org/TR/XMLHttpRequest/) for details about
+     * accessing elements of the response.
+     *
+     * @param {Function} options.success The function to be called upon success of the request.
+     * The callback is passed the following parameters:
+     * @param {Object} options.success.response The XMLHttpRequest object containing the response data.
+     * @param {Object} options.success.options The parameter to the request call.
+     *
+     * @param {Function} options.failure The function to be called upon success of the request.
+     * The callback is passed the following parameters:
+     * @param {Object} options.failure.response The XMLHttpRequest object containing the response data.
+     * @param {Object} options.failure.options The parameter to the request call.
+     *
+     * @param {Object} options.scope The scope in which to execute the callbacks: The "this" object for
+     * the callback function. If the `url`, or `params` options were specified as functions from which to
+     * draw values, then this also serves as the scope for those function calls. Defaults to the browser
+     * window.
+     *
+     * @param {Number} options.timeout The timeout in milliseconds to be used for this request.
+     * Defaults to 30 seconds.
+     *
+     * @param {Ext.Element/HTMLElement/String} options.form The `<form>` Element or the id of the `<form>`
+     * to pull parameters from.
+     *
+     * @param {Boolean} options.isUpload **Only meaningful when used with the `form` option.**
+     *
+     * True if the form object is a file upload (will be set automatically if the form was configured
+     * with **`enctype`** `"multipart/form-data"`).
+     *
+     * File uploads are not performed using normal "Ajax" techniques, that is they are **not**
      * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
-     * DOM <tt>&lt;form></tt> element temporarily modified to have its
-     * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
-     * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
-     * but removed after the return data has been gathered.</p>
-     * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
-     * server is using JSON to send the return object, then the
-     * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
-     * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
-     * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
-     * is created containing a <tt>responseText</tt> property in order to conform to the
-     * requirements of event handlers and callbacks.</p>
-     * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
-     * and some server technologies (notably JEE) may require some custom processing in order to
-     * retrieve parameter names and parameter values from the packet content.</p>
-     * </div></li>
-     * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
-     * headers to set for the request.</div></li>
-     * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
-     * to use for the post. Note: This will be used instead of params for the post
-     * data. Any params will be appended to the URL.</div></li>
-     * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
-     * data to use as the post. Note: This will be used instead of params for the post
-     * data. Any params will be appended to the URL.</div></li>
-     * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
-     * to add a unique cache-buster param to GET requests.</div></li>
-     * </ul></p>
-     * <p>The options object may also contain any other property which might be needed to perform
-     * postprocessing in a callback because it is passed to callback functions.</p>
-     * @return {Object} request The request object. This may be used
-     * to cancel the request.
+     * DOM `<form>` element temporarily modified to have its [target][] set to refer to a dynamically
+     * generated, hidden `<iframe>` which is inserted into the document but removed after the return data
+     * has been gathered.
+     *
+     * The server response is parsed by the browser to create the document for the IFRAME. If the
+     * server is using JSON to send the return object, then the [Content-Type][] header must be set to
+     * "text/html" in order to tell the browser to insert the text unchanged into the document body.
+     *
+     * The response text is retrieved from the document, and a fake XMLHttpRequest object is created
+     * containing a `responseText` property in order to conform to the requirements of event handlers
+     * and callbacks.
+     *
+     * Be aware that file upload packets are sent with the content type [multipart/form][] and some server
+     * technologies (notably JEE) may require some custom processing in order to retrieve parameter names
+     * and parameter values from the packet content.
+     *
+     * [target]: http://www.w3.org/TR/REC-html40/present/frames.html#adef-target
+     * [Content-Type]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
+     * [multipart/form]: http://www.faqs.org/rfcs/rfc2388.html
+     *
+     * @param {Object} options.headers Request headers to set for the request.
+     *
+     * @param {Object} options.xmlData XML document to use for the post. Note: This will be used instead
+     * of params for the post data. Any params will be appended to the URL.
+     *
+     * @param {Object/String} options.jsonData JSON data to use as the post. Note: This will be used
+     * instead of params for the post data. Any params will be appended to the URL.
+     *
+     * @param {Boolean} options.disableCaching True to add a unique cache-buster param to GET requests.
+     *
+     * @param {Boolean} options.withCredentials True to add the withCredentials property to the XHR object
+     *
+     * @return {Object} The request object. This may be used to cancel the request.
      */
     request : function(options) {
         options = options || {};
@@ -233,7 +271,12 @@ failure: function(response, opts) {
             }
 
             // create a connection object
-            xhr = this.getXhrInstance();
+
+            if ((options.cors === true || me.cors === true) && Ext.isIe && Ext.ieVersion >= 8) {
+                xhr = new XDomainRequest();
+            } else {
+                xhr = this.getXhrInstance();
+            }
 
             async = options.async !== false ? (options.async || me.async) : false;
 
@@ -244,6 +287,10 @@ failure: function(response, opts) {
                 xhr.open(requestOptions.method, requestOptions.url, async);
             }
 
+            if (options.withCredentials === true || me.withCredentials === true) {
+                xhr.withCredentials = true;
+            }
+
             headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);
 
             // create the transaction object
@@ -259,7 +306,7 @@ failure: function(response, opts) {
                 }, options.timeout || me.timeout)
             };
             me.requests[request.id] = request;
-
+            me.latestId = request.id;
             // bind our statechange listener
             if (async) {
                 xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
@@ -278,13 +325,13 @@ failure: function(response, opts) {
     },
 
     /**
-     * Upload a form using a hidden iframe.
-     * @param {Mixed} form The form to upload
+     * Uploads a form using a hidden iframe.
+     * @param {String/HTMLElement/Ext.Element} form The form to upload
      * @param {String} url The url to post to
      * @param {String} params Any extra parameters to pass
      * @param {Object} options The initial options
      */
-    upload: function(form, url, params, options){
+    upload: function(form, url, params, options) {
         form = Ext.getDom(form);
         options = options || {};
 
@@ -350,7 +397,13 @@ failure: function(response, opts) {
         });
     },
 
-    onUploadComplete: function(frame, options){
+    /**
+     * @private
+     * Callback handler for the upload function. After we've submitted the form via the iframe this creates a bogus
+     * response object to simulate an XHR and populates its responseText from the now-loaded iframe's document body
+     * (or a textarea inside the body). We then clean up by removing the iframe
+     */
+    onUploadComplete: function(frame, options) {
         var me = this,
             // bogus response object
             response = {
@@ -359,7 +412,7 @@ failure: function(response, opts) {
             }, doc, firstChild;
 
         try {
-            doc = frame.contentWindow.document || frame.contentDocument || window.frames[id].document;
+            doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
             if (doc) {
                 if (doc.body) {
                     if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea
@@ -385,7 +438,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Detect whether the form is intended to be used for an upload.
+     * Detects whether the form is intended to be used for an upload.
      * @private
      */
     isFormUpload: function(options){
@@ -397,7 +450,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Get the form object from options.
+     * Gets the form object from options.
      * @private
      * @param {Object} options The request options
      * @return {HTMLElement} The form, null if not passed
@@ -407,7 +460,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Set various options such as the url, params for the request
+     * Sets various options such as the url, params for the request
      * @param {Object} options The initial options
      * @param {Object} scope The scope to execute in
      * @return {Object} The params for the request
@@ -497,6 +550,7 @@ failure: function(response, opts) {
 
     /**
      * Template method for overriding url
+     * @template
      * @private
      * @param {Object} options
      * @param {String} url
@@ -513,6 +567,7 @@ failure: function(response, opts) {
 
     /**
      * Template method for overriding params
+     * @template
      * @private
      * @param {Object} options
      * @param {String} params
@@ -522,7 +577,7 @@ failure: function(response, opts) {
         var form = this.getForm(options),
             serializedForm;
         if (form && !this.isFormUpload(options)) {
-            serializedForm = Ext.core.Element.serializeForm(form);
+            serializedForm = Ext.Element.serializeForm(form);
             params = params ? (params + '&' + serializedForm) : serializedForm;
         }
         return params;
@@ -530,6 +585,7 @@ failure: function(response, opts) {
 
     /**
      * Template method for overriding method
+     * @template
      * @private
      * @param {Object} options
      * @param {String} method
@@ -620,11 +676,14 @@ failure: function(response, opts) {
     })(),
 
     /**
-     * Determine whether this object has a request outstanding.
-     * @param {Object} request (Optional) defaults to the last transaction
+     * Determines whether this object has a request outstanding.
+     * @param {Object} [request] Defaults to the last transaction
      * @return {Boolean} True if there is an outstanding request.
      */
     isLoading : function(request) {
+        if (!request) {
+            request = this.getLatest();
+        }
         if (!(request && request.xhr)) {
             return false;
         }
@@ -634,13 +693,15 @@ failure: function(response, opts) {
     },
 
     /**
-     * Aborts any outstanding request.
-     * @param {Object} request (Optional) defaults to the last request
+     * Aborts an active request.
+     * @param {Object} [request] Defaults to the last request
      */
     abort : function(request) {
-        var me = this,
-            requests = me.requests,
-            id;
+        var me = this;
+        
+        if (!request) {
+            request = me.getLatest();
+        }
 
         if (request && me.isLoading(request)) {
             /*
@@ -656,14 +717,37 @@ failure: function(response, opts) {
             }
             me.onComplete(request);
             me.cleanup(request);
-        } else if (!request) {
-            for(id in requests) {
-                if (requests.hasOwnProperty(id)) {
-                    me.abort(requests[id]);
-                }
+        }
+    },
+    
+    /**
+     * Aborts all active requests
+     */
+    abortAll: function(){
+        var requests = this.requests,
+            id;
+        
+        for (id in requests) {
+            if (requests.hasOwnProperty(id)) {
+                this.abort(requests[id]);
             }
         }
     },
+    
+    /**
+     * Gets the most recent request
+     * @private
+     * @return {Object} The request. Null if there is no recent request
+     */
+    getLatest: function(){
+        var id = this.latestId,
+            request;
+            
+        if (id) {
+            request = this.requests[id];
+        }
+        return request || null;
+    },
 
     /**
      * Fires when the state of the xhr changes
@@ -679,7 +763,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Clear the timeout on the request
+     * Clears the timeout on the request
      * @private
      * @param {Object} The request
      */
@@ -689,7 +773,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Clean up any left over information from the request
+     * Cleans up any left over information from the request
      * @private
      * @param {Object} The request
      */
@@ -707,10 +791,21 @@ failure: function(response, opts) {
     onComplete : function(request) {
         var me = this,
             options = request.options,
-            result = me.parseStatus(request.xhr.status),
-            success = result.success,
+            result,
+            success,
             response;
 
+        try {
+            result = me.parseStatus(request.xhr.status);
+        } catch (e) {
+            // in some browsers we can't access the status if the readyState is not 4, so the request has failed
+            result = {
+                success : false,
+                isException : false
+            };
+        }
+        success = result.success;
+
         if (success) {
             response = me.createResponse(request);
             me.fireEvent('requestcomplete', me, response, options);
@@ -730,7 +825,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Check if the response status was successful
+     * Checks if the response status was successful
      * @param {Number} status The status code
      * @return {Object} An object containing success/status state
      */
@@ -760,7 +855,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Create the response object
+     * Creates the response object
      * @private
      * @param {Object} request
      */
@@ -804,7 +899,7 @@ failure: function(response, opts) {
     },
 
     /**
-     * Create the exception object
+     * Creates the exception object
      * @private
      * @param {Object} request
      */
@@ -819,3 +914,4 @@ failure: function(response, opts) {
         };
     }
 });
+