X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/530ef4b6c5b943cfa68b779d11cf7de29aa878bf..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/Connection.html diff --git a/docs/source/Connection.html b/docs/source/Connection.html index ecd9bca8..54999ceb 100644 --- a/docs/source/Connection.html +++ b/docs/source/Connection.html @@ -1,359 +1,298 @@ - - - - The source code - - - - -
/*!
- * Ext JS Library 3.2.1
- * Copyright(c) 2006-2010 Ext JS, Inc.
- * licensing@extjs.com
- * http://www.extjs.com/license
+Sencha Documentation Project
/**
+ * @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.
+ *
+ * Requests made by this class are asynchronous, and will return immediately. No data from the server will be available
+ * 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 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
+ * target set to refer to a dynamically generated, hidden &lt;iframe&gt; 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.
  */
-(function(){
-    var BEFOREREQUEST = "beforerequest",
-        REQUESTCOMPLETE = "requestcomplete",
-        REQUESTEXCEPTION = "requestexception",
-        UNDEFINED = undefined,
-        LOAD = 'load',
-        POST = 'POST',
-        GET = 'GET',
-        WINDOW = window;
-
-    
/** - * @class Ext.data.Connection - * @extends Ext.util.Observable - *

The 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.

- *

Requests made by this class are asynchronous, and will return immediately. No data from - * the server will be available 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}.

- *

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 <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.

- *

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.

- *

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.

- * @constructor - * @param {Object} config a configuration object. +Ext.define('Ext.data.Connection', { + mixins: { + observable: 'Ext.util.Observable' + }, + + statics: { + requestId: 0 + }, + + url: null, + async: true, + method: null, + username: '', + password: '', + + /** + * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true) + * @type Boolean */ - Ext.data.Connection = function(config){ + disableCaching: true, + + /** + * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching + * through a cache buster. Defaults to '_dc' + * @type String + */ + disableCachingParam: '_dc', + + /** + * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000) + */ + timeout : 30000, + + /** + * @param {Object} extraParams (Optional) Any parameters to be appended to the request. + */ + + useDefaultHeader : true, + defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8', + useDefaultXhrHeader : true, + defaultXhrHeader : 'XMLHttpRequest', + + constructor : function(config) { + config = config || {}; Ext.apply(this, config); + this.addEvents( -
/** - * @event beforerequest + /** + * @event beforerequest * Fires before a network request is made to retrieve a data object. * @param {Connection} conn This Connection object. * @param {Object} options The options config object passed to the {@link #request} method. */ - BEFOREREQUEST, -
/** - * @event requestcomplete + 'beforerequest', + /** + * @event requestcomplete * Fires if the request was successfully completed. * @param {Connection} conn This Connection object. * @param {Object} response The XHR object containing the response data. - * See The XMLHttpRequest Object + * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a> * for details. * @param {Object} options The options config object passed to the {@link #request} method. */ - REQUESTCOMPLETE, -
/** - * @event requestexception + 'requestcomplete', + /** + * @event requestexception * Fires if an error HTTP status was returned from the server. - * See HTTP Status Code Definitions + * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a> * for details of HTTP status codes. * @param {Connection} conn This Connection object. * @param {Object} response The XHR object containing the response data. - * See The XMLHttpRequest Object + * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a> * for details. * @param {Object} options The options config object passed to the {@link #request} method. */ - REQUESTEXCEPTION + 'requestexception' ); - Ext.data.Connection.superclass.constructor.call(this); - }; - - Ext.extend(Ext.data.Connection, Ext.util.Observable, { -
/** - * @cfg {String} url (Optional)

The default URL to be used for requests to the server. Defaults to undefined.

- *

The url config may be a function which returns the URL to use for the Ajax request. The scope - * (this reference) of the function is the scope option passed to the {@link #request} method.

- */ -
/** - * @cfg {Object} extraParams (Optional) An object containing properties which are used as - * extra parameters to each request made by this object. (defaults to undefined) - */ -
/** - * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added - * to each request made by this object. (defaults to undefined) - */ -
/** - * @cfg {String} method (Optional) The default HTTP method to be used for requests. - * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used; - * otherwise, GET will be used.) - */ -
/** - * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000) - */ - timeout : 30000, -
/** - * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false) - * @type Boolean - */ - autoAbort:false, - -
/** - * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true) - * @type Boolean - */ - disableCaching: true, - -
/** - * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching - * through a cache buster. Defaults to '_dc' - * @type String - */ - disableCachingParam: '_dc', - -
/** - *

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.

- *

+        this.requests = {};
+        this.mixins.observable.constructor.call(this);
+    },
+
+    /**
+     * <p>Sends an HTTP request to a remote server.</p>
+     * <p><b>Important:</b> 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);
-   }
+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:
    - *
  • url : String/Function (Optional)
    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 - * {@link #url}.
  • - *
  • params : Object/String/Function (Optional)
    - * 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.
  • - *
  • method : String (Optional)
    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.
  • - *
  • callback : Function (Optional)
    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:
      - *
    • options : Object
      The parameter to the request call.
    • - *
    • success : Boolean
      True if the request succeeded.
    • - *
    • response : Object
      The XMLHttpRequest object containing the response data. - * See http://www.w3.org/TR/XMLHttpRequest/ for details about - * accessing elements of the response.
    • - *
  • - *
  • success : Function (Optional)
    The function - * to be called upon success of the request. The callback is passed the following - * parameters:
      - *
    • response : Object
      The XMLHttpRequest object containing the response data.
    • - *
    • options : Object
      The parameter to the request call.
    • - *
  • - *
  • failure : Function (Optional)
    The function - * to be called upon failure of the request. The callback is passed the - * following parameters:
      - *
    • response : Object
      The XMLHttpRequest object containing the response data.
    • - *
    • options : Object
      The parameter to the request call.
    • - *
  • - *
  • scope : Object (Optional)
    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.
  • - *
  • timeout : Number (Optional)
    The timeout in milliseconds to be used for this request. Defaults to 30 seconds.
  • - *
  • form : Element/HTMLElement/String (Optional)
    The <form> - * Element or the id of the <form> to pull parameters from.
  • - *
  • isUpload : Boolean (Optional)
    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 <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.

    - *
  • - *
  • headers : Object (Optional)
    Request - * headers to set for the request.
  • - *
  • xmlData : Object (Optional)
    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.
  • - *
  • jsonData : Object/String (Optional)
    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.
  • - *
  • disableCaching : Boolean (Optional)
    True - * to add a unique cache-buster param to GET requests.
  • - *

- *

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.

- * @return {Number} transactionId The id of the server transaction. This may be used - * to cancel the request. - */ - request : function(o){ - var me = this; - if(me.fireEvent(BEFOREREQUEST, me, o)){ - if (o.el) { - if(!Ext.isEmpty(o.indicatorText)){ - me.indicatorText = '
'+o.indicatorText+"
"; - } - if(me.indicatorText) { - Ext.getDom(o.el).innerHTML = me.indicatorText; - } - o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) { - Ext.getDom(o.el).innerHTML = response.responseText; - }); - } - - var p = o.params, - url = o.url || me.url, - method, - cb = {success: me.handleResponse, - failure: me.handleFailure, - scope: me, - argument: {options: o}, - timeout : o.timeout || me.timeout - }, - form, - serForm; - - - if (Ext.isFunction(p)) { - p = p.call(o.scope||WINDOW, o); - } - - p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p); - - if (Ext.isFunction(url)) { - url = url.call(o.scope || WINDOW, o); - } - - if((form = Ext.getDom(o.form))){ - url = url || form.action; - if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) { - return me.doFormUpload.call(me, o, p, url); - } - serForm = Ext.lib.Ajax.serializeForm(form); - p = p ? (p + '&' + serForm) : serForm; - } - - method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET); + * </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 + * 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> + * 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. + */ + request : function(options) { + options = options || {}; + var me = this, + scope = options.scope || window, + username = options.username || me.username, + password = options.password || me.password || '', + async, + requestOptions, + request, + headers, + xhr; + + if (me.fireEvent('beforerequest', me, options) !== false) { + + requestOptions = me.setOptions(options, scope); + + if (this.isFormUpload(options) === true) { + this.upload(options.form, requestOptions.url, requestOptions.data, options); + return null; + } - if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){ - var dcp = o.disableCachingParam || me.disableCachingParam; - url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime())); - } + // if autoabort is set, cancel the current transactions + if (options.autoAbort === true || me.autoAbort) { + me.abort(); + } - o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {}); + // create a connection object + xhr = this.getXhrInstance(); - if(o.autoAbort === true || me.autoAbort) { - me.abort(); - } + async = options.async !== false ? (options.async || me.async) : false; - if((method == GET || o.xmlData || o.jsonData) && p){ - url = Ext.urlAppend(url, p); - p = ''; - } - return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o)); - }else{ - return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null; + // open the request + if (username) { + xhr.open(requestOptions.method, requestOptions.url, async, username, password); + } else { + xhr.open(requestOptions.method, requestOptions.url, async); } - }, -
/** - * Determine whether this object has a request outstanding. - * @param {Number} transactionId (Optional) defaults to the last transaction - * @return {Boolean} True if there is an outstanding request. - */ - isLoading : function(transId){ - return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId; - }, - -
/** - * Aborts any outstanding request. - * @param {Number} transactionId (Optional) defaults to the last transaction - */ - abort : function(transId){ - if(transId || this.isLoading()){ - Ext.lib.Ajax.abort(transId || this.transId); - } - }, - - // private - handleResponse : function(response){ - this.transId = false; - var options = response.argument.options; - response.argument = options ? options.argument : null; - this.fireEvent(REQUESTCOMPLETE, this, response, options); - if(options.success){ - options.success.call(options.scope, response, options); + headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params); + + // create the transaction object + request = { + id: ++Ext.data.Connection.requestId, + xhr: xhr, + headers: headers, + options: options, + async: async, + timeout: setTimeout(function() { + request.timedout = true; + me.abort(request); + }, options.timeout || me.timeout) + }; + me.requests[request.id] = request; + + // bind our statechange listener + if (async) { + xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]); } - if(options.callback){ - options.callback.call(options.scope, options, true, response); - } - }, - - // private - handleFailure : function(response, e){ - this.transId = false; - var options = response.argument.options; - response.argument = options ? options.argument : null; - this.fireEvent(REQUESTEXCEPTION, this, response, options, e); - if(options.failure){ - options.failure.call(options.scope, response, options); - } - if(options.callback){ - options.callback.call(options.scope, options, false, response); + + // start the request! + xhr.send(requestOptions.data); + if (!async) { + return this.onComplete(request); } - }, - - // private - doFormUpload : function(o, ps, url){ - var id = Ext.id(), - doc = document, - frame = doc.createElement('iframe'), - form = Ext.getDom(o.form), + return request; + } else { + Ext.callback(options.callback, options.scope, [options, undefined, undefined]); + return null; + } + }, + + /** + * Upload a form using a hidden iframe. + * @param {Mixed} 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){ + form = Ext.getDom(form); + options = options || {}; + + var id = Ext.id(), + frame = document.createElement('iframe'), hiddens = [], - hd, encoding = 'multipart/form-data', buf = { target: form.target, @@ -361,233 +300,525 @@ Ext.Ajax.request({ encoding: form.encoding, enctype: form.enctype, action: form.action - }; - - /* - * Originally this behaviour was modified for Opera 10 to apply the secure URL after - * the frame had been added to the document. It seems this has since been corrected in - * Opera so the behaviour has been reverted, the URL will be set before being added. - */ - Ext.fly(frame).set({ - id: id, - name: id, - cls: 'x-hidden', - src: Ext.SSL_SECURE_URL - }); - - doc.body.appendChild(frame); - - // This is required so that IE doesn't pop the response up in a new window. - if(Ext.isIE){ - document.frames[id].name = id; - } - + }, hiddenItem; - Ext.fly(form).set({ - target: id, - method: POST, - enctype: encoding, - encoding: encoding, - action: url || buf.action - }); + /* + * Originally this behaviour was modified for Opera 10 to apply the secure URL after + * the frame had been added to the document. It seems this has since been corrected in + * Opera so the behaviour has been reverted, the URL will be set before being added. + */ + Ext.fly(frame).set({ + id: id, + name: id, + cls: Ext.baseCSSPrefix + 'hide-display', + src: Ext.SSL_SECURE_URL + }); + + document.body.appendChild(frame); + + // This is required so that IE doesn't pop the response up in a new window. + if (document.frames) { + document.frames[id].name = id; + } - // add dynamic params - Ext.iterate(Ext.urlDecode(ps, false), function(k, v){ - hd = doc.createElement('input'); - Ext.fly(hd).set({ + Ext.fly(form).set({ + target: id, + method: 'POST', + enctype: encoding, + encoding: encoding, + action: url || buf.action + }); + + // add dynamic params + if (params) { + Ext.iterate(Ext.Object.fromQueryString(params), function(name, value){ + hiddenItem = document.createElement('input'); + Ext.fly(hiddenItem).set({ type: 'hidden', - value: v, - name: k + value: value, + name: name }); - form.appendChild(hd); - hiddens.push(hd); + form.appendChild(hiddenItem); + hiddens.push(hiddenItem); }); + } - function cb(){ - var me = this, - // bogus response object - r = {responseText : '', - responseXML : null, - argument : o.argument}, - doc, - firstChild; - - try{ - doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document; - if(doc){ - if(doc.body){ - if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea - r.responseText = firstChild.value; - }else{ - r.responseText = doc.body.innerHTML; - } - } - //in IE the document may still have a body even if returns XML. - r.responseXML = doc.XMLDocument || doc; + Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true}); + form.submit(); + + Ext.fly(form).set(buf); + Ext.each(hiddens, function(h) { + Ext.removeNode(h); + }); + }, + + onUploadComplete: function(frame, options){ + var me = this, + // bogus response object + response = { + responseText: '', + responseXML: null + }, doc, firstChild; + + try { + doc = frame.contentWindow.document || frame.contentDocument || window.frames[id].document; + if (doc) { + if (doc.body) { + if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in textarea + response.responseText = firstChild.value; + } else { + response.responseText = doc.body.innerHTML; } } - catch(e) {} + //in IE the document may still have a body even if returns XML. + response.responseXML = doc.XMLDocument || doc; + } + } catch (e) { + } - Ext.EventManager.removeListener(frame, LOAD, cb, me); + me.fireEvent('requestcomplete', me, response, options); - me.fireEvent(REQUESTCOMPLETE, me, r, o); + Ext.callback(options.success, options.scope, [response, options]); + Ext.callback(options.callback, options.scope, [options, true, response]); - function runCallback(fn, scope, args){ - if(Ext.isFunction(fn)){ - fn.apply(scope, args); - } - } + setTimeout(function(){ + Ext.removeNode(frame); + }, 100); + }, - runCallback(o.success, o.scope, [r, o]); - runCallback(o.callback, o.scope, [o, true, r]); + /** + * Detect whether the form is intended to be used for an upload. + * @private + */ + isFormUpload: function(options){ + var form = this.getForm(options); + if (form) { + return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype'))); + } + return false; + }, + + /** + * Get the form object from options. + * @private + * @param {Object} options The request options + * @return {HTMLElement} The form, null if not passed + */ + getForm: function(options){ + return Ext.getDom(options.form) || null; + }, + + /** + * Set 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 + */ + setOptions: function(options, scope){ + var me = this, + params = options.params || {}, + extraParams = me.extraParams, + urlParams = options.urlParams, + url = options.url || me.url, + jsonData = options.jsonData, + method, + disableCache, + data; + + + // allow params to be a method that returns the params object + if (Ext.isFunction(params)) { + params = params.call(scope, options); + } - if(!me.debugUploads){ - setTimeout(function(){Ext.removeNode(frame);}, 100); - } - } + // allow url to be a method that returns the actual url + if (Ext.isFunction(url)) { + url = url.call(scope, options); + } - Ext.EventManager.on(frame, LOAD, cb, this); - form.submit(); + url = this.setupUrl(options, url); - Ext.fly(form).set(buf); - Ext.each(hiddens, function(h) { - Ext.removeNode(h); + //<debug> + if (!url) { + Ext.Error.raise({ + options: options, + msg: 'No URL specified' }); } - }); -})(); - -
/** - * @class Ext.Ajax - * @extends Ext.data.Connection - *

The global Ajax request class that provides a simple way to make Ajax requests - * with maximum flexibility.

- *

Since Ext.Ajax is a singleton, you can set common properties/events for it once - * and override them at the request function level only if necessary.

- *

Common Properties you may want to set are:

    - *
  • {@link #method}

  • - *
  • {@link #extraParams}

  • - *
  • {@link #url}

  • - *
- *

-// Default headers to pass in every request
-Ext.Ajax.defaultHeaders = {
-    'Powered-By': 'Ext'
-};
- * 
- *

- *

Common Events you may want to set are:

    - *
  • {@link Ext.data.Connection#beforerequest beforerequest}

  • - *
  • {@link Ext.data.Connection#requestcomplete requestcomplete}

  • - *
  • {@link Ext.data.Connection#requestexception requestexception}

  • - *
- *

-// Example: show a spinner during all Ajax requests
-Ext.Ajax.on('beforerequest', this.showSpinner, this);
-Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
-Ext.Ajax.on('requestexception', this.hideSpinner, this);
- * 
- *

- *

An example request:

- *

-// Basic request
-Ext.Ajax.{@link Ext.data.Connection#request request}({
-   url: 'foo.php',
-   success: someFn,
-   failure: otherFn,
-   headers: {
-       'my-header': 'foo'
-   },
-   params: { foo: 'bar' }
-});
+        //</debug>
 
-// Simple ajax form submission
-Ext.Ajax.{@link Ext.data.Connection#request request}({
-    form: 'some-form',
-    params: 'foo=bar'
-});
- * 
- *

- * @singleton - */ -Ext.Ajax = new Ext.data.Connection({ -
/** - * @cfg {String} url @hide - */ -
/** - * @cfg {Object} extraParams @hide - */ -
/** - * @cfg {Object} defaultHeaders @hide + // check for xml or json data, and make sure json data is encoded + data = options.rawData || options.xmlData || jsonData || null; + if (jsonData && !Ext.isPrimitive(jsonData)) { + data = Ext.encode(data); + } + + // make sure params are a url encoded string and include any extraParams if specified + if (Ext.isObject(params)) { + params = Ext.Object.toQueryString(params); + } + + if (Ext.isObject(extraParams)) { + extraParams = Ext.Object.toQueryString(extraParams); + } + + params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : ''); + + urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams; + + params = this.setupParams(options, params); + + // decide the proper method for this request + method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase(); + this.setupMethod(options, method); + + + disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false; + // if the method is get append date to prevent caching + if (method === 'GET' && disableCache) { + url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime())); + } + + // if the method is get or there is json/xml data append the params to the url + if ((method == 'GET' || data) && params) { + url = Ext.urlAppend(url, params); + params = null; + } + + // allow params to be forced into the url + if (urlParams) { + url = Ext.urlAppend(url, urlParams); + } + + return { + url: url, + method: method, + data: data || params || null + }; + }, + + /** + * Template method for overriding url + * @private + * @param {Object} options + * @param {String} url + * @return {String} The modified url */ -
/** - * @cfg {String} method (Optional) @hide + setupUrl: function(options, url){ + var form = this.getForm(options); + if (form) { + url = url || form.action; + } + return url; + }, + + + /** + * Template method for overriding params + * @private + * @param {Object} options + * @param {String} params + * @return {String} The modified params */ -
/** - * @cfg {Number} timeout (Optional) @hide + setupParams: function(options, params) { + var form = this.getForm(options), + serializedForm; + if (form && !this.isFormUpload(options)) { + serializedForm = Ext.core.Element.serializeForm(form); + params = params ? (params + '&' + serializedForm) : serializedForm; + } + return params; + }, + + /** + * Template method for overriding method + * @private + * @param {Object} options + * @param {String} method + * @return {String} The modified method */ -
/** - * @cfg {Boolean} autoAbort (Optional) @hide + setupMethod: function(options, method){ + if (this.isFormUpload(options)) { + return 'POST'; + } + return method; + }, + + /** + * Setup all the headers for the request + * @private + * @param {Object} xhr The xhr object + * @param {Object} options The options for the request + * @param {Object} data The data for the request + * @param {Object} params The params for the request */ + setupHeaders: function(xhr, options, data, params){ + var me = this, + headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}), + contentType = me.defaultPostHeader, + jsonData = options.jsonData, + xmlData = options.xmlData, + key, + header; + + if (!headers['Content-Type'] && (data || params)) { + if (data) { + if (options.rawData) { + contentType = 'text/plain'; + } else { + if (xmlData && Ext.isDefined(xmlData)) { + contentType = 'text/xml'; + } else if (jsonData && Ext.isDefined(jsonData)) { + contentType = 'application/json'; + } + } + } + headers['Content-Type'] = contentType; + } -
/** - * @cfg {Boolean} disableCaching (Optional) @hide + if (me.useDefaultXhrHeader && !headers['X-Requested-With']) { + headers['X-Requested-With'] = me.defaultXhrHeader; + } + // set up all the request headers on the xhr object + try{ + for (key in headers) { + if (headers.hasOwnProperty(key)) { + header = headers[key]; + xhr.setRequestHeader(key, header); + } + + } + } catch(e) { + me.fireEvent('exception', key, header); + } + return headers; + }, + + /** + * Creates the appropriate XHR transport for the browser. + * @private */ + getXhrInstance: (function(){ + var options = [function(){ + return new XMLHttpRequest(); + }, function(){ + return new ActiveXObject('MSXML2.XMLHTTP.3.0'); + }, function(){ + return new ActiveXObject('MSXML2.XMLHTTP'); + }, function(){ + return new ActiveXObject('Microsoft.XMLHTTP'); + }], i = 0, + len = options.length, + xhr; + + for(; i < len; ++i) { + try{ + xhr = options[i]; + xhr(); + break; + }catch(e){} + } + return xhr; + })(), -
/** - * @property disableCaching - * True to add a unique cache-buster param to GET requests. (defaults to true) - * @type Boolean + /** + * Determine whether this object has a request outstanding. + * @param {Object} request (Optional) defaults to the last transaction + * @return {Boolean} True if there is an outstanding request. */ -
/** - * @property url - * The default URL to be used for requests to the server. (defaults to undefined) - * If the server receives all requests through one URL, setting this once is easier than - * entering it on every request. - * @type String + isLoading : function(request) { + if (!(request && request.xhr)) { + return false; + } + // if there is a connection and readyState is not 0 or 4 + var state = request.xhr.readyState; + return !(state === 0 || state == 4); + }, + + /** + * Aborts any outstanding request. + * @param {Object} request (Optional) defaults to the last request */ -
/** - * @property extraParams - * An object containing properties which are used as extra parameters to each request made - * by this object (defaults to undefined). Session information and other data that you need - * to pass with each request are commonly put here. - * @type Object + abort : function(request) { + var me = this, + requests = me.requests, + id; + + if (request && me.isLoading(request)) { + /** + * Clear out the onreadystatechange here, this allows us + * greater control, the browser may/may not fire the function + * depending on a series of conditions. + */ + request.xhr.onreadystatechange = null; + request.xhr.abort(); + me.clearTimeout(request); + if (!request.timedout) { + request.aborted = true; + } + me.onComplete(request); + me.cleanup(request); + } else if (!request) { + for(id in requests) { + if (requests.hasOwnProperty(id)) { + me.abort(requests[id]); + } + } + } + }, + + /** + * Fires when the state of the xhr changes + * @private + * @param {Object} request The request */ -
/** - * @property defaultHeaders - * An object containing request headers which are added to each request made by this object - * (defaults to undefined). - * @type Object + onStateChange : function(request) { + if (request.xhr.readyState == 4) { + this.clearTimeout(request); + this.onComplete(request); + this.cleanup(request); + } + }, + + /** + * Clear the timeout on the request + * @private + * @param {Object} The request */ -
/** - * @property method - * The default HTTP method to be used for requests. Note that this is case-sensitive and - * should be all caps (defaults to undefined; if not set but params are present will use - * "POST", otherwise will use "GET".) - * @type String + clearTimeout: function(request){ + clearTimeout(request.timeout); + delete request.timeout; + }, + + /** + * Clean up any left over information from the request + * @private + * @param {Object} The request */ -
/** - * @property timeout - * The timeout in milliseconds to be used for requests. (defaults to 30000) - * @type Number + cleanup: function(request){ + request.xhr = null; + delete request.xhr; + }, + + /** + * To be called when the request has come back from the server + * @private + * @param {Object} request + * @return {Object} The response */ - -
/** - * @property autoAbort - * Whether a new request should abort any pending requests. (defaults to false) - * @type Boolean + onComplete : function(request) { + var me = this, + options = request.options, + result = me.parseStatus(request.xhr.status), + success = result.success, + response; + + if (success) { + response = me.createResponse(request); + me.fireEvent('requestcomplete', me, response, options); + Ext.callback(options.success, options.scope, [response, options]); + } else { + if (result.isException || request.aborted || request.timedout) { + response = me.createException(request); + } else { + response = me.createResponse(request); + } + me.fireEvent('requestexception', me, response, options); + Ext.callback(options.failure, options.scope, [response, options]); + } + Ext.callback(options.callback, options.scope, [options, success, response]); + delete me.requests[request.id]; + return response; + }, + + /** + * Check if the response status was successful + * @param {Number} status The status code + * @return {Object} An object containing success/status state + */ + parseStatus: function(status) { + // see: https://prototype.lighthouseapp.com/projects/8886/tickets/129-ie-mangles-http-response-status-code-204-to-1223 + status = status == 1223 ? 204 : status; + + var success = (status >= 200 && status < 300) || status == 304, + isException = false; + + if (!success) { + switch (status) { + case 12002: + case 12029: + case 12030: + case 12031: + case 12152: + case 13030: + isException = true; + break; + } + } + return { + success: success, + isException: isException + }; + }, + + /** + * Create the response object + * @private + * @param {Object} request */ - autoAbort : false, + createResponse : function(request) { + var xhr = request.xhr, + headers = {}, + lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'), + count = lines.length, + line, index, key, value, response; + + while (count--) { + line = lines[count]; + index = line.indexOf(':'); + if(index >= 0) { + key = line.substr(0, index).toLowerCase(); + if (line.charAt(index + 1) == ' ') { + ++index; + } + headers[key] = line.substr(index + 1); + } + } -
/** - * Serialize the passed form into a url encoded string - * @param {String/HTMLElement} form - * @return {String} + request.xhr = null; + delete request.xhr; + + response = { + request: request, + requestId : request.id, + status : xhr.status, + statusText : xhr.statusText, + getResponseHeader : function(header){ return headers[header.toLowerCase()]; }, + getAllResponseHeaders : function(){ return headers; }, + responseText : xhr.responseText, + responseXML : xhr.responseXML + }; + + // If we don't explicitly tear down the xhr reference, IE6/IE7 will hold this in the closure of the + // functions created with getResponseHeader/getAllResponseHeaders + xhr = null; + return response; + }, + + /** + * Create the exception object + * @private + * @param {Object} request */ - serializeForm : function(form){ - return Ext.lib.Ajax.serializeForm(form); + createException : function(request) { + return { + request : request, + requestId : request.id, + status : request.aborted ? -1 : 0, + statusText : request.aborted ? 'transaction aborted' : 'communication failure', + aborted: request.aborted, + timedout: request.timedout + }; } }); -
- - \ No newline at end of file +
\ No newline at end of file