// This override adds diagnostics to the Ext.layout.Context class. /** */ Ext.define('Ext.diag.layout.Context', { override: 'Ext.layout.Context', requires: [ 'Ext.perf.Monitor' ], logOn: { //boxParent: true, //calculate: true, //cancelComponent: true, //cancelLayout: true, //doInvalidate: true, //flush: true, //flushInvalidate: true, //invalidate: true, //initItem: true, //layoutDone: true, //queueLayout: true, //resetLayout: true, //runCycle: true, //setProp: true, 0:0 }, //profileLayoutsByType: true, //reportOnSuccess: true, cancelComponent: function (comp) { if (this.logOn.cancelComponent) { Ext.log('cancelCmp: ', comp.id); } this.callParent(arguments); }, cancelLayout: function (layout) { if (this.logOn.cancelLayout) { Ext.log('cancelLayout: ', this.getLayoutName(layout)); } this.callParent(arguments); }, callLayout: function (layout, methodName) { var accum = this.accumByType[layout.type], frame = accum && accum.enter(); this.callParent(arguments); if (accum) { frame.leave(); } }, checkRemainingLayouts: function () { var me = this, expected = 0, key, layout; for (key in me.layouts) { layout = me.layouts[key]; if (me.layouts.hasOwnProperty(key) && layout.running) { ++expected; } } if (me.remainingLayouts != expected) { Ext.Error.raise({ msg: 'Bookkeeping error me.remainingLayouts' }); } }, flush: function () { if (this.logOn.flush) { var items = this.flushQueue; Ext.log('--- Flush ', items && items.getCount()); } return this.callParent(arguments); }, flushInvalidates: function () { if (this.logOn.flushInvalidate) { Ext.log('>> flushInvalidates'); } var ret = this.callParent(arguments); if (this.logOn.flushInvalidate) { Ext.log('<< flushInvalidates'); } return ret; }, getCmp: function (target) { var ret = this.callParent(arguments); if (!ret.wrapsComponent) { Ext.Error.raise({ msg: target.id + ' is not a component' }); } return ret; }, getEl: function (parent, target) { var ret = this.callParent(arguments); if (ret && ret.wrapsComponent) { Ext.Error.raise({ msg: parent.id + '/' + target.id + ' is a component (expected element)' }); } return ret; }, getLayoutName: function (layout) { return layout.owner.id + '<' + layout.type + '>'; }, layoutDone: function (layout) { var me = this, name = me.getLayoutName(layout); if (me.logOn.layoutDone) { Ext.log('layoutDone: ', name, ' ( ', me.remainingLayouts, ' running)'); } if (!layout.running) { Ext.Error.raise({ msg: name + ' is already done' }); } if (!me.remainingLayouts) { Ext.Error.raise({ msg: name + ' finished but no layouts are running' }); } me.callParent(arguments); }, layoutTreeHasFailures: function (layout, reported) { var me = this; function hasFailure (lo) { var failure = !lo.done, key, childLayout; if (lo.done) { for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { childLayout = me.layouts[key]; if (childLayout.owner.ownerLayout === lo) { if (hasFailure(childLayout)) { failure = true; } } } } } return failure; } if (hasFailure(layout)) { return true; } function markReported (lo) { var key, childLayout; reported[lo.id] = 1; for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { childLayout = me.layouts[key]; if (childLayout.owner.ownerLayout === lo) { markReported(childLayout); } } } } markReported(layout); return false; }, queueLayout: function (layout) { if (layout.done || layout.blockCount || layout.pending) { Ext.Error.raise({ msg: this.getLayoutName(layout) + ' should not be queued for layout' }); } if (this.logOn.queueLayout) { Ext.log('Queue ', this.getLayoutName(layout)); } return this.callParent(arguments); }, reportLayoutResult: function (layout, reported) { var me = this, owner = layout.owner, ownerContext = me.getCmp(owner), blockedBy = [], triggeredBy = [], key, value, i, length, childLayout, item, setBy, info; reported[layout.id] = 1; for (key in layout.blockedBy) { if (layout.blockedBy.hasOwnProperty(key)) { blockedBy.push(layout.blockedBy[key]); } } blockedBy.sort(); for (key in me.triggersByLayoutId[layout.id]) { if (me.triggersByLayoutId[layout.id].hasOwnProperty(key)) { value = me.triggersByLayoutId[layout.id][key]; triggeredBy.push({ name: key, info: value }); } } triggeredBy.sort(function (a, b) { return a.name < b.name ? -1 : (b.name < a.name ? 1 : 0); }); Ext.log({indent: 1}, (layout.done ? '++' : '--'), me.getLayoutName(layout), (ownerContext.isBoxParent ? ' [isBoxParent]' : ''), (ownerContext.boxChildren ? ' - boxChildren: ' + ownerContext.state.boxesMeasured + '/' + ownerContext.boxChildren.length : ''), ownerContext.boxParent ? (' - boxParent: ' + ownerContext.boxParent.id) : '', ' - size: ', ownerContext.widthModel.name, '/', ownerContext.heightModel.name); if (!layout.done || me.reportOnSuccess) { if (blockedBy.length) { ++Ext.log.indent; Ext.log({indent: 1}, 'blockedBy: count=',layout.blockCount); length = blockedBy.length; for (i = 0; i < length; i++) { Ext.log(blockedBy[i]); } Ext.log.indent -= 2; } if (triggeredBy.length) { ++Ext.log.indent; Ext.log({indent: 1}, 'triggeredBy: count='+layout.triggerCount); length = triggeredBy.length; for (i = 0; i < length; i++) { info = value.info || value; item = info.item; setBy = (item.setBy && item.setBy[info.name]) || '?'; value = triggeredBy[i]; Ext.log( value.name, ' (', item.props[info.name], ') dirty: ', (item.dirty ? !!item.dirty[info.name] : false), ', setBy: ', setBy ); } Ext.log.indent -= 2; } } for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { childLayout = me.layouts[key]; if (!childLayout.done && childLayout.owner.ownerLayout === layout) { me.reportLayoutResult(childLayout, reported); } } } for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { childLayout = me.layouts[key]; if (childLayout.done && childLayout.owner.ownerLayout === layout) { me.reportLayoutResult(childLayout, reported); } } } --Ext.log.indent; }, resetLayout: function (layout) { var me = this, type = layout.type, name = me.getLayoutName(layout), accum = me.accumByType[type], frame; if (me.logOn.resetLayout) { Ext.log('resetLayout: ', name, ' ( ', me.remainingLayouts, ' running)'); } if (!me.state) { // if (first time ... before run) if (!accum && me.profileLayoutsByType) { me.accumByType[type] = accum = Ext.Perf.get('layout_' + layout.type); } me.numByType[type] = (me.numByType[type] || 0) + 1; } frame = accum && accum.enter(); me.callParent(arguments); if (accum) { frame.leave(); } me.checkRemainingLayouts(); }, round: function (t) { return Math.round(t * 1000) / 1000; }, run: function () { var me = this, ret, time, key, value, i, layout, boxParent, children, n, reported, unreported, calcs, total, calcsLength, calc; me.accumByType = {}; me.calcsByType = {}; me.numByType = {}; me.timesByType = {}; me.triggersByLayoutId = {}; Ext.log.indentSize = 3; Ext.log('==================== LAYOUT ===================='); time = Ext.perf.getTimestamp(); ret = me.callParent(arguments); time = Ext.perf.getTimestamp() - time; if (me.logOn.boxParent && me.boxParents) { for (key in me.boxParents) { if (me.boxParents.hasOwnProperty(key)) { boxParent = me.boxParents[key]; children = boxParent.boxChildren; n = children.length; Ext.log('boxParent: ', boxParent.id); for (i = 0; i < n; ++i) { Ext.log(' --> ', children[i].id); } } } } if (ret) { Ext.log('----------------- SUCCESS -----------------'); } else { Ext.log( {level: 'error' }, '----------------- FAILURE -----------------' ); } for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { layout = me.layouts[key]; if (layout.running) { Ext.log.error('Layout left running: ', me.getLayoutName(layout)); } if (layout.ownerContext) { Ext.log.error('Layout left connected: ', me.getLayoutName(layout)); } } } if (!ret || me.reportOnSuccess) { reported = {}; unreported = 0; for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { layout = me.layouts[key]; if (me.items[layout.owner.el.id].isTopLevel) { if (me.reportOnSuccess || me.layoutTreeHasFailures(layout, reported)) { me.reportLayoutResult(layout, reported); } } } } // Just in case we missed any layouts... for (key in me.layouts) { if (me.layouts.hasOwnProperty(key)) { layout = me.layouts[key]; if (!reported[layout.id]) { if (!unreported) { Ext.log('----- Unreported!! -----'); } ++unreported; me.reportLayoutResult(layout, reported); } } } } Ext.log('Cycles: ', me.cycleCount, ', Flushes: ', me.flushCount, ', Calculates: ', me.calcCount, ' in ', me.round(time), ' msec'); Ext.log('Calculates by type:'); /*Ext.Object.each(me.numByType, function (type, total) { Ext.log(type, ': ', total, ' in ', me.calcsByType[type], ' tries (', Math.round(me.calcsByType[type] / total * 10) / 10, 'x) at ', me.round(me.timesByType[type]), ' msec (avg ', me.round(me.timesByType[type] / me.calcsByType[type]), ' msec)'); });*/ calcs = []; for (key in me.numByType) { if (me.numByType.hasOwnProperty(key)) { total = me.numByType[key]; calcs.push({ type : key, total : total, calcs : me.calcsByType[key], multiple : Math.round(me.calcsByType[key] / total * 10) / 10, calcTime : me.round(me.timesByType[key]), avgCalcTime: me.round(me.timesByType[key] / me.calcsByType[key]) }); } } calcs.sort(function (a,b) { return b.calcTime - a.calcTime; }); calcsLength = calcs.length; for (i=0; i<calcsLength; i++) { calc = calcs[i]; Ext.log( calc.type, ': ', calc.total, ' in ', calc.calcs, ' tries (', calc.multiple, 'x) at ', calc.calcTime, ' msec (avg ', calc.avgCalcTime, ' msec)' ); } return ret; }, runCycle: function () { if (this.logOn.runCycle) { Ext.log('>>> Cycle ', this.cycleCount, ' (queue length: ', this.layoutQueue.length, ')'); } return this.callParent(arguments); }, runLayout: function (layout) { var me = this, type = layout.type, accum = me.accumByType[type], frame, ret, time; if (me.logOn.calculate) { Ext.log('-- calculate ', this.getLayoutName(layout)); } frame = accum && accum.enter(); time = Ext.perf.getTimestamp(); ret = me.callParent(arguments); time = Ext.perf.getTimestamp() - time; if (accum) { frame.leave(); } me.calcsByType[type] = (me.calcsByType[type] || 0) + 1; me.timesByType[type] = (me.timesByType[type] || 0) + time; /* add a / to the front of this line to enable layout completion logging if (layout.done) { var ownerContext = me.getCmp(layout.owner), props = ownerContext.props; if (layout.isComponentLayout) { Ext.log('complete ', layout.owner.id, ':', type, ' w=',props.width, ' h=', props.height); } else { Ext.log('complete ', layout.owner.id, ':', type, ' cw=',props.contentWidth, ' ch=', props.contentHeight); } }**/ return ret; } });