text: /**
   *
   */
  Ext.define('Ext.grid.Tree', {
      extend: 'Ext.grid.Grid',
      xtype: 'tree',
      alternateClassName: 'Ext.tree.Tree',
  
      classCls: Ext.baseCSSPrefix + 'treelist',
      expanderFirstCls: Ext.baseCSSPrefix + 'treelist-expander-first',
      expanderOnlyCls: Ext.baseCSSPrefix + 'treelist-expander-only',
  
      /**
       * @event beforenodeexpand
       * Fires before an row is visually expanded. May be vetoed by returning false from a handler.
       * @param {Ext.grid.Row} row                    The row to be expanded
       * @param {Ext.data.NodeInterface} record       The record to be expanded
       */
  
      /**
       * @event nodeexpand
       * Fires after an row has been visually expanded and its child nodes are visible in the tree.
       * @param {Ext.grid.Row} row                    The row that was expanded
       * @param {Ext.data.NodeInterface} record       The record that was expanded
       */
  
      /**
       * @event beforenodecollapse
       * Fires before an row is visually collapsed. May be vetoed by returning false from a handler.
       * @param {Ext.grid.Row} node                   The row to be collapsed
       * @param {Ext.data.NodeInterface} record       The record to be collapsed
       */
  
      /**
       * @event nodecollapse
       * Fires after an row has been visually collapsed and its child nodes are no longer visible in the tree.
       * @param {Ext.grid.Row} node                   The row that was collapsed
       * @param {Ext.data.NodeInterface} record       The record that was collapsed
       */
  
      cachedConfig: {
          expanderFirst: true,
  
          /**
           * @cfg {Boolean} expanderOnly
           * `true` to expand only on the click of the expander element. Setting this to
           * `false` will allow expansion on click of any part of the element.
           */
          expanderOnly: true
      },
  
      config: {
          root: {},
  
          /**
           * @cfg {Boolean} selectOnExpander
           * `true` to select the node when clicking the expander.
           */
          selectOnExpander: false,
  
          /**
           * @cfg {Boolean} [singleExpand=false]
           * `true` if only 1 node per branch may be expanded.
           */
          singleExpand: null,
  
          rootVisible: true,
  
          iconSize: null,
  
          indent: null,
  
          displayField: 'text',
  
          columns: false, // Non-null to force running the applier.
  
          rowLines: false,
  
          /**
           * @cfg {Boolean} [folderSort=false]
           * True to automatically prepend a leaf sorter to the store.
           */
          folderSort: false
      },
  
      // Instruct rows to create view models so we can use data binding
      itemConfig: {
          viewModel: true
      },
  
      eventsSelector: '.' + Ext.baseCSSPrefix + 'grid-cell',
  
      constructor: function (config) {
          var me = this,
              el;
  
          me.callParent([config]);
  
          el = me.element;
          if (el.isPainted()) {
              me.syncIconSize();
          } else {
              el.on({
                  scope: me,
                  painted: me.syncIconSize,
                  single: true
              });
          }
      },
  
      onItemTrigger: function (me, index, target, record, e) {
          var cell = me.getCellFromEvent(e);
  
          if (cell && (!cell.isTreeCell || me.getSelectOnExpander() || e.target !== cell.expanderElement.dom)) {
              me.callParent([me, index, target, record, e]);
          }
      },
  
      applyColumns: function (columns) {
          if (!columns) {
              this.setHideHeaders(true);
              columns = [{
                  xtype: 'treecolumn',
                  text: 'Name',
                  dataIndex: this.getDisplayField(),
                  minWidth: 100,
                  flex: 1
              }];
          }
          return columns;
      },
  
      updateStore: function (newStore, oldStore) {
          var me = this,
              oldRoot,
              newRoot;
  
          // We take over from event firing so we can relay
          if (oldStore) {
              oldRoot = oldStore.getRootNode();
              Ext.destroy(me.storeListeners, me.storeRelayers);
          }
  
          if (newStore) {
  
              // If there is no root node defined, then create one.
              // Ensure a first onRootChange is called so we can hook into the event firing
              if (newRoot = newStore.getRoot()) {
                  me.onRootChange(newRoot);
              } else {
                  newStore.setRoot(me.getRoot());
                  newRoot = newStore.getRoot();
              }
  
              // Store must have the same idea about root visibility as us before callParent binds it.
              if (!('rootVisible' in newStore.initialConfig)) {
                  newStore.setRootVisible(me.getRootVisible());
              }
  
              me.callParent([newStore, oldStore]);
              newStore.folderSort = me.getFolderSort();
  
              // Monitor the TreeStore for the root node being changed. Return a Destroyable object
              me.storeListeners = me.mon(newStore, {
                  destroyable: true,
                  rootchange: me.onRootChange,
                  scope: me
              });
  
              // Relay store events. relayEvents always returns a Destroyable object.
              me.storeRelayers = me.relayEvents(newStore, [
              /**
               * @event beforeload
               * @inheritdoc Ext.data.TreeStore#beforeload
               */
                  'beforeload',
  
              /**
               * @event load
               * @inheritdoc Ext.data.TreeStore#load
               */
                  'load'
              ]);
  
              // If rootVisible is false, we *might* need to expand the node.
              // If store is autoLoad, that will already have been kicked off.
              // If its already expanded, or in the process of loading, the TreeStore
              // has started that at the end of updateRoot 
              if (!newStore.rootVisible && !newStore.autoLoad && !(newRoot.isExpanded() || newRoot.isLoading())) {
                  // A hidden root must be expanded, unless it's overridden with autoLoad: false.
                  // If it's loaded, set its expanded field (silently), and skip ahead to the onNodeExpand callback.
                  if (newRoot.isLoaded()) {
                      newRoot.data.expanded = true;
                      newStore.onNodeExpand(newRoot, newRoot.childNodes);
                  }
                  // Root is not loaded; go through the expand mechanism to force a load
                  // unless we were told explicitly not to load the store by setting
                  // autoLoad: false. This is useful with Direct proxy in cases when
                  // Direct API is loaded dynamically and may not be available at the time
                  // when TreePanel is created.
                  else if (newStore.autoLoad !== false && !newStore.hasPendingLoad()) {
                      newRoot.data.expanded = false;
                      newRoot.expand();
                  }
              }
  
              // TreeStore must have an upward link to the TreePanel so that nodes can find their owning tree in NodeInterface.getOwnerTree
              // TODO: NodeInterface.getOwnerTree is deprecated. Data class must not be coupled to UI. Remove this link
              // when that method is removed.
              newStore.ownerTree = me;
          }
      },
  
      onRootChange: function (newRoot, oldRoot) {
          var me = this,
              fireEventArgs;
  
          if (oldRoot) {
              delete oldRoot.fireEventArgs;
          }
  
          // We take over from event firing so we can relay.
          // Cannot use Function.createSequence. That does not return the return values
          if (newRoot) {
              fireEventArgs = newRoot.fireEventArgs;
              newRoot.fireEventArgs = function (eventName) {
                  // Fire on the original firer
                  var ret = fireEventArgs.apply(newRoot, arguments);
  
                  // If not stopped, fire through this Tree
                  if (ret !== false) {
                      arguments[0] = me.rootEventsMap[eventName] || ('item' + eventName);
                      ret = me.fireEventArgs.apply(me, arguments);
                  }
                  return ret;
              };
          }
      },
  
      updateUi: function (ui, oldUi) {
          this.callParent([ui, oldUi]);
  
          // Ensure that the cached iconSize is read from the style.
          delete this.iconSize;
          this.syncIconSize();
      },
  
      updateExpanderFirst: function (expanderFirst) {
          this.element.toggleCls(this.expanderFirstCls, expanderFirst);
      },
  
      updateExpanderOnly: function (value) {
          this.element.toggleCls(this.expanderOnlyCls, !value);
      },
  
      /**
       * Sets root node of this tree. All trees *always* have a root node. It may be {@link #rootVisible hidden}.
       *
       * If the passed node has not already been loaded with child nodes, and has its expanded field set, this triggers
       * the {@link #cfg-store} to load the child nodes of the root.
       * @param {Ext.data.TreeModel/Object} root
       * @return {Ext.data.TreeModel} The new root
       */
      setRootNode: function (root) {
          var store = this.getStore();
  
          root = store.setRoot(root);
  
          return root;
      },
  
      /**
       * Returns the root node for this tree.
       * @return {Ext.data.TreeModel}
       */
      getRootNode: function () {
          return this.getStore().getRoot();
      },
  
  
      /**
       * Expands a record that is loaded in the tree.
       * @param {Ext.data.Model} record The record to expand
       * @param {Boolean} [deep] True to expand nodes all the way down the tree hierarchy.
       * @param {Function} [callback] The function to run after the expand is completed
       * @param {Object} [scope] The scope of the callback function.
       */
      expandNode: function (record, deep, callback, scope) {
          return record.expand(deep, callback, scope || this);
      },
  
      /**
       * Collapses a record that is loaded in the tree.
       * @param {Ext.data.Model} record The record to collapse
       * @param {Boolean} [deep] True to collapse nodes all the way up the tree hierarchy.
       * @param {Function} [callback] The function to run after the collapse is completed
       * @param {Object} [scope] The scope of the callback function.
       */
      collapseNode: function (record, deep, callback, scope) {
          return record.collapse(deep, callback, scope || this);
      },
  
      /**
       * Expand all nodes
       * @param {Function} [callback] A function to execute when the expand finishes.
       * @param {Object} [scope] The scope of the callback function
       */
      expandAll: function (callback, scope) {
          var me = this,
              root = me.getRootNode();
  
          if (root) {
              Ext.suspendLayouts();
              root.expand(true, callback, scope || me);
              Ext.resumeLayouts(true);
          }
      },
  
      /**
       * Collapse all nodes
       * @param {Function} [callback] A function to execute when the collapse finishes.
       * @param {Object} [scope] The scope of the callback function
       */
      collapseAll: function (callback, scope) {
          var me = this,
              root = me.getRootNode();
  
          if (root) {
              Ext.suspendLayouts();
              scope = scope || me;
              if (me.getStore().rootVisible) {
                  root.collapse(true, callback, scope);
              } else {
                  root.collapseChildren(true, callback, scope);
              }
              Ext.resumeLayouts(true);
          }
      },
  
      privates: {
          rootEventsMap: {
              beforeappend: 'beforeitemappend',
              beforeremove: 'beforeritememove',
              beforemove: 'beforeitemmove',
              beforeinsert: 'beforeiteminsert',
              beforeexpand: 'beforeitemexpand',
              beforecollapse: 'beforeitemcollapse'
          },
  
          syncIconSize: function () {
              var me = this,
                  size = me.iconSize ||
                      (me.iconSize = parseInt(me.element.getStyle('background-position'), 10));
  
              me.setIconSize(size);
          },
  
          defaultIconSize: 22,
  
          updateIconSize: function (value) {
              this.setIndent(value || this.defaultIconSize);
          },
  
          updateIndent: function (value) {
              Ext.Array.each(this.query('treecell'), function (cell) {
                  cell.setIndent(value);
              });
          }
      }
  });
  