/hippo/src/main/webapp/yui/profilerviewer/profilerviewer-debug.js

http://hdbc.googlecode.com/ · JavaScript · 1229 lines · 621 code · 140 blank · 468 comment · 57 complexity · 9e6f600727b2590682cca7e9d3c7f88a MD5 · raw file

  1. /*
  2. Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.7.0
  6. */
  7. (function() {
  8. /**
  9. * The ProfilerViewer module provides a graphical display for viewing
  10. * the output of the YUI Profiler <http://developer.yahoo.com/yui/profiler>.
  11. * @module profilerviewer
  12. * @requires yahoo, dom, event, element, profiler, yuiloader
  13. */
  14. /**
  15. * A widget to view YUI Profiler output.
  16. * @namespace YAHOO.widget
  17. * @class ProfilerViewer
  18. * @extends YAHOO.util.Element
  19. * @constructor
  20. * @param {HTMLElement | String | Object} el(optional) The html
  21. * element into which the ProfileViewer should be rendered.
  22. * An element will be created if none provided.
  23. * @param {Object} attr (optional) A key map of the ProfilerViewer's
  24. * initial attributes. Ignored if first arg is an attributes object.
  25. */
  26. YAHOO.widget.ProfilerViewer = function(el, attr) {
  27. attr = attr || {};
  28. if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
  29. attr = el;
  30. el = attr.element || null;
  31. }
  32. if (!el && !attr.element) {
  33. el = this._createProfilerViewerElement();
  34. }
  35. YAHOO.widget.ProfilerViewer.superclass.constructor.call(this, el, attr);
  36. this._init();
  37. YAHOO.log("ProfilerViewer instantiated.", "info", "ProfilerViewer");
  38. };
  39. YAHOO.extend(YAHOO.widget.ProfilerViewer, YAHOO.util.Element);
  40. // Static members of YAHOO.widget.ProfilerViewer:
  41. YAHOO.lang.augmentObject(YAHOO.widget.ProfilerViewer, {
  42. /**
  43. * Classname for ProfilerViewer containing element.
  44. * @static
  45. * @property CLASS
  46. * @type string
  47. * @public
  48. * @default "yui-pv"
  49. */
  50. CLASS: 'yui-pv',
  51. /**
  52. * Classname for ProfilerViewer button dashboard.
  53. * @static
  54. * @property CLASS_DASHBOARD
  55. * @type string
  56. * @public
  57. * @default "yui-pv-dashboard"
  58. */
  59. CLASS_DASHBOARD: 'yui-pv-dashboard',
  60. /**
  61. * Classname for the "refresh data" button.
  62. * @static
  63. * @property CLASS_REFRESH
  64. * @type string
  65. * @public
  66. * @default "yui-pv-refresh"
  67. */
  68. CLASS_REFRESH: 'yui-pv-refresh',
  69. /**
  70. * Classname for busy indicator in the dashboard.
  71. * @static
  72. * @property CLASS_BUSY
  73. * @type string
  74. * @public
  75. * @default "yui-pv-busy"
  76. */
  77. CLASS_BUSY: 'yui-pv-busy',
  78. /**
  79. * Classname for element containing the chart and chart
  80. * legend elements.
  81. * @static
  82. * @property CLASS_CHART_CONTAINER
  83. * @type string
  84. * @public
  85. * @default "yui-pv-chartcontainer"
  86. */
  87. CLASS_CHART_CONTAINER: 'yui-pv-chartcontainer',
  88. /**
  89. * Classname for element containing the chart.
  90. * @static
  91. * @property CLASS_CHART
  92. * @type string
  93. * @public
  94. * @default "yui-pv-chart"
  95. */
  96. CLASS_CHART: 'yui-pv-chart',
  97. /**
  98. * Classname for element containing the chart's legend.
  99. * @static
  100. * @property CLASS_CHART_LEGEND
  101. * @type string
  102. * @public
  103. * @default "yui-pv-chartlegend"
  104. */
  105. CLASS_CHART_LEGEND: 'yui-pv-chartlegend',
  106. /**
  107. * Classname for element containing the datatable.
  108. * @static
  109. * @property CLASS_TABLE
  110. * @type string
  111. * @public
  112. * @default "yui-pv-table"
  113. */
  114. CLASS_TABLE: 'yui-pv-table',
  115. /**
  116. * Strings used in the UI.
  117. * @static
  118. * @property STRINGS
  119. * @object
  120. * @public
  121. * @default English language strings for UI.
  122. */
  123. STRINGS: {
  124. title: "YUI Profiler (beta)",
  125. buttons: {
  126. viewprofiler: "View Profiler Data",
  127. hideprofiler: "Hide Profiler Report",
  128. showchart: "Show Chart",
  129. hidechart: "Hide Chart",
  130. refreshdata: "Refresh Data"
  131. },
  132. colHeads: {
  133. //key: [column head label, width in pixels]
  134. fn: ["Function/Method", null], //must auto-size
  135. calls: ["Calls", 40],
  136. avg: ["Average", 80],
  137. min: ["Shortest", 70],
  138. max: ["Longest", 70],
  139. total: ["Total Time", 70],
  140. pct: ["Percent", 70]
  141. },
  142. millisecondsAbbrev: "ms",
  143. initMessage: "initialiazing chart...",
  144. installFlashMessage: "Unable to load Flash content. The YUI Charts Control requires Flash Player 9.0.45 or higher. You can download the latest version of Flash Player from the <a href='http://www.adobe.com/go/getflashplayer'>Adobe Flash Player Download Center</a>."
  145. },
  146. /**
  147. * Function used to format numbers in milliseconds
  148. * for chart; must be publicly accessible, per Charts spec.
  149. * @static
  150. * @property timeAxisLabelFunction
  151. * @type function
  152. * @private
  153. */
  154. timeAxisLabelFunction: function(n) {
  155. var a = (n === Math.floor(n)) ? n : (Math.round(n*1000))/1000;
  156. return (a + " " + YAHOO.widget.ProfilerViewer.STRINGS.millisecondsAbbrev);
  157. },
  158. /**
  159. * Function used to format percent numbers for chart; must
  160. * be publicly accessible, per Charts spec.
  161. * @static
  162. * @property percentAxisLabelFunction
  163. * @type function
  164. * @private
  165. */
  166. percentAxisLabelFunction: function(n) {
  167. var a = (n === Math.floor(n)) ? n : (Math.round(n*100))/100;
  168. return (a + "%");
  169. }
  170. },true);
  171. //
  172. // STANDARD SHORTCUTS
  173. //
  174. var Dom = YAHOO.util.Dom;
  175. var Event = YAHOO.util.Event;
  176. var Profiler = YAHOO.tool.Profiler;
  177. var PV = YAHOO.widget.ProfilerViewer;
  178. var proto = PV.prototype;
  179. //
  180. // PUBLIC METHODS
  181. //
  182. /**
  183. * Refreshes the data displayed in the ProfilerViewer. When called,
  184. * this will invoke a refresh of the DataTable and (if displayed)
  185. * the Chart.
  186. * @method refreshData
  187. * @return void
  188. * @public
  189. */
  190. proto.refreshData = function() {
  191. YAHOO.log("Data refresh requested via refreshData method.", "info", "ProfilerViewer");
  192. this.fireEvent("dataRefreshEvent");
  193. };
  194. /**
  195. * Returns the element containing the console's header.
  196. * @method getHeadEl
  197. * @return HTMLElement
  198. * @public
  199. */
  200. proto.getHeadEl = function() {
  201. YAHOO.log("Head element requested via getHeadEl.", "info", "ProfilerViewer");
  202. return (this._headEl) ? Dom.get(this._headEl) : false;
  203. };
  204. /**
  205. * Returns the element containing the console's body, including
  206. * the chart and the datatable..
  207. * @method getBodyEl
  208. * @return HTMLElement
  209. * @public
  210. */
  211. proto.getBodyEl = function() {
  212. YAHOO.log("Body element requested via getBodyEl.", "info", "ProfilerViewer");
  213. return (this._bodyEl) ? Dom.get(this._bodyEl) : false;
  214. };
  215. /**
  216. * Returns the element containing the console's chart.
  217. * @method getChartEl
  218. * @return HTMLElement
  219. * @public
  220. */
  221. proto.getChartEl = function() {
  222. YAHOO.log("Chart element requested via getChartEl.", "info", "ProfilerViewer");
  223. return (this._chartEl) ? Dom.get(this._chartEl) : false;
  224. };
  225. /**
  226. * Returns the element containing the console's dataTable.
  227. * @method getTableEl
  228. * @return HTMLElement
  229. * @public
  230. */
  231. proto.getTableEl = function() {
  232. YAHOO.log("DataTable element requested via getTableEl.", "info", "ProfilerViewer");
  233. return (this._tableEl) ? Dom.get(this._tableEl) : false;
  234. };
  235. /**
  236. * Returns the element containing the console's DataTable
  237. * instance.
  238. * @method getDataTable
  239. * @return YAHOO.widget.DataTable
  240. * @public
  241. */
  242. proto.getDataTable = function() {
  243. YAHOO.log("DataTable instance requested via getDataTable.", "info", "ProfilerViewer");
  244. return this._dataTable;
  245. };
  246. /**
  247. * Returns the element containing the console's Chart instance.
  248. * @method getChart
  249. * @return YAHOO.widget.BarChart
  250. * @public
  251. */
  252. proto.getChart = function() {
  253. YAHOO.log("Chart instance requested via getChart.", "info", "ProfilerViewer");
  254. return this._chart;
  255. };
  256. //
  257. // PRIVATE PROPERTIES
  258. //
  259. proto._rendered = false;
  260. proto._headEl = null;
  261. proto._bodyEl = null;
  262. proto._toggleVisibleEl = null;
  263. proto._busyEl = null;
  264. proto._busy = false;
  265. proto._tableEl = null;
  266. proto._dataTable = null;
  267. proto._chartEl = null;
  268. proto._chartLegendEl = null;
  269. proto._chartElHeight = 250;
  270. proto._chart = null;
  271. proto._chartInitialized = false;
  272. //
  273. // PRIVATE METHODS
  274. //
  275. proto._init = function() {
  276. /**
  277. * CUSTOM EVENTS
  278. **/
  279. /**
  280. * Fired when a data refresh is requested. No arguments are passed
  281. * with this event.
  282. *
  283. * @event refreshDataEvent
  284. */
  285. this.createEvent("dataRefreshEvent");
  286. /**
  287. * Fired when the viewer canvas first renders. No arguments are passed
  288. * with this event.
  289. *
  290. * @event renderEvent
  291. */
  292. this.createEvent("renderEvent");
  293. this.on("dataRefreshEvent", this._refreshDataTable, this, true);
  294. this._initLauncherDOM();
  295. if(this.get("showChart")) {
  296. this.on("sortedByChange", this._refreshChart);
  297. }
  298. YAHOO.log("ProfilerViewer instance initialization complete.", "info", "ProfilerViewer");
  299. };
  300. /**
  301. * If no element is passed in, create it as the first element
  302. * in the document.
  303. * @method _createProfilerViewerElement
  304. * @return HTMLElement
  305. * @private
  306. */
  307. proto._createProfilerViewerElement = function() {
  308. YAHOO.log("Creating root element...", "info", "ProfilerViewer");
  309. var el = document.createElement("div");
  310. document.body.insertBefore(el, document.body.firstChild);
  311. Dom.addClass(el, this.SKIN_CLASS);
  312. Dom.addClass(el, PV.CLASS);
  313. YAHOO.log(el);
  314. return el;
  315. };
  316. /**
  317. * Provides a readable name for the ProfilerViewer instance.
  318. * @method toString
  319. * @return String
  320. * @private
  321. */
  322. proto.toString = function() {
  323. return "ProfilerViewer " + (this.get('id') || this.get('tagName'));
  324. };
  325. /**
  326. * Toggles visibility of the viewer canvas.
  327. * @method _toggleVisible
  328. * @return void
  329. * @private
  330. */
  331. proto._toggleVisible = function() {
  332. YAHOO.log("Toggling visibility to " + !this.get("visible") + ".", "info", "ProfilerViewer");
  333. var newVis = (this.get("visible")) ? false : true;
  334. this.set("visible", newVis);
  335. };
  336. /**
  337. * Shows the viewer canvas.
  338. * @method show
  339. * @return void
  340. * @private
  341. */
  342. proto._show = function() {
  343. if(!this._busy) {
  344. this._setBusyState(true);
  345. if(!this._rendered) {
  346. var loader = new YAHOO.util.YUILoader();
  347. if (this.get("base")) {
  348. loader.base = this.get("base");
  349. }
  350. var modules = ["datatable"];
  351. if(this.get("showChart")) {
  352. modules.push("charts");
  353. }
  354. loader.insert({ require: modules,
  355. onSuccess: function() {
  356. this._render();
  357. },
  358. scope: this});
  359. } else {
  360. var el = this.get("element");
  361. Dom.removeClass(el, "yui-pv-minimized");
  362. this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.hideprofiler;
  363. //The Flash Charts component can't be set to display:none,
  364. //and even after positioning it offscreen the screen
  365. //may fail to repaint in some browsers. Adding an empty
  366. //style rule to the console body can help force a repaint:
  367. Dom.addClass(el, "yui-pv-null");
  368. Dom.removeClass(el, "yui-pv-null");
  369. //Always refresh data when changing to visible:
  370. this.refreshData();
  371. }
  372. }
  373. };
  374. /**
  375. * Hides the viewer canvas.
  376. * @method hide
  377. * @return void
  378. * @private
  379. */
  380. proto._hide = function() {
  381. this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.viewprofiler;
  382. Dom.addClass(this.get("element"), "yui-pv-minimized");
  383. };
  384. /**
  385. * Render the viewer canvas
  386. * @method _render
  387. * @return void
  388. * @private
  389. */
  390. proto._render = function() {
  391. YAHOO.log("Beginning to render ProfilerViewer canvas...", "info", "ProfilerViewer");
  392. Dom.removeClass(this.get("element"), "yui-pv-minimized");
  393. this._initViewerDOM();
  394. this._initDataTable();
  395. if(this.get("showChart")) {
  396. this._initChartDOM();
  397. this._initChart();
  398. }
  399. this._rendered = true;
  400. this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.hideprofiler;
  401. this.fireEvent("renderEvent");
  402. YAHOO.log("ProfilerViewer rendering complete...", "info", "ProfilerViewer");
  403. };
  404. /**
  405. * Set up the DOM structure for the ProfilerViewer launcher.
  406. * @method _initLauncherDOM
  407. * @private
  408. */
  409. proto._initLauncherDOM = function() {
  410. YAHOO.log("Creating the launcher...", "info", "ProfilerViewer");
  411. var el = this.get("element");
  412. Dom.addClass(el, PV.CLASS);
  413. Dom.addClass(el, "yui-pv-minimized");
  414. this._headEl = document.createElement("div");
  415. Dom.addClass(this._headEl, "hd");
  416. var s = PV.STRINGS.buttons;
  417. var b = (this.get("visible")) ? s.hideprofiler : s.viewprofiler;
  418. this._toggleVisibleEl = this._createButton(b, this._headEl);
  419. this._refreshEl = this._createButton(s.refreshdata, this._headEl);
  420. Dom.addClass(this._refreshEl, PV.CLASS_REFRESH);
  421. this._busyEl = document.createElement("span");
  422. this._headEl.appendChild(this._busyEl);
  423. var title = document.createElement("h4");
  424. title.innerHTML = PV.STRINGS.title;
  425. this._headEl.appendChild(title);
  426. el.appendChild(this._headEl);
  427. Event.on(this._toggleVisibleEl, "click", this._toggleVisible, this, true);
  428. Event.on(this._refreshEl, "click", function() {
  429. if(!this._busy) {
  430. this._setBusyState(true);
  431. this.fireEvent("dataRefreshEvent");
  432. }
  433. }, this, true);
  434. };
  435. /**
  436. * Set up the DOM structure for the ProfilerViewer canvas,
  437. * including the holder for the DataTable.
  438. * @method _initViewerDOM
  439. * @private
  440. */
  441. proto._initViewerDOM = function() {
  442. YAHOO.log("Creating DOM structure for viewer...", "info", "ProfilerViewer");
  443. var el = this.get("element");
  444. this._bodyEl = document.createElement("div");
  445. Dom.addClass(this._bodyEl, "bd");
  446. this._tableEl = document.createElement("div");
  447. Dom.addClass(this._tableEl, PV.CLASS_TABLE);
  448. this._bodyEl.appendChild(this._tableEl);
  449. el.appendChild(this._bodyEl);
  450. };
  451. /**
  452. * Set up the DOM structure for the ProfilerViewer canvas.
  453. * @method _initChartDOM
  454. * @private
  455. */
  456. proto._initChartDOM = function() {
  457. YAHOO.log("Adding DOM structure for chart...", "info", "ProfilerViewer");
  458. this._chartContainer = document.createElement("div");
  459. Dom.addClass(this._chartContainer, PV.CLASS_CHART_CONTAINER);
  460. var chl = document.createElement("div");
  461. Dom.addClass(chl, PV.CLASS_CHART_LEGEND);
  462. var chw = document.createElement("div");
  463. this._chartLegendEl = document.createElement("dl");
  464. this._chartLegendEl.innerHTML = "<dd>" + PV.STRINGS.initMessage + "</dd>";
  465. this._chartEl = document.createElement("div");
  466. Dom.addClass(this._chartEl, PV.CLASS_CHART);
  467. var msg = document.createElement("p");
  468. msg.innerHTML = PV.STRINGS.installFlashMessage;
  469. this._chartEl.appendChild(msg);
  470. this._chartContainer.appendChild(chl);
  471. chl.appendChild(chw);
  472. chw.appendChild(this._chartLegendEl);
  473. this._chartContainer.appendChild(this._chartEl);
  474. this._bodyEl.insertBefore(this._chartContainer,this._tableEl);
  475. };
  476. /**
  477. * Create anchor elements for use as buttons. Args: label
  478. * is text to appear on the face of the button, parentEl
  479. * is the el to which the anchor will be attached, position
  480. * is true for inserting as the first node and false for
  481. * inserting as the last node of the parentEl.
  482. * @method _createButton
  483. * @private
  484. */
  485. proto._createButton = function(label, parentEl, position) {
  486. var b = document.createElement("a");
  487. b.innerHTML = b.title = label;
  488. if(parentEl) {
  489. if(!position) {
  490. parentEl.appendChild(b);
  491. } else {
  492. parentEl.insertBefore(b, parentEl.firstChild);
  493. }
  494. }
  495. return b;
  496. };
  497. /**
  498. * Set's console busy state.
  499. * @method _setBusyState
  500. * @private
  501. **/
  502. proto._setBusyState = function(b) {
  503. if(b) {
  504. Dom.addClass(this._busyEl, PV.CLASS_BUSY);
  505. this._busy = true;
  506. } else {
  507. Dom.removeClass(this._busyEl, PV.CLASS_BUSY);
  508. this._busy = false;
  509. }
  510. };
  511. /**
  512. * Generages a sorting function based on current sortedBy
  513. * values.
  514. * @method _createProfilerViewerElement
  515. * @private
  516. **/
  517. proto._genSortFunction = function(key, dir) {
  518. var by = key;
  519. var direction = dir;
  520. return function(a, b) {
  521. if (direction == YAHOO.widget.DataTable.CLASS_ASC) {
  522. return a[by] - b[by];
  523. } else {
  524. return ((a[by] - b[by]) * -1);
  525. }
  526. };
  527. };
  528. /**
  529. * Utility function for array sums.
  530. * @method _arraySum
  531. * @private
  532. **/
  533. var _arraySum = function(arr){
  534. var ct = 0;
  535. for(var i = 0; i < arr.length; ct+=arr[i++]){}
  536. return ct;
  537. };
  538. /**
  539. * Retrieves data from Profiler, filtering and sorting as needed
  540. * based on current widget state. Adds calculated percentage
  541. * column and function name to data returned by Profiler.
  542. * @method _getProfilerData
  543. * @private
  544. **/
  545. proto._getProfilerData = function() {
  546. YAHOO.log("Profiler data requested from function DataSource.", "info", "ProfilerViewer");
  547. var obj = Profiler.getFullReport();
  548. var arr = [];
  549. var totalTime = 0;
  550. for (name in obj) {
  551. if (YAHOO.lang.hasOwnProperty(obj, name)) {
  552. var r = obj[name];
  553. var o = {};
  554. o.fn = name; //add function name to record
  555. o.points = r.points.slice(); //copy live array
  556. o.calls = r.calls;
  557. o.min = r.min;
  558. o.max = r.max;
  559. o.avg = r.avg;
  560. o.total = _arraySum(o.points);
  561. o.points = r.points;
  562. var f = this.get("filter");
  563. if((!f) || (f(o))) {
  564. arr.push(o);
  565. totalTime += o.total;
  566. }
  567. }
  568. }
  569. //add calculated percentage column
  570. for (var i = 0, j = arr.length; i < j; i++) {
  571. arr[i].pct = (totalTime) ? (arr[i].total * 100) / totalTime : 0;
  572. }
  573. var sortedBy = this.get("sortedBy");
  574. var key = sortedBy.key;
  575. var dir = sortedBy.dir;
  576. arr.sort(this._genSortFunction(key, dir));
  577. YAHOO.log("Returning data from DataSource: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer");
  578. return arr;
  579. };
  580. /**
  581. * Set up the DataTable.
  582. * @method _initDataTable
  583. * @private
  584. */
  585. proto._initDataTable = function() {
  586. YAHOO.log("Creating DataTable instance...", "info", "ProfilerViewer");
  587. var self = this;
  588. //Set up the JS Function DataSource, pulling data from
  589. //the Profiler.
  590. this._dataSource = new YAHOO.util.DataSource(
  591. function() {
  592. return self._getProfilerData.call(self);
  593. },
  594. {
  595. responseType: YAHOO.util.DataSource.TYPE_JSARRAY,
  596. maxCacheEntries: 0
  597. }
  598. );
  599. var ds = this._dataSource;
  600. ds.responseSchema =
  601. {
  602. fields: [ "fn", "avg", "calls", "max", "min", "total", "pct", "points"]
  603. };
  604. //Set up the DataTable.
  605. var formatTimeValue = function(elCell, oRecord, oColumn, oData) {
  606. var a = (oData === Math.floor(oData)) ? oData : (Math.round(oData*1000))/1000;
  607. elCell.innerHTML = a + " " + PV.STRINGS.millisecondsAbbrev;
  608. };
  609. var formatPercent = function(elCell, oRecord, oColumn, oData) {
  610. var a = (oData === Math.floor(oData)) ? oData : (Math.round(oData*100))/100;
  611. elCell.innerHTML = a + "%";
  612. };
  613. var a = YAHOO.widget.DataTable.CLASS_ASC;
  614. var d = YAHOO.widget.DataTable.CLASS_DESC;
  615. var c = PV.STRINGS.colHeads;
  616. var f = formatTimeValue;
  617. var cols = [
  618. {key:"fn", sortable:true, label: c.fn[0],
  619. sortOptions: {defaultDir:a},
  620. resizeable: (YAHOO.util.DragDrop) ? true : false,
  621. minWidth:c.fn[1]},
  622. {key:"calls", sortable:true, label: c.calls[0],
  623. sortOptions: {defaultDir:d},
  624. width:c.calls[1]},
  625. {key:"avg", sortable:true, label: c.avg[0],
  626. sortOptions: {defaultDir:d},
  627. formatter:f,
  628. width:c.avg[1]},
  629. {key:"min", sortable:true, label: c.min[0],
  630. sortOptions: {defaultDir:a},
  631. formatter:f,
  632. width:c.min[1]},
  633. {key:"max", sortable:true, label: c.max[0],
  634. sortOptions: {defaultDir:d},
  635. formatter:f,
  636. width:c.max[1]},
  637. {key:"total", sortable:true, label: c.total[0],
  638. sortOptions: {defaultDir:d},
  639. formatter:f,
  640. width:c.total[1]},
  641. {key:"pct", sortable:true, label: c.pct[0],
  642. sortOptions: {defaultDir:d},
  643. formatter:formatPercent,
  644. width:c.pct[1]}
  645. ];
  646. this._dataTable = new YAHOO.widget.DataTable(this._tableEl, cols, ds, {
  647. scrollable:true,
  648. height:this.get("tableHeight"),
  649. initialRequest:null,
  650. sortedBy: {
  651. key: "total",
  652. dir: YAHOO.widget.DataTable.CLASS_DESC
  653. }
  654. });
  655. var dt = this._dataTable;
  656. //Wire up DataTable events to drive the rest of the UI.
  657. dt.subscribe("sortedByChange", this._sortedByChange, this, true);
  658. dt.subscribe("renderEvent", this._dataTableRenderHandler, this, true);
  659. dt.subscribe("initEvent", this._dataTableRenderHandler, this, true);
  660. Event.on(this._tableEl.getElementsByTagName("th"), "click", this._thClickHandler, this, true);
  661. YAHOO.log("DataTable initialized.", "info", "ProfilerViewer");
  662. };
  663. /**
  664. * Proxy the sort event in DataTable into the ProfilerViewer
  665. * attribute.
  666. * @method _sortedByChange
  667. * @private
  668. **/
  669. proto._sortedByChange = function(o) {
  670. if(o.newValue && o.newValue.key) {
  671. YAHOO.log("Relaying DataTable sortedBy value change; new key: " + o.newValue.key + "; new direction: " + o.newValue.dir + ".", "info", "ProfilerViewer");
  672. this.set("sortedBy", {key: o.newValue.key, dir:o.newValue.dir});
  673. }
  674. };
  675. /**
  676. * Proxy the render event in DataTable into the ProfilerViewer
  677. * attribute.
  678. * @method _dataTableRenderHandler
  679. * @private
  680. **/
  681. proto._dataTableRenderHandler = function(o) {
  682. YAHOO.log("DataTable's render event has fired.", "info", "ProfilerViewer");
  683. this._setBusyState(false);
  684. };
  685. /**
  686. * Event handler for clicks on the DataTable's sortable column
  687. * heads.
  688. * @method _thClickHandler
  689. * @private
  690. **/
  691. proto._thClickHandler = function(o) {
  692. YAHOO.log("DataTable's header row was clicked for sorting.", "info", "ProfilerViewer");
  693. this._setBusyState(true);
  694. };
  695. /**
  696. * Refresh DataTable, getting new data from Profiler.
  697. * @method _refreshDataTable
  698. * @private
  699. **/
  700. proto._refreshDataTable = function(args) {
  701. YAHOO.log("Beginning to refresh DataTable contents...", "info", "ProfilerViewer");
  702. var dt = this._dataTable;
  703. dt.getDataSource().sendRequest("", dt.onDataReturnInitializeTable, dt);
  704. YAHOO.log("DataTable refresh complete.", "info", "ProfilerViewer");
  705. };
  706. /**
  707. * Refresh chart, getting new data from table.
  708. * @method _refreshChart
  709. * @private
  710. **/
  711. proto._refreshChart = function() {
  712. YAHOO.log("Beginning to refresh Chart contents...", "info", "ProfilerViewer");
  713. switch (this.get("sortedBy").key) {
  714. case "fn":
  715. /*Keep the same data on the chart, but force update to
  716. reflect new sort order on function/method name: */
  717. this._chart.set("dataSource", this._chart.get("dataSource"));
  718. /*no further action necessary; chart redraws*/
  719. return;
  720. case "calls":
  721. /*Null out the xAxis formatting before redrawing chart.*/
  722. this._chart.set("xAxis", this._chartAxisDefinitionPlain);
  723. break;
  724. case "pct":
  725. this._chart.set("xAxis", this._chartAxisDefinitionPercent);
  726. break;
  727. default:
  728. /*Set the default xAxis; redraw legend; set the new series definition.*/
  729. this._chart.set("xAxis", this._chartAxisDefinitionTime);
  730. break;
  731. }
  732. this._drawChartLegend();
  733. this._chart.set("series", this._getSeriesDef(this.get("sortedBy").key));
  734. YAHOO.log("Chart refresh complete.", "info", "ProfilerViewer");
  735. };
  736. /**
  737. * Get data for the Chart from DataTable recordset
  738. * @method _getChartData
  739. * @private
  740. */
  741. proto._getChartData = function() {
  742. YAHOO.log("Getting data for chart from function DataSource.", "info", "ProfilerViewer");
  743. //var records = this._getProfilerData();
  744. var records = this._dataTable.getRecordSet().getRecords(0, this.get("maxChartFunctions"));
  745. var arr = [];
  746. for (var i = 0, j = records.length; i<j; i++) {
  747. arr.push(records[i].getData());
  748. }
  749. YAHOO.log("Returning data to Chart: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer");
  750. return arr;
  751. };
  752. /**
  753. * Build series definition based on current configuration attributes.
  754. * @method _getSeriesDef
  755. * @private
  756. */
  757. proto._getSeriesDef = function(field) {
  758. var sd = this.get("chartSeriesDefinitions")[field];
  759. var arr = [];
  760. for(var i = 0, j = sd.group.length; i<j; i++) {
  761. var c = this.get("chartSeriesDefinitions")[sd.group[i]];
  762. arr.push(
  763. {displayName:c.displayName,
  764. xField:c.xField,
  765. style: {color:c.style.color, size:c.style.size}
  766. }
  767. );
  768. }
  769. YAHOO.log("Returning new series definition to chart: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer");
  770. return arr;
  771. };
  772. /**
  773. * Set up the Chart.
  774. * @method _initChart
  775. * @private
  776. */
  777. proto._initChart = function() {
  778. YAHOO.log("Initializing chart...", "info", "ProfilerViewer");
  779. this._sizeChartCanvas();
  780. YAHOO.widget.Chart.SWFURL = this.get("swfUrl");
  781. var self = this;
  782. //Create DataSource based on records currently displayed
  783. //at the top of the sort list in the DataTable.
  784. var ds = new YAHOO.util.DataSource(
  785. //force the jsfunction DataSource to run in the scope of
  786. //the ProfilerViewer, not in the YAHOO.util.DataSource scope:
  787. function() {
  788. return self._getChartData.call(self);
  789. },
  790. {
  791. responseType: YAHOO.util.DataSource.TYPE_JSARRAY,
  792. maxCacheEntries: 0
  793. }
  794. );
  795. ds.responseSchema =
  796. {
  797. fields: [ "fn", "avg", "calls", "max", "min", "total", "pct" ]
  798. };
  799. ds.subscribe('responseEvent', this._sizeChartCanvas, this, true);
  800. //Set up the chart itself.
  801. this._chartAxisDefinitionTime = new YAHOO.widget.NumericAxis();
  802. this._chartAxisDefinitionTime.labelFunction = "YAHOO.widget.ProfilerViewer.timeAxisLabelFunction";
  803. this._chartAxisDefinitionPercent = new YAHOO.widget.NumericAxis();
  804. this._chartAxisDefinitionPercent.labelFunction = "YAHOO.widget.ProfilerViewer.percentAxisLabelFunction";
  805. this._chartAxisDefinitionPlain = new YAHOO.widget.NumericAxis();
  806. this._chart = new YAHOO.widget.BarChart( this._chartEl, ds,
  807. {
  808. yField: "fn",
  809. series: this._getSeriesDef(this.get("sortedBy").key),
  810. style: this.get("chartStyle"),
  811. xAxis: this._chartAxisDefinitionTime
  812. } );
  813. this._drawChartLegend();
  814. this._chartInitialized = true;
  815. this._dataTable.unsubscribe("initEvent", this._initChart, this);
  816. this._dataTable.subscribe("initEvent", this._refreshChart, this, true);
  817. YAHOO.log("Chart initialization complete.", "info", "ProfilerViewer");
  818. };
  819. /**
  820. * Set up the Chart's legend
  821. * @method _drawChartLegend
  822. * @private
  823. **/
  824. proto._drawChartLegend = function() {
  825. YAHOO.log("Drawing chart legend...", "info", "ProfilerViewer");
  826. var seriesDefs = this.get("chartSeriesDefinitions");
  827. var currentDef = seriesDefs[this.get("sortedBy").key];
  828. var l = this._chartLegendEl;
  829. l.innerHTML = "";
  830. for(var i = 0, j = currentDef.group.length; i<j; i++) {
  831. var c = seriesDefs[currentDef.group[i]];
  832. var dt = document.createElement("dt");
  833. Dom.setStyle(dt, "backgroundColor", "#" + c.style.color);
  834. var dd = document.createElement("dd");
  835. dd.innerHTML = c.displayName;
  836. l.appendChild(dt);
  837. l.appendChild(dd);
  838. }
  839. };
  840. /**
  841. * Resize the chart's canvas if based on number of records
  842. * returned from the chart's datasource.
  843. * @method _sizeChartCanvas
  844. * @private
  845. **/
  846. proto._sizeChartCanvas = function(o) {
  847. YAHOO.log("Resizing chart canvas...", "info", "ProfilerViewer");
  848. var bars = (o) ? o.response.length : this.get("maxChartFunctions");
  849. var s = (bars * 36) + 34;
  850. if (s != parseInt(this._chartElHeight, 10)) {
  851. this._chartElHeight = s;
  852. Dom.setStyle(this._chartEl, "height", s + "px");
  853. }
  854. };
  855. /**
  856. * setAttributeConfigs TabView specific properties.
  857. * @method initAttributes
  858. * @param {Object} attr Hash of initial attributes
  859. * @method initAttributes
  860. * @private
  861. */
  862. proto.initAttributes = function(attr) {
  863. YAHOO.log("Initializing attributes...", "info", "ProfilerViewer");
  864. YAHOO.widget.ProfilerViewer.superclass.initAttributes.call(this, attr);
  865. /**
  866. * The YUI Loader base path from which to pull YUI files needed
  867. * in the rendering of the ProfilerViewer canvas. Passed directly
  868. * to YUI Loader. Leave blank to draw files from
  869. * yui.yahooapis.com.
  870. * @attribute base
  871. * @type string
  872. * @default ""
  873. */
  874. this.setAttributeConfig('base', {
  875. value: attr.base
  876. });
  877. /**
  878. * The height of the DataTable. The table will scroll
  879. * vertically if the content overflows the specified
  880. * height.
  881. * @attribute tableHeight
  882. * @type string
  883. * @default "15em"
  884. */
  885. this.setAttributeConfig('tableHeight', {
  886. value: attr.tableHeight || "15em",
  887. method: function(s) {
  888. if(this._dataTable) {
  889. this._dataTable.set("height", s);
  890. }
  891. }
  892. });
  893. /**
  894. * The default column key to sort by. Valid keys are: fn, calls,
  895. * avg, min, max, total. Valid dir values are:
  896. * YAHOO.widget.DataTable.CLASS_ASC and
  897. * YAHOO.widget.DataTable.CLASS_DESC (or their
  898. * string equivalents).
  899. * @attribute sortedBy
  900. * @type string
  901. * @default {key:"total", dir:"yui-dt-desc"}
  902. */
  903. this.setAttributeConfig('sortedBy', {
  904. value: attr.sortedBy || {key:"total", dir:"yui-dt-desc"}
  905. });
  906. /**
  907. * A filter function to use in selecting functions that will
  908. * appear in the ProfilerViewer report. The function is passed
  909. * a function report object and should return a boolean indicating
  910. * whether that function should be included in the ProfilerViewer
  911. * display. The argument is structured as follows:
  912. *
  913. * {
  914. * fn: <str function name>,
  915. * calls : <n number of calls>,
  916. * avg : <n average call duration>,
  917. * max: <n duration of longest call>,
  918. * min: <n duration of shortest call>,
  919. * total: <n total time of all calls>
  920. * points : <array time in ms of each call>
  921. * }
  922. *
  923. * For example, you would use the follwing filter function to
  924. * return only functions that have been called at least once:
  925. *
  926. * function(o) {
  927. * return (o.calls > 0);
  928. * }
  929. *
  930. * @attribute filter
  931. * @type function
  932. * @default null
  933. */
  934. this.setAttributeConfig('filter', {
  935. value: attr.filter || null,
  936. validator: YAHOO.lang.isFunction
  937. });
  938. /**
  939. * The path to the YUI Charts swf file; must be a full URI
  940. * or a path relative to the page being profiled. Changes at runtime
  941. * not supported; pass this value in at instantiation.
  942. * @attribute swfUrl
  943. * @type string
  944. * @default "http://yui.yahooapis.com/2.5.0/build/charts/assets/charts.swf"
  945. */
  946. this.setAttributeConfig('swfUrl', {
  947. value: attr.swfUrl || "http://yui.yahooapis.com/2.5.0/build/charts/assets/charts.swf"
  948. });
  949. /**
  950. * The maximum number of functions to profile in the chart. The
  951. * greater the number of functions, the greater the height of the
  952. * chart canvas.
  953. * height.
  954. * @attribute maxChartFunctions
  955. * @type int
  956. * @default 6
  957. */
  958. this.setAttributeConfig('maxChartFunctions', {
  959. value: attr.maxChartFunctions || 6,
  960. method: function(s) {
  961. if(this._rendered) {
  962. this._sizeChartCanvas();
  963. }
  964. },
  965. validator: YAHOO.lang.isNumber
  966. });
  967. /**
  968. * The style object that defines the chart's visual presentation.
  969. * Conforms to the style attribute passed to the Charts Control
  970. * constructor. See Charts Control User's Guide for more information
  971. * on how to format this object.
  972. * @attribute chartStyle
  973. * @type obj
  974. * @default See JS source for default definitions.
  975. */
  976. this.setAttributeConfig('chartStyle', {
  977. value: attr.chartStyle || {
  978. font:
  979. {
  980. name: "Arial",
  981. color: 0xeeee5c,
  982. size: 12
  983. },
  984. background:
  985. {
  986. color: "6e6e63"
  987. }
  988. },
  989. method: function() {
  990. if(this._rendered && this.get("showChart")) {
  991. this._refreshChart();
  992. }
  993. }
  994. });
  995. /**
  996. * The series definition information to use when charting
  997. * specific fields on the chart. displayName, xField,
  998. * and style members are used to construct the series
  999. * definition; the "group" member is the array of fields
  1000. * that should be charted when the table is sorted by a
  1001. * given field.
  1002. * @attribute chartSeriesDefinitions
  1003. * @type obj
  1004. * @default See JS source for full default definitions.
  1005. */
  1006. this.setAttributeConfig('chartSeriesDefinitions', {
  1007. value: attr.chartSeriesDefinitions || {
  1008. total: {
  1009. displayName: PV.STRINGS.colHeads.total[0],
  1010. xField: "total",
  1011. style: {color:"4d95dd", size:20},
  1012. group: ["total"]
  1013. },
  1014. calls: {
  1015. displayName: PV.STRINGS.colHeads.calls[0],
  1016. xField: "calls",
  1017. style: {color:"edff9f", size:20},
  1018. group: ["calls"]
  1019. },
  1020. avg: {
  1021. displayName: PV.STRINGS.colHeads.avg[0],
  1022. xField: "avg",
  1023. style: {color:"209daf", size:9},
  1024. group: ["avg", "min", "max"]
  1025. },
  1026. min: {
  1027. displayName: PV.STRINGS.colHeads.min[0],
  1028. xField: "min",
  1029. style: {color:"b6ecf4", size:9},
  1030. group: ["avg", "min", "max"]
  1031. },
  1032. max: {
  1033. displayName: PV.STRINGS.colHeads.max[0],
  1034. xField: "max",
  1035. style: {color:"29c7de", size:9},
  1036. group: ["avg", "min", "max"]
  1037. },
  1038. pct: {
  1039. displayName: PV.STRINGS.colHeads.pct[0],
  1040. xField: "pct",
  1041. style: {color:"C96EDB", size:20},
  1042. group: ["pct"]
  1043. }
  1044. },
  1045. method: function() {
  1046. if(this._rendered && this.get("showChart")) {
  1047. this._refreshChart();
  1048. }
  1049. }
  1050. });
  1051. /**
  1052. * The default visibility setting for the viewer canvas. If true,
  1053. * the viewer will load all necessary files and render itself
  1054. * immediately upon instantiation; otherwise, the viewer will
  1055. * load only minimal resources until the user toggles visibility
  1056. * via the UI.
  1057. * @attribute visible
  1058. * @type boolean
  1059. * @default false
  1060. */
  1061. this.setAttributeConfig('visible', {
  1062. value: attr.visible || false,
  1063. validator: YAHOO.lang.isBoolean,
  1064. method: function(b) {
  1065. if(b) {
  1066. this._show();
  1067. } else {
  1068. if (this._rendered) {
  1069. this._hide();
  1070. }
  1071. }
  1072. }
  1073. });
  1074. /**
  1075. * The default visibility setting for the chart.
  1076. * @attribute showChart
  1077. * @type boolean
  1078. * @default true
  1079. */
  1080. this.setAttributeConfig('showChart', {
  1081. value: attr.showChart || true,
  1082. validator: YAHOO.lang.isBoolean,
  1083. writeOnce: true
  1084. });
  1085. YAHOO.widget.ProfilerViewer.superclass.initAttributes.call(this, attr);
  1086. YAHOO.log("Attributes initialized.", "info", "ProfilerViewer");
  1087. };
  1088. })();
  1089. YAHOO.register("profilerviewer", YAHOO.widget.ProfilerViewer, {version: "2.7.0", build: "1799"});