2
0
forked from Wavyzz/dolibarr

Upgrade lib for phpdebugbar

This commit is contained in:
Laurent Destailleur
2020-09-07 21:18:26 +02:00
parent fa22b09630
commit b5376d3322
85 changed files with 8675 additions and 172 deletions

View File

@@ -108,6 +108,7 @@ class ExceptionsCollector extends DataCollector implements Renderable
'code' => $e->getCode(),
'file' => $filePath,
'line' => $e->getLine(),
'stack_trace' => $e->getTraceAsString(),
'surrounding_lines' => $lines,
'xdebug_link' => $this->getXdebugLink($filePath, $e->getLine())
);

View File

@@ -14,13 +14,13 @@ class TraceablePDO extends PDO
/** @var PDO */
protected $pdo;
/** @var array */
protected $executedStatements = array();
/** @var TracedStatement[] */
protected $executedStatements = [];
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DebugBar\DataCollector\PDO\TraceablePDOStatement', array($this)));
$this->pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, [TraceablePDOStatement::class, [$this]]);
}
/**
@@ -130,7 +130,7 @@ class TraceablePDO extends PDO
* PDO::prepare returns a PDOStatement object. If the database server cannot successfully prepare
* the statement, PDO::prepare returns FALSE or emits PDOException (depending on error handling).
*/
public function prepare($statement, $driver_options = array())
public function prepare($statement, $driver_options = [])
{
return $this->pdo->prepare($statement, $driver_options);
}
@@ -202,7 +202,7 @@ class TraceablePDO extends PDO
$ex = null;
try {
$result = call_user_func_array(array($this->pdo, $method), $args);
$result = $this->__call($method, $args);
} catch (PDOException $e) {
$ex = $e;
}
@@ -264,7 +264,7 @@ class TraceablePDO extends PDO
/**
* Returns the list of executed statements as TracedStatement objects
*
* @return array
* @return TracedStatement[]
*/
public function getExecutedStatements()
{
@@ -274,7 +274,7 @@ class TraceablePDO extends PDO
/**
* Returns the list of failed statements
*
* @return array
* @return TracedStatement[]
*/
public function getFailedExecutedStatements()
{
@@ -306,6 +306,6 @@ class TraceablePDO extends PDO
*/
public function __call($name, $args)
{
return call_user_func_array(array($this->pdo, $name), $args);
return call_user_func_array([$this->pdo, $name], $args);
}
}

View File

@@ -15,7 +15,7 @@ class TraceablePDOStatement extends PDOStatement
protected $pdo;
/** @var array */
protected $boundParameters = array();
protected $boundParameters = [];
/**
* TraceablePDOStatement constructor.
@@ -42,8 +42,8 @@ class TraceablePDOStatement extends PDOStatement
public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null)
{
$this->boundParameters[$column] = $param;
$args = array_merge(array($column, &$param), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindColumn'), $args);
$args = array_merge([$column, &$param], array_slice(func_get_args(), 2));
return call_user_func_array(['parent', 'bindColumn'], $args);
}
/**
@@ -64,8 +64,8 @@ class TraceablePDOStatement extends PDOStatement
public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null)
{
$this->boundParameters[$parameter] = $variable;
$args = array_merge(array($parameter, &$variable), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindParam'), $args);
$args = array_merge([$parameter, &$variable], array_slice(func_get_args(), 2));
return call_user_func_array(['parent', 'bindParam'], $args);
}
/**
@@ -83,7 +83,7 @@ class TraceablePDOStatement extends PDOStatement
public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR)
{
$this->boundParameters[$parameter] = $value;
return call_user_func_array(array("parent", 'bindValue'), func_get_args());
return call_user_func_array(['parent', 'bindValue'], func_get_args());
}
/**
@@ -93,6 +93,7 @@ class TraceablePDOStatement extends PDOStatement
* @param array $input_parameters [optional] An array of values with as many elements as there
* are bound parameters in the SQL statement being executed. All values are treated as
* PDO::PARAM_STR.
* @throws PDOException
* @return bool TRUE on success or FALSE on failure.
*/
public function execute($input_parameters = null)

View File

@@ -32,7 +32,7 @@ class TracedStatement
* @param array $params
* @param string $preparedId
*/
public function __construct($sql, array $params = array(), $preparedId = null)
public function __construct($sql, array $params = [], $preparedId = null)
{
$this->sql = $sql;
$this->parameters = $this->checkParameters($params);
@@ -52,8 +52,8 @@ class TracedStatement
/**
* @param \Exception|null $exception
* @param int $rowCount
* @param null $endTime
* @param null $endMemory
* @param float $endTime
* @param int $endMemory
*/
public function end(\Exception $exception = null, $rowCount = 0, $endTime = null, $endMemory = null)
{
@@ -68,8 +68,8 @@ class TracedStatement
/**
* Check parameters for illegal (non UTF-8) strings, like Binary data.
*
* @param $params
* @return mixed
* @param array $params
* @return array
*/
public function checkParameters($params)
{
@@ -82,7 +82,7 @@ class TracedStatement
}
/**
* Returns the SQL string used for the query
* Returns the SQL string used for the query, without filled parameters
*
* @return string
*/
@@ -107,15 +107,29 @@ class TracedStatement
}
$sql = $this->sql;
$cleanBackRefCharMap = ['%' => '%%', '$' => '$%', '\\' => '\\%'];
foreach ($this->parameters as $k => $v) {
$v = "$quoteLeft$v$quoteRight";
if (!is_numeric($k)) {
$sql = preg_replace("/{$k}\b/", $v, $sql, 1);
$backRefSafeV = strtr($v, $cleanBackRefCharMap);
$v = "$quoteLeft$backRefSafeV$quoteRight";
if (is_numeric($k)) {
$marker = "\?";
} else {
$p = strpos($sql, '?');
$sql = substr($sql, 0, $p) . $v. substr($sql, $p + 1);
$marker = (preg_match("/^:/", $k)) ? $k : ":" . $k;
}
$matchRule = "/({$marker}(?!\w))(?=(?:[^$quotationChar]|[$quotationChar][^$quotationChar]*[$quotationChar])*$)/";
for ($i = 0; $i <= mb_substr_count($sql, $k); $i++) {
$sql = preg_replace($matchRule, $v, $sql, 1);
}
}
$sql = strtr($sql, array_flip($cleanBackRefCharMap));
return $sql;
}
@@ -136,7 +150,7 @@ class TracedStatement
*/
public function getParameters()
{
$params = array();
$params = [];
foreach ($this->parameters as $name => $param) {
$params[$name] = htmlentities($param, ENT_QUOTES, 'UTF-8', false);
}
@@ -164,7 +178,7 @@ class TracedStatement
}
/**
* @return mixed
* @return float
*/
public function getStartTime()
{
@@ -172,7 +186,7 @@ class TracedStatement
}
/**
* @return mixed
* @return float
*/
public function getEndTime()
{
@@ -180,9 +194,9 @@ class TracedStatement
}
/**
* Returns the duration in seconds of the execution
* Returns the duration in seconds + microseconds of the execution
*
* @return int
* @return float
*/
public function getDuration()
{
@@ -190,7 +204,7 @@ class TracedStatement
}
/**
* @return mixed
* @return int
*/
public function getStartMemory()
{
@@ -198,7 +212,7 @@ class TracedStatement
}
/**
* @return mixed
* @return int
*/
public function getEndMemory()
{
@@ -238,7 +252,7 @@ class TracedStatement
/**
* Returns the exception's code
*
* @return string
* @return int|string
*/
public function getErrorCode()
{

View File

@@ -70,6 +70,8 @@ class JavascriptRenderer
protected $ajaxHandlerClass = 'PhpDebugBar.AjaxHandler';
protected $ajaxHandlerBindToFetch = false;
protected $ajaxHandlerBindToJquery = true;
protected $ajaxHandlerBindToXHR = false;
@@ -477,6 +479,27 @@ class JavascriptRenderer
return $this->ajaxHandlerClass;
}
/**
* Sets whether to call bindToFetch() on the ajax handler
*
* @param boolean $bind
*/
public function setBindAjaxHandlerToFetch($bind = true)
{
$this->ajaxHandlerBindToFetch = $bind;
return $this;
}
/**
* Checks whether bindToFetch() will be called on the ajax handler
*
* @return boolean
*/
public function isAjaxHandlerBoundToFetch()
{
return $this->ajaxHandlerBindToFetch;
}
/**
* Sets whether to call bindToJquery() on the ajax handler
*
@@ -1022,6 +1045,9 @@ class JavascriptRenderer
$this->variableName,
$this->ajaxHandlerAutoShow ? 'true' : 'false'
);
if ($this->ajaxHandlerBindToFetch) {
$js .= sprintf("%s.ajaxHandler.bindToFetch();\n", $this->variableName);
}
if ($this->ajaxHandlerBindToXHR) {
$js .= sprintf("%s.ajaxHandler.bindToXHR();\n", $this->variableName);
} elseif ($this->ajaxHandlerBindToJquery) {

View File

@@ -36,7 +36,7 @@ div.phpdebugbar-drag-capture {
z-index: 10001;
background: none;
display: none;
cursor: n-resize;
cursor: ns-resize;
}
div.phpdebugbar-closed {
@@ -108,7 +108,7 @@ div.phpdebugbar-resize-handle {
width: 100%;
background: none;
border-bottom: 1px solid #ccc;
cursor: n-resize;
cursor: ns-resize;
}
div.phpdebugbar-closed, div.phpdebugbar-minimized{

View File

@@ -95,7 +95,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
// ------------------------------------------------------------------
/**
* Base class for all elements with a visual component
*
@@ -123,7 +123,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Called after the constructor
*
*
* @param {Object} options
*/
initialize: function(options) {
@@ -137,7 +137,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Sets the value of an attribute
*
*
* @param {String} attr Can also be an object to set multiple attributes at once
* @param {Object} value
*/
@@ -159,7 +159,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Checks if an attribute exists and is not null
*
*
* @param {String} attr
* @return {[type]} [description]
*/
@@ -169,7 +169,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Returns the value of an attribute
*
*
* @param {String} attr
* @return {Object}
*/
@@ -181,7 +181,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
* Registers a callback function that will be called whenever the value of the attribute changes
*
* If cb is a jQuery element, text() will be used to fill the element
*
*
* @param {String} attr
* @param {Function} cb
*/
@@ -213,7 +213,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
* Creates a subclass
*
* Code from Backbone.js
*
*
* @param {Array} props Prototype properties
* @return {Function}
*/
@@ -223,7 +223,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
var child = function() { return parent.apply(this, arguments); };
$.extend(child, parent);
var Surrogate = function(){ this.constructor = child; };
var Surrogate = function() { this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
$.extend(child.prototype, props);
@@ -237,7 +237,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Tab
*
*
* A tab is composed of a tab label which is always visible and
* a tab panel which is visible only when the tab is active.
*
@@ -350,7 +350,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Formats the title of a dataset
*
*
* @this {DatasetTitleFormater}
* @param {String} id
* @param {Object} data
@@ -379,6 +379,13 @@ if (typeof(PhpDebugBar) == 'undefined') {
} else {
filename = uri.substr(uri.lastIndexOf('/') + 1);
}
// truncate the filename in the label, if it's too long
var maxLength = 150;
if (filename.length > maxLength) {
filename = filename.substr(0, maxLength) + '...';
}
var label = "#" + nb + " " + filename + suffix + ' (' + data['__meta']['datetime'].split(' ')[1] + ')';
return label;
}
@@ -589,7 +596,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
*/
createTab: function(name, widget, title) {
var tab = new Tab({
title: title || (name.replace(/[_\-]/g, ' ').charAt(0).toUpperCase() + name.slice(1)),
title: title || (name.replace(/[_\-]/g, ' ').charAt(0).toUpperCase() + name.slice(1)),
widget: widget
});
return this.addTab(name, tab);
@@ -645,7 +652,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Adds an indicator
*
*
* @this {DebugBar}
* @param {String} name Internal name
* @param {Indicator} indicator Indicator object
@@ -668,7 +675,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Returns a control
*
*
* @param {String} name
* @return {Object}
*/
@@ -680,7 +687,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Checks if there's a control under the specified name
*
*
* @this {DebugBar}
* @param {String} name
* @return {Boolean}
@@ -691,7 +698,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Checks if a tab with the specified name exists
*
*
* @this {DebugBar}
* @param {String} name
* @return {Boolean}
@@ -702,7 +709,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Checks if an indicator with the specified name exists
*
*
* @this {DebugBar}
* @param {String} name
* @return {Boolean}
@@ -713,7 +720,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Removes all tabs and indicators from the debug bar and hides it
*
*
* @this {DebugBar}
*/
reset: function() {
@@ -730,7 +737,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Open the debug bar and display the specified tab
*
*
* @this {DebugBar}
* @param {String} name If not specified, display the first tab
*/
@@ -781,13 +788,13 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Checks if the panel is minimized
*
*
* @return {Boolean}
*/
isMinimized: function() {
return this.$el.hasClass(csscls('minimized'));
},
/**
* Close the debug bar
*
@@ -802,7 +809,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
this.$el.addClass(csscls('closed'));
this.recomputeBottomOffset();
},
/**
* Checks if the panel is closed
*
@@ -811,7 +818,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
isClosed: function() {
return this.$el.hasClass(csscls('closed'));
},
/**
* Restore the debug bar
*
@@ -825,6 +832,8 @@ if (typeof(PhpDebugBar) == 'undefined') {
var tab = localStorage.getItem('phpdebugbar-tab');
if (this.isTab(tab)) {
this.showTab(tab);
} else {
this.showTab();
}
this.$el.removeClass(csscls('closed'));
this.resize();
@@ -839,7 +848,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
if (this.isClosed()) {
return $('body').css('margin-bottom', this.options.bodyMarginBottomHeight || '');
}
var offset = parseInt(this.$el.height()) + (this.options.bodyMarginBottomHeight || 0);
$('body').css('margin-bottom', offset);
}
@@ -856,7 +865,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
*
* Example:
* {"memory": ["memory.peak_usage_str", "0B"]}
*
*
* @this {DebugBar}
* @param {Object} map
*/
@@ -880,7 +889,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
*
* For this method to be usefull, you need to specify
* a dataMap using setDataMap()
*
*
* @this {DebugBar}
* @param {Object} data
* @return {String} Dataset's id
@@ -895,10 +904,10 @@ if (typeof(PhpDebugBar) == 'undefined') {
*
* If more than one dataset are added, the dataset selector
* will be displayed.
*
*
* For this method to be usefull, you need to specify
* a dataMap using setDataMap()
*
*
* @this {DebugBar}
* @param {Object} data
* @param {String} id The name of this set, optional
@@ -924,7 +933,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Loads a dataset using the open handler
*
*
* @param {String} id
* @param {Bool} show Whether to show the new dataset, optional (default: true)
*/
@@ -941,7 +950,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Returns the data from a dataset
*
*
* @this {DebugBar}
* @param {String} id
* @return {Object}
@@ -952,7 +961,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Switch the currently displayed dataset
*
*
* @this {DebugBar}
* @param {String} id
*/
@@ -963,7 +972,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Called when the current dataset is modified.
*
*
* @this {DebugBar}
* @param {Object} data
*/
@@ -982,7 +991,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Sets the handler to open past dataset
*
*
* @this {DebugBar}
* @param {object} handler
*/
@@ -997,7 +1006,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Returns the handler to open past dataset
*
*
* @this {DebugBar}
* @return {object}
*/
@@ -1011,7 +1020,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
DebugBar.Indicator = Indicator;
// ------------------------------------------------------------------
/**
* AjaxHandler
*
@@ -1028,31 +1037,49 @@ if (typeof(PhpDebugBar) == 'undefined') {
$.extend(AjaxHandler.prototype, {
/**
* Handles an XMLHttpRequest
*
* Handles a Fetch API Response or an XMLHttpRequest
*
* @this {AjaxHandler}
* @param {XMLHttpRequest} xhr
* @param {Response|XMLHttpRequest} response
* @return {Bool}
*/
handle: function(xhr) {
// Check if the debugbar header is available
if (xhr.getAllResponseHeaders().indexOf(this.headerName) === -1){
handle: function(response) {
// Check if the debugbar header is available
if (this.isFetch(response) && !response.headers.has(this.headerName + '-id')) {
return true;
} else if (this.isXHR(response) && response.getAllResponseHeaders().indexOf(this.headerName) === -1) {
return true;
}
if (!this.loadFromId(xhr)) {
return this.loadFromData(xhr);
if (!this.loadFromId(response)) {
return this.loadFromData(response);
}
return true;
},
getHeader: function(response, header) {
if (this.isFetch(response)) {
return response.headers.get(header)
}
return response.getResponseHeader(header)
},
isFetch: function(response) {
return Object.prototype.toString.call(response) == '[object Response]'
},
isXHR: function(response) {
return Object.prototype.toString.call(response) == '[object XMLHttpRequest]'
},
/**
* Checks if the HEADER-id exists and loads the dataset using the open handler
*
* @param {XMLHttpRequest} xhr
*
* @param {Response|XMLHttpRequest} response
* @return {Bool}
*/
loadFromId: function(xhr) {
var id = this.extractIdFromHeaders(xhr);
loadFromId: function(response) {
var id = this.extractIdFromHeaders(response);
if (id && this.debugbar.openHandler) {
this.debugbar.loadDataSet(id, "(ajax)", undefined, this.autoShow);
return true;
@@ -1062,22 +1089,22 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Extracts the id from the HEADER-id
*
* @param {XMLHttpRequest} xhr
*
* @param {Response|XMLHttpRequest} response
* @return {String}
*/
extractIdFromHeaders: function(xhr) {
return xhr.getResponseHeader(this.headerName + '-id');
extractIdFromHeaders: function(response) {
return this.getHeader(response, this.headerName + '-id');
},
/**
* Checks if the HEADER exists and loads the dataset
*
* @param {XMLHttpRequest} xhr
*
* @param {Response|XMLHttpRequest} response
* @return {Bool}
*/
loadFromData: function(xhr) {
var raw = this.extractDataFromHeaders(xhr);
loadFromData: function(response) {
var raw = this.extractDataFromHeaders(response);
if (!raw) {
return false;
}
@@ -1093,18 +1120,18 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Extract the data as a string from headers of an XMLHttpRequest
*
*
* @this {AjaxHandler}
* @param {XMLHttpRequest} xhr
* @param {Response|XMLHttpRequest} response
* @return {string}
*/
extractDataFromHeaders: function(xhr) {
var data = xhr.getResponseHeader(this.headerName);
extractDataFromHeaders: function(response) {
var data = this.getHeader(response, this.headerName);
if (!data) {
return;
}
for (var i = 1;; i++) {
var header = xhr.getResponseHeader(this.headerName + '-' + i);
var header = this.getHeader(response, this.headerName + '-' + i);
if (!header) {
break;
}
@@ -1115,7 +1142,7 @@ if (typeof(PhpDebugBar) == 'undefined') {
/**
* Parses the string data into an object
*
*
* @this {AjaxHandler}
* @param {string} data
* @return {string}
@@ -1124,9 +1151,35 @@ if (typeof(PhpDebugBar) == 'undefined') {
return JSON.parse(data);
},
/**
* Attaches an event listener to fetch
*
* @this {AjaxHandler}
*/
bindToFetch: function() {
var self = this;
var proxied = window.fetch;
if (proxied === undefined && proxied.polyfill !== undefined) {
return;
}
window.fetch = function () {
var promise = proxied.apply(this, arguments);
promise.then(function (response) {
self.handle(response);
}, function (e) {
self.handle(response);
});
return promise;
};
},
/**
* Attaches an event listener to jQuery.ajaxComplete()
*
*
* @this {AjaxHandler}
* @param {jQuery} jq Optional
*/
@@ -1138,10 +1191,10 @@ if (typeof(PhpDebugBar) == 'undefined') {
}
});
},
/**
* Attaches an event listener to XMLHttpRequest
*
*
* @this {AjaxHandler}
*/
bindToXHR: function() {

View File

@@ -19,6 +19,7 @@ div.phpdebugbar-openhandler {
width: 70%;
height: 70%;
background: #fff;
color: #000;
border: 2px solid #888;
overflow: auto;
z-index: 20001;

File diff suppressed because one or more lines are too long

View File

@@ -428,10 +428,29 @@ if (typeof(PhpDebugBar) == 'undefined') {
render: function() {
this.bindAttr('data', function(data) {
// ported from php DataFormatter
var formatDuration = function(seconds) {
if (seconds < 0.001)
return (seconds * 1000000).toFixed() + 'μs';
else if (seconds < 1)
return (seconds * 1000).toFixed(2) + 'ms';
return (seconds).toFixed(2) + 's';
};
this.$el.empty();
if (data.measures) {
var aggregate = {};
for (var i = 0; i < data.measures.length; i++) {
var measure = data.measures[i];
if(!aggregate[measure.label])
aggregate[measure.label] = { count: 0, duration: 0 };
aggregate[measure.label]['count'] += 1;
aggregate[measure.label]['duration'] += measure.duration;
var m = $('<div />').addClass(csscls('measure')),
li = $('<li />'),
left = (measure.relative_start * 100 / data.duration).toFixed(2),
@@ -468,6 +487,30 @@ if (typeof(PhpDebugBar) == 'undefined') {
});
}
}
// convert to array and sort by duration
aggregate = $.map(aggregate, function(data, label) {
return {
label: label,
data: data
}
}).sort(function(a, b) {
return b.data.duration - a.data.duration
});
// build table and add
var aggregateTable = $('<table style="display: table; border: 0; width: 99%"></table>').addClass(csscls('params'));
$.each(aggregate, function(i, aggregate) {
width = Math.min((aggregate.data.duration * 100 / data.duration).toFixed(2), 100);
aggregateTable.append('<tr><td class="' + csscls('name') + '">' + aggregate.data.count + ' x ' + aggregate.label + ' (' + width + '%)</td><td class="' + csscls('value') + '">' +
'<div class="' + csscls('measure') +'">' +
'<span class="' + csscls('value') + '" style="width:' + width + '%"></span>' +
'<span class="' + csscls('label') + '">' + formatDuration(aggregate.data.duration) + '</span>' +
'</div></td></tr>');
});
this.$el.append('<li/>').find('li:last').append(aggregateTable);
}
});
}
@@ -515,6 +558,13 @@ if (typeof(PhpDebugBar) == 'undefined') {
}
});
}
if (e.stack_trace) {
e.stack_trace.split("\n").forEach(function(trace) {
var $traceLine = $('<div />');
$('<span />').addClass(csscls('filename')).text(trace).appendTo($traceLine);
$traceLine.appendTo(li);
});
}
}});
this.$list.$el.appendTo(this.$el);

View File

@@ -18,7 +18,7 @@
this.$list = new PhpDebugBar.Widgets.ListWidget({ itemRenderer: function(li, tpl) {
$('<span />').addClass(csscls('name')).text(tpl.name).appendTo(li);
if (typeof tpl.xdebug_link !== 'undefined') {
if (typeof tpl.xdebug_link !== 'undefined' && tpl.xdebug_link !== null) {
if (tpl.xdebug_link.ajax) {
$('<a title="' + tpl.xdebug_link.url + '"></a>').on('click', function () {
$.ajax(tpl.xdebug_link.url);