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