text: /*
  Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  Code licensed under the BSD License:
  http://developer.yahoo.net/yui/license.txt
  version: 2.7.0r2
  */
  (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) {
  		if(o.newValue && o.newValue.key) {
  			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._getProfilerData();
  		var records = this._dataTable.getRecordSet().getRecords(0, this.get("maxChartFunctions"));
  		var arr = [];
  		for (var i = 0, j = records.length; i<j; 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.7.0r2", build: "1799"});
  