text: /**
   * A specialized grid implementation intended to mimic the traditional property grid as typically seen in
   * development IDEs.  Each row in the grid represents a property of some object, and the data is stored
   * as a set of name/value pairs in {@link Ext.grid.property.Property Properties}. By default, the editors
   * shown are inferred from the data in the cell. More control over this can be specified by using the
   * {@link #sourceConfig} option. Example usage:
   *
   *     @example
   *     Ext.create('Ext.grid.property.Grid', {
   *         title: 'Properties Grid',
   *         width: 300,
   *         renderTo: Ext.getBody(),
   *         source: {
   *             "(name)": "My Object",
   *             "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),
   *             "Available": false,
   *             "Version": 0.01,
   *             "Description": "A test object"
   *         }
   *     });
   */
  Ext.define('Ext.grid.property.Grid', {
  
      extend: 'Ext.grid.Panel',
  
      alias: 'widget.propertygrid',
  
      alternateClassName: 'Ext.grid.PropertyGrid',
  
      uses: [
          'Ext.grid.plugin.CellEditing',
          'Ext.grid.property.Store',
          'Ext.grid.property.HeaderContainer',
          'Ext.XTemplate',
          'Ext.grid.CellEditor',
          'Ext.form.field.Date',
          'Ext.form.field.Text',
          'Ext.form.field.Number',
          'Ext.form.field.ComboBox'
      ],
  
      /**
       * @cfg {Object} sourceConfig
       * This option allows various configurations to be set for each field in the property grid.
       * None of these configurations are required
       *
       * ####displayName
       * A custom name to appear as label for this field. If specified, the display name will be shown
       * in the name column instead of the property name. Example usage:
       *
       *     new Ext.grid.property.Grid({
       *         source: {
       *             clientIsAvailable: true
       *         },
       *         sourceConfig: {
       *             clientIsAvailable: {
       *                 // Custom name different to the field
       *                 displayName: 'Available'
       *             }
       *         }
       *     });
       *
       * ####renderer
       * A function used to transform the underlying value before it is displayed in the grid.
       * By default, the grid supports strongly-typed rendering of strings, dates, numbers and booleans using built-in form editors,
       * but any custom type can be supported and associated with the type of the value. Example usage:
       *
       *     new Ext.grid.property.Grid({
       *         source: {
       *             clientIsAvailable: true
       *         },
       *         sourceConfig: {
       *             clientIsAvailable: {
       *                 // Custom renderer to change the color based on the value
       *                 renderer: function(v){
       *                     var color = v ? 'green' : 'red';
       *                     return '<span style="color: ' + color + ';">' + v + '</span>';
       *                 }
       *             }
       *         }
       *     });
       *
       * ####type
       * Used to explicitly specify the editor type for a particular value. By default, the type is
       * automatically inferred from the value. See {@link #inferTypes}. Accepted values are:
       *
       * - 'date'
       * - 'boolean'
       * - 'number'
       * - 'string'
       *
       * For more advanced control an editor configuration can be passed (see the next section).
       * Example usage:
       *
       *     new Ext.grid.property.Grid({
       *         source: {
       *             attending: null
       *         },
       *         sourceConfig: {
       *             attending: {
       *                 // Force the type to be a numberfield, a null value would otherwise default to a textfield
       *                 type: 'number'
       *             }
       *         }
       *     });
       *
       * ####editor
       * Allows the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
       * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
       * associated with a custom input control by specifying a custom editor. Example usage
       *
       *     new Ext.grid.property.Grid({
       *         // Data object containing properties to edit
       *         source: {
       *             evtStart: '10:00 AM'
       *         },
       *
       *         sourceConfig: {
       *             evtStart: {
       *                 editor: Ext.create('Ext.form.field.Time', {selectOnFocus: true}),
       *                 displayName: 'Start Time'
       *             }
       *         }
       *     });
       */
  
      /**
       * @cfg {Object} propertyNames
       * An object containing custom property name/display name pairs.
       * If specified, the display name will be shown in the name column instead of the property name.
       * @deprecated See {@link #sourceConfig} displayName
       */
  
      /**
       * @cfg {Object} source
       * A data object to use as the data source of the grid (see {@link #setSource} for details).
       */
  
      /**
       * @cfg {Object} customEditors
       * An object containing name/value pairs of custom editor type definitions that allow
       * the grid to support additional types of editable fields.  By default, the grid supports strongly-typed editing
       * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
       * associated with a custom input control by specifying a custom editor.  The name of the editor
       * type should correspond with the name of the property that will use the editor.  Example usage:
       *
       *     var grid = new Ext.grid.property.Grid({
       *
       *         // Custom editors for certain property names
       *         customEditors: {
       *             evtStart: Ext.create('Ext.form.TimeField', {selectOnFocus: true})
       *         },
       *
       *         // Displayed name for property names in the source
       *         propertyNames: {
       *             evtStart: 'Start Time'
       *         },
       *
       *         // Data object containing properties to edit
       *         source: {
       *             evtStart: '10:00 AM'
       *         }
       *     });
       * @deprecated See {@link #sourceConfig} editor
       */
  
      /**
       * @cfg {Object} customRenderers
       * An object containing name/value pairs of custom renderer type definitions that allow
       * the grid to support custom rendering of fields.  By default, the grid supports strongly-typed rendering
       * of strings, dates, numbers and booleans using built-in form editors, but any custom type can be supported and
       * associated with the type of the value.  The name of the renderer type should correspond with the name of the property
       * that it will render.  Example usage:
       *
       *     var grid = Ext.create('Ext.grid.property.Grid', {
       *         customRenderers: {
       *             Available: function(v){
       *                 if (v) {
       *                     return '<span style="color: green;">Yes</span>';
       *                 } else {
       *                     return '<span style="color: red;">No</span>';
       *                 }
       *             }
       *         },
       *         source: {
       *             Available: true
       *         }
       *     });
       * @deprecated See {@link #sourceConfig} renderer
       */
  
      /**
       * @cfg {String} valueField
       * The name of the field from the property store to use as the value field name.
       * This may be useful if you do not configure the property Grid from an object, but use your own store configuration.
       */
      valueField: 'value',
  
      /**
       * @cfg {String} nameField
       * The name of the field from the property store to use as the property field name.
       * This may be useful if you do not configure the property Grid from an object, but use your own store configuration.
       */
      nameField: 'name',
  
      /**
       * @cfg {Boolean} inferTypes
       * True to automatically infer the {@link #sourceConfig type} based on the initial value passed
       * for each field. This ensures the editor remains the correct type even if the value is blanked
       * and becomes empty.
       */
      inferTypes: true,
  
      /**
       * @cfg {Number/String} [nameColumnWidth=115]
       * Specify the width for the name column. The value column will take any remaining space.
       */
  
      // private config overrides
      enableColumnMove: false,
      columnLines: true,
      stripeRows: false,
      trackMouseOver: false,
      clicksToEdit: 1,
      enableHdMenu: false,
  
      gridCls: Ext.baseCSSPrefix + 'property-grid',
  
      /**
       * @event beforepropertychange
       * Fires before a property value changes.  Handlers can return false to cancel the property change
       * (this will internally call {@link Ext.data.Model#reject} on the property's record).
       * @param {Object} source The source data object for the grid (corresponds to the same object passed in
       * as the {@link #source} config property).
       * @param {String} recordId The record's id in the data store
       * @param {Object} value The current edited property value
       * @param {Object} oldValue The original property value prior to editing
       */
  
      /**
       * @event propertychange
       * Fires after a property value has changed.
       * @param {Object} source The source data object for the grid (corresponds to the same object passed in
       * as the {@link #source} config property).
       * @param {String} recordId The record's id in the data store
       * @param {Object} value The current edited property value
       * @param {Object} oldValue The original property value prior to editing
       */
  
      initComponent: function () {
          var me = this,
          // selectOnFocus: true results in weird exceptions thrown when tabbing
          // between cell editors in IE and there's no known cure at the moment
              selectOnFocus = !Ext.isIE,
              view;
  
          me.source = me.source || {};
          me.addCls(me.gridCls);
          me.plugins = me.plugins || [];
  
          // Enable cell editing. Inject a custom startEdit which always edits column 1 regardless of which column was clicked.
          me.plugins.push(new Ext.grid.plugin.CellEditing({
              clicksToEdit: me.clicksToEdit,
  
              // Inject a startEdit which always edits the value column
              startEdit: function (record, column) {
                  // Maintainer: Do not change this 'this' to 'me'! It is the CellEditing object's own scope.
                  return this.self.prototype.startEdit.call(this, record, me.valueColumn);
              },
  
              // Gets an editor based on the property name and not column itemId
              getEditor: function (record, column) {
                  return this.getCachedEditor(record.get(me.nameField), record, column);
              }
          }));
  
          me.selModel = {
              type: 'cellmodel',
              onCellSelect: function (position) {
                  // We are only allowed to select the value column.
                  position.column = me.valueColumn;
                  position.colIdx = me.valueColumn.getVisibleIndex();
                  return this.self.prototype.onCellSelect.call(this, position);
              }
          };
  
          me.sourceConfig = Ext.apply({}, me.sourceConfig);
  
          // Create a property.Store from the source object unless configured with a store
          if (!me.store) {
              me.propStore = me.store = new Ext.grid.property.Store(me, me.source);
          }
          me.configure(me.sourceConfig);
  
          if (me.sortableColumns) {
              me.store.sort('name', 'ASC');
          }
          me.columns = new Ext.grid.property.HeaderContainer(me, me.store);
  
          me.callParent();
  
          var view = me.getView();
  
          // Inject a custom implementation of walkCells which only goes up or down
          view.walkCells = me.walkCells;
  
          // Inject a custom implementation that only allows focusing value column
          view.getDefaultFocusPosition = me.getDefaultFocusPosition;
  
          // Set up our default editor set for the 4 atomic data types
          me.editors = {
              'date': new Ext.grid.CellEditor({
                  field: new Ext.form.field.Date({
                      selectOnFocus: selectOnFocus
                  })
              }),
              'string': new Ext.grid.CellEditor({
                  field: new Ext.form.field.Text({
                      selectOnFocus: selectOnFocus
                  })
              }),
              'number': new Ext.grid.CellEditor({
                  field: new Ext.form.field.Number({
                      selectOnFocus: selectOnFocus
                  })
              }),
              'boolean': new Ext.grid.CellEditor({
                  field: new Ext.form.field.ComboBox({
                      editable: false,
                      store: [[true, me.headerCt.trueText], [false, me.headerCt.falseText]]
                  })
              })
          };
  
          // Track changes to the data so we can fire our events.
          me.store.on('update', me.onUpdate, me);
      },
  
      configure: function (config) {
          var me = this,
              store = me.store,
              i = 0,
              len = me.store.getCount(),
              nameField = me.nameField,
              valueField = me.valueField,
              name, value, rec, type;
  
          me.configureLegacy(config);
  
          if (me.inferTypes) {
              for (; i < len; ++i) {
                  rec = store.getAt(i);
                  name = rec.get(nameField);
                  if (!me.getConfigProp(name, 'type')) {
                      value = rec.get(valueField);
                      if (Ext.isDate(value)) {
                          type = 'date';
                      } else if (Ext.isNumber(value)) {
                          type = 'number';
                      } else if (Ext.isBoolean(value)) {
                          type = 'boolean';
                      } else {
                          type = 'string';
                      }
                      me.setConfigProp(name, 'type', type);
                  }
              }
          }
      },
  
      getConfigProp: function (fieldName, key, defaultValue) {
          var config = this.sourceConfig[fieldName],
              out;
  
          if (config) {
              out = config[key];
          }
          return out || defaultValue;
      },
  
      setConfigProp: function (fieldName, key, value) {
          var sourceCfg = this.sourceConfig,
              o = sourceCfg[fieldName];
  
          if (!o) {
              o = sourceCfg[fieldName] = {
                  __copied: true
              };
          } else if (!o.__copied) {
              o = Ext.apply({
                  __copied: true
              }, o);
              sourceCfg[fieldName] = o;
          }
          o[key] = value;
          return value;
      },
  
      // to be deprecated in 4.2
      configureLegacy: function (config) {
          var me = this;
  
          me.copyLegacyObject(config, me.customRenderers, 'renderer');
          me.copyLegacyObject(config, me.customEditors, 'editor');
          me.copyLegacyObject(config, me.propertyNames, 'displayName');
  
          //<debug>
          // exclude types since it's new
          if (me.customRenderers || me.customEditors || me.propertyNames) {
              if (Ext.global.console && Ext.global.console.warn) {
                  Ext.global.console.warn(this.$className, 'customRenderers, customEditors & propertyNames have been consolidated into a new config, see "sourceConfig". These configurations will be deprecated.');
              }
          }
          //</debug>
      },
  
      copyLegacyObject: function (config, o, keyName) {
          var key;
  
          for (key in o) {
              if (o.hasOwnProperty(key)) {
                  if (!config[key]) {
                      config[key] = {};
                  }
                  config[key][keyName] = o[key];
              }
          }
      },
  
      /**
       * @private
       */
      onUpdate: function (store, record, operation) {
          var me = this,
              v, oldValue;
  
          if (me.rendered && operation === Ext.data.Model.EDIT) {
              v = record.get(me.valueField);
              oldValue = record.modified.value;
              if (me.fireEvent('beforepropertychange', me.source, record.getId(), v, oldValue) !== false) {
                  if (me.source) {
                      me.source[record.getId()] = v;
                  }
                  record.commit();
                  me.fireEvent('propertychange', me.source, record.getId(), v, oldValue);
              } else {
                  record.reject();
              }
          }
      },
  
      // Custom implementation of walkCells which only goes up and down.
      // Runs in the scope of the TableView
      walkCells: function (pos, direction, e, preventWrap, verifierFn, scope) {
          var me = this,
              valueColumn = me.ownerCt.valueColumn;
  
          if (direction === 'left') {
              direction = 'up';
          } else if (direction === 'right') {
              direction = 'down';
          }
          pos = Ext.view.Table.prototype.walkCells.call(me, pos, direction, e, preventWrap, verifierFn, scope);
  
          // We are only allowed to navigate to the value column.
          pos.column = valueColumn;
          pos.colIdx = valueColumn.getVisibleIndex();
          return pos;
      },
  
      getDefaultFocusPosition: function () {
          var view = this, // NOT grid!
              focusPosition;
  
          focusPosition = new Ext.grid.CellContext(view).setColumn(1);
  
          return focusPosition;
      },
  
      /**
       * @private
       * Returns the correct editor type for the property type, or a custom one keyed by the property name
       */
      getCellEditor: function (record, column) {
          var me = this,
              propName = record.get(me.nameField),
              val = record.get(me.valueField),
              editor = me.getConfigProp(propName, 'editor'),
              type = me.getConfigProp(propName, 'type'),
              editors = me.editors,
              field;
  
          // A custom editor was found. If not already wrapped with a CellEditor, wrap it, and stash it back
          // If it's not even a Field, just a config object, instantiate it before wrapping it.
          if (editor) {
              if (!(editor instanceof Ext.grid.CellEditor)) {
                  if (!(editor instanceof Ext.form.field.Base)) {
                      editor = Ext.ComponentManager.create(editor, 'textfield');
                  }
                  editor = me.setConfigProp(propName, 'editor', new Ext.grid.CellEditor({field: editor}));
              }
          } else if (type) {
              switch (type) {
                  case 'date':
                      editor = editors.date;
                      break;
                  case 'number':
                      editor = editors.number;
                      break;
                  case 'boolean':
                      // Cannot be ".boolean" - YUI hates using reserved words like that
                      editor = me.editors['boolean']; // jshint ignore:line
                      break;
                  default:
                      editor = editors.string;
              }
          } else if (Ext.isDate(val)) {
              editor = editors.date;
          } else if (Ext.isNumber(val)) {
              editor = editors.number;
          } else if (Ext.isBoolean(val)) {
              // Cannot be ".boolean" - YUI hates using reserved words like that
              editor = editors['boolean']; // jshint ignore:line
          } else {
              editor = editors.string;
          }
  
          field = editor.field;
  
          if (field && field.ui === 'default' && !field.hasOwnProperty('ui')) {
              field.ui = me.editingPlugin.defaultFieldUI;
          }
  
          // Give the editor a unique ID because the CellEditing plugin caches them
          editor.editorId = propName;
          editor.field.column = me.valueColumn;
  
          if (propName) {
              propName = Ext.String.htmlEncode(propName);
  
              if (field.rendered) {
                  field.inputEl.dom.setAttribute('aria-label', propName);
              }
              else {
                  field.ariaLabel = propName;
              }
          }
  
          return editor;
      },
  
      doDestroy: function () {
          var me = this;
  
          me.destroyEditors(me.editors);
          me.destroyEditors(me.customEditors);
  
          me.callParent();
      },
  
      destroyEditors: function (editors) {
          for (var ed in editors) {
              if (editors.hasOwnProperty(ed)) {
                  Ext.destroy(editors[ed]);
              }
          }
      },
  
      /**
       * Sets the source data object containing the property data.  The data object can contain one or more name/value
       * pairs representing all of the properties of an object to display in the grid, and this data will automatically
       * be loaded into the grid's {@link #store}.  The values should be supplied in the proper data type if needed,
       * otherwise string type will be assumed.  If the grid already contains data, this method will replace any
       * existing data.  See also the {@link #source} config value.  Example usage:
       *
       *     grid.setSource({
       *         "(name)": "My Object",
       *         "Created": Ext.Date.parse('10/15/2006', 'm/d/Y'),  // date type
       *         "Available": false,  // boolean type
       *         "Version": .01,      // decimal type
       *         "Description": "A test object"
       *     });
       *
       * @param {Object} source The data object.
       * @param {Object} [sourceConfig] A new {@link #sourceConfig object}. If this argument is not passed
       * the current configuration will be re-used. To reset the config, pass `null` or an empty object literal.
       */
      setSource: function (source, sourceConfig) {
          var me = this;
  
          me.source = source;
          if (sourceConfig !== undefined) {
              me.sourceConfig = Ext.apply({}, sourceConfig);
              me.configure(me.sourceConfig);
          }
          me.propStore.setSource(source);
      },
  
      /**
       * Gets the source data object containing the property data.  See {@link #setSource} for details regarding the
       * format of the data object.
       * @return {Object} The data object.
       */
      getSource: function () {
          return this.propStore.getSource();
      },
  
      /**
       * Gets the value of a property.
       * @param {String} prop The name of the property.
       * @return {Object} The property value. `null` if there is no value.
       *
       * @since 5.1.1
       */
      getProperty: function (prop) {
          return this.propStore.getProperty(prop);
      },
  
      /**
       * Sets the value of a property.
       * @param {String} prop The name of the property to set.
       * @param {Object} value The value to test.
       * @param {Boolean} [create=false] `true` to create the property if it doesn't already exist.
       */
      setProperty: function (prop, value, create) {
          this.propStore.setValue(prop, value, create);
      },
  
      /**
       * Removes a property from the grid.
       * @param {String} prop The name of the property to remove.
       */
      removeProperty: function (prop) {
          this.propStore.remove(prop);
      }
  
      /**
       * @cfg {Object} store
       * @private
       */
      /**
       * @cfg {Object} columns
       * @private
       */
  });
  