windowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinux
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
344 lines
13 KiB
9 years ago
|
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 <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> 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> 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> 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']
|
||
|
});
|
||
|
});
|