PageSourceSearch

https://zimra.co.zw/media/vendor/debugbar/widgets.js

js zimra.co.zw collected 2026-09-24 18:11:30 UTC 38,032 bytes, 960 lines download raw bytes

1if (typeof(PhpDebugBar) == 'undefined') {
2    // namespace
3    var PhpDebugBar = {};
4    PhpDebugBar.$ = jQuery;
5}
6
7(function($) {
8
9    /**
10     * @namespace
11     */
12    PhpDebugBar.Widgets = {};
13
14    var csscls = PhpDebugBar.utils.makecsscls('phpdebugbar-widgets-');
15
16    /**
17     * Replaces spaces with &nbsp; and line breaks with <br>
18     *
19     * @param {String} text
20     * @return {String}
21     */
22    var htmlize = PhpDebugBar.Widgets.htmlize = function(text) {
23        return text.replace(/\n/g, '<br>').replace(/\s/g, "&nbsp;")
24    };
25
26    /**
27     * Returns a string representation of value, using JSON.stringify
28     * if it's an object.
29     *
30     * @param {Object} value
31     * @param {Boolean} prettify Uses htmlize() if true
32     * @return {String}
33     */
34    var renderValue = PhpDebugBar.Widgets.renderValue = function(value, prettify) {
35        if (typeof(value) !== 'string') {
36            if (prettify) {
37                return htmlize(JSON.stringify(value, undefined, 2));
38            }
39            return JSON.stringify(value);
40        }
41        return value;
42    };
43
44    /**
45     * Highlights a block of code
46     *
47     * @param  {String} code
48     * @param  {String} lang
49     * @return {String}
50     */
51    var highlight = PhpDebugBar.Widgets.highlight = function(code, lang) {
52        if (typeof(code) === 'string') {
53            if (typeof(hljs) === 'undefined') {
54                return htmlize(code);
55            }
56            if (lang && hljs.getLanguage(lang)) {
57                return hljs.highlight(code, {language: lang}).value;
58            }
59            return hljs.highlightAuto(code).value;
60        }
61
62        if (typeof(hljs) === 'object') {
63            code.each(function(i, e) { hljs.highlightElement(e); });
64        }
65        return code;
66    };
67
68    /**
69     * Creates a <pre> element with a block of code
70     *
71     * @param  {String} code
72     * @param  {String} lang
73     * @param  {Number} [firstLineNumber] If provided, shows line numbers beginning with the given value.
74     * @param  {Number} [highlightedLine] If provided, the given line number will be highlighted.
75     * @return {String}
76     */
77    var createCodeBlock = PhpDebugBar.Widgets.createCodeBlock = function(code, lang, firstLineNumber, highlightedLine) {
78        var pre = $('<pre />').addClass(csscls('code-block'));
79        // Add a newline to prevent <code> element from vertically collapsing too far if the last
80        // code line was empty: that creates problems with the horizontal scrollbar being
81        // incorrectly positioned - most noticeable when line numbers are shown.
82        var codeElement = $('<code />').text(code + '\n').appendTo(pre);
83
84        // Format the code
85        if (lang) {
86            codeElement.addClass("language-" + lang);
87        }
88        highlight(codeElement).removeClass('hljs');
89
90        // Show line numbers in a list
91        if (!isNaN(parseFloat(firstLineNumber))) {
92            var lineCount = code.split('\n').length;
93            var $lineNumbers = $('<ul />').prependTo(pre);
94            pre.children().addClass(csscls('numbered-code'));
95            for (var i = firstLineNumber; i < firstLineNumber + lineCount; i++) {
96                var li = $('<li />').text(i).appendTo($lineNumbers);
97
98                // Add a span with a special class if we are supposed to highlight a line.
99                if (highlightedLine === i) {
100                    li.addClass(csscls('highlighted-line')).append('<span>&nbsp;</span>');
101                }
102            }
103        }
104
105        return pre;
106    };
107
108    var getDictValue = PhpDebugBar.utils.getDictValue = function(dict, key, default_value) {
109        var d = dict, parts = key.split('.');
110        for (var i = 0; i < parts.length; i++) {
111            if (!d[parts[i]]) {
112                return default_value;
113            }
114            d = d[parts[i]];
115        }
116        return d;
117    }
118
119    // ------------------------------------------------------------------
120    // Generic widgets
121    // ------------------------------------------------------------------
122
123    /**
124     * Displays array element in a <ul> list
125     *
126     * Options:
127     *  - data
128     *  - itemRenderer: a function used to render list items (optional)
129     */
130    var ListWidget = PhpDebugBar.Widgets.ListWidget = PhpDebugBar.Widget.extend({
131
132        tagName: 'ul',
133
134        className: csscls('list'),
135
136        initialize: function(options) {
137            if (!options['itemRenderer']) {
138                options['itemRenderer'] = this.itemRenderer;
139            }
140            this.set(options);
141        },
142
143        render: function() {
144            this.bindAttr(['itemRenderer', 'data'], function() {
145                this.$el.empty();
146                if (!this.has('data')) {
147                    return;
148                }
149
150                var data = this.get('data');
151                for (var i = 0; i < data.length; i++) {
152                    var li = $('<li />').addClass(csscls('list-item')).appendTo(this.$el);
153                    this.get('itemRenderer')(li, data[i]);
154                }
155            });
156        },
157
158        /**
159         * Renders the content of a <li> element
160         *
161         * @param {jQuery} li The <li> element as a jQuery Object
162         * @param {Object} value An item from the data array
163         */
164        itemRenderer: function(li, value) {
165            li.html(renderValue(value));
166        }
167
168    });
169
170    // ------------------------------------------------------------------
171
172    /**
173     * Displays object property/value paris in a <dl> list
174     *
175     * Options:
176     *  - data
177     *  - itemRenderer: a function used to render list items (optional)
178     */
179    var KVListWidget = PhpDebugBar.Widgets.KVListWidget = ListWidget.extend({
180
181        tagName: 'dl',
182
183        className: csscls('kvlist'),
184
185        render: function() {
186            this.bindAttr(['itemRenderer', 'data'], function() {
187                this.$el.empty();
188                if (!this.has('data')) {
189                    return;
190                }
191
192                var self = this;
193                $.each(this.get('data'), function(key, value) {
194                    var dt = $('<dt />').addClass(csscls('key')).appendTo(self.$el);
195                    var dd = $('<dd />').addClass(csscls('value')).appendTo(self.$el);
196                    self.get('itemRenderer')(dt, dd, key, value);
197                });
198            });
199        },
200
201        /**
202         * Renders the content of the <dt> and <dd> elements
203         *
204         * @param {jQuery} dt The <dt> element as a jQuery Object
205         * @param {jQuery} dd The <dd> element as a jQuery Object
206         * @param {String} key Property name
207         * @param {Object} value Property value
208         */
209        itemRenderer: function(dt, dd, key, value) {
210            dt.text(key);
211            dd.html(htmlize(value));
212        }
213
214    });
215
216    // ------------------------------------------------------------------
217
218    /**
219     * An extension of KVListWidget where the data represents a list
220     * of variables
221     *
222     * Options:
223     *  - data
224     */
225    var VariableListWidget = PhpDebugBar.Widgets.VariableListWidget = KVListWidget.extend({
226
227        className: csscls('kvlist varlist'),
228
229        itemRenderer: function(dt, dd, key, value) {
230            $('<span />').attr('title', key).text(key).appendTo(dt);
231
232            var v = value && value.value || value;
233            if (v && v.length > 100) {
234                v = v.substr(0, 100) + "...";
235            }
236            var prettyVal = null;
237            dd.text(v).click(function() {
238                if (dd.hasClass(csscls('pretty'))) {
239                    dd.text(v).removeClass(csscls('pretty'));
240                } else {
241                    prettyVal = prettyVal || createCodeBlock(value);
242                    dd.addClass(csscls('pretty')).empty().append(prettyVal);
243                }
244            });
245        }
246
247    });
248
249    // ------------------------------------------------------------------
250
251    /**
252     * An extension of KVListWidget where the data represents a list
253     * of variables whose contents are HTML; this is useful for showing
254     * variable output from VarDumper's HtmlDumper.
255     *
256     * Options:
257     *  - data
258     */
259    var HtmlVariableListWidget = PhpDebugBar.Widgets.HtmlVariableListWidget = KVListWidget.extend({
260
261        className: csscls('kvlist htmlvarlist'),
262
263        itemRenderer: function(dt, dd, key, value) {
264            $('<span />').attr('title', $('<i />').html(key ?? '').text()).html(key ?? '').appendTo(dt);
265            dd.html(value && value.value || value);
266
267            if (value && value.xdebug_link) {
268                var header = $('<span />').addClass(csscls('filename')).text(value.xdebug_link.filename + ( value.xdebug_link.line ? "#" + value.xdebug_link.line : ''));
269                if (value.xdebug_link) {
270                    if (value.xdebug_link.ajax) {
271                        $('<a title="' + value.xdebug_link.url + '"></a>').on('click', function () {
272                            $.ajax(value.xdebug_link.url);
273                        }).addClass(csscls('editor-link')).appendTo(header);
274                    } else {
275                        $('<a href="' + value.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
276                    }
277                }
278                header.appendTo(dd);
279            }
280        }
281
282    });
283
284    // ------------------------------------------------------------------
285
286    /**
287     * Displays array element in a <table> list, columns keys map
288     * useful for showing a multiple values table
289     *
290     * Options:
291     *  - data
292     *  - key_map: list of keys to be displayed with an optional label
293     *             example: {key1: label1, key2: label2} or [key1, key2]
294     */
295    var TableVariableListWidget = PhpDebugBar.Widgets.TableVariableListWidget =  PhpDebugBar.Widget.extend({
296
297        tagName: 'div',
298
299        className: csscls('tablevarlist'),
300
301        render: function() {
302            this.bindAttr('data', function(data) {
303                this.$el.empty();
304
305                if (!this.has('data')) {
306                    return;
307                }
308
309                this.$table = $('<table />').addClass(csscls('tablevar')).appendTo(this.$el);
310                var $header = $('<tr />').addClass(csscls('header')).append('<td />').appendTo(this.$table);
311                var key_map = data.key_map || {value: 'Value'};
312
313                if (Array.isArray(key_map)) {
314                    key_map = Object.fromEntries(key_map.map(k => [k, null]));
315                }
316
317                $.each(key_map, function(key, label) {
318                    var colTitle = $('<td />').text(label ?? key).appendTo($header);
319
320                    if (data.badges && data.badges[key]) {
321                        $('<span />').text(data.badges[key]).addClass(csscls('badge')).appendTo(colTitle);
322                    }
323                });
324
325                var self = this;
326                $.each(data.data, function(key, values) {
327                    var $tr = $('<tr />').addClass(csscls('item')).appendTo(self.$table);
328                    $('<td />').addClass(csscls('key')).text(key).appendTo($tr);
329
330                    if (typeof values !== 'object' || values === null) {
331                        $('<td />').addClass(csscls('value')).text(values ?? '').appendTo($tr);
332                        return;
333                    }
334
335                    $.each(key_map, function(key) {
336                        $('<td />').addClass(csscls('value')).text(values[key] ?? '').appendTo($tr);
337                    });
338
339                    if (values.xdebug_link) {
340                        var filename = $('<span />').addClass(csscls('filename'))
341                            .text(values.xdebug_link.filename + ( values.xdebug_link.line ? "#" + values.xdebug_link.line : ''))
342                            .appendTo($('<td />').addClass(csscls('editor')).appendTo($tr));
343                        if (values.xdebug_link.ajax) {
344                            $('<a title="' + values.xdebug_link.url + '"></a>').on('click', function () {
345                                $.ajax(values.xdebug_link.url);
346                            }).addClass(csscls('editor-link')).appendTo(filename);
347                        } else {
348                            $('<a href="' + values.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(filename);
349                        }
350
351                        if (!data.xdebug_link) {
352                            data.xdebug_link = true;
353                            $header.append($('<td />'));
354                        }
355                    }
356                });
357
358                if (!data.summary) return;
359
360                var $tr = $('<tr />').addClass(csscls('summary')).appendTo(self.$table);
361                $('<td />').addClass(csscls('key')).appendTo($tr);
362
363                if (typeof data.summary !== 'object' || data.summary === null) {
364                    $('<td />').addClass(csscls('value')).text(data.summary ?? '').appendTo($tr);
365                } else {
366                    $.each(key_map, function(key) {
367                        $('<td />').addClass(csscls('value')).text(data.summary[key] ?? '').appendTo($tr);
368                    });
369                }
370
371                if (data.xdebug_link) {
372                    $('<td />').appendTo($tr);
373                }
374            });
375        }
376    });
377
378    // ------------------------------------------------------------------
379
380    /**
381     * Iframe widget
382     *
383     * Options:
384     *  - data
385     */
386    var IFrameWidget = PhpDebugBar.Widgets.IFrameWidget = PhpDebugBar.Widget.extend({
387
388        tagName: 'iframe',
389
390        className: csscls('iframe'),
391
392        render: function() {
393            this.$el.attr({
394                seamless: "seamless",
395                border: "0",
396                width: "100%",
397                height: "100%"
398            });
399            this.bindAttr('data', function(url) { this.$el.attr('src', url); });
400        }
401
402    });
403
404
405    // ------------------------------------------------------------------
406    // Collector specific widgets
407    // ------------------------------------------------------------------
408
409    /**
410     * Widget for the MessagesCollector
411     *
412     * Uses ListWidget under the hood
413     *
414     * Options:
415     *  - data
416     */
417    var MessagesWidget = PhpDebugBar.Widgets.MessagesWidget = PhpDebugBar.Widget.extend({
418
419        className: csscls('messages'),
420
421        render: function() {
422            var self = this;
423
424            this.$list = new ListWidget({ itemRenderer: function(li, value) {
425                if (value.message_html) {
426                    var val = $('<span />').addClass(csscls('value')).html(value.message_html).appendTo(l
426i);
427                } else {
428                    var m = value.message;
429                    if (m.length > 100) {
430                        m = m.substr(0, 100) + "...";
431                    }
432
433                    var val = $('<span />').addClass(csscls('value')).text(m).appendTo(li);
434                    if (!value.is_string || value.message.length > 100) {
435                        var prettyVal = value.message;
436                        if (!value.is_string) {
437                            prettyVal = null;
438                        }
439                        li.css('cursor', 'pointer').click(function () {
440                            if (window.getSelection().type == "Range") {
441                                return''
442                            }
443                            if (val.hasClass(csscls('pretty'))) {
444                                val.text(m).removeClass(csscls('pretty'));
445                            } else {
446                                prettyVal = prettyVal || createCodeBlock(value.message, 'php');
447                                val.addClass(csscls('pretty')).empty().append(prettyVal);
448                            }
449                        });
450                    }
451                }
452                if (value.xdebug_link) {
453                    var header = $('<span />').addClass(csscls('filename')).text(value.xdebug_link.filename + ( value.xdebug_link.line ? "#" + value.xdebug_link.line : ''));
454                    if (value.xdebug_link) {
455                        if (value.xdebug_link.ajax) {
456                            $('<a title="' + value.xdebug_link.url + '"></a>').on('click', function () {
457                                $.ajax(value.xdebug_link.url);
458                            }).addClass(csscls('editor-link')).appendTo(header);
459                        } else {
460                            $('<a href="' + value.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
461                        }
462                    }
463                    header.prependTo(li);
464                }
465                if (value.collector) {
466                    $('<span />').addClass(csscls('collector')).text(value.collector).prependTo(li);
467                }
468                if (value.label) {
469                    val.addClass(csscls(value.label));
470                    $('<span />').addClass(csscls('label')).text(value.label).prependTo(li);
471                }
472            }});
473
474            this.$list.$el.appendTo(this.$el);
475            this.$toolbar = $('<div><i class="phpdebugbar-fa phpdebugbar-fa-search"></i></div>').addClass(csscls('toolbar')).appendTo(this.$el);
476
477            $('<input type="text" name="search" aria-label="Search" placeholder="Search" />')
478                .on('change', function() { self.set('search', this.value); })
479                .appendTo(this.$toolbar);
480
481            this.bindAttr('data', function(data) {
482                this.set({excludelabel: [], excludecollector: [], search: ''});
483                this.$toolbar.find(csscls('.filter')).remove();
484
485                var labels = [], collectors = [], self = this,
486                    createFilterItem = function (type, value) {
487                        $('<a />')
488                            .addClass(csscls('filter')).addClass(csscls(type))
489                            .text(value).attr('rel', value)
490                            .on('click', function() { self.onFilterClick(this, type); })
491                            .appendTo(self.$toolbar)
492                    };
493
494                data.forEach(function (item) {
495                    if (!labels.includes(item.label || 'none')) {
496                        labels.push(item.label || 'none');
497                    }
498
499                    if (!collectors.includes(item.collector || 'none')) {
500                        collectors.push(item.collector || 'none');
501                    }
502                });
503
504                if (labels.length > 1) {
505                    labels.forEach(label => createFilterItem('label', label));
506                }
507
508                if (collectors.length === 1) {
509                    return;
510                }
511
512                $('<a />').addClass(csscls('filter')).css('visibility', 'hidden').appendTo(self.$toolbar);
513                collectors.forEach(collector => createFilterItem('collector', collector));
514            });
515
516            this.bindAttr(['excludelabel', 'excludecollector', 'search'], function() {
517                var excludelabel = this.get('excludelabel') || [],
518                    excludecollector = this.get('excludecollector') || [],
519                    search = this.get('search'),
520                    caseless = false,
521                    fdata = [];
522
523                if (search && search === search.toLowerCase()) {
524                    caseless = true;
525                }
526
527                this.get('data').forEach(function (item) {
528                    var message = caseless ? item.message.toLowerCase() : item.message;
529
530                    if (
531                        !excludelabel.includes(item.label || undefined) &&
532                        !excludecollector.includes(item.collector || undefined) &&
533                        (!search || message.indexOf(search) > -1)
534                    ) {
535                        fdata.push(item);
536                    }
537                });
538
539                this.$list.set('data', fdata);
540            });
541        },
542
543        onFilterClick: function(el, type) {
544            $(el).toggleClass(csscls('excluded'));
545
546            var excluded = [];
547            this.$toolbar.find(csscls('.filter') + csscls('.excluded') + csscls('.' + type)).each(function() {
548                excluded.push(this.rel === 'none' || !this.rel ? undefined : this.rel);
549            });
550
551            this.set('exclude' + type, excluded);
552        }
553
554    });
555
556    // ------------------------------------------------------------------
557
558    /**
559     * Widget for the TimeDataCollector
560     *
561     * Options:
562     *  - data
563     */
564    var TimelineWidget = PhpDebugBar.Widgets.TimelineWidget = PhpDebugBar.Widget.extend({
565
566        tagName: 'ul',
567
568        className: csscls('timeline'),
569
570        render: function() {
571            this.bindAttr('data', function(data) {
572
573                // ported from php DataFormatter
574                var formatDuration = function(seconds) {
575                    if (seconds < 0.001)
576                        return (seconds * 1000000).toFixed() + 'μs';
577                    else if (seconds < 0.1)
578                        return (seconds * 1000).toFixed(2) + 'ms';
579                    else if (seconds < 1)
580                        return (seconds * 1000).toFixed() + 'ms';
581                    return (seconds).toFixed(2) +  's';
582                };
583
584                // ported from php DataFormatter
585                var formatBytes = function formatBytes(size) {
586                    if (size === 0 || size === null) {
587                        return '0B';
588                    }
589
590                    var sign = size < 0 ? '-' : '',
591                        size = Math.abs(size),
592                        base = Math.log(size) / Math.log(1024),
593                        suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
594                    return sign + (Math.round(Math.pow(1024, base - Math.floor(base)) * 100) / 100) + suffixes[Math.floor(base)];
595                }
596
597                this.$el.empty();
598                if (data.measures) {
599                    var aggregate = {};
600
601                    for (var i = 0; i < data.measures.length; i++) {
602                        var measure = data.measures[i];
603                        var group = measure.group || measure.label;
604
605                        if(!aggregate[group]) {
606                            aggregate[group] = { count: 0, duration: 0, memory : 0 };
607                        }
608
609                        aggregate[group]['count'] += 1;
610                        aggregate[group]['duration'] += measure.duration;
611                        aggregate[group]['memory'] += (measure.memory || 0);
612
613                        var m = $('<div />').addClass(csscls('measure')),
614                            li = $('<li />'),
615                            left = (measure.relative_start * 100 / data.duration).toFixed(2),
616                            width = Math.min((measure.duration * 100 / data.duration).toFixed(2), 100 - left);
617
618                        m.append($('<span />').addClass(csscls('value')).css({
619                            left: left + "%",
620                            width: width + "%"
621                        }));
622                        m.append($('<span />').addClass(csscls('label'))
623                            .text(measure.label.replace(/\s+/g, ' ') + ( measure.duration ? " (" + measure.duration_str +(measure.memory ? '/' + measure.memory_str: '') + ")" : "")));
624
625                        if (measure.collector) {
626                            $('<span />').addClass(csscls('collector')).text(measure.collector).appendTo(m);
627                        }
628
629                        m.appendTo(li);
630                        this.$el.append(li);
631
632                        if (measure.params && !$.isEmptyObject(measure.params)) {
633                            var table = $('<table><tr><th colspan="2">Params</th></tr></table>').hide().addClass(csscls('params')).appendTo(li);
634                            for (var key in measure.params) {
635                                if (typeof measure.params[key] !== 'function') {
636                                    table.append('<tr><td class="' + csscls('name') + '">' + key + '</td><td class="' + csscls('value') +
637                                    '"><pre><code>' + measure.params[key] + '</code></pre></td></tr>');
638                                }
639                            }
640                            li.css('cursor', 'pointer').click(function() {
641                                if (window.getSelection().type == "Range") {
642                                    return '';
643                                }
644                                var table = $(this).find('table');
645                                if (table.is(':visible')) {
646                                    table.hide();
647                                } else {
648                                    table.show();
649                                }
650                            }).on('click', '.sf-dump', function(event) {
651                                event.stopPropagation();
652                            });
653                        }
654                    }
655
656                    // convert to array and sort by duration
657                    aggregate = $.map(aggregate, function(data, label) {
658                       return {
659                           label: label,
660                           data: data
661                       }
662                    }).sort(function(a, b) {
663                        return b.data.duration - a.data.duration
664                    });
665
666                    // build table and add
667                    var aggregateTable = $('<table></table>').addClass(csscls('params'));
668                    $.each(aggregate, function(i, aggregate) {
669                        width = Math.min((aggregate.data.duration * 100 / data.duration).toFixed(2), 100);
670
671                        aggregateTable.append('<tr><td class="' + csscls('name') + '">' +
672                            aggregate.data.count + ' x ' + $('<i />').text(aggregate.label.replace(/\s+/g, ' ')).html() + ' (' + width + '%)</td><td class="' + csscls('value') + '">' +
673                            '<div class="' + csscls('measure') +'">' +
674                                '<span class="' + csscls('value') + '"></span>' +
675                                '<span class="' + csscls('label') + '">' + formatDuration(aggregate.data.duration) + (aggregate.data.memory ? '/' + formatBytes(aggregate.data.memory) : '') + '</span>' +
676                            '</div></td></tr>');
677                        aggregateTable.find('span.' + csscls('value') + ':last').css({width: width + "%" });
678                    });
679
680                    this.$el.append('<li/>').find('li:last').append(aggregateTable);
681                }
682            });
683        }
684
685    });
686
687    // ------------------------------------------------------------------
688
689    /**
690     * Widget for the displaying exceptions
691     *
692     * Options:
693     *  - data
694     */
695    var ExceptionsWidget = PhpDebugBar.Widgets.ExceptionsWidget = PhpDebugBar.Widget.extend({
696
697        className: csscls('exceptions'),
698
699        render: function() {
700            this.$list = new ListWidget({ itemRenderer: function(li, e) {
701                $('<span />').addClass(csscls('message')).text(e.message).appendTo(li);
702                if (e.file) {
703                    var header = $('<span />').addClass(csscls('filename')).text(e.file + "#" + e.line);
704                    if (e.xdebug_link) {
705                        if (e.xdebug_link.ajax) {
706                            $('<a title="' + e.xdebug_link.url + '"></a>').on('click', function () {
707                                fetch(e.xdebug_link.url);
708                            }).addClass(csscls('editor-link')).appendTo(header);
709                        } else {
710                            $('<a href="' + e.xdebug_link.url + '"></a>').addClass(csscls('editor-link')).appendTo(header);
711                        }
712                    }
713                    header.appendTo(li);
714                }
715                if (e.type) {
716                    $('<span />').addClass(csscls('type')).text(e.type).appendTo(li);
717                }
718                if (e.surrounding_lines) {
719                    var startLine = (e.line - 3) <= 0 ? 1 : e.line - 3;
720                    var pre = createCodeBlock(e.surrounding_lines.join(""), 'php', startLine, e.line).addClass(csscls('file')).appendTo(li);
721                    if (!e.stack_trace_html) {
722                        // This click event makes the var-dumper hard to use.
723                        li.click(function () {
724                            if (pre.is(':visible')) {
725                                pre.hide();
726                            } else {
727                                pre.show();
728                            }
729                        });
730                    }
731                }
732                if (e.stack_trace_html) {
733                    var $trace = $('<span />').addClass(csscls('filename')).html(e.stack_trace_html);
734                    $trace.find('samp[data-depth="1"]').removeClass('sf-dump-expanded').addClass('sf-dump-compact').parent()
735                        .find('>.sf-dump-note').html((_, t) => t.replace(/^array:/, '<span class="sf-dump-key">Stack Trace:</span> ') + ' files');
736                    $trace.appendTo(li);
737                } else if (e.stack_trace) {
738                    e.stack_trace.split("\n").forEach(function (trace) {
739                        var $traceLine = $('<div />');
740                        $('<span />').addClass(csscls('filename')).text(trace).appendTo($traceLine);
741                        $traceLine.appendTo(li);
742                    });
743                }
744            }});
745            this.$list.$el.appendTo(this.$el);
746
747            this.bindAttr('data', function(data) {
748                this.$list.set('data', data);
749                if (data.length == 1) {
750                    this.$list.$el.children().first().find(csscls('.file')).show();
751                }
752            });
753
754        }
755
756    });
757
758    /**
759     * Displays datasets in a table
760     *
761     */
762    var DatasetWidget = PhpDebugBar.Widgets.DatasetWidget = PhpDebugBar.Widget.extend({
763
764        initialize: function(options) {
765            if (!options['itemRenderer']) {
766                options['itemRenderer'] = this.itemRenderer;
767            }
768            this.set(options);
769            this.set('autoshow', null);
770            this.set('id', null);
771            this.set('sort', localStorage.getItem('debugbar-history-sort') || 'asc');
772            this.$el.addClass(csscls('dataset-history'))
773
774            this.renderHead();
775        },
776
777        renderHead: function() {
778            this.$el.empty();
779            this.$actions = $('<div />').addClass(csscls('dataset-actions')).appendTo(this.$el);
780
781            var self = this;
782            var debugbar = self.get('debugbar');
783
784            this.$autoshow = $('<input type=checkbox>')
785                .on('click', function() {
786                    if (debugbar.ajaxHandler) {
787                        debugbar.ajaxHandler.setAutoShow($(this).is(':checked'));
788                    }
789                    if (debugbar.controls['__settings']) {
790                        debugbar.controls['__settings'].get('widget').set('autoshow', this.autoShow);
791                    }
792                });
793
794            $('<label>Autoshow</label>')
795                .append(this.$autoshow)
796                .appendTo(this.$actions)
797
798
799            this.$clearbtn = $('<a>Clear</a>')
800                .appendTo(this.$actions)
801                .on('click', function() {
802                    self.$table.empty();
803                });
804
805            this.$showBtn = $('<a>Show all</a>')
806                .appendTo(this.$actions)
807                .on('click', function() {
808                    self.searchInput.val(null);
809                    self.methodInput.val(null);
810                    self.set('search', null);
811                    self.set('method', null);
812                });
813
814            this.methodInput = $('<select name="method" style="width:100px"><option>
814(method)</option><option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option></select>')
815                .on('change', function() { self.set('method', this.value)})
816                .appendTo(this.$actions)
817
818            this.searchInput = $('<input type="text" name="search" aria-label="Search" placeholder="Search" />')
819                .on('input', function() { self.set('search', this.value); })
820                .appendTo(this.$actions);
821
822
823            this.$table = $('<tbody />');
824
825            $('<table/>')
826                .append($('<thead/>')
827                    .append($('<tr/>')
828                        .append($('<th></th>').css('width', '30px'))
829                        .append($('<th>Date ↕</th>').css('width', '175px').click(function() {
830                            self.set('sort', self.get('sort') === 'asc' ? 'desc' : 'asc')
831                            localStorage.setItem('debugbar-history-sort', self.get('sort'))
832                        }))
833                        .append($('<th>Method</th>').css('width', '80px'))
834                        .append($('<th>URL</th>'))
835                        .append($('<th width="40%">Data</th>')))
836                )
837                .append(this.$table)
838                .appendTo(this.$el);
839
840
841        },
842
843        renderDatasets: function() {
844            this.$table.empty();
845            var self = this;
846            $.each(this.get('data'), function(key, data) {
847                if (!data.__meta) {
848                    return;
849                }
850
851                self.get('itemRenderer')(self, data);
852            });
853        },
854
855        render: function() {
856            this.bindAttr('data', function() {
857                if (this.get('autoshow') === null && this.get('debugbar').ajaxHandler) {
858                    this.set('autoshow', this.get('debugbar').ajaxHandler.autoShow);
859                }
860
861                if (!this.has('data')) {
862                    return;
863                }
864
865                // Render the latest item
866                var datasets = this.get('data');
867                var data = datasets[Object.keys(datasets)[Object.keys(datasets).length - 1]]
868                if (!data.__meta) {
869                    return;
870                }
871
872                this.get('itemRenderer')(this, data);
873           });
874            this.bindAttr(['itemRenderer', 'search', 'method', 'sort'], function() {
875                this.renderDatasets();
876            })
877            this.bindAttr('autoshow', function() {
878                var autoshow = this.get('autoshow');
879                this.$autoshow.prop('checked', autoshow);
880            })
881            this.bindAttr('id', function() {
882                var id = this.get('id');
883                this.$table.find('.' + csscls('active')).removeClass(csscls('active'));
884                this.$table.find('tr[data-id=' + id+']').addClass(csscls('active'));
885            })
886        },
887
888        /**
889         * Renders the content of a dataset item
890         *
891         * @param {Object} value An item from the data array
892         */
893        itemRenderer: function(widget, data) {
894            var meta = data.__meta;
895
896            var $badges = $('<td />');
897            var tr = $('<tr />');
898            if (widget.get('sort') === 'asc') {
899                tr.appendTo(widget.$table);
900            } else {
901                tr.prependTo(widget.$table);
902            }
903
904            var clickHandler = function() {
905                var debugbar = widget.get('debugbar');
906                debugbar.showDataSet(meta.id, debugbar.datesetTitleFormater.format('', data, meta.suffix, meta.nb));
907                widget.$table.find('.' + csscls('active')).removeClass(csscls('active'));
908                tr.addClass(csscls('active'));
909
910                if ($(this).data('tab')) {
911                    debugbar.showTab($(this).data('tab'));
912                }
913            }
914
915            tr.attr('data-id', meta['id'])
916                .append($('<td>#' + meta['nb'] + '</td>').click(clickHandler))
917                .append($('<td>' + meta['datetime'] + '</td>').click(clickHandler))
918                .append($('<td>' + meta['method'] + '</td>').click(clickHandler))
919                .append($('<td />').append(meta['uri'] + (meta['suffix'] ? ' ' + meta['suffix'] : '')).click(clickHandler))
920                .css('cursor', 'pointer')
921                .addClass(csscls('table-row'))
922
923            var debugbar = widget.get('debugbar');
924            $.each(debugbar.dataMap, function(key, def) {
925                var d = getDictValue(data, def[0], def[1]);
926                if (key.indexOf(':') != -1) {
927                    key = key.split(':');
928                    if (key[1] === 'badge' && d > 0) {
929                        var control = debugbar.getControl(key[0]);
930                        var $a = $('<a>').attr('title', control.get('title')).data('tab', key[0]);
931                        if (control.$icon) {
932                            $a.append(debugbar.getControl(key[0]).$icon.clone());
933                        }
934                        if (control.$badge) {
935                            $a.append(debugbar.getControl(key[0]).$badge.clone().css('display', 'inline-block').text(d));
936                        }
937                        $a.appendTo($badges).click(clickHandler);
938                    } else if (key[1] === 'tooltip') {
939                        debugbar.getControl(key[0]).set('tooltip', d);
940                    }
941                }
942            });
943            tr.append($badges);
944
945            if (debugbar.activeDatasetId === meta['id']) {
946                tr.addClass(csscls('active'));
947            }
948
949            var search = widget.get('search');
950            var method = widget.get('method');
951            if ((search && meta['uri'].indexOf(search) == -1) || (method && meta['method'] !== method)) {
952                tr.hide();
953            }
954        }
955
956    });
957
958
959
960})(PhpDebugBar.$);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.