| /* |
| Copyright (c) 2008, Yahoo! Inc. All rights reserved. |
| Code licensed under the BSD License: |
| http://developer.yahoo.net/yui/license.txt |
| version: 2.6.0 |
| */ |
| (function() { |
| |
| /** |
| * The ProfilerViewer module provides a graphical display for viewing |
| * the output of the YUI Profiler <http://developer.yahoo.com/yui/profiler>. |
| * @module profilerviewer |
| * @requires yahoo, dom, event, element, profiler, yuiloader |
| */ |
| |
| /** |
| * A widget to view YUI Profiler output. |
| * @namespace YAHOO.widget |
| * @class ProfilerViewer |
| * @extends YAHOO.util.Element |
| * @constructor |
| * @param {HTMLElement | String | Object} el(optional) The html |
| * element into which the ProfileViewer should be rendered. |
| * An element will be created if none provided. |
| * @param {Object} attr (optional) A key map of the ProfilerViewer's |
| * initial attributes. Ignored if first arg is an attributes object. |
| */ |
| YAHOO.widget.ProfilerViewer = function(el, attr) { |
| attr = attr || {}; |
| if (arguments.length == 1 && !YAHOO.lang.isString(el) && !el.nodeName) { |
| attr = el; |
| el = attr.element || null; |
| } |
| if (!el && !attr.element) { |
| el = this._createProfilerViewerElement(); |
| } |
| |
| YAHOO.widget.ProfilerViewer.superclass.constructor.call(this, el, attr); |
| |
| this._init(); |
| |
| YAHOO.log("ProfilerViewer instantiated.", "info", "ProfilerViewer"); |
| }; |
| |
| YAHOO.extend(YAHOO.widget.ProfilerViewer, YAHOO.util.Element); |
| |
| // Static members of YAHOO.widget.ProfilerViewer: |
| YAHOO.lang.augmentObject(YAHOO.widget.ProfilerViewer, { |
| /** |
| * Classname for ProfilerViewer containing element. |
| * @static |
| * @property CLASS |
| * @type string |
| * @public |
| * @default "yui-pv" |
| */ |
| CLASS: 'yui-pv', |
| |
| /** |
| * Classname for ProfilerViewer button dashboard. |
| * @static |
| * @property CLASS_DASHBOARD |
| * @type string |
| * @public |
| * @default "yui-pv-dashboard" |
| */ |
| CLASS_DASHBOARD: 'yui-pv-dashboard', |
| |
| /** |
| * Classname for the "refresh data" button. |
| * @static |
| * @property CLASS_REFRESH |
| * @type string |
| * @public |
| * @default "yui-pv-refresh" |
| */ |
| CLASS_REFRESH: 'yui-pv-refresh', |
| |
| /** |
| * Classname for busy indicator in the dashboard. |
| * @static |
| * @property CLASS_BUSY |
| * @type string |
| * @public |
| * @default "yui-pv-busy" |
| */ |
| CLASS_BUSY: 'yui-pv-busy', |
| |
| /** |
| * Classname for element containing the chart and chart |
| * legend elements. |
| * @static |
| * @property CLASS_CHART_CONTAINER |
| * @type string |
| * @public |
| * @default "yui-pv-chartcontainer" |
| */ |
| CLASS_CHART_CONTAINER: 'yui-pv-chartcontainer', |
| |
| /** |
| * Classname for element containing the chart. |
| * @static |
| * @property CLASS_CHART |
| * @type string |
| * @public |
| * @default "yui-pv-chart" |
| */ |
| CLASS_CHART: 'yui-pv-chart', |
| |
| /** |
| * Classname for element containing the chart's legend. |
| * @static |
| * @property CLASS_CHART_LEGEND |
| * @type string |
| * @public |
| * @default "yui-pv-chartlegend" |
| */ |
| CLASS_CHART_LEGEND: 'yui-pv-chartlegend', |
| |
| /** |
| * Classname for element containing the datatable. |
| * @static |
| * @property CLASS_TABLE |
| * @type string |
| * @public |
| * @default "yui-pv-table" |
| */ |
| CLASS_TABLE: 'yui-pv-table', |
| |
| /** |
| * Strings used in the UI. |
| * @static |
| * @property STRINGS |
| * @object |
| * @public |
| * @default English language strings for UI. |
| */ |
| STRINGS: { |
| title: "YUI Profiler (beta)", |
| buttons: { |
| viewprofiler: "View Profiler Data", |
| hideprofiler: "Hide Profiler Report", |
| showchart: "Show Chart", |
| hidechart: "Hide Chart", |
| refreshdata: "Refresh Data" |
| }, |
| colHeads: { |
| //key: [column head label, width in pixels] |
| fn: ["Function/Method", null], //must auto-size |
| calls: ["Calls", 40], |
| avg: ["Average", 80], |
| min: ["Shortest", 70], |
| max: ["Longest", 70], |
| total: ["Total Time", 70], |
| pct: ["Percent", 70] |
| }, |
| millisecondsAbbrev: "ms", |
| initMessage: "initialiazing chart...", |
| 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>." |
| }, |
| |
| /** |
| * Function used to format numbers in milliseconds |
| * for chart; must be publicly accessible, per Charts spec. |
| * @static |
| * @property timeAxisLabelFunction |
| * @type function |
| * @private |
| */ |
| timeAxisLabelFunction: function(n) { |
| var a = (n === Math.floor(n)) ? n : (Math.round(n*1000))/1000; |
| return (a + " " + YAHOO.widget.ProfilerViewer.STRINGS.millisecondsAbbrev); |
| }, |
| |
| /** |
| * Function used to format percent numbers for chart; must |
| * be publicly accessible, per Charts spec. |
| * @static |
| * @property percentAxisLabelFunction |
| * @type function |
| * @private |
| */ |
| percentAxisLabelFunction: function(n) { |
| var a = (n === Math.floor(n)) ? n : (Math.round(n*100))/100; |
| return (a + "%"); |
| } |
| |
| |
| },true); |
| |
| |
| // |
| // STANDARD SHORTCUTS |
| // |
| var Dom = YAHOO.util.Dom; |
| var Event = YAHOO.util.Event; |
| var Profiler = YAHOO.tool.Profiler; |
| var PV = YAHOO.widget.ProfilerViewer; |
| var proto = PV.prototype; |
| |
| |
| // |
| // PUBLIC METHODS |
| // |
| |
| /** |
| * Refreshes the data displayed in the ProfilerViewer. When called, |
| * this will invoke a refresh of the DataTable and (if displayed) |
| * the Chart. |
| * @method refreshData |
| * @return void |
| * @public |
| */ |
| proto.refreshData = function() { |
| YAHOO.log("Data refresh requested via refreshData method.", "info", "ProfilerViewer"); |
| this.fireEvent("dataRefreshEvent"); |
| }; |
| |
| /** |
| * Returns the element containing the console's header. |
| * @method getHeadEl |
| * @return HTMLElement |
| * @public |
| */ |
| proto.getHeadEl = function() { |
| YAHOO.log("Head element requested via getHeadEl.", "info", "ProfilerViewer"); |
| return (this._headEl) ? Dom.get(this._headEl) : false; |
| }; |
| |
| /** |
| * Returns the element containing the console's body, including |
| * the chart and the datatable.. |
| * @method getBodyEl |
| * @return HTMLElement |
| * @public |
| */ |
| proto.getBodyEl = function() { |
| YAHOO.log("Body element requested via getBodyEl.", "info", "ProfilerViewer"); |
| return (this._bodyEl) ? Dom.get(this._bodyEl) : false; |
| }; |
| |
| /** |
| * Returns the element containing the console's chart. |
| * @method getChartEl |
| * @return HTMLElement |
| * @public |
| */ |
| proto.getChartEl = function() { |
| YAHOO.log("Chart element requested via getChartEl.", "info", "ProfilerViewer"); |
| return (this._chartEl) ? Dom.get(this._chartEl) : false; |
| }; |
| |
| /** |
| * Returns the element containing the console's dataTable. |
| * @method getTableEl |
| * @return HTMLElement |
| * @public |
| */ |
| proto.getTableEl = function() { |
| YAHOO.log("DataTable element requested via getTableEl.", "info", "ProfilerViewer"); |
| return (this._tableEl) ? Dom.get(this._tableEl) : false; |
| }; |
| |
| /** |
| * Returns the element containing the console's DataTable |
| * instance. |
| * @method getDataTable |
| * @return YAHOO.widget.DataTable |
| * @public |
| */ |
| proto.getDataTable = function() { |
| YAHOO.log("DataTable instance requested via getDataTable.", "info", "ProfilerViewer"); |
| return this._dataTable; |
| }; |
| |
| /** |
| * Returns the element containing the console's Chart instance. |
| * @method getChart |
| * @return YAHOO.widget.BarChart |
| * @public |
| */ |
| proto.getChart = function() { |
| YAHOO.log("Chart instance requested via getChart.", "info", "ProfilerViewer"); |
| return this._chart; |
| }; |
| |
| |
| // |
| // PRIVATE PROPERTIES |
| // |
| proto._rendered = false; |
| proto._headEl = null; |
| proto._bodyEl = null; |
| proto._toggleVisibleEl = null; |
| proto._busyEl = null; |
| proto._busy = false; |
| |
| proto._tableEl = null; |
| proto._dataTable = null; |
| |
| proto._chartEl = null; |
| proto._chartLegendEl = null; |
| proto._chartElHeight = 250; |
| proto._chart = null; |
| proto._chartInitialized = false; |
| |
| // |
| // PRIVATE METHODS |
| // |
| |
| proto._init = function() { |
| /** |
| * CUSTOM EVENTS |
| **/ |
| |
| /** |
| * Fired when a data refresh is requested. No arguments are passed |
| * with this event. |
| * |
| * @event refreshDataEvent |
| */ |
| this.createEvent("dataRefreshEvent"); |
| |
| /** |
| * Fired when the viewer canvas first renders. No arguments are passed |
| * with this event. |
| * |
| * @event renderEvent |
| */ |
| this.createEvent("renderEvent"); |
| |
| this.on("dataRefreshEvent", this._refreshDataTable, this, true); |
| |
| this._initLauncherDOM(); |
| |
| if(this.get("showChart")) { |
| this.on("sortedByChange", this._refreshChart); |
| } |
| |
| YAHOO.log("ProfilerViewer instance initialization complete.", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * If no element is passed in, create it as the first element |
| * in the document. |
| * @method _createProfilerViewerElement |
| * @return HTMLElement |
| * @private |
| */ |
| proto._createProfilerViewerElement = function() { |
| YAHOO.log("Creating root element...", "info", "ProfilerViewer"); |
| |
| var el = document.createElement("div"); |
| document.body.insertBefore(el, document.body.firstChild); |
| Dom.addClass(el, this.SKIN_CLASS); |
| Dom.addClass(el, PV.CLASS); |
| YAHOO.log(el); |
| return el; |
| }; |
| |
| /** |
| * Provides a readable name for the ProfilerViewer instance. |
| * @method toString |
| * @return String |
| * @private |
| */ |
| proto.toString = function() { |
| return "ProfilerViewer " + (this.get('id') || this.get('tagName')); |
| }; |
| |
| /** |
| * Toggles visibility of the viewer canvas. |
| * @method _toggleVisible |
| * @return void |
| * @private |
| */ |
| proto._toggleVisible = function() { |
| YAHOO.log("Toggling visibility to " + !this.get("visible") + ".", "info", "ProfilerViewer"); |
| |
| var newVis = (this.get("visible")) ? false : true; |
| this.set("visible", newVis); |
| }; |
| |
| /** |
| * Shows the viewer canvas. |
| * @method show |
| * @return void |
| * @private |
| */ |
| proto._show = function() { |
| if(!this._busy) { |
| this._setBusyState(true); |
| if(!this._rendered) { |
| var loader = new YAHOO.util.YUILoader(); |
| if (this.get("base")) { |
| loader.base = this.get("base"); |
| } |
| |
| var modules = ["datatable"]; |
| if(this.get("showChart")) { |
| modules.push("charts"); |
| } |
| |
| loader.insert({ require: modules, |
| onSuccess: function() { |
| this._render(); |
| }, |
| scope: this}); |
| } else { |
| var el = this.get("element"); |
| Dom.removeClass(el, "yui-pv-minimized"); |
| this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.hideprofiler; |
| |
| //The Flash Charts component can't be set to display:none, |
| //and even after positioning it offscreen the screen |
| //may fail to repaint in some browsers. Adding an empty |
| //style rule to the console body can help force a repaint: |
| Dom.addClass(el, "yui-pv-null"); |
| Dom.removeClass(el, "yui-pv-null"); |
| |
| //Always refresh data when changing to visible: |
| this.refreshData(); |
| } |
| } |
| }; |
| |
| /** |
| * Hides the viewer canvas. |
| * @method hide |
| * @return void |
| * @private |
| */ |
| proto._hide = function() { |
| this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.viewprofiler; |
| Dom.addClass(this.get("element"), "yui-pv-minimized"); |
| }; |
| |
| /** |
| * Render the viewer canvas |
| * @method _render |
| * @return void |
| * @private |
| */ |
| proto._render = function() { |
| YAHOO.log("Beginning to render ProfilerViewer canvas...", "info", "ProfilerViewer"); |
| |
| Dom.removeClass(this.get("element"), "yui-pv-minimized"); |
| |
| this._initViewerDOM(); |
| this._initDataTable(); |
| if(this.get("showChart")) { |
| this._initChartDOM(); |
| this._initChart(); |
| } |
| this._rendered = true; |
| this._toggleVisibleEl.innerHTML = PV.STRINGS.buttons.hideprofiler; |
| |
| this.fireEvent("renderEvent"); |
| |
| YAHOO.log("ProfilerViewer rendering complete...", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * Set up the DOM structure for the ProfilerViewer launcher. |
| * @method _initLauncherDOM |
| * @private |
| */ |
| proto._initLauncherDOM = function() { |
| YAHOO.log("Creating the launcher...", "info", "ProfilerViewer"); |
| |
| var el = this.get("element"); |
| Dom.addClass(el, PV.CLASS); |
| Dom.addClass(el, "yui-pv-minimized"); |
| |
| this._headEl = document.createElement("div"); |
| Dom.addClass(this._headEl, "hd"); |
| |
| var s = PV.STRINGS.buttons; |
| var b = (this.get("visible")) ? s.hideprofiler : s.viewprofiler; |
| |
| this._toggleVisibleEl = this._createButton(b, this._headEl); |
| |
| this._refreshEl = this._createButton(s.refreshdata, this._headEl); |
| Dom.addClass(this._refreshEl, PV.CLASS_REFRESH); |
| |
| this._busyEl = document.createElement("span"); |
| this._headEl.appendChild(this._busyEl); |
| |
| var title = document.createElement("h4"); |
| title.innerHTML = PV.STRINGS.title; |
| this._headEl.appendChild(title); |
| |
| el.appendChild(this._headEl); |
| |
| Event.on(this._toggleVisibleEl, "click", this._toggleVisible, this, true); |
| Event.on(this._refreshEl, "click", function() { |
| if(!this._busy) { |
| this._setBusyState(true); |
| this.fireEvent("dataRefreshEvent"); |
| } |
| }, this, true); |
| }; |
| |
| /** |
| * Set up the DOM structure for the ProfilerViewer canvas, |
| * including the holder for the DataTable. |
| * @method _initViewerDOM |
| * @private |
| */ |
| proto._initViewerDOM = function() { |
| YAHOO.log("Creating DOM structure for viewer...", "info", "ProfilerViewer"); |
| |
| var el = this.get("element"); |
| this._bodyEl = document.createElement("div"); |
| Dom.addClass(this._bodyEl, "bd"); |
| this._tableEl = document.createElement("div"); |
| Dom.addClass(this._tableEl, PV.CLASS_TABLE); |
| this._bodyEl.appendChild(this._tableEl); |
| el.appendChild(this._bodyEl); |
| }; |
| |
| /** |
| * Set up the DOM structure for the ProfilerViewer canvas. |
| * @method _initChartDOM |
| * @private |
| */ |
| proto._initChartDOM = function() { |
| YAHOO.log("Adding DOM structure for chart...", "info", "ProfilerViewer"); |
| |
| this._chartContainer = document.createElement("div"); |
| Dom.addClass(this._chartContainer, PV.CLASS_CHART_CONTAINER); |
| |
| var chl = document.createElement("div"); |
| Dom.addClass(chl, PV.CLASS_CHART_LEGEND); |
| |
| var chw = document.createElement("div"); |
| |
| this._chartLegendEl = document.createElement("dl"); |
| this._chartLegendEl.innerHTML = "<dd>" + PV.STRINGS.initMessage + "</dd>"; |
| |
| this._chartEl = document.createElement("div"); |
| Dom.addClass(this._chartEl, PV.CLASS_CHART); |
| |
| var msg = document.createElement("p"); |
| msg.innerHTML = PV.STRINGS.installFlashMessage; |
| this._chartEl.appendChild(msg); |
| |
| this._chartContainer.appendChild(chl); |
| chl.appendChild(chw); |
| chw.appendChild(this._chartLegendEl); |
| this._chartContainer.appendChild(this._chartEl); |
| this._bodyEl.insertBefore(this._chartContainer,this._tableEl); |
| }; |
| |
| |
| /** |
| * Create anchor elements for use as buttons. Args: label |
| * is text to appear on the face of the button, parentEl |
| * is the el to which the anchor will be attached, position |
| * is true for inserting as the first node and false for |
| * inserting as the last node of the parentEl. |
| * @method _createButton |
| * @private |
| */ |
| proto._createButton = function(label, parentEl, position) { |
| var b = document.createElement("a"); |
| b.innerHTML = b.title = label; |
| if(parentEl) { |
| if(!position) { |
| parentEl.appendChild(b); |
| } else { |
| parentEl.insertBefore(b, parentEl.firstChild); |
| } |
| } |
| return b; |
| }; |
| |
| /** |
| * Set's console busy state. |
| * @method _setBusyState |
| * @private |
| **/ |
| proto._setBusyState = function(b) { |
| if(b) { |
| Dom.addClass(this._busyEl, PV.CLASS_BUSY); |
| this._busy = true; |
| } else { |
| Dom.removeClass(this._busyEl, PV.CLASS_BUSY); |
| this._busy = false; |
| } |
| }; |
| |
| /** |
| * Generages a sorting function based on current sortedBy |
| * values. |
| * @method _createProfilerViewerElement |
| * @private |
| **/ |
| proto._genSortFunction = function(key, dir) { |
| var by = key; |
| var direction = dir; |
| return function(a, b) { |
| if (direction == YAHOO.widget.DataTable.CLASS_ASC) { |
| return a[by] - b[by]; |
| } else { |
| return ((a[by] - b[by]) * -1); |
| } |
| }; |
| }; |
| |
| /** |
| * Utility function for array sums. |
| * @method _arraySum |
| * @private |
| **/ |
| var _arraySum = function(arr){ |
| var ct = 0; |
| for(var i = 0; i < arr.length; ct+=arr[i++]){} |
| return ct; |
| }; |
| |
| /** |
| * Retrieves data from Profiler, filtering and sorting as needed |
| * based on current widget state. Adds calculated percentage |
| * column and function name to data returned by Profiler. |
| * @method _getProfilerData |
| * @private |
| **/ |
| proto._getProfilerData = function() { |
| YAHOO.log("Profiler data requested from function DataSource.", "info", "ProfilerViewer"); |
| |
| var obj = Profiler.getFullReport(); |
| var arr = []; |
| var totalTime = 0; |
| for (name in obj) { |
| if (YAHOO.lang.hasOwnProperty(obj, name)) { |
| var r = obj[name]; |
| var o = {}; |
| o.fn = name; //add function name to record |
| o.points = r.points.slice(); //copy live array |
| o.calls = r.calls; |
| o.min = r.min; |
| o.max = r.max; |
| o.avg = r.avg; |
| o.total = _arraySum(o.points); |
| o.points = r.points; |
| var f = this.get("filter"); |
| if((!f) || (f(o))) { |
| arr.push(o); |
| totalTime += o.total; |
| } |
| } |
| } |
| |
| //add calculated percentage column |
| for (var i = 0, j = arr.length; i < j; i++) { |
| arr[i].pct = (totalTime) ? (arr[i].total * 100) / totalTime : 0; |
| } |
| |
| var sortedBy = this.get("sortedBy"); |
| var key = sortedBy.key; |
| var dir = sortedBy.dir; |
| |
| arr.sort(this._genSortFunction(key, dir)); |
| |
| YAHOO.log("Returning data from DataSource: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer"); |
| |
| return arr; |
| }; |
| |
| /** |
| * Set up the DataTable. |
| * @method _initDataTable |
| * @private |
| */ |
| proto._initDataTable = function() { |
| YAHOO.log("Creating DataTable instance...", "info", "ProfilerViewer"); |
| |
| var self = this; |
| |
| //Set up the JS Function DataSource, pulling data from |
| //the Profiler. |
| this._dataSource = new YAHOO.util.DataSource( |
| function() { |
| return self._getProfilerData.call(self); |
| }, |
| { |
| responseType: YAHOO.util.DataSource.TYPE_JSARRAY, |
| maxCacheEntries: 0 |
| } |
| ); |
| var ds = this._dataSource; |
| |
| ds.responseSchema = |
| { |
| fields: [ "fn", "avg", "calls", "max", "min", "total", "pct", "points"] |
| }; |
| |
| //Set up the DataTable. |
| var formatTimeValue = function(elCell, oRecord, oColumn, oData) { |
| var a = (oData === Math.floor(oData)) ? oData : (Math.round(oData*1000))/1000; |
| elCell.innerHTML = a + " " + PV.STRINGS.millisecondsAbbrev; |
| }; |
| |
| var formatPercent = function(elCell, oRecord, oColumn, oData) { |
| var a = (oData === Math.floor(oData)) ? oData : (Math.round(oData*100))/100; |
| elCell.innerHTML = a + "%"; |
| }; |
| |
| var a = YAHOO.widget.DataTable.CLASS_ASC; |
| var d = YAHOO.widget.DataTable.CLASS_DESC; |
| var c = PV.STRINGS.colHeads; |
| var f = formatTimeValue; |
| |
| var cols = [ |
| {key:"fn", sortable:true, label: c.fn[0], |
| sortOptions: {defaultDir:a}, |
| resizeable: (YAHOO.util.DragDrop) ? true : false, |
| minWidth:c.fn[1]}, |
| {key:"calls", sortable:true, label: c.calls[0], |
| sortOptions: {defaultDir:d}, |
| width:c.calls[1]}, |
| {key:"avg", sortable:true, label: c.avg[0], |
| sortOptions: {defaultDir:d}, |
| formatter:f, |
| width:c.avg[1]}, |
| {key:"min", sortable:true, label: c.min[0], |
| sortOptions: {defaultDir:a}, |
| formatter:f, |
| width:c.min[1]}, |
| {key:"max", sortable:true, label: c.max[0], |
| sortOptions: {defaultDir:d}, |
| formatter:f, |
| width:c.max[1]}, |
| {key:"total", sortable:true, label: c.total[0], |
| sortOptions: {defaultDir:d}, |
| formatter:f, |
| width:c.total[1]}, |
| {key:"pct", sortable:true, label: c.pct[0], |
| sortOptions: {defaultDir:d}, |
| formatter:formatPercent, |
| width:c.pct[1]} |
| ]; |
| |
| this._dataTable = new YAHOO.widget.DataTable(this._tableEl, cols, ds, { |
| scrollable:true, |
| height:this.get("tableHeight"), |
| initialRequest:null, |
| sortedBy: { |
| key: "total", |
| dir: YAHOO.widget.DataTable.CLASS_DESC |
| } |
| }); |
| var dt = this._dataTable; |
| |
| //Wire up DataTable events to drive the rest of the UI. |
| dt.subscribe("sortedByChange", this._sortedByChange, this, true); |
| dt.subscribe("renderEvent", this._dataTableRenderHandler, this, true); |
| dt.subscribe("initEvent", this._dataTableRenderHandler, this, true); |
| Event.on(this._tableEl.getElementsByTagName("th"), "click", this._thClickHandler, this, true); |
| YAHOO.log("DataTable initialized.", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * Proxy the sort event in DataTable into the ProfilerViewer |
| * attribute. |
| * @method _sortedByChange |
| * @private |
| **/ |
| proto._sortedByChange = function(o) { |
| YAHOO.log("Relaying DataTable sortedBy value change; new key: " + o.newValue.key + "; new direction: " + o.newValue.dir + ".", "info", "ProfilerViewer"); |
| this.set("sortedBy", {key: o.newValue.key, dir:o.newValue.dir}); |
| }; |
| |
| /** |
| * Proxy the render event in DataTable into the ProfilerViewer |
| * attribute. |
| * @method _dataTableRenderHandler |
| * @private |
| **/ |
| proto._dataTableRenderHandler = function(o) { |
| YAHOO.log("DataTable's render event has fired.", "info", "ProfilerViewer"); |
| this._setBusyState(false); |
| }; |
| |
| /** |
| * Event handler for clicks on the DataTable's sortable column |
| * heads. |
| * @method _thClickHandler |
| * @private |
| **/ |
| proto._thClickHandler = function(o) { |
| YAHOO.log("DataTable's header row was clicked for sorting.", "info", "ProfilerViewer"); |
| this._setBusyState(true); |
| }; |
| |
| /** |
| * Refresh DataTable, getting new data from Profiler. |
| * @method _refreshDataTable |
| * @private |
| **/ |
| proto._refreshDataTable = function(args) { |
| YAHOO.log("Beginning to refresh DataTable contents...", "info", "ProfilerViewer"); |
| var dt = this._dataTable; |
| dt.getDataSource().sendRequest("", dt.onDataReturnInitializeTable, dt); |
| YAHOO.log("DataTable refresh complete.", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * Refresh chart, getting new data from table. |
| * @method _refreshChart |
| * @private |
| **/ |
| proto._refreshChart = function() { |
| YAHOO.log("Beginning to refresh Chart contents...", "info", "ProfilerViewer"); |
| |
| switch (this.get("sortedBy").key) { |
| case "fn": |
| /*Keep the same data on the chart, but force update to |
| reflect new sort order on function/method name: */ |
| this._chart.set("dataSource", this._chart.get("dataSource")); |
| |
| /*no further action necessary; chart redraws*/ |
| return; |
| case "calls": |
| /*Null out the xAxis formatting before redrawing chart.*/ |
| this._chart.set("xAxis", this._chartAxisDefinitionPlain); |
| break; |
| case "pct": |
| this._chart.set("xAxis", this._chartAxisDefinitionPercent); |
| break; |
| default: |
| /*Set the default xAxis; redraw legend; set the new series definition.*/ |
| this._chart.set("xAxis", this._chartAxisDefinitionTime); |
| break; |
| } |
| |
| this._drawChartLegend(); |
| this._chart.set("series", this._getSeriesDef(this.get("sortedBy").key)); |
| |
| YAHOO.log("Chart refresh complete.", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * Get data for the Chart from DataTable recordset |
| * @method _getChartData |
| * @private |
| */ |
| proto._getChartData = function() { |
| YAHOO.log("Getting data for chart from function DataSource.", "info", "ProfilerViewer"); |
| var records = this._dataTable.getRecordSet().getRecords(0, this.get("maxChartFunctions")); |
| var arr = []; |
| for (var i = records.length - 1; i>-1; i--) { |
| arr.push(records[i].getData()); |
| } |
| YAHOO.log("Returning data to Chart: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer"); |
| return arr; |
| }; |
| |
| /** |
| * Build series definition based on current configuration attributes. |
| * @method _getSeriesDef |
| * @private |
| */ |
| proto._getSeriesDef = function(field) { |
| var sd = this.get("chartSeriesDefinitions")[field]; |
| var arr = []; |
| for(var i = 0, j = sd.group.length; i<j; i++) { |
| var c = this.get("chartSeriesDefinitions")[sd.group[i]]; |
| arr.push( |
| {displayName:c.displayName, |
| xField:c.xField, |
| style: {color:c.style.color, size:c.style.size} |
| } |
| ); |
| } |
| |
| YAHOO.log("Returning new series definition to chart: " + YAHOO.lang.dump(arr), "info", "ProfilerViewer"); |
| return arr; |
| }; |
| |
| /** |
| * Set up the Chart. |
| * @method _initChart |
| * @private |
| */ |
| proto._initChart = function() { |
| YAHOO.log("Initializing chart...", "info", "ProfilerViewer"); |
| |
| this._sizeChartCanvas(); |
| |
| YAHOO.widget.Chart.SWFURL = this.get("swfUrl"); |
| |
| var self = this; |
| |
| //Create DataSource based on records currently displayed |
| //at the top of the sort list in the DataTable. |
| var ds = new YAHOO.util.DataSource( |
| //force the jsfunction DataSource to run in the scope of |
| //the ProfilerViewer, not in the YAHOO.util.DataSource scope: |
| function() { |
| return self._getChartData.call(self); |
| }, |
| { |
| responseType: YAHOO.util.DataSource.TYPE_JSARRAY, |
| maxCacheEntries: 0 |
| } |
| ); |
| |
| ds.responseSchema = |
| { |
| fields: [ "fn", "avg", "calls", "max", "min", "total", "pct" ] |
| }; |
| |
| ds.subscribe('responseEvent', this._sizeChartCanvas, this, true); |
| |
| //Set up the chart itself. |
| this._chartAxisDefinitionTime = new YAHOO.widget.NumericAxis(); |
| this._chartAxisDefinitionTime.labelFunction = "YAHOO.widget.ProfilerViewer.timeAxisLabelFunction"; |
| |
| this._chartAxisDefinitionPercent = new YAHOO.widget.NumericAxis(); |
| this._chartAxisDefinitionPercent.labelFunction = "YAHOO.widget.ProfilerViewer.percentAxisLabelFunction"; |
| |
| this._chartAxisDefinitionPlain = new YAHOO.widget.NumericAxis(); |
| |
| this._chart = new YAHOO.widget.BarChart( this._chartEl, ds, |
| { |
| yField: "fn", |
| series: this._getSeriesDef(this.get("sortedBy").key), |
| style: this.get("chartStyle"), |
| xAxis: this._chartAxisDefinitionTime |
| } ); |
| |
| this._drawChartLegend(); |
| this._chartInitialized = true; |
| this._dataTable.unsubscribe("initEvent", this._initChart, this); |
| this._dataTable.subscribe("initEvent", this._refreshChart, this, true); |
| |
| YAHOO.log("Chart initialization complete.", "info", "ProfilerViewer"); |
| }; |
| |
| /** |
| * Set up the Chart's legend |
| * @method _drawChartLegend |
| * @private |
| **/ |
| proto._drawChartLegend = function() { |
| YAHOO.log("Drawing chart legend...", "info", "ProfilerViewer"); |
| var seriesDefs = this.get("chartSeriesDefinitions"); |
| var currentDef = seriesDefs[this.get("sortedBy").key]; |
| var l = this._chartLegendEl; |
| l.innerHTML = ""; |
| for(var i = 0, j = currentDef.group.length; i<j; i++) { |
| var c = seriesDefs[currentDef.group[i]]; |
| var dt = document.createElement("dt"); |
| Dom.setStyle(dt, "backgroundColor", "#" + c.style.color); |
| var dd = document.createElement("dd"); |
| dd.innerHTML = c.displayName; |
| l.appendChild(dt); |
| l.appendChild(dd); |
| } |
| }; |
| |
| /** |
| * Resize the chart's canvas if based on number of records |
| * returned from the chart's datasource. |
| * @method _sizeChartCanvas |
| * @private |
| **/ |
| proto._sizeChartCanvas = function(o) { |
| YAHOO.log("Resizing chart canvas...", "info", "ProfilerViewer"); |
| var bars = (o) ? o.response.length : this.get("maxChartFunctions"); |
| var s = (bars * 36) + 34; |
| if (s != parseInt(this._chartElHeight, 10)) { |
| this._chartElHeight = s; |
| Dom.setStyle(this._chartEl, "height", s + "px"); |
| } |
| }; |
| |
| /** |
| * setAttributeConfigs TabView specific properties. |
| * @method initAttributes |
| * @param {Object} attr Hash of initial attributes |
| * @method initAttributes |
| * @private |
| */ |
| proto.initAttributes = function(attr) { |
| YAHOO.log("Initializing attributes...", "info", "ProfilerViewer"); |
| YAHOO.widget.ProfilerViewer.superclass.initAttributes.call(this, attr); |
| /** |
| * The YUI Loader base path from which to pull YUI files needed |
| * in the rendering of the ProfilerViewer canvas. Passed directly |
| * to YUI Loader. Leave blank to draw files from |
| * yui.yahooapis.com. |
| * @attribute base |
| * @type string |
| * @default "" |
| */ |
| this.setAttributeConfig('base', { |
| value: attr.base |
| }); |
| |
| /** |
| * The height of the DataTable. The table will scroll |
| * vertically if the content overflows the specified |
| * height. |
| * @attribute tableHeight |
| * @type string |
| * @default "15em" |
| */ |
| this.setAttributeConfig('tableHeight', { |
| value: attr.tableHeight || "15em", |
| method: function(s) { |
| if(this._dataTable) { |
| this._dataTable.set("height", s); |
| } |
| } |
| }); |
| |
| /** |
| * The default column key to sort by. Valid keys are: fn, calls, |
| * avg, min, max, total. Valid dir values are: |
| * YAHOO.widget.DataTable.CLASS_ASC and |
| * YAHOO.widget.DataTable.CLASS_DESC (or their |
| * string equivalents). |
| * @attribute sortedBy |
| * @type string |
| * @default {key:"total", dir:"yui-dt-desc"} |
| */ |
| this.setAttributeConfig('sortedBy', { |
| value: attr.sortedBy || {key:"total", dir:"yui-dt-desc"} |
| }); |
| |
| /** |
| * A filter function to use in selecting functions that will |
| * appear in the ProfilerViewer report. The function is passed |
| * a function report object and should return a boolean indicating |
| * whether that function should be included in the ProfilerViewer |
| * display. The argument is structured as follows: |
| * |
| * { |
| * fn: <str function name>, |
| * calls : <n number of calls>, |
| * avg : <n average call duration>, |
| * max: <n duration of longest call>, |
| * min: <n duration of shortest call>, |
| * total: <n total time of all calls> |
| * points : <array time in ms of each call> |
| * } |
| * |
| * For example, you would use the follwing filter function to |
| * return only functions that have been called at least once: |
| * |
| * function(o) { |
| * return (o.calls > 0); |
| * } |
| * |
| * @attribute filter |
| * @type function |
| * @default null |
| */ |
| this.setAttributeConfig('filter', { |
| value: attr.filter || null, |
| validator: YAHOO.lang.isFunction |
| }); |
| |
| /** |
| * The path to the YUI Charts swf file; must be a full URI |
| * or a path relative to the page being profiled. Changes at runtime |
| * not supported; pass this value in at instantiation. |
| * @attribute swfUrl |
| * @type string |
| * @default "http://yui.yahooapis.com/2.5.0/build/charts/assets/charts.swf" |
| */ |
| this.setAttributeConfig('swfUrl', { |
| value: attr.swfUrl || "http://yui.yahooapis.com/2.5.0/build/charts/assets/charts.swf" |
| }); |
| |
| /** |
| * The maximum number of functions to profile in the chart. The |
| * greater the number of functions, the greater the height of the |
| * chart canvas. |
| * height. |
| * @attribute maxChartFunctions |
| * @type int |
| * @default 6 |
| */ |
| this.setAttributeConfig('maxChartFunctions', { |
| value: attr.maxChartFunctions || 6, |
| method: function(s) { |
| if(this._rendered) { |
| this._sizeChartCanvas(); |
| } |
| }, |
| validator: YAHOO.lang.isNumber |
| }); |
| |
| /** |
| * The style object that defines the chart's visual presentation. |
| * Conforms to the style attribute passed to the Charts Control |
| * constructor. See Charts Control User's Guide for more information |
| * on how to format this object. |
| * @attribute chartStyle |
| * @type obj |
| * @default See JS source for default definitions. |
| */ |
| this.setAttributeConfig('chartStyle', { |
| value: attr.chartStyle || { |
| font: |
| { |
| name: "Arial", |
| color: 0xeeee5c, |
| size: 12 |
| }, |
| background: |
| { |
| color: "6e6e63" |
| } |
| }, |
| method: function() { |
| if(this._rendered && this.get("showChart")) { |
| this._refreshChart(); |
| } |
| } |
| }); |
| |
| /** |
| * The series definition information to use when charting |
| * specific fields on the chart. displayName, xField, |
| * and style members are used to construct the series |
| * definition; the "group" member is the array of fields |
| * that should be charted when the table is sorted by a |
| * given field. |
| * @attribute chartSeriesDefinitions |
| * @type obj |
| * @default See JS source for full default definitions. |
| */ |
| this.setAttributeConfig('chartSeriesDefinitions', { |
| value: attr.chartSeriesDefinitions || { |
| total: { |
| displayName: PV.STRINGS.colHeads.total[0], |
| xField: "total", |
| style: {color:"4d95dd", size:20}, |
| group: ["total"] |
| }, |
| calls: { |
| displayName: PV.STRINGS.colHeads.calls[0], |
| xField: "calls", |
| style: {color:"edff9f", size:20}, |
| group: ["calls"] |
| }, |
| avg: { |
| displayName: PV.STRINGS.colHeads.avg[0], |
| xField: "avg", |
| style: {color:"209daf", size:9}, |
| group: ["avg", "min", "max"] |
| }, |
| min: { |
| displayName: PV.STRINGS.colHeads.min[0], |
| xField: "min", |
| style: {color:"b6ecf4", size:9}, |
| group: ["avg", "min", "max"] |
| }, |
| max: { |
| displayName: PV.STRINGS.colHeads.max[0], |
| xField: "max", |
| style: {color:"29c7de", size:9}, |
| group: ["avg", "min", "max"] |
| }, |
| pct: { |
| displayName: PV.STRINGS.colHeads.pct[0], |
| xField: "pct", |
| style: {color:"C96EDB", size:20}, |
| group: ["pct"] |
| } |
| }, |
| method: function() { |
| if(this._rendered && this.get("showChart")) { |
| this._refreshChart(); |
| } |
| } |
| }); |
| |
| /** |
| * The default visibility setting for the viewer canvas. If true, |
| * the viewer will load all necessary files and render itself |
| * immediately upon instantiation; otherwise, the viewer will |
| * load only minimal resources until the user toggles visibility |
| * via the UI. |
| * @attribute visible |
| * @type boolean |
| * @default false |
| */ |
| this.setAttributeConfig('visible', { |
| value: attr.visible || false, |
| validator: YAHOO.lang.isBoolean, |
| method: function(b) { |
| if(b) { |
| this._show(); |
| } else { |
| if (this._rendered) { |
| this._hide(); |
| } |
| } |
| } |
| }); |
| |
| /** |
| * The default visibility setting for the chart. |
| * @attribute showChart |
| * @type boolean |
| * @default true |
| */ |
| this.setAttributeConfig('showChart', { |
| value: attr.showChart || true, |
| validator: YAHOO.lang.isBoolean, |
| writeOnce: true |
| |
| }); |
| |
| YAHOO.widget.ProfilerViewer.superclass.initAttributes.call(this, attr); |
| |
| YAHOO.log("Attributes initialized.", "info", "ProfilerViewer"); |
| }; |
| |
| })(); |
| YAHOO.register("profilerviewer", YAHOO.widget.ProfilerViewer, {version: "2.6.0", build: "1321"}); |