text: /**
   * The Form panel presents a set of form fields and provides convenient ways to load and save data. Usually a form
   * panel just contains the set of fields you want to display, ordered inside the items configuration like this:
   *
   *     @example
   *     var form = Ext.create('Ext.form.Panel', {
   *         fullscreen: true,
   *         items: [
   *             {
   *                 xtype: 'textfield',
   *                 name: 'name',
   *                 label: 'Name'
   *             },
   *             {
   *                 xtype: 'emailfield',
   *                 name: 'email',
   *                 label: 'Email'
   *             },
   *             {
   *                 xtype: 'passwordfield',
   *                 name: 'password',
   *                 label: 'Password'
   *             }
   *         ]
   *     });
   *
   * Here we just created a simple form panel which could be used as a registration form to sign up to your service. We
   * added a plain {@link Ext.field.Text text field} for the user's Name, an {@link Ext.field.Email email field} and
   * finally a {@link Ext.field.Password password field}. In each case we provided a {@link Ext.field.Field#name name}
   * config on the field so that we can identify it later on when we load and save data on the form.
   *
   * ##Loading data
   *
   * Using the form we created above, we can load data into it in a few different ways, the easiest is to use
   * {@link #setValues}:
   *
   *     form.setValues({
   *         name: 'Peter',
   *         email: 'peter.venkman@gb.com',
   *         password: 'secret'
   *     });
   *
   * It's also easy to load {@link Ext.data.Model Model} instances into a form - let's say we have a User model and want
   * to load a particular instance into our form:
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         config: {
   *             fields: ['name', 'email', 'password']
   *         }
   *     });
   *
   *     var ed = Ext.create('MyApp.model.User', {
   *         name: 'Peter',
   *         email: 'peter.venkman@gb.com',
   *         password: 'secret'
   *     });
   *
   *     form.setRecord(ed);
   *
   * ##Retrieving form data
   *
   * Getting data out of the form panel is simple and is usually achieve via the {@link #getValues} method:
   *
   *     var values = form.getValues();
   *
   *     //values now looks like this:
   *     {
   *         name: 'Peter',
   *         email: 'peter.venkman@gb.com',
   *         password: 'secret'
   *     }
   *
   * It's also possible to listen to the change events on individual fields to get more timely notification of changes
   * that the user is making. Here we expand on the example above with the User model, updating the model as soon as
   * any of the fields are changed:
   *
   *     var form = Ext.create('Ext.form.Panel', {
   *         listeners: {
   *             '> field': {
   *                 change: function(field, newValue, oldValue) {
   *                     ed.set(field.getName(), newValue);
   *                 }
   *             }
   *         },
   *         items: [
   *             {
   *                 xtype: 'textfield',
   *                 name: 'name',
   *                 label: 'Name'
   *             },
   *             {
   *                 xtype: 'emailfield',
   *                 name: 'email',
   *                 label: 'Email'
   *             },
   *             {
   *                 xtype: 'passwordfield',
   *                 name: 'password',
   *                 label: 'Password'
   *             }
   *         ]
   *     });
   *
   * The above attached a listener to the {@link Ext.field.Text#change change} event of each form
   * field that is a direct child of the form panel. Our listener gets the name of the field that fired the change event,
   * and updates our {@link Ext.data.Model Model} instance with the new value. For example, changing the email field
   * in the form will update the Model's email field.
   *
   * ##Submitting forms
   *
   * There are a few ways to submit form data. In our example above we have a Model instance that we have updated, giving
   * us the option to use the Model's {@link Ext.data.Model#save save} method to persist the changes back to our server,
   * without using a traditional form submission. Alternatively, we can send a normal browser form submit using the
   * {@link #method} method:
   *
   *     form.submit({
   *         url: 'url/to/submit/to',
   *         method: 'POST',
   *         success: function() {
   *             alert('form submitted successfully!');
   *         }
   *     });
   *
   * In this case we provided the `url` to submit the form to inside the submit call - alternatively you can just set the
   * {@link #url} configuration when you create the form. We can specify other parameters (see {@link #method} for a
   * full list), including callback functions for success and failure, which are called depending on whether or not the
   * form submission was successful. These functions are usually used to take some action in your app after your data
   * has been saved to the server side.
   */
  Ext.define('Ext.form.Panel', {
      alternateClassName: 'Ext.form.FormPanel',
      extend  : 'Ext.Panel',
      xtype   : 'formpanel',
      requires: ['Ext.XTemplate', 'Ext.field.Checkbox', 'Ext.Ajax'],
  
      /**
       * @event submit
       * @preventable
       * Fires upon successful (Ajax-based) form submission.
       * @param {Ext.form.Panel} this This FormPanel.
       * @param {Object} result The result object as returned by the server.
       * @param {Ext.event.Event} e The event object.
       */
  
      /**
       * @event beforesubmit
       * @preventable
       * Fires immediately preceding any Form submit action.
       * Implementations may adjust submitted form values or options prior to execution.
       * A return value of `false` from this listener will abort the submission
       * attempt (regardless of `standardSubmit` configuration).
       * @param {Ext.form.Panel} this This FormPanel.
       * @param {Object} values A hash collection of the qualified form values about to be submitted.
       * @param {Object} options Submission options hash (only available when `standardSubmit` is `false`).
       * @param {Ext.event.Event} e The event object if the form was submitted via a HTML5 form submit event.
       */
  
      /**
       * @event exception
       * Fires when either the Ajax HTTP request reports a failure OR the server returns a `success:false`
       * response in the result payload.
       * @param {Ext.form.Panel} this This FormPanel.
       * @param {Object} result Either a failed Ext.data.Connection request object or a failed (logical) server.
       * response payload.
       */
  
      config: {
          /**
           * @cfg {String} baseCls
           * @inheritdoc
           */
          baseCls: Ext.baseCSSPrefix + 'form',
  
          /**
           * @cfg {Boolean} standardSubmit
           * Whether or not we want to perform a standard form submit.
           * @accessor
           */
          standardSubmit: false,
  
          /**
           * @cfg {String} url
           * The default url for submit actions.
           * @accessor
           */
          url: null,
  
          /**
           * @cfg {String} enctype
           * The enctype attribute for the form, specifies how the form should be encoded when submitting
           */
          enctype: null,
  
          /**
           * @cfg {Object} baseParams
           * Optional hash of params to be sent (when `standardSubmit` configuration is `false`) on every submit.
           * @accessor
           */
          baseParams: null,
  
          /**
           * @cfg {Object} submitOnAction
           * When this is set to `true`, the form will automatically submit itself whenever the `action`
           * event fires on a field in this form. The action event usually fires whenever you press
           * go or enter inside a textfield.
           * @accessor
           */
          submitOnAction: false,
  
          /**
           * @cfg {Ext.data.Model} record The model instance of this form. Can by dynamically set at any time.
           * @accessor
           */
          record: null,
  
          /**
           * @cfg {String} method
           * The method which this form will be submitted. `post` or `get`.
           */
          method: 'post',
  
          /**
           * @inheritdoc
           */
          scrollable: true,
  
          /**
           * @cfg {Boolean} trackResetOnLoad
           * If set to true, {@link #reset}() resets to the last loaded or {@link #setValues}() data instead of
           * when the form was first created.
           */
          trackResetOnLoad:false,
  
          /**
           * @cfg {Object} api
           * If specified, load and submit actions will be loaded and submitted via Ext Direct.  Methods which have been imported by
           * {@link Ext.direct.Manager} can be specified here to load and submit forms. API methods may also be
           * specified as strings and will be parsed into the actual functions when the first submit or load has occurred. Such as the following:
           *
           *     api: {
           *         load: App.ss.MyProfile.load,
           *         submit: App.ss.MyProfile.submit
           *     }
           *
           *     api: {
           *         load: 'App.ss.MyProfile.load',
           *         submit: 'App.ss.MyProfile.submit'
           *     }
           *
           * Load actions can use {@link #paramOrder} or {@link #paramsAsHash} to customize how the load method
           * is invoked.  Submit actions will always use a standard form submit. The `formHandler` configuration
           * (see Ext.direct.RemotingProvider#action) must be set on the associated server-side method which has
           * been imported by {@link Ext.direct.Manager}.
           */
          api: null,
  
          /**
           * @cfg {String/String[]} paramOrder
           * A list of params to be executed server side. Only used for the {@link #api} `load`
           * configuration.
           *
           * Specify the params in the order in which they must be executed on the
           * server-side as either (1) an Array of String values, or (2) a String of params
           * delimited by either whitespace, comma, or pipe. For example,
           * any of the following would be acceptable:
           *
           *     paramOrder: ['param1','param2','param3']
           *     paramOrder: 'param1 param2 param3'
           *     paramOrder: 'param1,param2,param3'
           *     paramOrder: 'param1|param2|param'
           */
          paramOrder: null,
  
          /**
           * @cfg {Boolean} paramsAsHash
           * Only used for the {@link #api} `load` configuration. If true, parameters will be sent as a
           * single hash collection of named arguments. Providing a {@link #paramOrder} nullifies this
           * configuration.
           */
          paramsAsHash: null,
  
          /**
           * @cfg {Number} timeout
           * Timeout for form actions in seconds.
           */
          timeout: 30,
  
          /**
           * @cfg {Boolean} multipartDetection
           * If this is enabled the form will automatically detect the need to use 'multipart/form-data' during submission.
           */
          multipartDetection: true,
  
          /**
           * @cfg {Boolean} enableSubmissionForm
           * The submission form is generated but never added to the dom. It is a submittable version of your form panel, allowing for fields
           * that are not simple textfields to be properly submitted to servers. It will also send values that are easier to parse
           * with server side code.
           *
           * If this is false we will attempt to subject the raw form inside the form panel.
           */
          enableSubmissionForm: true
      },
  
      getElementConfig: function() {
          var config = this.callParent();
          config.tag = "form";
          // Added a submit input for standard form submission. This cannot have "display: none;" or it will not work
          config.children.push({
              tag: 'input',
              type: 'submit',
              style: 'visibility: hidden; width: 0; height: 0; position: absolute; right: 0; bottom: 0;'
          });
  
          return config;
      },
  
      /**
       * @private
       */
      initialize: function() {
          var me = this;
          me.callParent();
  
          me.element.on({
              submit: 'onSubmit',
              scope : me
          });
      },
  
      applyEnctype: function(newValue) {
          var  form = this.element.dom || null;
          if(form) {
              if (newValue) {
                  form.setAttribute("enctype", newValue);
              } else {
                  form.setAttribute("enctype");
              }
          }
      },
  
      updateRecord: function(newRecord) {
          var fields, values, name;
  
          if (newRecord) {
              values = this.getValues();
              for (name in values) {
                  if (values.hasOwnProperty(name) && newRecord.getField(name)) {
                      newRecord.set(name, values[name]);
                  }
              }
          }
          return this;
      },
  
      /**
       * Loads matching fields from a model instance into this form.
       * @param {Ext.data.Model} record The model instance.
       * @return {Ext.form.Panel} This form.
       */
      setRecord: function(record) {
          var me = this;
  
          if (record && record.data) {
              me.setValues(record.data);
          }
  
          me._record = record;
  
          return this;
      },
  
      /**
       * @private
       */
      onSubmit: function(e) {
          var me = this;
          if (e && !me.getStandardSubmit()) {
              e.stopEvent();
          } else {
              this.submit(null, e);
          }
      },
  
      updateSubmitOnAction: function(newSubmitOnAction) {
          if (newSubmitOnAction) {
              this.on({
                  action: 'onFieldAction',
                  scope: this
              });
          } else {
              this.un({
                  action: 'onFieldAction',
                  scope: this
              });
          }
      },
  
      /**
       * @private
       */
      onFieldAction: function(field) {
          if (this.getSubmitOnAction()) {
              field.blur();
              this.submit();
          }
      },
  
      /**
       * Performs a Ajax-based submission of form values (if {@link #standardSubmit} is false) or otherwise
       * executes a standard HTML Form submit action.
       *
       * **Notes**
       *
       *  1. Only the first parameter is implemented. Put all other parameters inside the first
       *  parameter:
       *
       *     submit({params: "" ,headers: "" etc.})
       *
       *  2. Submit example:
       *
       *     myForm.submit({
       *       url: 'PostMyData/To',
       *       method: 'Post',
       *       success: function() { Ext.Msg.alert("success"); },
       *       failure: function() { Ext.Msg.alert("error"); }
       *     });
       *
       *  3. Parameters and values only submit for a POST and not for a GET.
       *
       * @param {Object} options
       * The configuration when submitting this form.
       *
       * The following are the configurations when submitting via Ajax only:
       *
       * @param {String} options.url
       * The url for the action (defaults to the form's {@link #url}).
       *
       * @param {String} options.method
       * The form method to use (defaults to the form's {@link #method}, or POST if not defined).
       *
       * @param {Object} options.headers
       * Request headers to set for the action.
       *
       * @param {Boolean} [options.autoAbort=false]
       * `true` to abort any pending Ajax request prior to submission.
       * __Note:__ Has no effect when `{@link #standardSubmit}` is enabled.
       *
       * @param {Number} options.timeout
       * The number is seconds the loading will timeout in.
       *
       * The following are the configurations when loading via Ajax or Direct:
       *
       * @param {String/Object} options.params
       * The params to pass when submitting this form (defaults to this forms {@link #baseParams}).
       * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
       *
       * @param {Boolean} [options.submitDisabled=false]
       * `true` to submit all fields regardless of disabled state.
       * __Note:__ Has no effect when `{@link #standardSubmit}` is enabled.
       *
       * @param {String/Object} [options.waitMsg]
       * If specified, the value which is passed to the loading {@link #masked mask}. See {@link #masked} for
       * more information.
       *
       * @param {Function} options.success
       * The callback that will be invoked after a successful response. A response is successful if
       * a response is received from the server and is a JSON object where the `success` property is set
       * to `true`, `{"success": true}`.
       *
       * The function is passed the following parameters and can be used for submitting via Ajax or Direct:
       *
       * @param {Ext.form.Panel} options.success.form
       * The {@link Ext.form.Panel} that requested the action.
       *
       * @param {Object/Ext.direct.Event} options.success.result
       * The result object returned by the server as a result of the submit request. If the submit is sent using Ext Direct,
       * this will return the {@link Ext.direct.Event} instance, otherwise will return an Object.
       *
       * @param {Object} options.success.data
       * The parsed data returned by the server.
       *
       * @param {Function} options.failure
       * The callback that will be invoked after a failed transaction attempt.
       *
       * The function is passed the following parameters and can be used for submitting via Ajax or Direct:
       *
       * @param {Ext.form.Panel} options.failure.form
       * The {@link Ext.form.Panel} that requested the submit.
       *
       * @param {Ext.form.Panel} options.failure.result
       * The failed response or result object returned by the server which performed the operation.
       *
       * @param {Object} options.success.data
       * The parsed data returned by the server.
       *
       * @param {Object} options.scope
       * The scope in which to call the callback functions (The `this` reference for the callback functions).
       *
       * @return {Ext.data.Connection} The request object if the {@link #standardSubmit} config is false.
       * If the standardSubmit config is true, then the return value is undefined.
       */
      submit: function(options, e) {
          options = options || {};
  
          var me = this,
              formValues = me.getValues(me.getStandardSubmit() || !options.submitDisabled),
              form = me.element.dom || {};
  
          if(this.getEnableSubmissionForm()) {
              form = this.createSubmissionForm(form, formValues);
          }
  
          options = Ext.apply({
              url : me.getUrl() || form.action,
              submit: false,
              form: form,
              method : me.getMethod() || form.method || 'post',
              autoAbort : false,
              params : null,
              waitMsg : null,
              headers : null,
              success : null,
              failure : null
          }, options || {});
  
          return me.fireAction('beforesubmit', [me, formValues, options, e], 'doBeforeSubmit', null, null, 'after');
      },
  
      createSubmissionForm: function(form, values) {
          var fields = this.getFields(),
              name, input, field, fileinputElement, inputComponent;
  
          if(form.nodeType === 1) {
              form = form.cloneNode(false);
  
              for (name in values) {
                  input = document.createElement("input");
                  input.setAttribute("type", "text");
                  input.setAttribute("name", name);
                  input.setAttribute("value", values[name]);
                  form.appendChild(input);
              }
          }
  
          for (name in fields) {
              if (fields.hasOwnProperty(name)) {
                  field = fields[name];
                  if(field.isFile) {
                      if(!form.$fileswap) form.$fileswap = [];
  
                      inputComponent = field.getComponent().input;
                      fileinputElement = inputComponent.dom;
                      input = fileinputElement.cloneNode(true);
                      fileinputElement.parentNode.insertBefore(input, fileinputElement.nextSibling);
                      form.appendChild(fileinputElement);
                      form.$fileswap.push({original: fileinputElement, placeholder: input});
                  } else if(field.isPassword) {
                      if(field.getComponent().getType !== "password") {
                          field.setRevealed(false);
                      }
                  }
              }
          }
  
          return form;
      },
  
      doBeforeSubmit: function(me, formValues, options) {
          var form = options.form || {},
              multipartDetected = false;
  
          if(this.getMultipartDetection() === true) {
              this.getFieldsAsArray().forEach(function(field) {
                  if(field.isFile === true) {
                      multipartDetected = true;
                      return false;
                  }
              });
  
              if(multipartDetected) {
                  form.setAttribute("enctype", "multipart/form-data");
              }
          }
  
          if(options.enctype) {
              form.setAttribute("enctype", options.enctype);
          }
  
          if (me.getStandardSubmit()) {
              if (options.url && Ext.isEmpty(form.action)) {
                  form.action = options.url;
              }
  
              // Spinner fields must have their components enabled *before* submitting or else the value
              // will not be posted.
              var fields = this.query('spinnerfield'),
                  ln = fields.length,
                  i, field;
  
              for (i = 0; i < ln; i++) {
                  field = fields[i];
                  if (!field.getDisabled()) {
                      field.getComponent().setDisabled(false);
                  }
              }
  
              form.method = (options.method || form.method).toLowerCase();
              form.submit();
          } else {
              var api = me.getApi(),
                  url = options.url || me.getUrl(),
                  scope = options.scope || me,
                  waitMsg = options.waitMsg,
                  failureFn = function(response, responseText) {
                      if (Ext.isFunction(options.failure)) {
                          options.failure.call(scope, me, response, responseText);
                      }
  
                      me.fireEvent('exception', me, response);
                  },
                  successFn = function(response, responseText) {
                      if (Ext.isFunction(options.success)) {
                          options.success.call(options.scope || me, me, response, responseText);
                      }
  
                      me.fireEvent('submit', me, response);
                  },
                  submit;
  
              if (options.waitMsg) {
                  if (typeof waitMsg === 'string') {
                      waitMsg = {
                          xtype   : 'loadmask',
                          message : waitMsg
                      };
                  }
  
                  me.setMasked(waitMsg);
              }
  
              if (api) {
                  submit = api.submit;
  
                  if (typeof submit === 'string') {
                      submit = Ext.direct.Manager.parseMethod(submit);
  
                      if (submit) {
                          api.submit = submit;
                      }
                  }
  
                  if (submit) {
                      return submit(this.element, function(data, response, success) {
                          me.setMasked(false);
  
                          if (success) {
                              if (data.success) {
                                  successFn(response, data);
                              } else {
                                  failureFn(response, data);
                              }
                          } else {
                              failureFn(response, data);
                          }
                      }, this);
                  }
              } else {
                  var request = Ext.merge({},
                      {
                          url: url,
                          timeout: this.getTimeout() * 1000,
                          form: form,
                          scope: me
                      },
                      options
                  );
                  delete request.success;
                  delete request.failure;
  
                  request.params = Ext.merge(me.getBaseParams() || {}, options.params);
                  request.header = Ext.apply(
                      {
                          'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
                      },
                      options.headers || {}
                  );
                  request.callback = function(callbackOptions, success, response) {
                      var me = this,
                          responseText = response.responseText,
                          responseXML = response.responseXML,
                          statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);
  
                      if(form.$fileswap) {
                          var original, placeholder;
                          Ext.each(form.$fileswap, function(item) {
                              original = item.original;
                              placeholder = item.placeholder;
  
                              placeholder.parentNode.insertBefore(original, placeholder.nextSibling);
                              placeholder.parentNode.removeChild(placeholder);
                          });
                          form.$fileswap = null;
                          delete form.$fileswap;
                      }
  
                      me.setMasked(false);
  
                      if(response.success === false) success = false;
                      if (success) {
                          if (statusResult && responseText && responseText.length === 0) {
                              success = true;
                          } else {
                              if(!Ext.isEmpty(response.responseBytes)) {
                                  success = statusResult.success;
                              }else {
                                  if(Ext.isString(responseText) && response.request.options.responseType === "text") {
                                      response.success = true;
                                  } else if(Ext.isString(responseText)) {
                                      try {
                                          response = Ext.decode(responseText);
                                      }catch (e){
                                          response.success = false;
                                          response.error = e;
                                          response.message = e.message;
                                      }
                                  } else if(Ext.isSimpleObject(responseText)) {
                                      response = responseText;
                                      Ext.applyIf(response, {success:true});
                                  }
  
                                  if(!Ext.isEmpty(responseXML)){
                                      response.success = true;
                                  }
                                  success = !!response.success;
                              }
                          }
                          if (success) {
                              successFn(response, responseText);
                          } else {
                              failureFn(response, responseText);
                          }
                      }
                      else {
                          failureFn(response, responseText);
                      }
                  };
  
                  if(Ext.feature.has.XHR2 && request.xhr2) {
                      delete request.form;
                      var formData = new FormData(form);
                      if (request.params) {
                          Ext.iterate(request.params, function(name, value) {
                              if (Ext.isArray(value)) {
                                  Ext.each(value, function(v) {
                                      formData.append(name, v);
                                  });
                              } else {
                                  formData.append(name, value);
                              }
                          });
                          delete request.params;
                      }
                      request.data = formData;
                  }
  
                  return Ext.Ajax.request(request);
              }
          }
      },
  
      /**
       * Performs an Ajax or Ext Direct call to load values for this form.
       *
       * @param {Object} options
       * The configuration when loading this form.
       *
       * The following are the configurations when loading via Ajax only:
       *
       * @param {String} options.url
       * The url for the action (defaults to the form's {@link #url}).
       *
       * @param {String} options.method
       * The form method to use (defaults to the form's {@link #method}, or GET if not defined).
       *
       * @param {Object} options.headers
       * Request headers to set for the action.
       *
       * @param {Number} options.timeout
       * The number is seconds the loading will timeout in.
       *
       * The following are the configurations when loading via Ajax or Direct:
       *
       * @param {Boolean} [options.autoAbort=false]
       * `true` to abort any pending Ajax request prior to loading.
       *
       * @param {String/Object} options.params
       * The params to pass when submitting this form (defaults to this forms {@link #baseParams}).
       * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.
       *
       * @param {String/Object} [options.waitMsg]
       * If specified, the value which is passed to the loading {@link #masked mask}. See {@link #masked} for
       * more information.
       *
       * @param {Function} options.success
       * The callback that will be invoked after a successful response. A response is successful if
       * a response is received from the server and is a JSON object where the `success` property is set
       * to `true`, `{"success": true}`.
       *
       * The function is passed the following parameters and can be used for loading via Ajax or Direct:
       *
       * @param {Ext.form.Panel} options.success.form
       * The {@link Ext.form.Panel} that requested the load.
       *
       * @param {Object/Ext.direct.Event} options.success.result
       * The result object returned by the server as a result of the load request. If the loading was done via Ext Direct,
       * will return the {@link Ext.direct.Event} instance, otherwise will return an Object.
       *
       * @param {Object} options.success.data
       * The parsed data returned by the server.
       *
       * @param {Function} options.failure
       * The callback that will be invoked after a failed transaction attempt.
       *
       * The function is passed the following parameters and can be used for loading via Ajax or Direct:
       *
       * @param {Ext.form.Panel} options.failure.form
       * The {@link Ext.form.Panel} that requested the load.
       *
       * @param {Ext.form.Panel} options.failure.result
       * The failed response or result object returned by the server which performed the operation.
       *
       * @param {Object} options.success.data
       * The parsed data returned by the server.
       *
       * @param {Object} options.scope
       * The scope in which to call the callback functions (The `this` reference for the callback functions).
       *
       * @return {Ext.data.Connection} The request object.
       */
      load : function(options) {
          options = options || {};
  
          var me = this,
              api = me.getApi(),
              url = me.getUrl() || options.url,
              waitMsg = options.waitMsg,
              successFn = function(response, data) {
                  me.setValues(data.data);
  
                  if (Ext.isFunction(options.success)) {
                      options.success.call(options.scope || me, me, response, data);
                  }
  
                  me.fireEvent('load', me, response);
              },
              failureFn = function(response, data) {
                  if (Ext.isFunction(options.failure)) {
                      options.failure.call(scope, me, response, data);
                  }
  
                  me.fireEvent('exception', me, response);
              },
              load, method, args;
  
          if (options.waitMsg) {
              if (typeof waitMsg === 'string') {
                  waitMsg = {
                      xtype   : 'loadmask',
                      message : waitMsg
                  };
              }
  
              me.setMasked(waitMsg);
          }
  
          if (api) {
              load = api.load;
  
              if (typeof load === 'string') {
                  load = Ext.direct.Manager.parseMethod(load);
  
                  if (load) {
                      api.load = load;
                  }
              }
  
              if (load) {
                  method = load.directCfg.method;
                  args = method.getArgs(me.getParams(options.params), me.getParamOrder(), me.getParamsAsHash());
  
                  args.push(function(data, response, success) {
                      me.setMasked(false);
  
                      if (success) {
                          successFn(response, data);
                      } else {
                          failureFn(response, data);
                      }
                  }, me);
  
                  return load.apply(window, args);
              }
          } else if (url) {
              return Ext.Ajax.request({
                  url: url,
                  timeout: (options.timeout || this.getTimeout()) * 1000,
                  method: options.method || 'GET',
                  autoAbort: options.autoAbort,
                  headers: Ext.apply(
                      {
                          'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
                      },
                      options.headers || {}
                  ),
                  callback: function(callbackOptions, success, response) {
                      var responseText = response.responseText,
                          statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);
  
                      me.setMasked(false);
  
                      if (success) {
                          if (statusResult && responseText.length === 0) {
                              success = true;
                          } else {
                              response = Ext.decode(responseText);
                              success = !!response.success;
                          }
                          if (success) {
                              successFn(response, responseText);
                          } else {
                              failureFn(response, responseText);
                          }
                      }
                      else {
                          failureFn(response, responseText);
                      }
                  }
              });
          }
      },
  
      /**
       * @private
       */
      getParams : function(params) {
          return Ext.apply({}, params, this.getBaseParams());
      },
  
      /**
       * Sets the values of form fields in bulk. Example usage:
       *
       *     myForm.setValues({
       *         name: 'Ed',
       *         crazy: true,
       *         username: 'edspencer'
       *     });
       *
       * If there groups of checkbox fields with the same name, pass their values in an array. For example:
       *
       *     myForm.setValues({
       *         name: 'Jacky',
       *         crazy: false,
       *         hobbies: [
       *             'reading',
       *             'cooking',
       *             'gaming'
       *         ]
       *     });
       *
       * @param {Object} values field name => value mapping object.
       * @return {Ext.form.Panel} This form.
       */
      setValues: function(values) {
          var fields = this.getFields(),
              me = this,
              name, field, value, ln, i, f;
  
          values = values || {};
  
          for (name in values) {
              if (values.hasOwnProperty(name)) {
                  field = fields[name];
                  value = values[name];
  
                  if (field) {
                      // If there are multiple fields with the same name. Checkboxes, radio fields and maybe event just normal fields..
                      if (Ext.isArray(field)) {
                          ln = field.length;
  
                          // Loop through each of the fields
                          for (i = 0; i < ln; i++) {
                              f = field[i];
  
                              if (f.isRadio) {
                                  // If it is a radio field just use setGroupValue which will handle all of the radio fields
                                  f.setGroupValue(value);
                                  break;
                              } else if (f.isCheckbox) {
                                  if (Ext.isArray(value)) {
                                     f.setChecked((value.indexOf(f._value) != -1));
                                 } else {
                                     f.setChecked((value == f._value));
                                 }
                              } else {
                                  // If it is a bunch of fields with the same name, check if the value is also an array, so we can map it
                                  // to each field
                                  if (Ext.isArray(value)) {
                                      f.setValue(value[i]);
                                  }
                              }
                          }
                      } else {
                          if (field.isRadio || field.isCheckbox) {
                              // If the field is a radio or a checkbox
                              field.setChecked(value);
                          } else {
                              // If just a normal field
                              field.setValue(value);
                          }
                      }
  
                      if (me.getTrackResetOnLoad()) {
                         field.resetOriginalValue();
                      }
                  }
              }
          }
  
          return this;
      },
  
      /**
       * Returns an object containing the value of each field in the form, keyed to the field's name.
       * For groups of checkbox fields with the same name, it will be arrays of values. For example:
       *
       *     {
       *         name: "Jacky Nguyen", // From a TextField
       *         favorites: [
       *             'pizza',
       *             'noodle',
       *             'cake'
       *         ]
       *     }
       *
       * @param {Boolean} [enabled] `true` to return only enabled fields.
       * @param {Boolean} [all] `true` to return all fields even if they don't have a
       * {@link Ext.field.Field#name name} configured.
       * @return {Object} Object mapping field name to its value.
       */
      getValues: function(enabled, all) {
          var fields = this.getFields(),
              values = {},
              isArray = Ext.isArray,
              field, value, addValue, bucket, name, ln, i;
  
          // Function which you give a field and a name, and it will add it into the values
          // object accordingly
          addValue = function(field, name) {
              if (!all && (!name || name === 'null') || field.isFile) {
                  return;
              }
  
              if (field.isCheckbox) {
                  value = field.getSubmitValue();
              } else {
                  value = field.getValue();
              }
  
  
              if (!(enabled && field.getDisabled())) {
                  // RadioField is a special case where the value returned is the fields valUE
                  // ONLY if it is checked
                  if (field.isRadio) {
                      if (field.isChecked()) {
                          values[name] = value;
                      }
                  } else {
                      // Check if the value already exists
                      bucket = values[name];
                      if (!Ext.isEmpty(bucket)) {
                          // if it does and it isn't an array, we need to make it into an array
                          // so we can push more
                          if (!isArray(bucket)) {
                              bucket = values[name] = [bucket];
                          }
  
                          // Check if it is an array
                          if (isArray(value)) {
                              // Concat it into the other values
                              bucket = values[name] = bucket.concat(value);
                          } else {
                              // If it isn't an array, just pushed more values
                              bucket.push(value);
                          }
                      } else {
                          values[name] = value;
                      }
                  }
              }
          };
  
          // Loop through each of the fields, and add the values for those fields.
          for (name in fields) {
              if (fields.hasOwnProperty(name)) {
                  field = fields[name];
  
                  if (isArray(field)) {
                      ln = field.length;
                      for (i = 0; i < ln; i++) {
                          addValue(field[i], name);
                      }
                  } else {
                      addValue(field, name);
                  }
              }
          }
  
          return values;
      },
  
      /**
       * Resets all fields in the form back to their original values.
       * @return {Ext.form.Panel} This form.
       */
      reset: function() {
          this.getFieldsAsArray().forEach(function(field) {
              field.reset();
          });
  
          return this;
      },
  
      /**
       * A convenient method to disable all fields in this form.
       * @return {Ext.form.Panel} This form.
       */
      updateDisabled: function(newDisabled) {
          this.getFieldsAsArray().forEach(function(field) {
              field.setDisabled(newDisabled);
          });
  
          return this;
      },
  
      /**
       * @private
       */
      getFieldsAsArray: function() {
          var fields = [],
              getFieldsFrom = function(item) {
                  if (item.isField) {
                      fields.push(item);
                  }
  
                  if (item.isContainer) {
                      item.getItems().each(getFieldsFrom);
                  }
              };
  
          this.getItems().each(getFieldsFrom);
  
          return fields;
      },
  
      /**
       * Returns all {@link Ext.field.Field field} instances inside this form.
       * @param {Boolean} byName return only fields that match the given name, otherwise return all fields.
       * @return {Object/Array} All field instances, mapped by field name; or an array if `byName` is passed.
       */
      getFields: function(byName) {
          var fields = {},
              itemName;
  
          var getFieldsFrom = function(item) {
              if (item.isField) {
                  itemName = item.getName();
  
                  if ((byName && itemName == byName) || typeof byName == 'undefined') {
                      if (fields.hasOwnProperty(itemName)) {
                          if (!Ext.isArray(fields[itemName])) {
                              fields[itemName] = [fields[itemName]];
                          }
  
                          fields[itemName].push(item);
                      } else {
                          fields[itemName] = item;
                      }
                  }
  
              }
  
              if (item.isContainer) {
                  item.items.each(getFieldsFrom);
              }
          };
  
          this.getItems().each(getFieldsFrom);
  
          return (byName) ? (fields[byName] || []) : fields;
      },
  
      /**
       * Returns an array of fields in this formpanel.
       * @return {Ext.field.Field[]} An array of fields in this form panel.
       * @private
       */
      getFieldsArray: function() {
          var fields = [];
  
          var getFieldsFrom = function(item) {
              if (item.isField) {
                  fields.push(item);
              }
  
              if (item.isContainer) {
                  item.items.each(getFieldsFrom);
              }
          };
  
          this.items.each(getFieldsFrom);
  
          return fields;
      },
  
      getFieldsFromItem: Ext.emptyFn,
  
      /**
       * Shows a generic/custom mask over a designated Element.
       * @param {String/Object} cfg Either a string message or a configuration object supporting
       * the following options:
       *
       *     {
       *         message : 'Please Wait',
       *         cls : 'form-mask'
       *     }
       *
       * @param {Object} target
       * @return {Ext.form.Panel} This form
       * @deprecated 2.0.0 Please use {@link #setMasked} instead.
       */
      showMask: function(cfg, target) {
          //<debug>
          Ext.Logger.warn('showMask is now deprecated. Please use Ext.form.Panel#setMasked instead');
          //</debug>
  
          cfg = Ext.isObject(cfg) ? cfg.message : cfg;
  
          if (cfg) {
              this.setMasked({
                  xtype: 'loadmask',
                  message: cfg
              });
          } else {
              this.setMasked(true);
          }
  
          return this;
      },
  
      /**
       * Hides a previously shown wait mask (See {@link #showMask}).
       * @return {Ext.form.Panel} this
       * @deprecated 2.0.0 Please use {@link #unmask} or {@link #setMasked} instead.
       */
      hideMask: function() {
          this.setMasked(false);
          return this;
      },
  
      /**
       * Returns the currently focused field
       * @return {Ext.field.Field} The currently focused field, if one is focused or `null`.
       * @private
       */
      getFocusedField: function() {
          var fields = this.getFieldsArray(),
              ln = fields.length,
              field, i;
  
          for (i = 0; i < ln; i++) {
              field = fields[i];
              if (field.isFocused) {
                  return field;
              }
          }
  
          return null;
      },
  
      /**
       * @return {Boolean/Ext.field.Field} The next field if one exists, or `false`.
       * @private
       */
      getNextField: function() {
          var fields = this.getFieldsArray(),
              focusedField = this.getFocusedField(),
              index;
  
          if (focusedField) {
              index = fields.indexOf(focusedField);
  
              if (index !== fields.length - 1) {
                  index++;
                  return fields[index];
              }
          }
  
          return false;
      },
  
      /**
       * Tries to focus the next field in the form, if there is currently a focused field.
       * @return {Boolean/Ext.field.Field} The next field that was focused, or `false`.
       * @private
       */
      focusNextField: function() {
          var field = this.getNextField();
          if (field) {
              field.focus();
              return field;
          }
  
          return false;
      },
  
      /**
       * @private
       * @return {Boolean/Ext.field.Field} The next field if one exists, or `false`.
       */
      getPreviousField: function() {
          var fields = this.getFieldsArray(),
              focusedField = this.getFocusedField(),
              index;
  
          if (focusedField) {
              index = fields.indexOf(focusedField);
  
              if (index !== 0) {
                  index--;
                  return fields[index];
              }
          }
  
          return false;
      },
  
      /**
       * Tries to focus the previous field in the form, if there is currently a focused field.
       * @return {Boolean/Ext.field.Field} The previous field that was focused, or `false`.
       * @private
       */
      focusPreviousField: function() {
          var field = this.getPreviousField();
          if (field) {
              field.focus();
              return field;
          }
  
          return false;
      }
  });
  