text: Ext.define('Ext.app.bindinspector.Container', {
      extend: 'Ext.container.Container',
      alias: 'widget.bindinspector-container',
      requires: [
          'Ext.layout.container.Border',
          'Ext.tab.Panel',
          'Ext.app.bindinspector.ComponentDetail',
          'Ext.app.bindinspector.ComponentList',
          'Ext.app.bindinspector.Environment',
          'Ext.app.bindinspector.Util',
          'Ext.app.bindinspector.ViewModelDetail',
          'Ext.app.bindinspector.noconflict.BaseModel'
      ],
  
      isBindInspector: true,
      referenceHolder: true,
      
      layout: 'border',
      
      cls: Ext.baseCSSPrefix + 'bindinspector-container',
      //pickerCls: Ext.baseCSSPrefix + 'bindinspector-target-menu',
      //pickerPreviewBindingsCls: Ext.baseCSSPrefix + 'bindinspector-preview-bind',
      //pickerOpenBindingsCls: Ext.baseCSSPrefix + 'bindinspector-open-bind',
      //pickerPreviewVMCls: Ext.baseCSSPrefix + 'bindinspector-preview-vm',
      //pickerOpenVMCls: Ext.baseCSSPrefix + 'bindinspector-open-vm',
  
      // the default view for the bindings preview tab for:
      // 1)  when a component hasn't been selected
      // 2)  when a component is clicked, but has no bindings
      componentPreviewDefault: {
          xtype: 'container',
          cls: Ext.baseCSSPrefix + 'bindinspector-prev-default',
          padding: 20,
          layout: {
              type: 'hbox',
              align: 'middle',
              pack: 'center'
          },
          items: [{
              xtype: 'component',
              flex: 1,
              html: 'Select a component with bindings from the ComponentList to view the bindings details along with the component\'s inherited ViewModel'
          }]
      },
  
      initComponent: function() {
          var me = this;
  
          Ext.data.schema.Schema.get('Ext_app_bindinspector').clear();
          me.items = [{
              xtype: 'bindinspector-componentlist',
              reference: 'componentList',
              region: 'west',
              width: 400,
              split: true,
              collapsible: true,
              components: me.env.rootComponents,
              listeners: {
                  scope: me,
                  componentdblclick: me.onComponentDblclick,
                  componentselect: me.onComponentSelect
              }
          }, {
              xtype: 'tabpanel',
              region: 'center',
              reference: 'tabs',
              items: [{
                  title: 'Preview',
                  reference: 'preview',
                  layout: 'fit',
                  items: [me.componentPreviewDefault]
              }],
              listeners: {
                  add: function () {
                      this.getTabBar().setVisible(this.items.getCount() > 1);
                  },
                  remove: function () {
                      this.getTabBar().setVisible(this.items.getCount() > 1);
                  }
              }
          }];
          me.callParent();
  
          me.on('vmSearchClick', me.onVMSearchClick, me);
      },
  
      // When the search icon in the ViewModelDetail is clicked sort the items in the ComponentList panel to show
      // 1)  The viewmodels that have the root data node for the row clicked
      // 2)  All components with bindings that will consume the selected data point
      onVMSearchClick: function (rec) {
          var componentList = this.lookupReference('componentList'),
              store = componentList.getStore(),
              ownerVMs = rec.get('ownerVMs'),
              sourceVMs = [],
              bindCount = rec.get('bindCount'),
              names = [],
              consumerNodes = [],
              query = [],
              source, descriptor;
  
          // THE INDICATING OF THE SOURCE VMS
          store.suspendEvents();
          componentList.clearVMSearchIndicators();
          Ext.Array.forEach(ownerVMs, function (vm) {
              source = store.getById(vm.view);
              source.set('isSrcVM', rec);
              sourceVMs.push(source);
          });
          store.resumeEvents();
          componentList.getView().refresh();
  
          // cache the list of source VMs for the given root data point
          // used by ComponentList.clearVMSearchIndicators() to clear the selected indicators when clearing search results
          componentList.indicatedVM = sourceVMs;
  
          // THE INDICATING OF THE CONSUMER COMPONENTS
          if (bindCount > 0) {
              // find the descriptor path for the selected row
              rec.bubble(function (node) {
                  var nodeName = node.get('name');
                  if (nodeName) {
                      names.push(nodeName);
                  }
              });
              // and save as a '.' separated string of node values
              descriptor = names.reverse().join('.');
  
              // iterate over the component tree and find components whose bindings match the descriptor for the selected row
              componentList.getStore().getRootNode().cascadeBy(function (node) {
                  if (node.get('hasBindings')) {
                      Ext.Array.forEach(node.get('bindData'), function (binding) {
                          var tokenStr = [];
                          if (binding.tokens) {
                              Ext.Array.forEach(binding.tokens, function (token) {
                                  if (Ext.isArray(token)) {
                                      Ext.Array.forEach(token, function (t) {
                                          tokenStr.push(t);
                                      });
                                  } else {
                                      tokenStr.push(token);
                                  }
                              });
                          }
                          if (tokenStr.join('.') === descriptor) {
                              consumerNodes.push(node);
                          }
                      });
                  }
              });
          }
  
          // collect into an array all consuming components and source VMs for use in the componentlist filter in the next step
          Ext.Array.forEach(consumerNodes.concat(sourceVMs), function (node) {
              query.push(node.getId());
          });
          
          // filter to find the matching binding consumer components and source VMs
          componentList.expand();
          componentList.filterComponentTree(null, query);
  
          // pull the filter styling off of any nodes that are VM sources, but not a binding match
          Ext.Array.forEach(sourceVMs, function (src) {
              if (!Ext.Array.contains(consumerNodes)) {
                  src.set('filtervisible', false);
              }
          });
          componentList.down('#srcVMIndicator').show();   // show the indicator column used to show which VMs are source VMs
          componentList.down('#queryFieldTb').hide();     // hide the normal filter toolbar
          componentList.down('#vmQueryResultsTb').show(); // and show the one that indicates that it's filtered by VM search
      },
  
      // on component select review the component in the dedicated preview tab
      onComponentSelect: function (tree, rec, node) {
          var me = this,
              id = rec.getId(),
              preview = me.lookupReference('preview');
  
          if (preview.referringID !== id) {
              Ext.suspendLayouts();
              preview.removeAll();
              preview.add({
                  xtype: 'bindinspector-componentdetail',
                  env: me.env,
                  component: me.env.getCmp(id)
              });
              Ext.resumeLayouts(true);
              preview.referringID = id;
          }
  
          preview.show();
      },
  
      // on component dblclick (or clicking the 'open in own tab button') open the component's bindings view in its own tab
      onComponentDblclick: function(tree, rec, node)  {
          var id = rec.id,
              tabId = 'bindtab-' + id,
              tabs = this.lookupReference('tabs'),
              tab = tabs.items.get(tabId),
              component, reference;
          
          if (!tab) {
              component = this.env.getCmp(id);
              reference = component.reference;
              tab = tabs.add({
                  xtype: 'bindinspector-componentdetail',
                  env: this.env,
                  itemId: tabId,
                  title: reference ? '[' + reference + '] ' + id : id,
                  closable: true,
                  component: component
              });
          }
          
          tabs.setActiveTab(tab);
      },
  
      // loop over the view models and build a reference that shows where each
      // data point originates
      buildVMDataMap: function (vm) {
          var env = this.env,
              currVM = vm,
              dataMap = vm.dataMap,
              viewModels = [],
              data, isDirect;
  
          // if the datamap has not yet been created for this view model build it
          if (!dataMap) {
              dataMap = vm.dataMap = {};
  
              // collect up all ancestor view models which this view model will inherit from
              while (currVM) {
                  viewModels.push(currVM);
                  currVM = env.getVM(currVM.parent);
              }
  
              // loop through and see if each data point is found on this view model or an ancestor
              // and catalog all viewmodels where this data point is found (to how overlapping keys)
              Ext.Array.forEach(viewModels, function (item) {
                  var key;
                  data = item.data;
                  if (data && Ext.isObject(data)) {
                      for (key in data) {
                          isDirect = data.hasOwnProperty(key);
                          if (!dataMap[key]) {
                              dataMap[key] = {
                                  isDirect: isDirect
                              };
                          }
                          dataMap[key].ownerVMs = dataMap[key].ownerVMs || [];
                          if (isDirect) {
                              dataMap[key].ownerVMs.push(item);
                          }
                      }
                  }
              });
          }
  
          return vm;
      }
  });
  