PageRenderTime 60ms CodeModel.GetById 10ms app.highlight 41ms RepoModel.GetById 1ms app.codeStats 0ms

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