Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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