| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- if (typeof(PhpDebugBar) == 'undefined') {
- // namespace
- var PhpDebugBar = {};
- PhpDebugBar.$ = jQuery;
- }
- (function($) {
- /**
- * @namespace
- */
- PhpDebugBar.Widgets = {};
- var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
- /**
- * Replaces spaces with and line breaks with <br>
- *
- * @param {String} text
- * @return {String}
- */
- var htmlize = PhpDebugBar.Widgets.htmlize = function(text) {
- return text.replace(/\n/g, '<br>').replace(/\s/g, " ")
- };
- /**
- * Returns a string representation of value, using JSON.stringify
- * if it's an object.
- *
- * @param {Object} value
- * @param {Boolean} prettify Uses htmlize() if true
- * @return {String}
- */
- var renderValue = PhpDebugBar.Widgets.renderValue = function(value, prettify) {
- if (typeof(value) !== 'string') {
- if (prettify) {
- return htmlize(JSON.stringify(value, undefined, 2));
- }
- return JSON.stringify(value);
- }
- return value;
- };
- /**
- * Highlights a block of code
- *
- * @param {String} code
- * @param {String} lang
- * @return {String}
- */
- var highlight = PhpDebugBar.Widgets.highlight = function(code, lang) {
- if (typeof(code) === 'string') {
- if (typeof(hljs) === 'undefined') {
- return htmlize(code);
- }
- if (lang) {
- return hljs.highlight(lang, code).value;
- }
- return hljs.highlightAuto(code).value;
- }
- if (typeof(hljs) === 'object') {
- code.each(function(i, e) { hljs.highlightBlock(e); });
- }
- return code;
- };
- /**
- * Creates a <pre> element with a block of code
- *
- * @param {String} code
- * @param {String} lang
- * @param {Number} [firstLineNumber] If provided, shows line numbers beginning with the given value.
- * @param {Number} [highlightedLine] If provided, the given line number will be highlighted.
- * @return {String}
- */
- var createCodeBlock = PhpDebugBar.Widgets.createCodeBlock = function(code, lang, firstLineNumber, highlightedLine) {
- var pre = $('<pre />').addClass(csscls('code-block'));
- // Add a newline to prevent <code> element from vertically collapsing too far if the last
- // code line was empty: that creates problems with the horizontal scrollbar being
- // incorrectly positioned - most noticeable when line numbers are shown.
- var codeElement = $('<code />').text(code + '\n').appendTo(pre);
- // Add a span with a special class if we are supposed to highlight a line. highlight.js will
- // still correctly format code even with existing markup in it.
- if ($.isNumeric(highlightedLine)) {
- if ($.isNumeric(firstLineNumber)) {
- highlightedLine = highlightedLine - firstLineNumber + 1;
- }
- codeElement.html(function (index, html) {
- var currentLine = 1;
- return html.replace(/^.*$/gm, function(line) {
- if (currentLine++ == highlightedLine) {
- return '<span class="' + csscls('highlighted-line') + '">' + line + '</span>';
- } else {
- return line;
- }
- });
- });
- }
- // Format the code
- if (lang) {
- pre.addClass("language-" + lang);
- }
- highlight(pre);
- // Show line numbers in a list
- if ($.isNumeric(firstLineNumber)) {
- var lineCount = code.split('\n').length;
- var $lineNumbers = $('<ul />').prependTo(pre);
- pre.children().addClass(csscls('numbered-code'));
- for (var i = firstLineNumber; i < firstLineNumber + lineCount; i++) {
- $('<li />').text(i).appendTo($lineNumbers);
- }
- }
- return pre;
- };
- // ------------------------------------------------------------------
- // Generic widgets
- // ------------------------------------------------------------------
- /**
- * Displays array element in a <ul> list
- *
- * Options:
- * - data
- * - itemRenderer: a function used to render list items (optional)
- */
- var ListWidget = PhpDebugBar.Widgets.ListWidget = PhpDebugBar.Widget.extend({
- tagName: 'ul',
- className: csscls('list'),
- initialize: function(options) {
- if (!options['itemRenderer']) {
- options['itemRenderer'] = this.itemRenderer;
- }
- this.set(options);
- },
- render: function() {
- this.bindAttr(['itemRenderer', 'data'], function() {
- this.$el.empty();
- if (!this.has('data')) {
- return;
- }
- var data = this.get('data');
- for (var i = 0; i < data.length; i++) {
- var li = $('<li />').addClass(csscls('list-item')).appendTo(this.$el);
- this.get('itemRenderer')(li, data[i]);
- }
- });
- },
- /**
- * Renders the content of a <li> element
- *
- * @param {jQuery} li The <li> element as a jQuery Object
- * @param {Object} value An item from the data array
- */
- itemRenderer: function(li, value) {
- li.html(renderValue(value));
- }
- });
- // ------------------------------------------------------------------
- /**
- * Displays object property/value paris in a <dl> list
- *
- * Options:
- * - data
- * - itemRenderer: a function used to render list items (optional)
- */
- var KVListWidget = PhpDebugBar.Widgets.KVListWidget = ListWidget.extend({
- tagName: 'dl',
- className: csscls('kvlist'),
- render: function() {
- this.bindAttr(['itemRenderer', 'data'], function() {
- this.$el.empty();
- if (!this.has('data')) {
- return;
- }
- var self = this;
- $.each(this.get('data'), function(key, value) {
- var dt = $('<dt />').addClass(csscls('key')).appendTo(self.$el);
- var dd = $('<dd />').addClass(csscls('value')).appendTo(self.$el);
- self.get('itemRenderer')(dt, dd, key, value);
- });
- });
- },
- /**
- * Renders the content of the <dt> and <dd> elements
- *
- * @param {jQuery} dt The <dt> element as a jQuery Object
- * @param {jQuery} dd The <dd> element as a jQuery Object
- * @param {String} key Property name
- * @param {Object} value Property value
- */
- itemRenderer: function(dt, dd, key, value) {
- dt.text(key);
- dd.html(htmlize(value));
- }
- });
- // ------------------------------------------------------------------
- /**
- * An extension of KVListWidget where the data represents a list
- * of variables
- *
- * Options:
- * - data
- */
- var VariableListWidget = PhpDebugBar.Widgets.VariableListWidget = KVListWidget.extend({
- className: csscls('kvlist varlist'),
- itemRenderer: function(dt, dd, key, value) {
- $('<span />').attr('title', key).text(key).appendTo(dt);
- var v = value;
- if (v && v.length > 100) {
- v = v.substr(0, 100) + "...";
- }
- var prettyVal = null;
- dd.text(v).click(function() {
- if (dd.hasClass(csscls('pretty'))) {
- dd.text(v).removeClass(csscls('pretty'));
- } else {
- prettyVal = prettyVal || createCodeBlock(value);
- dd.addClass(csscls('pretty')).empty().append(prettyVal);
- }
- });
- }
- });
- // ------------------------------------------------------------------
- /**
- * An extension of KVListWidget where the data represents a list
- * of variables whose contents are HTML; this is useful for showing
- * variable output from VarDumper's HtmlDumper.
- *
- * Options:
- * - data
- */
- var HtmlVariableListWidget = PhpDebugBar.Widgets.HtmlVariableListWidget = KVListWidget.extend({
- className: csscls('kvlist htmlvarlist'),
- itemRenderer: function(dt, dd, key, value) {
- $('<span />').attr('title', key).text(key).appendTo(dt);
- dd.html(value);
- }
- });
- // ------------------------------------------------------------------
- /**
- * Iframe widget
- *
- * Options:
- * - data
- */
- var IFrameWidget = PhpDebugBar.Widgets.IFrameWidget = PhpDebugBar.Widget.extend({
- tagName: 'iframe',
- className: csscls('iframe'),
- render: function() {
- this.$el.attr({
- seamless: "seamless",
- border: "0",
- width: "100%",
- height: "100%"
- });
- this.bindAttr('data', function(url) { this.$el.attr('src', url); });
- }
- });
- // ------------------------------------------------------------------
- // Collector specific widgets
- // ------------------------------------------------------------------
- /**
- * Widget for the MessagesCollector
- *
- * Uses ListWidget under the hood
- *
- * Options:
- * - data
- */
- var MessagesWidget = PhpDebugBar.Widgets.MessagesWidget = PhpDebugBar.Widget.extend({
- className: csscls('messages'),
- render: function() {
- var self = this;
- this.$list = new ListWidget({ itemRenderer: function(li, value) {
- if (value.message_html) {
- var val = $('<span />').addClass(csscls('value')).html(value.message_html).appendTo(li);
- } else {
- var m = value.message;
- if (m.length > 100) {
- m = m.substr(0, 100) + "...";
- }
- var val = $('<span />').addClass(csscls('value')).text(m).appendTo(li);
- if (!value.is_string || value.message.length > 100) {
- var prettyVal = value.message;
- if (!value.is_string) {
- prettyVal = null;
- }
- li.css('cursor', 'pointer').click(function () {
- if (val.hasClass(csscls('pretty'))) {
- val.text(m).removeClass(csscls('pretty'));
- } else {
- prettyVal = prettyVal || createCodeBlock(value.message, 'php');
- val.addClass(csscls('pretty')).empty().append(prettyVal);
- }
- });
- }
- }
- if (value.collector) {
- $('<span />').addClass(csscls('collector')).text(value.collector).prependTo(li);
- }
- if (value.label) {
- val.addClass(csscls(value.label));
- $('<span />').addClass(csscls('label')).text(value.label).prependTo(li);
- }
- }});
- this.$list.$el.appendTo(this.$el);
- this.$toolbar = $('<div><i class="phpdebugbar-fa phpdebugbar-fa-search"></i></div>').addClass(csscls('toolbar')).appendTo(this.$el);
- $('<input type="text" />')
- .on('change', function() { self.set('search', this.value); })
- .appendTo(this.$toolbar);
- this.bindAttr('data', function(data) {
- this.set({ exclude: [], search: '' });
- this.$toolbar.find(csscls('.filter')).remove();
- var filters = [], self = this;
- for (var i = 0; i < data.length; i++) {
- if (!data[i].label || $.inArray(data[i].label, filters) > -1) {
- continue;
- }
- filters.push(data[i].label);
- $('<a />')
- .addClass(csscls('filter'))
- .text(data[i].label)
- .attr('rel', data[i].label)
- .on('click', function() { self.onFilterClick(this); })
- .appendTo(this.$toolbar);
- }
- });
- this.bindAttr(['exclude', 'search'], function() {
- var data = this.get('data'),
- exclude = this.get('exclude'),
- search = this.get('search'),
- caseless = false,
- fdata = [];
- if (search && search === search.toLowerCase()) {
- caseless = true;
- }
- for (var i = 0; i < data.length; i++) {
- var message = caseless ? data[i].message.toLowerCase() : data[i].message;
- if ((!data[i].label || $.inArray(data[i].label, exclude) === -1) && (!search || message.indexOf(search) > -1)) {
- fdata.push(data[i]);
- }
- }
- this.$list.set('data', fdata);
- });
- },
- onFilterClick: function(el) {
- $(el).toggleClass(csscls('excluded'));
- var excludedLabels = [];
- this.$toolbar.find(csscls('.filter') + csscls('.excluded')).each(function() {
- excludedLabels.push(this.rel);
- });
- this.set('exclude', excludedLabels);
- }
- });
- // ------------------------------------------------------------------
- /**
- * Widget for the TimeDataCollector
- *
- * Options:
- * - data
- */
- var TimelineWidget = PhpDebugBar.Widgets.TimelineWidget = PhpDebugBar.Widget.extend({
- tagName: 'ul',
- className: csscls('timeline'),
- 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),
- width = Math.min((measure.duration * 100 / data.duration).toFixed(2), 100 - left);
- m.append($('<span />').addClass(csscls('value')).css({
- left: left + "%",
- width: width + "%"
- }));
- m.append($('<span />').addClass(csscls('label')).text(measure.label + " (" + measure.duration_str + ")"));
- if (measure.collector) {
- $('<span />').addClass(csscls('collector')).text(measure.collector).appendTo(m);
- }
- m.appendTo(li);
- this.$el.append(li);
- if (measure.params && !$.isEmptyObject(measure.params)) {
- var table = $('<table><tr><th colspan="2">Params</th></tr></table>').addClass(csscls('params')).appendTo(li);
- for (var key in measure.params) {
- if (typeof measure.params[key] !== 'function') {
- table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
- '"><pre><code>' + measure.params[key] + '</code></pre></td></tr>');
- }
- }
- li.css('cursor', 'pointer').click(function() {
- var table = $(this).find('table');
- if (table.is(':visible')) {
- table.hide();
- } else {
- table.show();
- }
- });
- }
- }
- // 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);
- }
- });
- }
- });
- // ------------------------------------------------------------------
- /**
- * Widget for the displaying exceptions
- *
- * Options:
- * - data
- */
- var ExceptionsWidget = PhpDebugBar.Widgets.ExceptionsWidget = PhpDebugBar.Widget.extend({
- className: csscls('exceptions'),
- render: function() {
- this.$list = new ListWidget({ itemRenderer: function(li, e) {
- $('<span />').addClass(csscls('message')).text(e.message).appendTo(li);
- if (e.file) {
- var header = $('<span />').addClass(csscls('filename')).text(e.file + "#" + e.line);
- if (e.xdebug_link) {
- if (e.xdebug_link.ajax) {
- $('<a title="' + e.xdebug_link.url + '"></a>').on('click', function () {
- $.ajax(e.xdebug_link.url);
- }).addClass(csscls('editor-link')).appendTo(header);
- } else {
- $('<a href="' + e.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
- }
- }
- header.appendTo(li);
- }
- if (e.type) {
- $('<span />').addClass(csscls('type')).text(e.type).appendTo(li);
- }
- if (e.surrounding_lines) {
- var pre = createCodeBlock(e.surrounding_lines.join(""), 'php').addClass(csscls('file')).appendTo(li);
- if (!e.stack_trace_html) {
- // This click event makes the var-dumper hard to use.
- li.click(function () {
- if (pre.is(':visible')) {
- pre.hide();
- } else {
- pre.show();
- }
- });
- }
- }
- if (e.stack_trace_html) {
- var $trace = $('<span />').addClass(csscls('filename')).html(e.stack_trace_html);
- $trace.appendTo(li);
- } else 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);
- this.bindAttr('data', function(data) {
- this.$list.set('data', data);
- if (data.length == 1) {
- this.$list.$el.children().first().find(csscls('.file')).show();
- }
- });
- }
- });
- })(PhpDebugBar.$);
|