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