text: /**
   * Represents a grouping of items. The grouper works in a similar fashion as the
   * `Ext.util.Sorter` except that groups must be able to extract a value by which all items
   * in the group can be collected. By default this is derived from the `property` config
   * but can be customized using the `groupFn` if necessary.
   *
   * All items with the same group value compare as equal. If the group values do not compare
   * equally, the sort can be controlled further by setting `sortProperty` or `sorterFn`.
   */
  Ext.define('Ext.util.Grouper', {
      extend: 'Ext.util.Sorter',
  
      isGrouper: true,
  
      config: {
          /**
           * @cfg {Function} groupFn This function is called for each item in the collection
           * to determine the group to which it belongs. By default the `property` value is
           * used to group items.
           * @cfg {Object} groupFn.item The current item from the collection.
           * @cfg {String} groupFn.return The group identifier for the item.
           */
          groupFn: null,
  
          /**
           * @cfg {String} property The field by which records are grouped. Groups are 
           * sorted alphabetically by group value as the default. To sort groups by a different 
           * property, use the {@link #sortProperty} configuration.
           */
  
          /**
           * @cfg {String} sortProperty You can set this configuration if you want the groups
           * to be sorted on something other then the group string returned by the `groupFn`.
           * This serves the same role as `property` on a normal `Ext.util.Sorter`.
           */
          sortProperty: null
      },
  
      constructor: function(config) {
          //<debug>
          if (config) {
              if (config.getGroupString) {
                  Ext.Error.raise("Cannot set getGroupString - use groupFn instead");
              }
          }
          //</debug>
  
          this.callParent(arguments);
      },
  
      /**
       * Returns the value for grouping to be used.
       * @param {Ext.data.Model} item The Model instance
       * @return {String}
       */
      getGroupString: function (item) {
          var group = this._groupFn(item);
          return (group != null) ? String(group) : '';
      },
  
      sortFn: function (item1, item2) {
          var me = this,
              lhs = me._groupFn(item1),
              rhs = me._groupFn(item2),
              property = me._sortProperty, // Sorter's sortFn uses "_property"
              root = me._root,
              sorterFn = me._sorterFn,
              transform = me._transform;
  
          // Items with the same groupFn result must be equal... otherwise we sort them
          // by sorterFn or sortProperty.
          if (lhs === rhs) {
              return 0;
          }
  
          if (property || sorterFn) {
              if (sorterFn) {
                  return sorterFn.call(this, item1, item2);
              }
  
              if (root) {
                  item1 = item1[root];
                  item2 = item2[root];
              }
  
              lhs = item1[property];
              rhs = item2[property];
  
              if (transform) {
                  lhs = transform(lhs);
                  rhs = transform(rhs);
              }
          }
  
          return (lhs > rhs) ? 1 : (lhs < rhs ? -1 : 0);
      },
  
      standardGroupFn: function (item) {
          var root = this._root;
          return (root ? item[root] : item)[this._property];
      },
  
      updateSorterFn: function () {
          // don't callParent here - we don't want to smash sortFn w/sorterFn
      },
  
      updateProperty: function () {
          // we don't callParent since that is related to sorterFn smashing sortFn
          if (!this.getGroupFn()) {
              this.setGroupFn(this.standardGroupFn);
          }
      }
  });
  