Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

344 lines
13 KiB

Ext.define('Ext.app.bindinspector.ViewModelDetail', {
extend: 'Ext.tree.Panel',
alias: 'widget.bindinspector-viewmodeldetail',
rootVisible: false,
cls: Ext.baseCSSPrefix + 'bindinspector-viewmodeldetail',
inheritedCls: Ext.baseCSSPrefix + 'bindinspector-inherited',
notInheritedCls: Ext.baseCSSPrefix + 'bindinspector-not-inherited',
highlightedCls: Ext.baseCSSPrefix + 'bindinspector-highlighted',
unhighlightedCls: Ext.baseCSSPrefix + 'bindinspector-unhighlighted',
lastItemCls: Ext.baseCSSPrefix + 'bindinspector-last-item',
initComponent: function() {
var me = this,
vm = me.vm,
title = 'VM    ⇒    ',
env = me.up('bindinspector-container').env,
comp = env.getCmp(vm.view);
// add the component's reference to the title if it has a reference
if (comp.reference) {
title += '[' + comp.reference + '] • ';
}
me.title = title += comp.id;
me.viewConfig = {
getRowClass: function (record, index, rowParams, store) {
var data = record.get('hasData'),
stub = record.get('hasStub'),
cls = [],
highlighted = record.get('highlighted');
// indicate whether the root data property is inherited or belongs to this VM
if (record.get('inherited')) {
cls.push(me.inheritedCls);
} else {
cls.push(me.notInheritedCls);
}
// indicate whether the the data corresponds to the selected binding from
// ComponentDetail.onSelectionChange()
if (highlighted === true) {
cls.push(me.highlightedCls);
} else if (highlighted === -1) {
cls.push(me.unhighlightedCls);
}
// decoration for the last item in the tree (adds a shadow for modern browsers)
if (index === store.getCount() - 1) {
cls.push(me.lastItemCls);
}
// indicate if the data point is present, but there is no component binding to it
if (data && (!stub || record.get('cumulativeBindCount') === 0)) {
cls.push(me.dataOnlyCls);
}
return cls.join(' ');
}
};
me.store = {
model: me.Model,
root: {
text: 'Root',
expanded: true,
children: me.setupData(vm, vm.data, vm.rootStub)
}
};
me.columns = [{
width: 40,
tdCls: Ext.baseCSSPrefix + 'bindinspector-indicator-col',
align: 'center',
scope: me,
renderer: me.renderIndicator
}, {
flex: 1,
xtype: 'treecolumn',
dataIndex: 'name',
text: 'Name',
scope: me,
renderer: me.renderName
}, {
flex: 1,
dataIndex: 'value',
text: 'Value',
scope: me,
renderer: Ext.app.bindinspector.Util.valueRenderer
}, {
text: 'Bind #',
width: 64,
align: 'center',
renderer: me.renderBindCount,
scope: me
}, {
width: 40,
isSearch: true,
renderer: me.dataSrcConsumerRenderer,
scope: me
}];
me.callParent();
me.on('cellclick', me.onSearchCellClick, me);
},
dataOnlyNode: 'This item contains data but has nothing requesting the value',
stubOnlyNode: 'This item has the value requested but no data backing it',
dataPointLoading: 'Data point is loading (at the time the app snapshot was captured)',
dataPointLoadingCls: Ext.baseCSSPrefix + 'bindinspector-isloading',
zeroBindingCls: Ext.baseCSSPrefix + 'bi-zero-bind-count',
dataOnlyCls: Ext.baseCSSPrefix + 'bindinspector-data-only',
stubOnlyCls: Ext.baseCSSPrefix + 'bindinspector-stub-only',
// handler for when the icon in the search column (has config isSearch: true) is clicked // upwardly handled by Container
onSearchCellClick: function (view, td, cellIndex, rec, tr, rowIndex, e) {
if (view.getHeaderCt().getHeaderAtIndex(cellIndex).isSearch) {
this.up('bindinspector-container').fireEvent('vmSearchClick', rec);
}
},
// helper method to find the root data node from any passed node in the hierarchy
getFirstTierRec: function (rec) {
var isFirstTier = rec.parentNode.isRoot(),
firstTier;
if (!isFirstTier) {
rec.bubble(function (ni) {
if (ni.parentNode.isRoot()) {
firstTier = ni;
return false;
}
});
}
return isFirstTier ? rec : firstTier;
},
// renderer for the search icon column - shows a search icon and the root data node that will be searched for in all parent VMs when clicked
dataSrcConsumerRenderer: function (v, meta, rec) {
var firstTier = this.getFirstTierRec(rec),
firstTierName = firstTier.get('name');
meta.tdCls = Ext.baseCSSPrefix + 'bindinspector-data-search-cell';
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="Click to indicate within the Component List all ViewModels with a data property of &nbsp;<b>' + firstTierName + '</b>"';
},
// renderer for the indicator column - shows whether the data point originates from this VM, an ancestor VM, or in both this and some ancestor VM
renderIndicator: function (v, meta, rec) {
var ownerVMs = rec.get('ownerVMs'),
len = ownerVMs.length,
direct = false,
inherited = false,
val = '',
firstTier = this.getFirstTierRec(rec),
isFirstTier = firstTier === rec,
firstTierName = firstTier.get('name'),
vmPlural;
Ext.Array.forEach(ownerVMs, function (vm) {
if (vm.id === vm.thisVM) {
direct = true;
}
if (vm.id !== vm.thisVM) {
inherited = true;
}
});
if (direct && inherited) {
val = Ext.util.Format.format('<span style="color:#DB7851;">{0}</span>', isFirstTier ? '◓' : '-');
vmPlural = len > 1 ? 'VMs' : 'VM';
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; provided by this VM and ' + (len - 1) + ' ancestor ' + vmPlural + '"';
} else if (direct) {
val = isFirstTier ? '●' : '';
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; is provided by this VM"';
} else if (inherited) {
val = isFirstTier ? '○' : '';
vmPlural = len > 1 ? 'VMs' : 'VM';
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="<b>' + firstTierName + '</b>&nbsp; is provided by ' + len + ' ancestor ' + vmPlural + '"';
}
return val;
},
// renderer for the bind count column
renderBindCount: function (v, meta, rec) {
var len = rec.get('children').length,
bindCount = rec.get('bindCount') || 0,
total, bindingsText;
v = bindCount;
if (v === 0) {
v = '<span class="' + this.zeroBindingCls + '">' + v + '</span>';
}
if (len) {
total = rec.get('cumulativeBindCount') || '?';
if (total === 0 || total === '?') {
v += ' / <span class="' + this.zeroBindingCls + '">' + total + '</span>';
} else {
v += ' / ' + total;
}
}
bindingsText = 'Bindings Count = <b>' + bindCount + '</b>';
if (total && total !== 0 && total !== '?') {
bindingsText += '<br>Cumulative Bindings Count = <b>' + total + '</b>';
}
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="' + bindingsText + '"';
return v;
},
// renderer for the main tree column
renderName: function(v, meta, rec) {
var me = this,
data = rec.get('hasData'),
stub = rec.get('hasStub'),
tip = '';
if (rec.get('isLoading')) {
meta.tdCls = me.dataPointLoadingCls;
tip += me.dataPointLoading;
} else if (data && (!stub || rec.get('cumulativeBindCount') === 0)) {
tip += me.dataOnlyNode;
} else if (stub && !data) {
meta.tdCls = me.stubOnlyCls;
tip += me.stubOnlyNode;
}
if (tip !== '') {
meta.tdAttr = 'data-qclass="' + Ext.baseCSSPrefix + 'componentlist-tip" data-qtip="' + tip + '"';
}
return v;
},
// build method to construct the nodes displayed in the ViewModelDetail tree
setupData: function(vm, data, stub, inherited, ownerVMs) {
var merged = {},
out = [],
dataMap = vm.dataMap,
dm = [],
item, children, stubChild, key, stopDigging, linkInfo;
if (data && Ext.isObject(data)) {
if (data.isModel) {
data = data.data;
// prevent looping any deeper over the model
stopDigging = true;
} else if (data.isStore) {
stopDigging = true;
data = null;
}
if (data) {
for (key in data) {
if (!ownerVMs) {
dm = dataMap[key] ? dataMap[key].ownerVMs : [];
}
item = {
name: key,
value: data[key],
inherited: Ext.isDefined(inherited) ? inherited : !data.hasOwnProperty(key),
ownerVMs: Ext.isDefined(ownerVMs) ? ownerVMs : [],
hasData: true
};
Ext.Array.forEach(dm, function (v) {
item.ownerVMs.push({
id: v.id,
view: v.view,
thisVM: vm.id
});
});
stubChild = Ext.app.bindinspector.Util.getChildStub(key, stub);
if (stubChild) {
item.hasStub = true;
item.isLoading = stubChild.isLoading;
item.iconCls = stubChild.isLoading ? this.dataPointLoadingCls : '';
item.bindCount = stubChild.bindCount;
item.cumulativeBindCount = stubChild.cumulativeBindCount;
item.stub = stubChild;
}
merged[key] = item;
}
}
}
if (stub) {
children = stub.children;
for (key in children) {
stubChild = children[key];
item = merged[key];
if (!item) {
item = {
name: key,
value: stubChild.value || undefined,
inherited: inherited || false,
ownerVMs: ownerVMs || [],
hasData: false,
hasStub: true,
isLoading: stubChild.isLoading,
iconCls: stubChild.isLoading ? this.dataPointLoadingCls : '',
bindCount: stubChild.bindCount,
cumulativeBindCount: stubChild.cumulativeBindCount,
stub: stubChild
};
linkInfo = stubChild.linkInfo;
if (linkInfo && linkInfo.sameTarget) {
item.value = linkInfo.value;
// Fudge having data, since we don't want to show an icon
// for all links
item.hasData = item.value !== undefined;
}
merged[key] = item;
}
}
}
for (key in merged) {
item = merged[key];
//if (!stopDigging) { // was missing nested model data with stopDigging
item.children = this.setupData(vm, item.value, item.stub, item.inherited, item.ownerVMs);
//}
delete item.stub;
if (item.children && item.children.length) {
item.expanded = true;
item.leaf = false;
} else {
item.leaf = true;
}
out.push(merged[key]);
}
return out;
}
}, function() {
this.prototype.Model = Ext.define(null, {
extend: 'Ext.data.TreeModel',
fields: ['name', 'value', 'inherited', 'hasData', 'hasStub', 'isLoading', 'bindCount', 'cumulativeBindCount', 'highlighted']
});
});