linuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacos
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
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'] |
|
}); |
|
}); |