PageAnalyzer.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. //{"duration":8,"time":"2012-02-25T10:55:01","success":false,"cycleCount":6,"flushCount":5,"calcCount":7,"orphans":0,"layouts":[{"allDone":true,"done":true,"id":"autocomponent-1193","type":"autocomponent","name":"headercontainer-1115<autocomponent>","blocks":[],"boxParent":null,"isBoxParent":null,"triggers":[],"orphan":false,"heightModel":"configured","widthModel":"calculated","children":[],"duration":0,"totalTime":0,"count":1},{"allDone":false,"done":false,"id":"gridcolumn-1194","type":"gridcolumn","name":"headercontainer-1115<gridcolumn>","blocks":[],"boxParent":null,"isBoxParent":null,"triggers":[{"name":"gridview-1118.height","prop":"height","value":null,"dirty":false,"missing":true,"setBy":"??"},{"name":"gridcolumn-1116.height","prop":"height","value":22,"dirty":false,"missing":false,"setBy":"gridcolumn-1116<columncomponent>"},{"name":"headercontainer-1115.contentWidth","prop":"contentWidth","value":335,"dirty":false,"missing":false,"setBy":"headercontainer-1115<gridcolumn>"},{"name":"headercontainer-1115.height","prop":"height","value":0,"dirty":false,"missing":false,"setBy":"headercontainer-1115<autocomponent>"},{"name":"headercontainer-1115.width","prop":"width","value":335,"dirty":false,"missing":false,"setBy":"headercontainer-1115<autocomponent>"},{"name":"numbercolumn-1117.height","prop":"height","value":22,"dirty":false,"missing":false,"setBy":"numbercolumn-1117<columncomponent>"}],"orphan":false,"heightModel":"configured","widthModel":"calculated","children":[{"allDone":true,"done":true,"id":"columncomponent-1195","type":"columncomponent","name":"gridcolumn-1116<columncomponent>","blocks":[],"boxParent":null,"isBoxParent":true,"triggers":[{"name":"gridcolumn-1116.containerChildrenDone:dom","prop":"containerChildrenDone","value":true,"dirty":false,"missing":false,"setBy":"??"},{"name":"gridcolumn-1116.width:dom","prop":"width","value":168,"dirty":false,"missing":false,"setBy":"headercontainer-1115<gridcolumn>"}],"orphan":false,"heightModel":"shrinkWrap (calculatedFromShrinkWrap)","widthModel":"calculated","children":[],"duration":0,"totalTime":0,"count":2},{"allDone":true,"done":true,"id":"columncomponent-1197","type":"columncomponent","name":"numbercolumn-1117<columncomponent>","blocks":[],"boxParent":null,"isBoxParent":true,"triggers":[{"name":"numbercolumn-1117.containerChildrenDone:dom","prop":"containerChildrenDone","value":true,"dirty":false,"missing":false,"setBy":"??"},{"name":"numbercolumn-1117.width:dom","prop":"width","value":167,"dirty":false,"missing":false,"setBy":"headercontainer-1115<gridcolumn>"}],"orphan":false,"heightModel":"shrinkWrap (calculatedFromShrinkWrap)","widthModel":"calculated","children":[],"duration":2,"totalTime":2,"count":2}],"duration":4,"totalTime":6,"count":2}],"statsById":{"columncomponent-1195":{"duration":0,"count":2},"columncomponent-1197":{"duration":2,"count":2},"autocomponent-1193":{"duration":0,"count":1},"gridcolumn-1194":{"duration":4,"count":2}},"statsByType":{"columncomponent":{"duration":2,"layoutCount":2,"count":4},"autocomponent":{"duration":0,"layoutCount":1,"count":1},"gridcolumn":{"duration":4,"layoutCount":1,"count":2}},"flushTime":1,"flushInvalidateTime":1,"flushInvalidateCount":1,"flushLayoutStats":{"completeLayout":{"count":2,"time":0}},"totalTime":8,"num":5}
  2. Ext.Loader.setConfig({
  3. enabled: true
  4. });
  5. Ext.Loader.setPath({
  6. 'Ext.ux': '../ux/',
  7. 'PageAnalyzer': './'
  8. });
  9. Ext.require([
  10. 'Ext.data.*',
  11. 'Ext.tree.*',
  12. 'Ext.container.Viewport',
  13. 'Ext.layout.container.Border',
  14. 'Ext.History',
  15. 'Ext.tab.Panel',
  16. 'Ext.grid.column.Action',
  17. 'Ext.form.field.TextArea',
  18. 'Ext.form.field.Number',
  19. 'Ext.toolbar.TextItem',
  20. 'Ext.layout.container.Table',
  21. 'PageAnalyzer.models.LayoutTreeNode',
  22. 'PageAnalyzer.models.ComponentTreeNode',
  23. 'PageAnalyzer.Console',
  24. 'PageAnalyzer.Summary',
  25. 'Ext.ux.IFrame',
  26. 'Ext.state.Manager',
  27. 'Ext.state.CookieProvider'
  28. ]);
  29. Ext.define('PageAnalyzer.MainForm', {
  30. statsGatherCount: 0,
  31. layoutTpl: [
  32. '<tpl if="isBoxParent">',
  33. '<img class="x-tree-icon" src="resources/images/boxParent.gif">',
  34. '</tpl>',
  35. '{name:this.encode}',
  36. {
  37. encode: function (value) {
  38. return Ext.htmlEncode(value);
  39. }
  40. }
  41. ],
  42. runTpl: [
  43. 'Run #{num} ({time:this.date})',
  44. {
  45. date: function (value) {
  46. return Ext.Date.format(value, "Y-m-d H:i:s");
  47. }
  48. }
  49. ],
  50. triggerTpl: [
  51. '<div class="pgan-{[values.missing ? "missing" : "available"]}-value">',
  52. '{name} (={[String(values.value) || "?"]}) - dirty: {dirty} - setBy: {setBy:this.encode}',
  53. '</div>',
  54. {
  55. encode: function (value) {
  56. return Ext.htmlEncode(value);
  57. }
  58. }
  59. ],
  60. constructor: function (config) {
  61. Ext.state.Manager.setProvider(Ext.create('Ext.state.CookieProvider'));
  62. var me = this;
  63. Ext.apply(me, config);
  64. me.noCharts = me.getOption('nocharts');
  65. me.runNumber = 0;
  66. me.viewport = Ext.widget(me.createViewport());
  67. me.target = me.targetFrame.getWin();
  68. me.reloadBtn = me.viewport.down('#reloadBtn');
  69. me.stateText = me.viewport.down('#stateText');
  70. if (me.target) {
  71. setInterval(function() {
  72. me.updateConnectedState();
  73. }, 100);
  74. } else {
  75. me.reloadBtn.setDisabled(true);
  76. }
  77. Ext.History.init(function () {
  78. Ext.History.on({
  79. change: 'onHistoryChange',
  80. scope: me
  81. });
  82. me.onHistoryChange(Ext.History.getToken());
  83. });
  84. },
  85. roundTimestamp: function(num) {
  86. return Math.round(num * 100) / 100;
  87. },
  88. createLayoutTree: function() {
  89. var me = this,
  90. store = new Ext.data.TreeStore({
  91. proxy: 'memory',
  92. model: 'PageAnalyzer.models.LayoutTreeNode'
  93. }),
  94. tree = {
  95. xtype: 'treepanel',
  96. store: store,
  97. rootVisible: false,
  98. useArrows: true,
  99. title: 'Layouts',
  100. region: 'center',
  101. viewConfig: {
  102. getRowClass: function (record) {
  103. return 'pgan-' + record.data.type;
  104. }
  105. },
  106. columns: [
  107. {
  108. xtype: 'treecolumn',
  109. text: 'Layout',
  110. flex: 1,
  111. hideable: false,
  112. draggable: false,
  113. dataIndex: 'text'
  114. },
  115. {
  116. text: 'Triggers',
  117. width: 200,
  118. dataIndex: 'triggers'
  119. },
  120. {
  121. text: 'Blocks',
  122. width: 125,
  123. hidden: true,
  124. dataIndex: 'blocks',
  125. id: 'blocksCol'
  126. },
  127. {
  128. text: 'Width',
  129. width: 90,
  130. dataIndex: 'widthModel'
  131. },
  132. {
  133. text: 'Height',
  134. width: 90,
  135. dataIndex: 'heightModel'
  136. },
  137. {
  138. text: 'Box Parent',
  139. width: 100,
  140. hidden: true,
  141. dataIndex: 'boxParent',
  142. id: 'boxParentCol'
  143. },
  144. {
  145. text: 'Time',
  146. width: 70,
  147. dataIndex: 'duration',
  148. id: 'durationCol',
  149. renderer: me.roundTimestamp
  150. },
  151. {
  152. text: 'Calls',
  153. width: 50,
  154. dataIndex: 'count',
  155. id: 'countCol'
  156. },
  157. {
  158. text: 'Avg Time',
  159. width: 70,
  160. id: 'avgCol',
  161. renderer: function(value, metadata, rec) {
  162. return me.roundTimestamp(rec.data.duration / rec.data.count);
  163. }
  164. },
  165. {
  166. text: 'Tot Time',
  167. width: 70,
  168. dataIndex: 'totalTime',
  169. id: 'totalTimeCol',
  170. renderer: me.roundTimestamp
  171. },
  172. {
  173. menuDisabled: true,
  174. sortable: false,
  175. xtype: 'actioncolumn',
  176. hideable: false,
  177. width: 40,
  178. items: [{
  179. icon: 'resources/images/delete.gif',
  180. iconCls: 'pgan-delete-row',
  181. tooltip: 'Delete this run',
  182. handler: this.onDeleteLayoutRun,
  183. scope: this
  184. }, {
  185. icon: 'resources/images/info.gif',
  186. iconCls: 'pgan-show-row-data',
  187. tooltip: 'Show raw data for this run',
  188. handler: this.onShowLayoutRunRawData,
  189. scope: this
  190. }]
  191. }
  192. ]
  193. };
  194. this.layoutTree = Ext.widget(tree);
  195. this.layoutTree.getSelectionModel().on({
  196. selectionchange: this.onLayoutSelectionChange,
  197. scope: this
  198. });
  199. return this.layoutTree;
  200. },
  201. createComponentTree: function() {
  202. var me = this,
  203. store = new Ext.data.TreeStore({
  204. proxy: 'memory',
  205. model: 'PageAnalyzer.models.ComponentTreeNode'
  206. }),
  207. compTree = {
  208. xtype: 'treepanel',
  209. store: store,
  210. rootVisible: false,
  211. useArrows: true,
  212. title: 'Components',
  213. region: 'center',
  214. viewConfig: {
  215. getRowClass: function (record) {
  216. var data = record.data;
  217. if (data.isComponent) {
  218. return 'pgan-component';
  219. } else {
  220. return 'pgan-noncomponent';
  221. }
  222. }
  223. },
  224. columns: [
  225. {
  226. xtype: 'treecolumn',
  227. text: 'Component',
  228. flex: 1,
  229. hideable: false,
  230. draggable: false,
  231. width: 450,
  232. dataIndex: 'text'
  233. },
  234. {
  235. text: 'Width',
  236. width: 90,
  237. hidden: true,
  238. dataIndex: 'width'
  239. },
  240. {
  241. text: 'Height',
  242. width: 90,
  243. hidden: true,
  244. dataIndex: 'height'
  245. },
  246. {
  247. text: 'X',
  248. width: 90,
  249. hidden: true,
  250. dataIndex: 'x'
  251. },
  252. {
  253. text: 'Y',
  254. width: 90,
  255. hidden: true,
  256. dataIndex: 'y'
  257. },
  258. {
  259. text: 'CSS Class',
  260. width: 100,
  261. dataIndex: 'cssClass',
  262. id: 'cssClass'
  263. },
  264. {
  265. text: 'XType',
  266. width: 100,
  267. dataIndex: 'xtype',
  268. id: 'xtypeCol'
  269. },
  270. {
  271. text: 'Rendered',
  272. width: 100,
  273. dataIndex: 'rendered',
  274. id: 'renderedCol',
  275. hidden: true
  276. },
  277. {
  278. text: 'Hidden',
  279. width: 100,
  280. dataIndex: 'hidden',
  281. id: 'hiddenCol',
  282. hidden: true
  283. },
  284. {
  285. text: 'IsContainer',
  286. width: 100,
  287. dataIndex: 'isContainer',
  288. id: 'isContainerCol',
  289. hidden: true
  290. },
  291. {
  292. menuDisabled: true,
  293. sortable: false,
  294. xtype: 'actioncolumn',
  295. hideable: false,
  296. width: 20,
  297. items: [{
  298. icon: 'resources/images/info.gif',
  299. iconCls: 'pgan-display-comp-spec',
  300. tooltip: 'Display Component Test Spec',
  301. handler: this.onDisplayLayoutSpec,
  302. scope: this
  303. }]
  304. }
  305. ]
  306. };
  307. me.componentTree = Ext.widget(compTree);
  308. return me.componentTree;
  309. },
  310. createPerfPanel: function() {
  311. this.perfPanel = this.noCharts ? null : (Ext.widget({
  312. xtype: 'panel',
  313. title: 'Performance',
  314. layout: 'fit',
  315. border: false,
  316. tbar: [
  317. {
  318. text: 'Clear',
  319. handler: this.onClearStats,
  320. scope: this
  321. }
  322. ],
  323. items: [
  324. new PageAnalyzer.Console({
  325. itemId: 'perfcon'
  326. })
  327. ]
  328. }));
  329. return this.perfPanel;
  330. },
  331. createLayoutPanel: function() {
  332. var me = this;
  333. me.layoutPanel = Ext.widget({
  334. xtype: 'panel',
  335. title: 'Layout',
  336. layout: 'border',
  337. tbar: [
  338. {
  339. text: 'Load Run',
  340. iconCls: 'pgan-load-run',
  341. handler: me.onLoadRun,
  342. scope: me
  343. },
  344. {
  345. text: 'Show All Triggers',
  346. enableToggle: true,
  347. handler: me.onShowAllTriggers,
  348. scope: me
  349. },
  350. {
  351. text: 'Clear',
  352. handler: me.onClearLayouts,
  353. scope: me
  354. },
  355. '->',
  356. {
  357. text: 'Capture Layout Spec',
  358. handler: me.onDisplayLayoutSpec,
  359. scope: me
  360. }
  361. ],
  362. items: [
  363. me.createLayoutTree(),
  364. this.typeSummary = (this.noCharts ? null : new PageAnalyzer.Summary({
  365. title: 'Summaries',
  366. region: 'south',
  367. height: '50%',
  368. collapsible: true,
  369. split: true
  370. }))
  371. ]
  372. });
  373. return me.layoutPanel;
  374. },
  375. createComponentPanel: function() {
  376. var me = this;
  377. me.componentPanel = Ext.widget({
  378. xtype: 'panel',
  379. title: 'Components',
  380. layout: 'border',
  381. tbar: [
  382. {
  383. text: 'Generate Component Tree',
  384. handler: me.loadComponentTree,
  385. scope: me
  386. }
  387. ],
  388. items: [
  389. me.createComponentTree()
  390. ]
  391. });
  392. return me.componentPanel;
  393. },
  394. createTestPagePanel: function() {
  395. return this.targetFrame = Ext.create("Ext.ux.IFrame", {
  396. title: 'Page Under Test',
  397. hideMode: 'offsets'
  398. });
  399. },
  400. createViewport: function() {
  401. this.build = 1;
  402. var me = this,
  403. ret = {
  404. xtype: 'viewport',
  405. layout: 'fit',
  406. items: [
  407. {
  408. xtype: 'panel',
  409. layout: 'fit',
  410. tbar: {
  411. items: [
  412. {
  413. xtype: 'tbtext',
  414. itemId: 'stateText',
  415. tdAttrs: {
  416. width: '100px'
  417. }
  418. },
  419. {
  420. xtype: 'tbtext',
  421. text: 'Test URL:',
  422. itemId: 'titleLbl',
  423. tdAttrs: {
  424. width: '100px'
  425. }
  426. },
  427. me.targetUrlField = Ext.widget({
  428. xtype: 'textfield',
  429. itemId: 'targetUrl',
  430. stateId: 'targetUrlField',
  431. selectOnFocus: true,
  432. flex: 1,
  433. enableKeyEvents: true,
  434. listeners: {
  435. keypress: function(f, e) {
  436. if (e.getKey() === 13) {
  437. me.onLoadPage();
  438. }
  439. }
  440. }
  441. }),
  442. {
  443. text: 'Load',
  444. itemId: 'reloadBtn',
  445. iconCls: 'pgan-refresh',
  446. handler: me.onLoadPage,
  447. scope: me,
  448. tdAttrs: {
  449. width: '100px'
  450. }
  451. },
  452. me.noCharts ? null : {
  453. xtype: 'numberfield',
  454. fieldLabel: 'Build',
  455. style: 'margin-right: 10px;',
  456. labelWidth: 30,
  457. width: 100,
  458. value: me.build,
  459. listeners: {
  460. change: function (field) {
  461. me.build = field.getValue();
  462. }
  463. }
  464. },
  465. me.noCharts ? null : {
  466. text: 'Update Stats',
  467. handler: me.onGatherStats,
  468. scope: me
  469. },
  470. me.noCharts ? null : {
  471. text: 'Reset',
  472. handler: this.onResetStats,
  473. scope: this
  474. }
  475. ]
  476. },
  477. items: [
  478. Ext.create('Ext.tab.Panel', {
  479. items:[
  480. me.createTestPagePanel(),
  481. me.createLayoutPanel(),
  482. me.createComponentPanel(),
  483. me.createPerfPanel()
  484. ]
  485. })
  486. ]
  487. }
  488. ]
  489. };
  490. return ret;
  491. },
  492. addLayoutChildren: function (parent, children) {
  493. var n = children.length,
  494. triggersTpl = Ext.XTemplate.getTpl(this, 'triggerTpl'),
  495. textTpl = Ext.XTemplate.getTpl(this, 'layoutTpl'),
  496. child, data, i, j, k, node, triggers;
  497. for (i = 0; i < n; ++i) {
  498. child = children[i];
  499. triggers = [];
  500. for (j = 0, k = child.triggers.length; j < k; ++j) {
  501. triggers.push(triggersTpl.apply(child.triggers[j]));
  502. }
  503. data = {
  504. text: textTpl.apply(child),
  505. iconCls: 'pgan-layout' +
  506. (child.allDone ? '' : (child.done ? '-partial' : '-not')) + '-done',
  507. leaf: !child.children.length,
  508. triggers: triggers.join(''),
  509. blocks: child.blocks.join('<br>'),
  510. boxParent: child.boxParent,
  511. isBoxParent: child.isBoxParent,
  512. heightModel: child.heightModel,
  513. widthModel: child.widthModel,
  514. type: 'layout',
  515. duration: child.duration,
  516. totalTime: child.totalTime,
  517. count: child.count
  518. };
  519. if (data.boxParent) {
  520. this.showBoxParentCol = true;
  521. }
  522. if (data.blocks) {
  523. this.showBlocksCol = true;
  524. }
  525. node = new PageAnalyzer.models.LayoutTreeNode(data);
  526. parent.appendChild(node);
  527. this.addLayoutChildren(node, child.children);
  528. }
  529. },
  530. addLayoutRuns: function (run) {
  531. if (typeof run == 'string') {
  532. run = Ext.decode(run);
  533. }
  534. if (Ext.isArray(run)) {
  535. Ext.each(run, this.addLayoutRuns, this);
  536. return;
  537. }
  538. if (typeof run.time == 'string') {
  539. run.time = Ext.Date.parse(run.time, "Y-m-d\\TH:i:s");
  540. }
  541. if (typeof run.duration == 'string') {
  542. run.duration = parseInt(run.duration, 10);
  543. }
  544. //run.success = 0;
  545. run.num = ++this.runNumber;
  546. var node = new PageAnalyzer.models.LayoutTreeNode({
  547. text: Ext.XTemplate.getTpl(this, 'runTpl').apply(run),
  548. iconCls: 'pgan-' + (run.success ? 'good' : 'failed') + '-layout-run',
  549. type: 'layoutrun',
  550. duration: run.duration,
  551. totalTime: run.totalTime,
  552. count: 1
  553. });
  554. this.showBoxParentCol = this.showBlocksCol = false;
  555. node.rawData = run;
  556. node.appendChild(new PageAnalyzer.models.LayoutTreeNode({
  557. text: 'Flush',
  558. leaf: true,
  559. duration: run.flushTime,
  560. totalTime: run.flushTime,
  561. count: run.flushCount
  562. }));
  563. node.appendChild(new PageAnalyzer.models.LayoutTreeNode({
  564. text: 'Invalidate',
  565. leaf: true,
  566. duration: run.flushInvalidateTime,
  567. totalTime: run.flushInvalidateTime,
  568. count: run.flushInvalidateCount
  569. }));
  570. Ext.Object.each(run.flushLayoutStats, function(name, val) {
  571. node.appendChild(new PageAnalyzer.models.LayoutTreeNode({
  572. text: (name == 'notifyOwner')
  573. ? 'AfterLayout'
  574. : Ext.String.capitalize(name),
  575. leaf: true,
  576. duration: val.time,
  577. totalTime: val.time,
  578. count: val.count
  579. }));
  580. });
  581. this.addLayoutChildren(node, run.layouts);
  582. this.layoutTree.getRootNode().appendChild(node);
  583. Ext.suspendLayouts();
  584. if (this.showBoxParentCol) {
  585. this.layoutTree.down('#boxParentCol').show();
  586. }
  587. if (this.showBlocksCol) {
  588. this.layoutTree.down('#blocksCol').show();
  589. }
  590. Ext.resumeLayouts(true);
  591. },
  592. getOption: function (opt) {
  593. var s = window.location.search;
  594. var re = new RegExp('(?:^|[&?])' + opt + '(?:[=]([^&]*))?(?:$|[&])', 'i');
  595. var m = re.exec(s);
  596. return m ? (m[1] === undefined ? true : m[1]) : false;
  597. },
  598. onDeleteLayoutRun: function (view, recordIndex, cellIndex, item, e, record) {
  599. record.parentNode.removeChild(record);
  600. },
  601. onHistoryChange: function (token) {
  602. this.targetUrlField.setValue(token);
  603. },
  604. onLayoutSelectionChange: function(selModel, records) {
  605. var run;
  606. if (records.length) {
  607. run = records[0];
  608. while (run && run.data.type != 'layoutrun') {
  609. run = run.parentNode;
  610. }
  611. this.typeSummary.loadTypeSummary(run.rawData.statsByType);
  612. }
  613. },
  614. onShowLayoutRunRawData: function(view, recordIndex, cellIndex, item, e, record) {
  615. var text = Ext.JSON.encodeValue(record.rawData, '\n');
  616. Ext.widget({
  617. xtype: 'window',
  618. modal: true,
  619. title: 'Raw data for ' + record.data.text,
  620. //closeAction: 'close',
  621. autoShow: true,
  622. layout: 'fit',
  623. width: 500,
  624. height: 400,
  625. defaultFocus: 'rawData',
  626. items: [{
  627. xtype: 'textarea',
  628. value: text,
  629. itemId: 'rawData',
  630. readOnly: true,
  631. selectOnFocus: true
  632. }]
  633. }).show();
  634. //this.rawDataTextArea.setValue(text);
  635. },
  636. onLoadRun: function() {
  637. var me = this;
  638. var window = new Ext.Window({
  639. title: 'Load Run',
  640. width: 400,
  641. height: 320,
  642. layout: 'fit',
  643. modal: true,
  644. items: [{
  645. xtype: 'textarea'
  646. }],
  647. buttons: [{
  648. text: 'OK',
  649. handler: function() {
  650. var text = window.down('textarea').getValue();
  651. window.destroy();
  652. me.addLayoutRuns(text);
  653. }
  654. }, {
  655. text: 'cancel',
  656. handler: function() {
  657. window.destroy();
  658. }
  659. }]
  660. });
  661. window.show();
  662. },
  663. getCompNodeForComp: function(comp, refName) {
  664. return new PageAnalyzer.models.ComponentTreeNode({
  665. text: comp.id + (comp.itemId ? ' (' + comp.itemId + ')' : ''),
  666. compId: comp.id || comp.itemId,
  667. xtype: comp.xtype,
  668. refName: refName || '',
  669. rendered: comp.rendered,
  670. hidden: comp.hidden,
  671. isContainer: comp.isContainer,
  672. isElement: false,
  673. isComponent: true,
  674. iconCls: 'pgan-' +
  675. ((comp.rendered && !comp.hidden) ? 'rendered' : 'unrendered') +
  676. (comp.isContainer ? '-container' : '-component')
  677. });
  678. },
  679. getCompNodeForElem: function(el, root, refName) {
  680. root = root ? root.el : el;
  681. return new PageAnalyzer.models.ComponentTreeNode({
  682. text: refName,
  683. width: el.getWidth(),
  684. height: el.getHeight(),
  685. x: root ? (el.getX() - root.getX()) : el.getX(),
  686. y: root ? (el.getY() - root.getY()) : el.getY(),
  687. cssClass: el.dom ? el.dom.className : undefined,
  688. refName: refName,
  689. isContainer: false,
  690. isComponent: false,
  691. isElement: true,
  692. hidden: !el.isVisible(true),
  693. iconCls: el.isVisible(true) ? 'pgan-visible-element' : 'pgan-hidden-element'
  694. });
  695. },
  696. getComponentTreeNodes: function(comps) {
  697. var me = this,
  698. compNodes = [],
  699. refName = '';
  700. if (!Ext.isArray(comps)) {
  701. comps = [comps];
  702. }
  703. Ext.each(comps, function(comp) {
  704. var container = comp.ownerCt,
  705. node = me.getCompNodeForComp(comp, refName),
  706. items = new PageAnalyzer.models.ComponentTreeNode({
  707. refName: 'items',
  708. iconCls: 'pgan-' + (comp.rendered ? 'rendered' : 'unrendered') + '-container',
  709. text: 'items',
  710. isComponent: false
  711. }),
  712. dockedItems = new PageAnalyzer.models.ComponentTreeNode({
  713. refName: 'dockedItems',
  714. iconCls: 'pgan-' + (comp.rendered ? 'rendered' : 'unrendered') + '-container',
  715. text: 'dockedItems',
  716. isComponent: false
  717. }),
  718. children;
  719. node.comp = comp;
  720. Ext.Object.each(comp, function(name, val){
  721. if (name != 'container' &&
  722. name != 'renderTo' &&
  723. name != 'constrainTo' &&
  724. name != 'focusEl' &&
  725. val &&
  726. val.dom &&
  727. val.dom != me.target.document) {
  728. var child = me.getCompNodeForElem(val, container, name);
  729. node.appendChild(child);
  730. }
  731. });
  732. if (comp.items && comp.items.items && comp.items.items.length) {
  733. children = me.getComponentTreeNodes(comp.items.items);
  734. Ext.each(children, function(child) {
  735. items.appendChild(child);
  736. });
  737. node.appendChild(items);
  738. }
  739. if (comp.dockedItems && comp.dockedItems.items && comp.dockedItems.items.length) {
  740. children = me.getComponentTreeNodes(comp.dockedItems.items);
  741. Ext.each(children, function(child) {
  742. dockedItems.appendChild(child);
  743. });
  744. node.appendChild(dockedItems);
  745. }
  746. compNodes.push(node);
  747. });
  748. return compNodes;
  749. },
  750. loadComponentTree: function() {
  751. var me = this,
  752. all = me.getTopLevelComponents(),
  753. nodes = me.getComponentTreeNodes(all),
  754. root = me.componentTree.getRootNode();
  755. root.removeAll();
  756. Ext.each(nodes, function(node){
  757. root.appendChild(node);
  758. });
  759. },
  760. getTopLevelComponents: function () {
  761. var me = this,
  762. all = me.target.Ext.ComponentManager.all.getArray(),
  763. top = [];
  764. Ext.each(all, function(comp){
  765. if(!comp.ownerCt) {
  766. top.push(comp);
  767. }
  768. });
  769. return top;
  770. },
  771. onDisplayLayoutSpec: function (view, recordIndex, cellIndex, item, e, record) {
  772. var testSpec = record.getJasmineSpec(),
  773. window = new Ext.Window({
  774. title: 'Component Test Spec',
  775. width: 400,
  776. height: 320,
  777. layout: 'fit',
  778. modal: true,
  779. maximizable: true,
  780. items: [{
  781. xtype: 'textarea',
  782. value: testSpec,
  783. selectOnFocus: true
  784. }],
  785. buttons: [{
  786. text: 'OK',
  787. handler: function() {
  788. window.destroy();
  789. }
  790. }]
  791. });
  792. window.show();
  793. },
  794. onClearLayouts: function() {
  795. this.layoutTree.getRootNode().removeAll();
  796. },
  797. onShowAllTriggers: function (button) {
  798. var comp = this.layoutTree;
  799. if (button.pressed) {
  800. comp.addCls('pgan-show-all-triggers');
  801. } else {
  802. comp.removeCls('pgan-show-all-triggers');
  803. }
  804. comp.updateLayout();
  805. },
  806. updateLayoutRuns: function() {
  807. var me = this,
  808. target = me.target,
  809. runs = target._layoutRuns;
  810. if (runs && runs.length) {
  811. target._layoutRuns = [];
  812. me.addLayoutRuns(runs);
  813. }
  814. },
  815. onGatherStats: function() {
  816. var me = this,
  817. target = me.target,
  818. perf = target.Ext.Perf,
  819. con = me.viewport.down('#perfcon');
  820. // Only gather page startup stats at each reload, not on each gather
  821. if (!me.statsGatherCount) {
  822. perf.get("Initialize").enter().leave(target.Ext._afterReadytime - target.Ext._beforeReadyTime);
  823. if (target.Ext._endTime) {
  824. perf.get("Load").enter().leave(target.Ext._endTime - target.Ext._startTime);
  825. perf.get("WaitForReady").enter().leave(target.Ext._readyTime - target.Ext._endTime);
  826. } else {
  827. perf.get("BeforeReady").enter().leave(target.Ext._readyTime - target.Ext._startTime);
  828. }
  829. }
  830. perf.updateGC();
  831. me.statsGatherCount++;
  832. var data = perf.getData(),
  833. accCfg = perf.currentConfig;
  834. con.addSample({
  835. env: 'x',
  836. build: me.build,
  837. test: target.location.pathname,
  838. data: data
  839. });
  840. if (accCfg) {
  841. con.setAccumulators(accCfg);
  842. }
  843. },
  844. onClearStats: function() {
  845. this.viewport.down('#perfcon').clearSamples();
  846. this.onResetStats();
  847. },
  848. onResetStats: function(){
  849. this.target.Ext.Perf.reset();
  850. },
  851. getHrefMinusHash: function() {
  852. var href = location.href.replace(Ext.History.getHash(), '');
  853. return href;
  854. },
  855. //-------------------------------------------------------------------------
  856. // Target page mgmt
  857. states: {
  858. // the target page has not loaded Ext
  859. disconnected: {
  860. text: 'Disconnected',
  861. style: {
  862. fontWeight: 'bold',
  863. color: 'red'
  864. }
  865. },
  866. // the target page is loaded and has Ext.isReady
  867. loaded: {
  868. text: 'Loaded',
  869. style: {
  870. fontWeight: 'bold',
  871. color: 'orange'
  872. }
  873. },
  874. // the target page is loading the layout hooks
  875. hooking: {
  876. text: 'Loading hooks',
  877. style: {
  878. fontWeight: 'bold',
  879. color: 'yellow'
  880. }
  881. },
  882. ready: {
  883. text: 'Ready',
  884. style: {
  885. fontWeight: 'bold',
  886. color: 'green'
  887. }
  888. }
  889. },
  890. getState: function() {
  891. var states = this.states,
  892. target = this.target;
  893. if (target && target.Ext && target.Ext.isReady) {
  894. if (target._layoutRuns && target.Ext._readyTime) {
  895. return states.ready;
  896. }
  897. if (target._hooking) {
  898. return states.hooking;
  899. }
  900. return states.loaded;
  901. }
  902. return states.disconnected;
  903. },
  904. injectScript: function (scriptSrc) {
  905. var me = this,
  906. url = me.getHrefMinusHash().replace(/\/[^/]+$/, '/' + scriptSrc),
  907. target = me.targetFrame.getDoc(),
  908. head = target.head || target.getElementsByTagName('head')[0];
  909. var script = target.createElement('script');
  910. script.src = url;
  911. script.type = 'text/javascript';
  912. head.appendChild(script);
  913. },
  914. injectHooks: function() {
  915. var me = this,
  916. target = me.target,
  917. perfConsole = me.viewport.down('#perfcon'),
  918. accumulatorConfigs = perfConsole && perfConsole.getAccumulators();
  919. Ext.log('injecting script hooks');
  920. target._hooking = 3;
  921. if (accumulatorConfigs) {
  922. target._accumulatorCfg = accumulatorConfigs;
  923. }
  924. me.injectScript('hooks.js');
  925. },
  926. onLoadPage: function() {
  927. var me = this,
  928. target = me.targetFrame,
  929. doc = target.getDoc(),
  930. targetUrl = me.targetUrlField.getValue();
  931. if (targetUrl && targetUrl != '' && doc) {
  932. // add a query parameter the target page url so it will pause at onReady so that
  933. // we can inject our code and resume it to capture the initial layout run
  934. // by setting a global override flag (in updateConnectedState):
  935. Ext.log('resetting target document state');
  936. me.target._layoutRuns = null;
  937. me.target._syncComplete = false;
  938. if (me.target.Ext) {
  939. me.target.Ext._readyTime = 0;
  940. }
  941. me.reloading = true;
  942. var hasPound = targetUrl.indexOf('#'),
  943. hasQuest = targetUrl.indexOf('?') ,
  944. qParm = (hasQuest > 0 ? '&' : '?') + 'ext-pauseReadyFire';
  945. if (targetUrl != Ext.History.getToken()) {
  946. Ext.History.add(targetUrl);
  947. }
  948. if (hasPound > 0) {
  949. targetUrl = targetUrl.substring(0, hasPound) + qParm + targetUrl.substring(hasPound + 1);
  950. } else {
  951. targetUrl += qParm;
  952. }
  953. Ext.log('reloading target document');
  954. me.targetFrame.load(targetUrl);
  955. me.currUrl = targetUrl;
  956. me.updateConnectedState();
  957. me.statsGatherCount = 0;
  958. } else {
  959. me.targetFrame.load('about:blank');
  960. }
  961. },
  962. updateConnectedState: function() {
  963. var me = this,
  964. states = me.states,
  965. state = me.getState();
  966. if (me.lastState != state) {
  967. me.lastState = state;
  968. me.stateText.el.setStyle(state.style);
  969. me.stateText.setText(state.text);
  970. }
  971. if (state == states.loaded) {
  972. me.injectHooks();
  973. } else if (state == states.ready) {
  974. if (me.reloading) {
  975. Ext.log('firing ready event in target document');
  976. me.reloading = false;
  977. me.target.Ext._continueFireReady = true;
  978. me.target.Ext.EventManager.readyEvent.fire();
  979. }
  980. me.updateLayoutRuns();
  981. }
  982. }
  983. });
  984. (function() {
  985. function run () {
  986. Ext.QuickTips.init();
  987. new PageAnalyzer.MainForm({
  988. targetHref: window.opener
  989. ? window.opener.location.href
  990. : null
  991. });
  992. }
  993. Ext.onReady(run);
  994. })();