initial commit

This commit is contained in:
boris authored and boris committed 2025-09-30 09:35:59 +01:00
1 parent a783a12c97
commit c7770ea03b
4695 files changed
+525784

No files matched your search

@@ -0,0 +1,88 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Bundle\WebProfilerBundle\Controller\ExceptionPanelController;
use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController;
use Symfony\Bundle\WebProfilerBundle\Controller\RouterController;
use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
use Symfony\Bundle\WebProfilerBundle\Csp\NonceGenerator;
use Symfony\Bundle\WebProfilerBundle\Profiler\CodeExtension;
use Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension;
use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
return static function (ContainerConfigurator $container) {
$container->services()
->set('web_profiler.controller.profiler', ProfilerController::class)
->public()
->args([
service('router')->nullOnInvalid(),
service('profiler')->nullOnInvalid(),
service('twig'),
param('data_collector.templates'),
service('web_profiler.csp.handler'),
param('kernel.project_dir'),
])
->set('web_profiler.controller.router', RouterController::class)
->public()
->args([
service('profiler')->nullOnInvalid(),
service('twig'),
service('router')->nullOnInvalid(),
null,
tagged_iterator('routing.expression_language_provider'),
])
->set('web_profiler.controller.exception_panel', ExceptionPanelController::class)
->public()
->args([
service('error_handler.error_renderer.html'),
service('profiler')->nullOnInvalid(),
])
->set('web_profiler.csp.handler', ContentSecurityPolicyHandler::class)
->args([
inline_service(NonceGenerator::class),
])
->set('twig.extension.webprofiler', WebProfilerExtension::class)
->args([
inline_service(HtmlDumper::class)
->args([null, param('kernel.charset'), HtmlDumper::DUMP_LIGHT_ARRAY])
->call('setDisplayOptions', [['maxStringLength' => 4096, 'fileLinkFormat' => service('debug.file_link_formatter')]]),
])
->tag('twig.extension')
->set('debug.file_link_formatter', FileLinkFormatter::class)
->args([
param('debug.file_link_format'),
service('request_stack')->ignoreOnInvalid(),
param('kernel.project_dir'),
'/_profiler/open?file=%%f&line=%%l#line%%l',
])
->set('debug.file_link_formatter.url_format', 'string')
->factory([FileLinkFormatter::class, 'generateUrlFormat'])
->args([
service('router'),
'_profiler_open_file',
'?file=%%f&line=%%l#line%%l',
])
->set('twig.extension.code', CodeExtension::class)
->args([service('debug.file_link_formatter'), param('kernel.project_dir'), param('kernel.charset')])
->tag('twig.extension')
;
};
@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use Symfony\Component\Routing\Loader\XmlFileLoader;
return function (RoutingConfigurator $routes): void {
foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT) as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) {
if (__DIR__ === dirname(realpath($trace['args'][3]))) {
trigger_deprecation('symfony/routing', '7.3', 'The "profiler.xml" routing configuration file is deprecated, import "profiler.php" instead.');
break;
}
}
}
$routes->add('_profiler_home', '/')
->controller('web_profiler.controller.profiler::homeAction')
;
$routes->add('_profiler_search', '/search')
->controller('web_profiler.controller.profiler::searchAction')
;
$routes->add('_profiler_search_bar', '/search_bar')
->controller('web_profiler.controller.profiler::searchBarAction')
;
$routes->add('_profiler_phpinfo', '/phpinfo')
->controller('web_profiler.controller.profiler::phpinfoAction')
;
$routes->add('_profiler_xdebug', '/xdebug')
->controller('web_profiler.controller.profiler::xdebugAction')
;
$routes->add('_profiler_font', '/font/{fontName}.woff2')
->controller('web_profiler.controller.profiler::fontAction')
;
$routes->add('_profiler_search_results', '/{token}/search/results')
->controller('web_profiler.controller.profiler::searchResultsAction')
;
$routes->add('_profiler_open_file', '/open')
->controller('web_profiler.controller.profiler::openAction')
;
$routes->add('_profiler', '/{token}')
->controller('web_profiler.controller.profiler::panelAction')
;
$routes->add('_profiler_router', '/{token}/router')
->controller('web_profiler.controller.router::panelAction')
;
$routes->add('_profiler_exception', '/{token}/exception')
->controller('web_profiler.controller.exception_panel::body')
;
$routes->add('_profiler_exception_css', '/{token}/exception.css')
->controller('web_profiler.controller.exception_panel::stylesheet')
;
};
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="profiler.php" type="php" />
</routes>
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use Symfony\Component\Routing\Loader\XmlFileLoader;
return function (RoutingConfigurator $routes): void {
foreach (debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT) as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof XmlFileLoader && 'doImport' === $trace['function']) {
if (__DIR__ === dirname(realpath($trace['args'][3]))) {
trigger_deprecation('symfony/routing', '7.3', 'The "wdt.xml" routing configuration file is deprecated, import "wdt.php" instead.');
break;
}
}
}
$routes->add('_wdt_stylesheet', '/styles')
->controller('web_profiler.controller.profiler::toolbarStylesheetAction')
;
$routes->add('_wdt', '/{token}')
->controller('web_profiler.controller.profiler::toolbarAction')
;
};
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="wdt.php" type="php" />
</routes>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://symfony.com/schema/dic/webprofiler"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema/dic/webprofiler"
elementFormDefault="qualified">
<xsd:element name="config" type="config" />
<xsd:complexType name="config">
<xsd:attribute name="toolbar" type="xsd:boolean" />
<xsd:sequence>
<xsd:element name="toolbar" type="toolbar" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="intercept-redirects" type="xsd:boolean" />
</xsd:complexType>
<xsd:complexType name="toolbar">
<xsd:attribute name="enabled" type="xsd:boolean" />
<xsd:attribute name="ajax-replace" type="xsd:boolean" />
</xsd:complexType>
</xsd:schema>
@@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
return static function (ContainerConfigurator $container) {
$container->services()
->set('web_profiler.debug_toolbar', WebDebugToolbarListener::class)
->args([
service('twig'),
param('web_profiler.debug_toolbar.intercept_redirects'),
param('web_profiler.debug_toolbar.mode'),
service('router')->ignoreOnInvalid(),
abstract_arg('paths that should be excluded from the AJAX requests shown in the toolbar'),
service('web_profiler.csp.handler'),
service('data_collector.dump')->ignoreOnInvalid(),
abstract_arg('whether to replace toolbar on AJAX requests or not'),
])
->tag('kernel.event_subscriber')
;
};
@@ -0,0 +1,6 @@
JetBrains Mono typeface (https://www.jetbrains.com/lp/mono/) is available
under the SIL Open Font License 1.1 and can be used free of charge, for both
commercial and non-commercial purposes. You do not need to give credit to
JetBrains, although we will appreciate it very much if you do.
Licence: https://github.com/JetBrains/JetBrainsMono/blob/master/OFL.txt
@@ -0,0 +1,35 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% set icon %}
{{ source('@WebProfiler/Icon/ajax.svg') }}
<span class="sf-toolbar-value sf-toolbar-ajax-request-counter">0</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<span class="sf-toolbar-header">
<b class="sf-toolbar-ajax-info"></b>
<b class="sf-toolbar-action">(<a class="sf-toolbar-ajax-clear" href="javascript:void(0);">Clear</a>)</b>
</span>
</div>
<div class="sf-toolbar-info-piece">
<table class="sf-toolbar-ajax-requests">
<thead>
<tr>
<th>#</th>
<th>Profile</th>
<th>Method</th>
<th>Type</th>
<th>Status</th>
<th>URL</th>
<th>Time</th>
</tr>
</thead>
<tbody class="sf-toolbar-ajax-request-list"></tbody>
</table>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }}
{% endblock %}
@@ -0,0 +1,178 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% if collector.totals.calls > 0 %}
{% set icon %}
{{ source('@WebProfiler/Icon/cache.svg') }}
<span class="sf-toolbar-value">{{ collector.totals.calls }}</span>
<span class="sf-toolbar-info-piece-additional-detail">
<span class="sf-toolbar-label">in</span>
<span class="sf-toolbar-value">{{ '%0.2f'|format(collector.totals.time * 1000) }}</span>
<span class="sf-toolbar-label">ms</span>
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Cache Calls</b>
<span>{{ collector.totals.calls }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Total time</b>
<span>{{ '%0.2f'|format(collector.totals.time * 1000) }} ms</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache hits</b>
<span>{{ collector.totals.hits }} / {{ collector.totals.reads }}{% if collector.totals.hit_read_ratio is not null %} ({{ collector.totals.hit_read_ratio }}%){% endif %}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Cache writes</b>
<span>{{ collector.totals.writes }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{ collector.totals.calls == 0 ? 'disabled' }}">
<span class="icon">
{{ source('@WebProfiler/Icon/cache.svg') }}
</span>
<strong>Cache</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Cache</h2>
{% if collector.totals.calls == 0 %}
<div class="empty empty-panel">
<p>No cache calls were made.</p>
</div>
{% else %}
{{ _self.render_metrics(collector.totals, true) }}
<h2>Pools</h2>
<div class="sf-tabs">
{# the empty merge is needed to turn the iterator into an array #}
{% set cache_pools_with_calls = collector.calls|filter(calls => calls|length > 0)|merge([]) %}
{% for name, calls in cache_pools_with_calls %}
<div class="tab {{ calls|length == 0 ? 'disabled' }}">
<h3 class="tab-title">{{ name }} <span class="badge">{{ collector.statistics[name].calls }}</span></h3>
<div class="tab-content">
<h4>Adapter</h4>
<div class="card">
{% if collector.adapters[name] is defined %}
<code>{{ collector.adapters[name] }}</code>
{% else %}
<span class="text-muted">Unable to get the adapter class.</span>
{% endif %}
</div>
{% if calls|length == 0 %}
<div class="empty">
<p>No calls were made for {{ name }} pool.</p>
</div>
{% else %}
<h4>Metrics</h4>
{{ _self.render_metrics(collector.statistics[name]) }}
<h4>Calls</h4>
<table>
<thead>
<tr>
<th>#</th>
<th>Time</th>
<th>Call</th>
<th>Hit</th>
</tr>
</thead>
<tbody>
{% for call in calls %}
<tr>
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
<td class="nowrap">{{ '%0.2f'|format((call.end - call.start) * 1000) }} ms</td>
<td class="nowrap">{{ call.name }}({{ call.namespace|default('') }})</td>
<td>{{ profiler_dump(call.value.result, maxDepth=2) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% if loop.last %}
<div class="tab">
<h3 class="tab-title">Pools without calls <span class="badge">{{ collector.calls|filter(calls => 0 == calls|length)|length }}</span></h3>
<div class="tab-content">
<table>
<thead>
<tr>
<th>Cache pools that received no calls</th>
</tr>
</thead>
<tbody>
{% for cache_pool in collector.calls|filter(calls => 0 == calls|length)|keys|sort %}
<tr><td>{{ cache_pool }}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock %}
{% macro render_metrics(pool, is_total = false) %}
<div class="metrics">
<div class="metric">
<span class="value">{{ pool.calls }}</span>
<span class="label">{{ is_total ? 'Total calls' : 'Calls' }}</span>
</div>
<div class="metric">
<span class="value">{{ '%0.2f'|format(pool.time * 1000) }} <span class="unit">ms</span></span>
<span class="label">{{ is_total ? 'Total time' : 'Time' }}</span>
</div>
<div class="metric-divider"></div>
<div class="metric-group">
<div class="metric">
<span class="value">{{ pool.reads }}</span>
<span class="label">{{ is_total ? 'Total reads' : 'Reads' }}</span>
</div>
<div class="metric">
<span class="value">{{ pool.writes }}</span>
<span class="label">{{ is_total ? 'Total writes' : 'Writes' }}</span>
</div>
<div class="metric">
<span class="value">{{ pool.deletes }}</span>
<span class="label">{{ is_total ? 'Total deletes' : 'Deletes' }}</span>
</div>
</div>
<div class="metric-divider"></div>
<div class="metric-group">
<div class="metric">
<span class="value">{{ pool.hits }}</span>
<span class="label">{{ is_total ? 'Total hits' : 'Hits' }}</span>
</div>
<div class="metric">
<span class="value">{{ pool.misses }}</span>
<span class="label">{{ is_total ? 'Total misses' : 'Misses' }}</span>
</div>
<div class="metric">
<span class="value">
{{ pool.hit_read_ratio ?? 0 }} <span class="unit">%</span>
</span>
<span class="label">Hits/reads</span>
</div>
</div>
</div>
{% endmacro %}
@@ -0,0 +1,249 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block menu %}
<span class="label">
<span class="icon">{{ source('@WebProfiler/Icon/command.svg') }}</span>
<strong>Console Command</strong>
</span>
{% endblock %}
{% block panel %}
<h2>
{% set command = collector.command %}
<a href="{{ command.file|file_link(command.line) }}">
{% if command.executor is defined %}
{{ command.executor|abbr_method }}
{% else %}
{{ command.class|abbr_class }}
{% endif %}
</a>
</h2>
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Command</h3>
<div class="tab-content">
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.duration }}</span>
<span class="label">Duration</span>
</div>
<div class="metric">
<span class="value">{{ collector.maxMemoryUsage }}</span>
<span class="label">Peak Memory Usage</span>
</div>
<div class="metric">
<span class="value">{{ collector.verbosityLevel }}</span>
<span class="label">Verbosity Level</span>
</div>
</div>
<div class="metrics">
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.signalable is not empty ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Signalable</span>
</div>
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.interactive ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Interactive</span>
</div>
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.validateInput ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Validate Input</span>
</div>
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.enabled ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Enabled</span>
</div>
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.visible ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Visible</span>
</div>
</div>
<h3>Arguments</h3>
{% if collector.arguments is empty %}
<div class="empty">
<p>No arguments were set</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.arguments, labels: ['Argument', 'Value'], maxDepth: 2 }, with_context=false) }}
{% endif %}
<h3>Options</h3>
{% if collector.options is empty %}
<div class="empty">
<p>No options were set</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.options, labels: ['Option', 'Value'], maxDepth: 2 }, with_context=false) }}
{% endif %}
{% if collector.interactive %}
<h3>Interactive Inputs</h3>
<p class="help">
The values which have been set interactively.
</p>
{% if collector.interactiveInputs is empty %}
<div class="empty">
<p>No inputs were set</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.interactiveInputs, labels: ['Input', 'Value'], maxDepth: 2 }, with_context=false) }}
{% endif %}
{% endif %}
<h3>Application inputs</h3>
{% if collector.applicationInputs is empty %}
<div class="empty">
<p>No application inputs are set</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.applicationInputs, labels: ['Input', 'Value'], maxDepth: 2 }, with_context=false) }}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Input / Output</h3>
<div class="tab-content">
<table>
<tr>
<td class="font-normal">Input</td>
<td class="font-normal">{{ profiler_dump(collector.input) }}</td>
</tr>
<tr>
<td class="font-normal">Output</td>
<td class="font-normal">{{ profiler_dump(collector.output) }}</td>
</tr>
</table>
</div>
</div>
<div class="tab">
<h3 class="tab-title">Helper Set</h3>
<div class="tab-content">
{% if collector.helperSet is empty %}
<div class="empty">
<p>No helpers</p>
</div>
{% else %}
<table class="{{ class|default('') }}">
<thead>
<tr>
<th scope="col">Helpers</th>
</tr>
</thead>
<tbody>
{% for helper in collector.helperSet|sort %}
<tr>
<td>{{ profiler_dump(helper) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
<div class="tab">
{% set request_collector = profile.collectors.request %}
<h3 class="tab-title">Server Parameters</h3>
<div class="tab-content">
<h3>Server Parameters</h3>
<h4>Defined in .env</h4>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: request_collector.dotenvvars }, with_context = false) }}
<h4>Defined as regular env variables</h4>
{% set requestserver = [] %}
{% for key, value in request_collector.requestserver|filter((_, key) => key not in request_collector.dotenvvars.keys) %}
{% set requestserver = requestserver|merge({(key): value}) %}
{% endfor %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: requestserver }, with_context = false) }}
</div>
</div>
{% if collector.signalable is not empty %}
<div class="tab">
<h3 class="tab-title">Signals</h3>
<div class="tab-content">
<h3>Subscribed signals</h3>
{{ collector.signalable|join(', ') }}
<h3>Handled signals</h3>
{% if collector.handledSignals is empty %}
<div class="empty">
<p>No signals handled</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th>Signal</th>
<th>Times handled</th>
<th>Total execution time</th>
<th>Memory peak</th>
</tr>
</thead>
<tbody>
{% for signal, data in collector.handledSignals %}
<tr>
<td>{{ signal }}</td>
<td>{{ data.handled }}</td>
<td>{{ data.duration }} ms</td>
<td>{{ data.memory }} MiB</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endif %}
{% if profile.parent %}
<div class="tab">
<h3 class="tab-title">Parent Command</h3>
<div class="tab-content">
<h3>
<a href="{{ path('_profiler', { token: profile.parent.token }) }}">Return to parent command</a>
<small>(token = {{ profile.parent.token }})</small>
</h3>
{{ profile.parent.url }}
</div>
</div>
{% endif %}
{% if profile.children|length %}
<div class="tab">
<h3 class="tab-title">Sub Commands <span class="badge">{{ profile.children|length }}</span></h3>
<div class="tab-content">
{% for child in profile.children %}
<h3>
{{ child.url }}
<small>(token = <a href="{{ path('_profiler', { token: child.token }) }}">{{ child.token }}</a>)</small>
</h3>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endblock %}
@@ -0,0 +1,300 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
.config-symfony-version-lts {
border: 0;
color: var(--color-muted);
font-size: 21px;
line-height: 33px;
}
.config-symfony-version-lts[title] {
text-decoration: none;
}
.config-symfony-version-status-badge {
background-color: var(--badge-background);
border-radius: 4px;
color: var(--badge-color);
display: inline-block;
font-size: 15px;
font-weight: bold;
margin: 10px 0 5px;
padding: 3px 7px;
white-space: nowrap;
}
.config-symfony-version-status-badge.status-success {
background-color: var(--badge-success-background);
color: var(--badge-success-color);
}
.config-symfony-version-status-badge.status-warning {
background-color: var(--badge-warning-background);
color: var(--badge-warning-color);
}
.config-symfony-version-status-badge.status-error {
background-color: var(--badge-danger-background);
color: var(--badge-danger-color);
}
.config-symfony-version-roadmap-link {
display: inline-block;
margin: 10px 5px 5px;
}
.config-symfony-eol {
margin-top: 5px;
}
</style>
{% endblock %}
{% block toolbar %}
{% if 'unknown' == collector.symfonyState %}
{% set block_status = '' %}
{% set symfony_version_status = 'Unable to retrieve information about the Symfony version.' %}
{% elseif 'eol' == collector.symfonyState %}
{% set block_status = 'red' %}
{% set symfony_version_status = 'This Symfony version will no longer receive security fixes.' %}
{% elseif 'eom' == collector.symfonyState %}
{% set block_status = 'yellow' %}
{% set symfony_version_status = 'This Symfony version will only receive security fixes.' %}
{% elseif 'dev' == collector.symfonyState %}
{% set block_status = 'yellow' %}
{% set symfony_version_status = 'This Symfony version is still in the development phase.' %}
{% else %}
{% set block_status = '' %}
{% set symfony_version_status = '' %}
{% endif %}
{% set icon %}
<span class="sf-toolbar-label">
{{ source('@WebProfiler/Icon/symfony.svg') }}
</span>
<span class="sf-toolbar-value">{{ collector.symfonyState is defined ? collector.symfonyversion : 'n/a' }}</span>
{% endset %}
{% set text %}
{% if symfony_version_status %}
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece">
<span>{{ symfony_version_status }}</span>
</div>
</div>
{% endif %}
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece">
<b>Profiler token</b>
<span>
{% if profiler_url %}
<a href="{{ profiler_url }}">{{ collector.token }}</a>
{% else %}
{{ collector.token }}
{% endif %}
</span>
</div>
{% if 'n/a' is not same as(collector.env) %}
<div class="sf-toolbar-info-piece">
<b>Environment</b>
<span>{{ collector.env }}</span>
</div>
{% endif %}
{% if 'n/a' is not same as(collector.debug) %}
<div class="sf-toolbar-info-piece">
<b>Debug</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.debug ? 'green' : 'red' }}">{{ collector.debug ? 'enabled' : 'disabled' }}</span>
</div>
{% endif %}
</div>
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece sf-toolbar-info-php">
<b>PHP version</b>
<span{% if collector.phpversionextra %} title="{{ collector.phpversion ~ collector.phpversionextra }}"{% endif %}>
{{ collector.phpversion }}
&nbsp; <a href="{{ path('_profiler_phpinfo') }}">View phpinfo()</a>
</span>
</div>
<div class="sf-toolbar-info-piece sf-toolbar-info-php-ext">
<b>PHP Extensions</b>
{% if collector.hasXdebugInfo %}
<a href="{{ path('_profiler_xdebug') }}" title="View xdebug_info()">
{% endif %}
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.hasXdebug ? 'green' : 'gray' }}">Xdebug {{ collector.hasXdebug ? '✓' : '✗' }}</span>
{% if collector.hasXdebugInfo %}
</a>
{% endif %}
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.hasapcu ? 'green' : 'gray' }}">APCu {{ collector.hasapcu ? '✓' : '✗' }}</span>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.haszendopcache ? 'green' : 'red' }}">OPcache {{ collector.haszendopcache ? '✓' : '✗' }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>PHP SAPI</b>
<span>{{ collector.sapiName }}</span>
</div>
</div>
<div class="sf-toolbar-info-group">
{% if collector.symfonyversion is defined %}
<div class="sf-toolbar-info-piece">
<b>Resources</b>
<span>
<a href="https://symfony.com/doc/{{ collector.symfonyversion }}/index.html" rel="help">
Read Symfony {{ collector.symfonyversion }} Docs
</a>
</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Help</b>
<span>
<a href="https://symfony.com/support">
Symfony Support Channels
</a>
</span>
</div>
{% endif %}
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: true, name: 'config', status: block_status, additional_classes: 'sf-toolbar-block-right' }) }}
{% endblock %}
{% block menu %}
<span class="label label-status-{{ collector.symfonyState == 'eol' ? 'red' : collector.symfonyState in ['eom', 'dev'] ? 'yellow' }}">
<span class="icon">{{ source('@WebProfiler/Icon/config.svg') }}</span>
<strong>Configuration</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Symfony Configuration</h2>
<div class="metrics">
<div class="metric">
<span class="value">
{{ collector.symfonyversion }}
{% if collector.symfonylts %}
<abbr class="config-symfony-version-lts" title="This is a Long-Term Support version">(LTS)</abbr>
{% endif %}
</span>
<span class="label">Symfony version</span>
</div>
{% if 'n/a' is not same as(collector.env) %}
<div class="metric">
<span class="value">{{ collector.env }}</span>
<span class="label">Environment</span>
</div>
{% endif %}
{% if 'n/a' is not same as(collector.debug) %}
<div class="metric">
<span class="value">{{ collector.debug ? 'enabled' : 'disabled' }}</span>
<span class="label">Debug</span>
</div>
{% endif %}
</div>
{% set symfony_status = { dev: 'In Development', stable: 'Maintained', eom: 'Security Fixes Only', eol: 'Unmaintained' } %}
{% set symfony_status_class = { dev: 'warning', stable: 'success', eom: 'warning', eol: 'error' } %}
<div class="metrics">
<div class="metric-group">
<div class="metric">
<span class="value">
<span class="config-symfony-version-status-badge status-{{ symfony_status_class[collector.symfonystate] }}">{{ symfony_status[collector.symfonystate]|upper }}</span>
</span>
<span class="label">Your Symfony version status</span>
</div>
{% if collector.symfonylts %}
<div class="metric">
<span class="value config-symfony-eol">
{{ collector.symfonyeom }}
</span>
<span class="label">Bug fixes {{ collector.symfonystate in ['eom', 'eol'] ? 'ended on' : 'until' }}</span>
</div>
{% endif %}
<div class="metric">
<span class="value config-symfony-eol">
{{ collector.symfonyeol }}
</span>
<span class="label">
{{ collector.symfonylts ? 'Security fixes' : 'Bug fixes and security fixes' }}
{{ 'eol' == collector.symfonystate ? 'ended on' : 'until' }}</span>
</div>
</div>
</div>
<a class="config-symfony-version-roadmap-link" href="https://symfony.com/releases/{{ collector.symfonyminorversion }}">View Symfony {{ collector.symfonyversion }} release details</a>
<h2>PHP Configuration</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.phpversion }}{% if collector.phpversionextra %} <span class="unit">{{ collector.phpversionextra }}</span>{% endif %}</span>
<span class="label">PHP version</span>
</div>
<div class="metric">
<span class="value">{{ collector.phparchitecture }} <span class="unit">bits</span></span>
<span class="label">Architecture</span>
</div>
<div class="metric">
<span class="value">{{ collector.phpintllocale }}</span>
<span class="label">Intl locale</span>
</div>
<div class="metric">
<span class="value">{{ collector.phptimezone }}</span>
<span class="label">Timezone</span>
</div>
</div>
<div class="metrics">
<div class="metric-group">
<div class="metric">
<span class="value value-is-icon {{ not collector.haszendopcache ? 'value-shows-no-color' }}" title="{{ collector.zendopcachestatus|default('') }}">{{ source('@WebProfiler/Icon/' ~ (collector.haszendopcache ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">OPcache</span>
</div>
<div class="metric">
<span class="value value-is-icon {{ not collector.hasapcu ? 'value-shows-no-color' }}" title="{{ collector.apcustatus|default('') }}">{{ source('@WebProfiler/Icon/' ~ (collector.hasapcu ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">APCu</span>
</div>
<div class="metric">
<span class="value value-is-icon {{ not collector.hasxdebug ? 'value-shows-no-color' }}" title="{{ collector.xdebugstatus|default('') }}">{{ source('@WebProfiler/Icon/' ~ (collector.hasxdebug ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Xdebug</span>
</div>
</div>
</div>
<p>
<a href="{{ path('_profiler_phpinfo') }}">View full PHP configuration</a>
</p>
{% if collector.bundles %}
<h2>Enabled Bundles <small>({{ collector.bundles|length }})</small></h2>
<table>
<thead>
<tr>
<th class="key">Name</th>
<th>Class</th>
</tr>
</thead>
<tbody>
{% for name in collector.bundles|keys|sort %}
<tr>
<th scope="row" class="font-normal">{{ name }}</th>
<td class="font-normal">{{ profiler_dump(collector.bundles[name]) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endblock %}
@@ -0,0 +1,126 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block menu %}
<span class="label">
<span class="icon">{{ source('@WebProfiler/Icon/event.svg') }}</span>
<strong>Events</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Dispatched Events</h2>
<div class="sf-tabs">
{% for dispatcherName, dispatcherData in collector.data %}
<div class="tab">
<h3 class="tab-title">{{ dispatcherName }}</h3>
<div class="tab-content">
{% if dispatcherData['called_listeners'] is empty %}
<div class="empty empty-panel">
<p>No events have been recorded. Check that debugging is enabled in the kernel.</p>
</div>
{% else %}
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Called Listeners <span class="badge">{{ dispatcherData['called_listeners']|length }}</span></h3>
<div class="tab-content">
{{ _self.render_table(dispatcherData['called_listeners']) }}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Not Called Listeners <span class="badge">{{ dispatcherData['not_called_listeners']|length }}</span></h3>
<div class="tab-content">
{% if dispatcherData['not_called_listeners'] is empty %}
<div class="empty">
<p>
<strong>There are no uncalled listeners</strong>.
</p>
<p>
All listeners were called or an error occurred
when trying to collect uncalled listeners (in which case check the
logs to get more information).
</p>
</div>
{% else %}
{{ _self.render_table(dispatcherData['not_called_listeners']) }}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Orphaned Events <span class="badge">{{ dispatcherData['orphaned_events']|length }}</span></h3>
<div class="tab-content">
{% if dispatcherData['orphaned_events'] is empty %}
<div class="empty">
<p>
<strong>There are no orphaned events</strong>.
</p>
<p>
All dispatched events were handled or an error occurred
when trying to collect orphaned events (in which case check the
logs to get more information).
</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th>Event</th>
</tr>
</thead>
<tbody>
{% for event in dispatcherData['orphaned_events'] %}
<tr>
<td class="font-normal">{{ event }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endblock %}
{% macro render_table(listeners) %}
<table>
<thead>
<tr>
<th class="text-right">Priority</th>
<th>Listener</th>
</tr>
</thead>
{% set previous_event = (listeners|first).event %}
{% for listener in listeners %}
{% if loop.first or listener.event != previous_event %}
{% if not loop.first %}
</tbody>
{% endif %}
<tbody>
<tr>
<th colspan="2" class="colored font-normal">{{ listener.event }}</th>
</tr>
{% set previous_event = listener.event %}
{% endif %}
<tr>
<td class="text-right nowrap">{{ listener.priority|default('-') }}</td>
<td class="font-normal">{{ profiler_dump(listener.stub) }}</td>
</tr>
{% if loop.last %}
</tbody>
{% endif %}
{% endfor %}
</table>
{% endmacro %}
@@ -0,0 +1,32 @@
.container {
max-width: none;
margin: 0;
padding: 0;
}
.container .container {
padding: 0;
}
.exception-summary {
background: var(--base-0);
border: var(--border);
box-shadow: 0 0 1px rgba(128, 128, 128, .2);
margin: 1em 0;
padding: 10px;
}
.exception-summary.exception-without-message {
display: none;
}
.exception-message {
color: var(--color-error);
}
.exception-metadata,
.exception-illustration {
display: none;
}
.exception-message-wrapper .container {
min-height: unset;
}
@@ -0,0 +1,45 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{% if collector.hasexception %}
<style>
{{ render(controller('web_profiler.controller.exception_panel::stylesheet', { token: token })) }}
{{ include('@WebProfiler/Collector/exception.css.twig') }}
</style>
{% endif %}
{{ parent() }}
{% endblock %}
{% block menu %}
<span class="label {{ collector.hasexception ? 'label-status-error' : 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/exception.svg') }}</span>
<strong>Exception</strong>
{% if collector.hasexception %}
<span class="count">
<span>1</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
{# these styles are needed to override some styles from Exception page, which wasn't
updated yet to the new style of the Symfony Profiler #}
<style>
.tab-navigation li { background: none; border: 0; font-size: 14px; }
.tab-navigation li.active { border-radius: 6px; }
.tab-navigation li.active .badge { background-color: var(--selected-badge-background); color: var(--selected-badge-color); }
</style>
<h2>Exceptions</h2>
{% if not collector.hasexception %}
<div class="empty empty-panel">
<p>No exception was thrown and caught.</p>
</div>
{% else %}
<div class="sf-reset">
{{ render(controller('web_profiler.controller.exception_panel::body', { token: token })) }}
</div>
{% endif %}
{% endblock %}
@@ -0,0 +1,706 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% if collector.data.nb_errors > 0 or collector.data.forms|length %}
{% set status_color = collector.data.nb_errors ? 'red' %}
{% set icon %}
{{ source('@WebProfiler/Icon/form.svg') }}
<span class="sf-toolbar-value">
{{ collector.data.nb_errors ?: collector.data.forms|length }}
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Number of forms</b>
<span class="sf-toolbar-status">{{ collector.data.forms|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Number of errors</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.data.nb_errors > 0 ? 'red' }}">{{ collector.data.nb_errors }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label label-status-{{ collector.data.nb_errors ? 'error' }} {{ collector.data.forms is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/form.svg') }}</span>
<strong>Forms</strong>
{% if collector.data.nb_errors > 0 %}
<span class="count">
<span>{{ collector.data.nb_errors }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
<style>
.form-type-class {
font-size: var(--font-size-body);
display: flex;
margin: 0 0 15px;
}
.form-type-class-label {
margin-right: 4px;
}
.form-type-class pre.sf-dump {
font-family: var(--font-family-system) !important;
font-size: var(--font-size-body) !important;
margin-left: 5px;
}
.form-type-class .sf-dump .sf-dump-str {
color: var(--color-link) !important;
text-decoration: underline;
}
.form-type-class .sf-dump .sf-dump-str:hover {
text-decoration: none;
}
#tree-menu {
float: left;
padding-right: 10px;
width: 220px;
}
#tree-menu ul {
list-style: none;
margin: 0;
padding-left: 0;
}
#tree-menu li {
margin: 0;
padding: 0;
width: 100%;
}
#tree-menu .empty {
border: 0;
box-shadow: none !important;
padding: 0;
}
#tree-details-container {
border-left: 1px solid var(--table-border-color);
margin-left: 230px;
padding-left: 20px;
}
.tree-details {
padding-bottom: 40px;
}
.tree-details h3 {
font-size: 18px;
position: relative;
}
.toggle-icon {
display: inline-block;
}
.closed .toggle-icon, .closed.toggle-icon {
}
.tree .tree-inner {
cursor: pointer;
padding: 5px 7px 5px 22px;
position: relative;
overflow: hidden;
text-overflow: ellipsis;
}
.tree .toggle-button {
width: 16px;
height: 16px;
margin-left: -18px;
display: inline-grid;
place-content: center;
background: none;
border: none;
transition: transform 200ms;
}
.tree .toggle-button.closed svg {
transform: rotate(-90deg);
}
.tree .toggle-button svg {
transform: rotate(0deg);
width: 16px;
height: 16px;
}
.tree .toggle-icon.empty {
width: 5px;
height: 5px;
position: absolute;
top: 50%;
margin-top: -2px;
margin-left: -13px;
}
.tree .tree-inner {
border-radius: 4px;
}
.tree ul ul .tree-inner {
padding-left: 32px;
}
.tree ul ul ul .tree-inner {
padding-left: 48px;
}
.tree ul ul ul ul .tree-inner {
padding-left: 64px;
}
.tree ul ul ul ul ul .tree-inner {
padding-left: 72px;
}
.tree .tree-inner:hover {
background: var(--gray-200);
}
.tree .tree-inner.active, .tree .tree-inner.active:hover {
background: var(--tree-active-background);
font-weight: bold;
}
.tree-details .toggle-icon {
width: 16px;
height: 16px;
/* vertically center the button */
position: absolute;
top: 50%;
margin-top: -9px;
margin-left: 6px;
}
.badge-error {
float: right;
background: var(--background-error);
color: #FFF;
padding: 1px 4px;
font-size: 10px;
font-weight: bold;
vertical-align: middle;
}
.has-error {
color: var(--color-error);
}
.errors h3 {
color: var(--color-error);
}
.errors th {
background: var(--background-error);
color: #FFF;
}
.errors .toggle-icon {
background-color: var(--background-error);
}
h3 a, h3 a:hover, h3 a:focus {
color: inherit;
text-decoration: inherit;
}
</style>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
window.addEventListener('DOMContentLoaded', () => {
new SymfonyProfilerFormPanel();
});
class SymfonyProfilerFormPanel {
#activeTreeItem;
#activeTreePanel;
#storage;
#storageKey = 'sf_toggle_data';
#togglerStates = {};
constructor() {
this.#storage = sessionStorage;
this.#initTrees();
this.#initTogglerButtons();
}
#initTrees() {
const treeItems = document.querySelectorAll('.tree .tree-inner');
treeItems.forEach((treeItem) => {
var targetId = treeItem.getAttribute('data-tab-target-id');
const target = document.getElementById(targetId);
if (!target) {
throw `Tab target ${targetId} does not exist`;
}
treeItem.addEventListener('click', (e) => {
this.#selectTreeItem(treeItem);
e.stopPropagation();
return false;
});
target.classList.add('hidden');
});
if (treeItems.length > 0) {
this.#selectTreeItem(treeItems[0]);
}
};
#selectTreeItem(treeItem) {
const treePanelId = treeItem.getAttribute('data-tab-target-id');
const treePanel = document.getElementById(treePanelId);
if (!treePanel) {
throw `The tree panel ${treePanelId} does not exist`;
}
if (this.#activeTreeItem) {
this.#activeTreeItem.classList.remove('active');
}
if (this.#activeTreePanel) {
this.#activeTreePanel.classList.add('hidden');
}
treeItem.classList.add('active');
treePanel.classList.remove('hidden');
this.#activeTreeItem = treeItem;
this.#activeTreePanel = treePanel;
}
#initTogglerButtons() {
this.#togglerStates = this.#getTogglerStates();
if (!this.#isObject(this.#togglerStates)) {
this.#togglerStates = {};
}
const buttons = document.querySelectorAll('.toggle-button');
buttons.forEach((button) => {
const targetId = button.getAttribute('data-toggle-target-id');
const target = document.getElementById(targetId);
if (!target) {
throw `Toggle target ${targetId} does not exist`;
}
// correct the initial state of the button
if (target.classList.contains('hidden')) {
button.classList.add('closed');
}
button.addEventListener('click', (e) => {
this.#toggleToggler(button);
e.stopPropagation();
return false;
});
if (this.#togglerStates.hasOwnProperty(targetId)) {
// open or collapse based on stored data
if (0 === this.#togglerStates[targetId]) {
this.#collapseToggler(button);
} else {
this.#expandToggler(button);
}
}
});
};
#isTogglerCollapsed(button) {
return button.classList.contains('closed');
}
#isTogglerExpanded(button) {
return !this.#isTogglerCollapsed(button);
}
#expandToggler(button) {
const targetId = button.getAttribute('data-toggle-target-id');
const target = document.getElementById(targetId);
if (!target) {
throw "Toggle target " + targetId + " does not exist";
}
if (this.#isTogglerCollapsed(button)) {
button.classList.remove('closed');
target.classList.remove('hidden');
this.#togglerStates[targetId] = 1;
this.#saveTogglerStates();
}
}
#collapseToggler(button) {
const targetId = button.getAttribute('data-toggle-target-id');
const target = document.getElementById(targetId);
if (!target) {
throw `Toggle target ${targetId} does not exist`;
}
if (this.#isTogglerExpanded(button)) {
button.classList.add('closed');
target.classList.add('hidden');
this.#togglerStates[targetId] = 0;
this.#saveTogglerStates();
}
}
#toggleToggler(button) {
if (button.classList.contains('closed')) {
this.#expandToggler(button);
} else {
this.#collapseToggler(button);
}
}
#saveTogglerStates() {
this.#storage.setItem(this.#storageKey, JSON.stringify(this.#togglerStates));
}
#getTogglerStates() {
const data = this.#storage.getItem(this.#storageKey);
if (null !== data) {
try {
return JSON.parse(data);
} catch(e) {
}
}
return {};
}
#isObject(variable) {
return null !== variable && 'object' === typeof variable && !Array.isArray(variable);
}
}
</script>
{% endblock %}
{% block panel %}
<h2>Forms</h2>
{% if collector.data.forms|length %}
<div id="tree-menu" class="tree">
<ul>
{% for formName, formData in collector.data.forms %}
{{ _self.form_tree_entry(formName, formData, true) }}
{% endfor %}
</ul>
</div>
<div id="tree-details-container">
{% for formName, formData in collector.data.forms %}
{{ _self.form_tree_details(formName, formData, collector.data.forms_by_hash, loop.first) }}
{% endfor %}
</div>
{% else %}
<div class="empty empty-panel">
<p>No forms were submitted.</p>
</div>
{% endif %}
{% endblock %}
{% macro form_tree_entry(name, data, is_root) %}
{% set has_error = data.errors is defined and data.errors|length > 0 %}
<li>
<div class="tree-inner" data-tab-target-id="{{ data.id }}-details" title="{{ name|default('(no name)') }}">
{% if has_error %}
<div class="badge-error">{{ data.errors|length }}</div>
{% endif %}
{% if data.children is not empty %}
<button class="toggle-button" data-toggle-target-id="{{ data.id }}-children">
{{ source('@WebProfiler/Icon/chevron-down.svg') }}
</button>
{% else %}
<div class="toggle-icon empty"></div>
{% endif %}
<span {% if has_error or data.has_children_error|default(false) %}class="has-error"{% endif %}>
{{ name|default('(no name)') }}
</span>
</div>
{% if data.children is not empty %}
<ul id="{{ data.id }}-children" {% if not is_root and not data.has_children_error|default(false) %}class="hidden"{% endif %}>
{% for childName, childData in data.children %}
{{ _self.form_tree_entry(childName, childData, false) }}
{% endfor %}
</ul>
{% endif %}
</li>
{% endmacro %}
{% macro form_tree_details(name, data, forms_by_hash, show) %}
<div class="tree-details{% if not show|default(false) %} hidden{% endif %}" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
<h2>{{ name|default('(no name)') }}</h2>
{% if data.type_class is defined %}
<div class="form-type-class">
<span class="form-type-class-label">Form type:</span>
{{ profiler_dump(data.type_class) }}
</div>
{% endif %}
{% set form_has_errors = data.errors ?? [] is not empty %}
<div class="sf-tabs">
<div class="tab {{ form_has_errors ? 'active' : 'disabled' }}">
<h3 class="tab-title">Errors</h3>
<div class="tab-content">
{{ _self.render_form_errors(data) }}
</div>
</div>
<div class="tab {{ not form_has_errors ? 'active' }}">
<h3 class="tab-title">Default Data</h3>
<div class="tab-content">
{{ _self.render_form_default_data(data) }}
</div>
</div>
<div class="tab {{ (data.submitted_data ?? []) is empty ? 'disabled' }}">
<h3 class="tab-title">Submitted Data</h3>
<div class="tab-content">
{{ _self.render_form_submitted_data(data) }}
</div>
</div>
<div class="tab {{ (data.passed_options ?? []) is empty ? 'disabled' }}">
<h3 class="tab-title">Passed Options</h3>
<div class="tab-content">
{{ _self.render_form_passed_options(data) }}
</div>
</div>
<div class="tab {{ (data.resolved_options ?? []) is empty ? 'disabled' }}">
<h3 class="tab-title">Resolved Options</h3>
<div class="tab-content">
{{ _self.render_form_resolved_options(data) }}
</div>
</div>
<div class="tab {{ (data.view_vars ?? []) is empty ? 'disabled' }}">
<h3 class="tab-title">View Vars</h3>
<div class="tab-content">
{{ _self.render_form_view_variables(data) }}
</div>
</div>
</div>
</div>
{% for childName, childData in data.children %}
{{ _self.form_tree_details(childName, childData, forms_by_hash) }}
{% endfor %}
{% endmacro %}
{% macro render_form_errors(data) %}
{% if data.errors is defined and data.errors|length > 0 %}
<div class="errors">
<table id="{{ data.id }}-errors">
<thead>
<tr>
<th>Message</th>
<th>Origin</th>
<th>Cause</th>
</tr>
</thead>
<tbody>
{% for error in data.errors %}
<tr>
<td>{{ error.message }}</td>
<td>
{% if error.origin is empty %}
<em>This form.</em>
{% elseif forms_by_hash[error.origin] is not defined %}
<em>Unknown.</em>
{% else %}
{{ forms_by_hash[error.origin].name }}
{% endif %}
</td>
<td>
{% if error.trace %}
<span class="newline">Caused by:</span>
{% for stacked in error.trace %}
{{ profiler_dump(stacked) }}
{% endfor %}
{% else %}
<em>Unknown.</em>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty">
<p>This form has no errors.</p>
</div>
{% endif %}
{% endmacro %}
{% macro render_form_default_data(data) %}
{% if data.default_data is defined %}
<table>
<thead>
<tr>
<th style="width: 180px">Property</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<th class="font-normal" scope="row">Model Format</th>
<td>
{% if data.default_data.model is defined %}
{{ profiler_dump(data.default_data.seek('model')) }}
{% else %}
<em class="font-normal text-muted">same as normalized format</em>
{% endif %}
</td>
</tr>
<tr>
<th class="font-normal" scope="row">Normalized Format</th>
<td>{{ profiler_dump(data.default_data.seek('norm')) }}</td>
</tr>
<tr>
<th class="font-normal" scope="row">View Format</th>
<td>
{% if data.default_data.view is defined %}
{{ profiler_dump(data.default_data.seek('view')) }}
{% else %}
<em class="font-normal text-muted">same as normalized format</em>
{% endif %}
</td>
</tr>
</tbody>
</table>
{% else %}
<div class="empty">
<p>This form has default data defined.</p>
</div>
{% endif %}
{% endmacro %}
{% macro render_form_submitted_data(data) %}
{% if data.submitted_data.norm is defined %}
<table>
<thead>
<tr>
<th style="width: 180px">Property</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<th class="font-normal" scope="row">View Format</th>
<td>
{% if data.submitted_data.view is defined %}
{{ profiler_dump(data.submitted_data.seek('view')) }}
{% else %}
<em class="font-normal text-muted">same as normalized format</em>
{% endif %}
</td>
</tr>
<tr>
<th class="font-normal" scope="row">Normalized Format</th>
<td>{{ profiler_dump(data.submitted_data.seek('norm')) }}</td>
</tr>
<tr>
<th class="font-normal" scope="row">Model Format</th>
<td>
{% if data.submitted_data.model is defined %}
{{ profiler_dump(data.submitted_data.seek('model')) }}
{% else %}
<em class="font-normal text-muted">same as normalized format</em>
{% endif %}
</td>
</tr>
</tbody>
</table>
{% else %}
<div class="empty">
<p>This form was not submitted.</p>
</div>
{% endif %}
{% endmacro %}
{% macro render_form_passed_options(data) %}
{% if data.passed_options ?? [] is not empty %}
<table>
<thead>
<tr>
<th style="width: 180px">Option</th>
<th>Passed Value</th>
<th>Resolved Value</th>
</tr>
</thead>
<tbody>
{% for option, value in data.passed_options %}
<tr>
<th>{{ option }}</th>
<td>{{ profiler_dump(value) }}</td>
<td>
{# values can be stubs #}
{% set option_value = (value.value is defined) ? value.value : value %}
{% set resolved_option_value = (data.resolved_options[option].value is defined)
? data.resolved_options[option].value
: data.resolved_options[option] %}
{% if resolved_option_value == option_value %}
<em class="font-normal text-muted">same as passed value</em>
{% else %}
{{ profiler_dump(data.resolved_options.seek(option)) }}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="empty">
<p>No options were passed when constructing this form.</p>
</div>
{% endif %}
{% endmacro %}
{% macro render_form_resolved_options(data) %}
<table>
<thead>
<tr>
<th style="width: 180px">Option</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for option, value in data.resolved_options ?? [] %}
<tr>
<th scope="row">{{ option }}</th>
<td>{{ profiler_dump(value) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}
{% macro render_form_view_variables(data) %}
<table>
<thead>
<tr>
<th style="width: 180px">Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for variable, value in data.view_vars ?? [] %}
<tr>
<th scope="row">{{ variable }}</th>
<td>{{ profiler_dump(value) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}
@@ -0,0 +1,175 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
.sf-profiler-httpclient-requests thead th {
vertical-align: top;
}
.sf-profiler-httpclient-requests .http-method {
border: 1px solid var(--header-status-request-method-color);
border-radius: 5px;
color: var(--header-status-request-method-color);
display: inline-block;
font-weight: 500;
line-height: 1;
margin-right: 6px;
padding: 2px 4px;
text-align: center;
white-space: nowrap;
}
.sf-profiler-httpclient-requests .status-response-status-code {
background: var(--gray-600);
border-radius: 4px;
color: var(--white);
display: inline-block;
font-size: 12px;
font-weight: bold;
margin-bottom: 2px;
padding: 1px 3px;
}
.sf-profiler-httpclient-requests .status-response-status-code.status-success { background: var(--header-success-status-code-background); color: var(--header-success-status-code-color); }
.sf-profiler-httpclient-requests .status-response-status-code.status-warning { background: var(--header-warning-status-code-background); color: var(--header-warning-status-code-color); }
.sf-profiler-httpclient-requests .status-response-status-code.status-error { background: var(--header-error-status-code-background); color: var(--header-error-status-code-color); }
</style>
{% endblock %}
{% block toolbar %}
{% if collector.requestCount %}
{% set icon %}
{{ source('@WebProfiler/Icon/http-client.svg') }}
{% set status_color = '' %}
<span class="sf-toolbar-value">{{ collector.requestCount }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Total requests</b>
<span>{{ collector.requestCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>HTTP errors</b>
<span class="sf-toolbar-status {{ collector.errorCount > 0 ? 'sf-toolbar-status-red' }}">{{ collector.errorCount }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{ collector.requestCount == 0 ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/http-client.svg') }}</span>
<strong>HTTP Client</strong>
{% if collector.requestCount %}
<span class="count">
{{ collector.requestCount }}
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>HTTP Client</h2>
{% if collector.requestCount == 0 %}
<div class="empty empty-panel">
<p>No HTTP requests were made.</p>
</div>
{% else %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.requestCount }}</span>
<span class="label">Total requests</span>
</div>
<div class="metric">
<span class="value">{{ collector.errorCount }}</span>
<span class="label">HTTP errors</span>
</div>
</div>
<h2>Clients</h2>
<div class="sf-tabs">
{% for name, client in collector.clients %}
<div class="tab {{ client.traces|length == 0 ? 'disabled' }}">
<h3 class="tab-title">{{ name }} <span class="badge">{{ client.traces|length }}</span></h3>
<div class="tab-content">
{% if client.traces|length == 0 %}
<div class="empty">
<p>No requests were made with the "{{ name }}" service.</p>
</div>
{% else %}
<h4>Requests</h4>
{% for trace in client.traces %}
{% set profiler_token = '' %}
{% set profiler_link = '' %}
{% if trace.info.response_headers is defined %}
{% for header in trace.info.response_headers %}
{% if header matches '/^x-debug-token: .*$/i' %}
{% set profiler_token = (header.getValue | slice('x-debug-token: ' | length)) %}
{% endif %}
{% if header matches '/^x-debug-token-link: .*$/i' %}
{% set profiler_link = (header.getValue | slice('x-debug-token-link: ' | length)) %}
{% endif %}
{% endfor %}
{% endif %}
<table class="sf-profiler-httpclient-requests">
<thead>
<tr>
<th>
<span class="http-method">{{ trace.method }}</span>
</th>
<th class="full-width">
{{ trace.url }}
</th>
{% if profiler_token and profiler_link %}
<th>
Profile
</th>
{% endif %}
{% if trace.curlCommand is defined and trace.curlCommand %}
<th>
<button class="btn btn-sm hidden" title="Copy as cURL" data-clipboard-text="{{ trace.curlCommand }}">Copy as cURL</button>
</th>
{% endif %}
</tr>
</thead>
<tbody>
{% if trace.options is not empty %}
<tr>
<th class="font-normal">Request options</th>
<td>{{ profiler_dump(trace.options, maxDepth=1) }}</td>
</tr>
{% endif %}
<tr>
<th class="font-normal">Response</th>
<td{% if trace.curlCommand is defined and trace.curlCommand %} colspan="2"{% endif %}>
{% if trace.http_code >= 500 %}
{% set responseStatus = 'error' %}
{% elseif trace.http_code >= 400 %}
{% set responseStatus = 'warning' %}
{% else %}
{% set responseStatus = 'success' %}
{% endif %}
<span class="font-normal status-response-status-code status-{{ responseStatus }}">
{{ trace.http_code }}
</span>
{{ profiler_dump(trace.info, maxDepth=1) }}
</td>
{% if profiler_token and profiler_link %}
<td>
<span><a href="{{ profiler_link }}" target="_blank">{{ profiler_token }}</a></span>
</td>
{% endif %}
</tr>
</tbody>
</table>
{% endfor %}
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
</div>
{% endblock %}
@@ -0,0 +1,581 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
:root {
--log-filter-active-num-color: #2563EB;
--log-timestamp-color: #555;
}
.theme-dark {
--log-filter-active-num-color: #2563EB;
--log-timestamp-color: #ccc;
}
.log-filters {
display: flex;
flex-wrap: wrap;
}
.log-filters .log-filter {
flex-shrink: 0;
margin-right: 15px;
position: relative;
}
.log-filters .log-filter summary {
align-items: center;
background: var(--button-background);
border-radius: 6px;
border: 1px solid var(--button-border-color);
box-shadow: var(--button-box-shadow);
color: var(--button-color);
cursor: pointer;
display: flex;
font-size: 13px;
font-weight: 500;
padding: 4px 8px;
white-space: nowrap;
}
.log-filters .log-filter summary:active {
box-shadow: none;
transform: translateY(1px);
}
.log-filters .log-filter summary .icon {
height: 18px;
width: 18px;
margin: 0 7px 0 0;
}
.log-filters .log-filter summary svg {
height: 18px;
width: 18px;
opacity: 0.7;
}
.log-filters .log-filter summary svg {
stroke-width: 2;
}
.log-filters .log-filter summary .filter-active-num {
color: var(--log-filter-active-num-color);
font-weight: bold;
padding: 0 1px;
}
.log-filter .tab-navigation {
position: relative;
}
.log-filter .tab-navigation input[type="radio"] {
position: absolute;
pointer-events: none;
opacity: 0;
}
.tab-navigation input[type="radio"]:checked + .tab-control {
background-color: var(--tab-active-background);
border-radius: 6px;
box-shadow: inset 0 0 0 1.5px var(--tab-active-border-color);
color: var(--tab-active-color);
position: relative;
z-index: 1;
}
.theme-dark .tab-navigation input[type="radio"]:checked + .tab-control {
box-shadow: inset 0 0 0 1px var(--tab-border-color);
}
.tab-navigation input[type="radio"]:focus-visible + .tab-control {
outline: 1px solid var(--color-link);
}
.tab-navigation input[type="radio"]:checked + .tab-control + input[type="radio"] + .tab-control:before{
width: 0;
}
.log-filters .log-filter .log-filter-content {
background: var(--base-0);
border: 1px solid var(--table-border-color);
border-radius: 6px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
padding: 15px;
position: absolute;
left: 0;
top: 32px;
max-width: 400px;
min-width: 200px;
z-index: 9999;
}
.log-filters .log-filter .log-filter-content .log-filter-option {
align-items: center;
display: flex;
}
.log-filter .filter-select-all-or-none {
margin-bottom: 10px;
}
.log-filter .filter-select-all-or-none button + button {
margin-left: 15px;
}
.log-filters .log-filter .log-filter-content .log-filter-option + .log-filter-option {
margin: 7px 0 0;
}
.log-filters .log-filter .log-filter-content .log-filter-option label {
cursor: pointer;
flex: 1;
padding-left: 10px;
}
table.logs {
border-bottom-width: 0;
border-collapse: collapse;
}
table.logs tr + tr td {
border-width: 1px 0 0;
}
table.logs .metadata {
display: block;
font-size: 12px;
}
.theme-dark tr.status-error td,
.theme-dark tr.status-warning td { border-bottom: unset; border-top: unset; }
table.logs .log-timestamp {
color: var(--log-timestamp-color);
}
table.logs .log-metadata {
margin: 8px 0 0;
}
table.logs .log-metadata > span {
display: inline-block;
}
table.logs .log-metadata > span + span {
margin-left: 10px;
}
table.logs .log-metadata .log-channel {
color: var(--base-1);
font-size: 13px;
font-weight: bold;
}
table.logs .log-metadata .badge {
background: var(--badge-light-background);
color: var(--badge-light-color);
}
table.logs .log-metadata .log-num-occurrences {
color: var(--color-muted);
font-size: 13px;
}
table.logs .log-metadata .context {
background: var(--code-block-background);
border-radius: 4px;
padding: 5px;
}
table.logs .log-metadata .context + .context {
margin-top: 10px;
}
.log-type-badge {
background: var(--badge-light-background);
box-shadow: none;
color: var(--badge-light-color);
display: inline-block;
font-family: var(--font-family-system);
margin-top: 5px;
}
.log-type-badge.badge-deprecation,
.log-type-badge.badge-warning {
background: var(--badge-warning-background);
color: var(--badge-warning-color);
}
.log-type-badge.badge-error {
background: var(--badge-danger-background);
color: var(--badge-danger-color);
}
.log-type-badge.badge-silenced {
background: #EDE9FE;
color: #6D28D9;
}
.theme-dark .log-type-badge.badge-silenced {
background: #5B21B6;
color: #EDE9FE;
}
tr.log-status-warning > td:first-child,
tr.log-status-error > td:first-child,
tr.log-status-silenced > td:first-child {
position: relative;
}
tr.log-status-warning > td:first-child:before,
tr.log-status-error > td:first-child:before,
tr.log-status-silenced > td:first-child:before {
background: transparent;
border-radius: 0;
content: '';
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
}
tr.log-status-warning > td:first-child:before {
background: var(--yellow-400);
}
tr.log-status-error > td:first-child:before {
background: var(--red-400);
}
tr.log-status-silenced > td:first-child:before {
background: #a78bfa;
}
.container-compilation-logs {
background: var(--table-background);
border: 1px solid var(--base-2);
border-radius: 6px;
margin-top: 30px;
padding: 15px;
}
.container-compilation-logs summary {
cursor: pointer;
}
.container-compilation-logs summary h4 {
margin: 0 0 5px;
}
.container-compilation-logs summary p {
margin: 0;
}
</style>
{% endblock %}
{% block javascripts %}
<script>
window.addEventListener('DOMContentLoaded', () => {
new SymfonyProfilerLoggerPanel();
});
class SymfonyProfilerLoggerPanel {
constructor() {
this.#initializeLogsTable();
}
#initializeLogsTable() {
this.#updateLogsTable();
document.querySelectorAll('.log-filter input').forEach((input) => {
input.addEventListener('change', () => { this.#updateLogsTable(); });
});
document.querySelectorAll('.filter-select-all-or-none button').forEach((link) => {
link.addEventListener('click', () => {
const selectAll = link.classList.contains('select-all');
link.closest('.log-filter-content').querySelectorAll('input').forEach((input) => {
input.checked = selectAll;
});
this.#updateLogsTable();
});
});
document.body.addEventListener('click', (event) => {
document.querySelectorAll('details.log-filter').forEach((filterElement) => {
if (!filterElement.contains(event.target) && filterElement.open) {
filterElement.open = false;
}
});
});
}
#updateLogsTable() {
const logs = document.querySelector('table.logs');
if (null === logs) {
return;
}
const selectedType = document.querySelector('#log-filter-type input:checked').value;
const priorities = document.querySelectorAll('#log-filter-priority input');
const allPriorities = Array.from(priorities).map((input) => input.value);
const selectedPriorities = Array.from(priorities).filter((input) => input.checked).map((input) => input.value);
const channels = document.querySelectorAll('#log-filter-channel input');
const selectedChannels = Array.from(channels).filter((input) => input.checked).map((input) => input.value);
/* hide rows that don't match the current filters */
let numVisibleRows = 0;
logs.querySelectorAll('tbody tr').forEach((row) => {
if ('all' !== selectedType && selectedType !== row.getAttribute('data-type')) {
row.style.display = 'none';
return;
}
const priority = row.getAttribute('data-priority');
if (false === selectedPriorities.includes(priority) && true === allPriorities.includes(priority)) {
row.style.display = 'none';
return;
}
if ('' !== row.getAttribute('data-channel') && false === selectedChannels.includes(row.getAttribute('data-channel'))) {
row.style.display = 'none';
return;
}
row.style.display = 'table-row';
numVisibleRows++;
});
document.querySelector('table.logs').style.display = 0 === numVisibleRows ? 'none' : 'table';
document.querySelector('.no-logs-message').style.display = 0 === numVisibleRows ? 'block' : 'none';
/* update the selected totals of all filters */
document.querySelector('#log-filter-priority .filter-active-num').innerText = (priorities.length === selectedPriorities.length) ? 'All' : selectedPriorities.length;
document.querySelector('#log-filter-channel .filter-active-num').innerText = (channels.length === selectedChannels.length) ? 'All' : selectedChannels.length;
}
}
</script>
{% endblock %}
{% block toolbar %}
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
{% set icon %}
{% set status_color = collector.counterrors ? 'red' : collector.countwarnings ? 'yellow' : 'none' %}
{{ source('@WebProfiler/Icon/logger.svg') }}
<span class="sf-toolbar-value">{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Errors</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.counterrors ? 'red' }}">{{ collector.counterrors|default(0) }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Warnings</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countwarnings ? 'yellow' }}">{{ collector.countwarnings|default(0) }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Deprecations</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'none' }}">{{ collector.countdeprecations|default(0) }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countwarnings ? 'warning' : 'none' }} {{ collector.logs is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/logger.svg') }}</span>
<strong>Logs</strong>
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
<span class="count">
<span>{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>Log Messages</h2>
{% if collector.processedLogs is empty %}
<div class="empty empty-panel">
<p>No log messages available.</p>
</div>
{% else %}
{% set has_error_logs = collector.processedLogs|column('type')|filter(type => 'error' == type)|length > 0 %}
{% set has_deprecation_logs = collector.processedLogs|column('type')|filter(type => 'deprecation' == type)|length > 0 %}
{% set filters = collector.filters %}
<div class="log-filters">
<div id="log-filter-type" class="log-filter">
<div class="tab-navigation">
{% set initially_active_tab = has_error_logs ? 'error' : has_deprecation_logs ? 'deprecation' : 'all' %}
<input type="radio" id="filter-log-type-all" name="filter-log-type" value="all" {{ 'all' == initially_active_tab ? 'checked' }}>
<label for="filter-log-type-all" class="tab-control">
All messages
</label>
<input type="radio" id="filter-log-type-error" name="filter-log-type" value="error" {{ 'error' == initially_active_tab ? 'checked' }}>
<label for="filter-log-type-error" class="tab-control">
Errors
<span class="badge status-{{ collector.counterrors ? 'error' }}">{{ collector.counterrors|default(0) }}</span>
</label>
<input type="radio" id="filter-log-type-deprecation" name="filter-log-type" value="deprecation" {{ 'deprecation' == initially_active_tab ? 'checked' }}>
<label for="filter-log-type-deprecation" class="tab-control">
Deprecations
<span class="badge status-{{ collector.countdeprecations ? 'warning' }}">{{ collector.countdeprecations|default(0) }}</span>
</label>
</div>
</div>
<details id="log-filter-priority" class="log-filter">
<summary>
<span class="icon">{{ source('@WebProfiler/Icon/filter.svg') }}</span>
Level (<span class="filter-active-num">{{ filters.priority|length - 1 }}</span>)
</summary>
<div class="log-filter-content">
<div class="filter-select-all-or-none">
<button type="button" class="btn btn-link select-all">Select All</button>
<button type="button" class="btn btn-link select-none">Select None</button>
</div>
{% for label, value in filters.priority %}
<div class="log-filter-option">
<input {{ 'debug' != value ? 'checked' }} type="checkbox" id="filter-log-level-{{ value }}" name="filter-log-level-{{ value }}" value="{{ value }}">
<label for="filter-log-level-{{ value }}">{{ label }}</label>
</div>
{% endfor %}
</div>
</details>
<details id="log-filter-channel" class="log-filter">
<summary>
<span class="icon">{{ source('@WebProfiler/Icon/filter.svg') }}</span>
Channel (<span class="filter-active-num">{{ filters.channel|length - 1 }}</span>)
</summary>
<div class="log-filter-content">
<div class="filter-select-all-or-none">
<button type="button" class="btn btn-link select-all">Select All</button>
<button type="button" class="btn btn-link select-none">Select None</button>
</div>
{% for value in filters.channel %}
<div class="log-filter-option">
<input {{ 'event' != value ? 'checked' }} type="checkbox" id="filter-log-channel-{{ value }}" name="filter-log-channel-{{ value }}" value="{{ value }}">
<label for="filter-log-channel-{{ value }}">{{ value|title }}</label>
</div>
{% endfor %}
</div>
</details>
</div>
<table class="logs">
<colgroup>
<col style="width: 140px">
<col>
</colgroup>
<thead>
<tr>
<th>Time</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{% for log in collector.processedLogs %}
{% set css_class = 'error' == log.type ? 'error'
: (log.priorityName == 'WARNING' or 'deprecation' == log.type) ? 'warning'
: 'silenced' == log.type ? 'silenced'
%}
<tr class="log-status-{{ css_class }}" data-type="{{ log.type }}" data-priority="{{ log.priority }}" data-channel="{{ log.channel }}" style="{{ 'event' == log.channel or 'DEBUG' == log.priorityName ? 'display: none' }}">
<td class="log-timestamp">
<time class="newline" title="{{ log.timestamp|date('r') }}" datetime="{{ log.timestamp|date(constant('DateTimeInterface::RFC3339_EXTENDED')) }}" data-convert-to-user-timezone data-render-as-time data-render-with-millisecond-precision>
{{ log.timestamp|date('H:i:s.v') }}
</time>
{% if log.type in ['error', 'deprecation', 'silenced'] or 'WARNING' == log.priorityName %}
<span class="log-type-badge badge badge-{{ css_class }}">
{% if 'error' == log.type or 'WARNING' == log.priorityName %}
{{ log.priorityName|lower }}
{% else %}
{{ log.type|lower }}
{% endif %}
</span>
{% else %}
<span class="log-type-badge badge badge-{{ css_class }}">
{{ log.priorityName|lower }}
</span>
{% endif %}
</td>
<td class="font-normal">
{{ _self.render_log_message('debug', loop.index, log) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="no-logs-message empty">
<p>There are no log messages.</p>
</div>
{% endif %}
{% set compilerLogTotal = collector.compilerLogs|reduce((total, logs) => total + logs|length, 0) %}
<details class="container-compilation-logs">
<summary>
<h4>Container Compilation Logs <span class="text-muted">({{ compilerLogTotal }})</span></h4>
<span class="text-muted">Log messages generated during the compilation of the service container.</span>
</summary>
{% if collector.compilerLogs is empty %}
<div class="empty">
<p>There are no compiler log messages.</p>
</div>
{% else %}
<table class="container-logs">
<thead>
<tr>
<th>Messages</th>
<th class="full-width">Class</th>
</tr>
</thead>
<tbody>
{% for class, logs in collector.compilerLogs %}
<tr>
<td class="font-normal text-right">{{ logs|length }}</td>
<td class="font-normal">
{% set context_id = 'context-compiler-' ~ loop.index %}
<button type="button" class="btn btn-link sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="{{ class }}">{{ class }}</button>
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
<ul class="break-long-words">
{% for log in logs %}
<li>{{ profiler_dump_log(log.message) }}</li>
{% endfor %}
</ul>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</details>
{% endblock %}
{% macro render_log_message(category, log_index, log) %}
{% set has_context = log.context is defined and log.context is not empty %}
{% set has_trace = log.context.exception.trace is defined %}
{% if not has_context %}
{{ profiler_dump_log(log.message) }}
{% else %}
{{ profiler_dump_log(log.message, log.context) }}
{% endif %}
<div class="log-metadata">
{% if log.channel %}
<span class="badge">{{ log.channel }}</span>
{% endif %}
{% if log.errorCount is defined and log.errorCount > 1 %}
<span class="log-num-occurrences">{{ log.errorCount }} times</span>
{% endif %}
{% if has_context %}
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
<span><button type="button" class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide context">Show context</button></span>
{% endif %}
{% if has_trace %}
{% set trace_id = 'trace-' ~ category ~ '-' ~ log_index %}
<span><button type="button" class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ trace_id }}" data-toggle-alt-content="Hide trace">Show trace</button></span>
{% endif %}
{% if has_context %}
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(log.context, maxDepth=1) }}
</div>
{% endif %}
{% if has_trace %}
<div id="{{ trace_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(log.context.exception.trace, maxDepth=1) }}
</div>
{% endif %}
</div>
{% endmacro %}
@@ -0,0 +1,511 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
:root {
--mailer-email-table-wrapper-background: var(--gray-100);
--mailer-email-table-active-row-background: #dbeafe;
--mailer-email-table-active-row-color: var(--color-text);
}
.theme-dark {
--mailer-email-table-wrapper-background: var(--gray-900);
--mailer-email-table-active-row-background: var(--gray-300);
--mailer-email-table-active-row-color: var(--gray-800);
}
.mailer-email-summary-table-wrapper {
background: var(--mailer-email-table-wrapper-background);
border-bottom: 4px double var(--table-border-color);
border-radius: inherit;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin: 0 -9px 10px -9px;
padding-bottom: 10px;
transform: translateY(-9px);
max-height: 265px;
overflow-y: auto;
}
.mailer-email-summary-table,
.mailer-email-summary-table tr,
.mailer-email-summary-table td {
border: 0;
border-radius: inherit;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
box-shadow: none;
}
.mailer-email-summary-table th {
color: var(--color-muted);
font-size: 13px;
padding: 4px 10px;
}
.mailer-email-summary-table tr td,
.mailer-email-summary-table tr:last-of-type td {
border: solid var(--table-border-color);
border-width: 1px 0;
}
.mailer-email-summary-table-row {
margin: 5px 0;
}
.mailer-email-summary-table-row:hover {
cursor: pointer;
}
.mailer-email-summary-table-row.active {
background: var(--mailer-email-table-active-row-background);
color: var(--mailer-email-table-active-row-color);
}
.mailer-email-summary-table-row td {
font-family: var(--font-family-system);
font-size: inherit;
}
.mailer-email-details {
display: none;
}
.mailer-email-details.active {
display: block;
}
.mailer-transport-information {
border-bottom: 1px solid var(--form-input-border-color);
padding-bottom: 5px;
font-size: var(--font-size-body);
margin: 5px 0 10px 5px;
}
.mailer-transport-information .badge {
font-size: inherit;
font-weight: inherit;
}
.mailer-message-subject {
font-size: 21px;
font-weight: bold;
margin: 5px;
}
.mailer-message-headers {
margin-bottom: 10px;
}
.mailer-message-headers p {
font-size: var(--font-size-body);
margin: 2px 5px;
}
.mailer-message-header-secondary {
color: var(--color-muted);
}
.mailer-message-attachments-title {
align-items: center;
display: flex;
font-size: var(--font-size-body);
font-weight: 600;
margin-bottom: 10px;
}
.mailer-message-attachments-title svg {
color: var(--color-muted);
margin-right: 5px;
height: 18px;
width: 18px;
}
.mailer-message-attachments-title span {
font-weight: normal;
margin-left: 4px;
}
.mailer-message-attachments-list {
list-style: none;
margin: 0 0 5px 20px;
padding: 0;
}
.mailer-message-attachments-list li {
align-items: center;
display: flex;
}
.mailer-message-attachments-list li svg {
margin-right: 5px;
height: 18px;
width: 18px;
}
.mailer-message-attachments-list li a {
margin-left: 5px;
}
.mailer-email-body {
margin: 0;
padding: 6px 8px;
}
.mailer-empty-email-body {
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23e5e5e5' stroke-width='4' stroke-dasharray='6%2c 14' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
border-radius: 6px;
color: var(--color-muted);
margin: 1em 0 0;
padding: .5em 1em;
}
.theme-dark .mailer-empty-email-body {
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23737373' stroke-width='4' stroke-dasharray='6%2c 14' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
}
.mailer-empty-email-body p {
font-size: var(--font-size-body);
margin: 0;
padding: 0.5em 0;
}
.mailer-message-download-raw {
align-items: center;
display: flex;
padding: 5px 0 0 5px;
}
.mailer-message-download-raw svg {
height: 18px;
width: 18px;
margin-right: 3px;
}
</style>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
window.addEventListener('DOMContentLoaded', () => {
new SymfonyProfilerMailerPanel();
});
class SymfonyProfilerMailerPanel {
constructor() {
this.#initializeEmailsTable();
}
#initializeEmailsTable() {
const emailRows = document.querySelectorAll('.mailer-email-summary-table-row');
emailRows.forEach((emailRow) => {
emailRow.addEventListener('click', () => {
emailRows.forEach((row) => row.classList.remove('active'));
emailRow.classList.add('active');
document.querySelectorAll('.mailer-email-details').forEach((emailDetails) => emailDetails.style.display = 'none');
document.querySelector(emailRow.getAttribute('data-target')).style.display = 'block';
});
});
}
}
</script>
{% endblock %}
{% block toolbar %}
{% set events = collector.events %}
{% if events.messages|length %}
{% set icon %}
{{ source('@WebProfiler/Icon/mailer.svg') }}
<span class="sf-toolbar-value">{{ events.messages|length }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Queued messages</b>
<span class="sf-toolbar-status">{{ events.events|filter(e => e.isQueued())|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Sent messages</b>
<span class="sf-toolbar-status">{{ events.events|filter(e => not e.isQueued())|length }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
{% set events = collector.events %}
<span class="label {{ events.messages is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/mailer.svg') }}</span>
<strong>Emails</strong>
{% if events.messages|length > 0 %}
<span class="count">
<span>{{ events.messages|length }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
{% set events = collector.events %}
<h2>Emails</h2>
{% if not events.messages|length %}
<div class="empty empty-panel">
<p>No emails were sent.</p>
</div>
{% else %}
<div class="metrics">
<div class="metric-group">
<div class="metric">
<span class="value">{{ events.events|filter(e => e.isQueued())|length }}</span>
<span class="label">Queued</span>
</div>
<div class="metric">
<span class="value">{{ events.events|filter(e => not e.isQueued())|length }}</span>
<span class="label">Sent</span>
</div>
</div>
</div>
{% endif %}
{% if events.transports|length > 1 %}
{% for transport in events.transports %}
<h2><code>{{ transport }}</code> transport</h2>
{{ _self.render_transport_details(collector, transport) }}
{% endfor %}
{% elseif events.transports is not empty %}
{{ _self.render_transport_details(collector, events.transports|first, true) }}
{% endif %}
{% macro render_transport_details(collector, transport, show_transport_name = false) %}
<div class="card">
{% set num_emails = collector.events.events(transport)|length %}
{% if num_emails > 1 %}
<div class="mailer-email-summary-table-wrapper">
<table class="mailer-email-summary-table">
<thead>
<tr>
<th>#</th>
<th>Subject</th>
<th>To</th>
<th class="visually-hidden">Actions</th>
</tr>
</thead>
<tbody>
{% for event in collector.events.events(transport) %}
<tr class="mailer-email-summary-table-row {{ loop.first ? 'active' }}" data-target="#email-{{ loop.index }}">
<td>{{ loop.index }}</td>
<td>
{% if event.message.subject is defined %}
{{ event.message.getSubject() ?? '(No subject)' }}
{% elseif event.message.headers.has('subject') %}
{{ event.message.headers.get('subject').bodyAsString()|default('(No subject)') }}
{% else %}
(No subject)
{% endif %}
</td>
<td>
{% if event.message.to is defined %}
{{ event.message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif event.message.headers.has('to') %}
{{ event.message.headers.get('to').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</td>
<td class="visually-hidden"><button class="mailer-email-summary-table-row-button" data-target="#email-{{ loop.index }}">View email details</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% for event in collector.events.events(transport) %}
<div class="mailer-email-details {{ loop.first ? 'active' }}" id="email-{{ loop.index }}">
{{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }}
</div>
{% endfor %}
{% else %}
{% set event = (collector.events.events(transport)|first) %}
{{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }}
{% endif %}
</div>
{% endmacro %}
{% macro render_email_details(collector, transport, message, message_is_queued, show_transport_name = false) %}
{% if show_transport_name %}
<p class="mailer-transport-information">
<strong>Status:</strong> <span class="badge badge-{{ message_is_queued ? 'warning' : 'success' }}">{{ message_is_queued ? 'Queued' : 'Sent' }}</span>
&bull;
<strong>Transport:</strong> <code>{{ transport }}</code>
</p>
{% endif %}
{% if message.headers is not defined %}
{# render the raw message contents #}
<a class="mailer-message-download-raw" href="data:application/octet-stream;base64,{{ collector.base64Encode(message.toString()) }}" download="email.eml">
{{ source('@WebProfiler/Icon/download.svg') }}
Download as EML file
</a>
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.toString() }}</pre>
{% else %}
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Email contents</h3>
<div class="tab-content">
<div class="card-block">
<p class="mailer-message-subject">
{% if message.subject is defined %}
{{ message.getSubject() ?? '(No subject)' }}
{% elseif message.headers.has('subject') %}
{{ message.headers.get('subject').bodyAsString()|default('(No subject)') }}
{% else %}
(No subject)
{% endif %}
</p>
<div class="mailer-message-headers">
<p>
<strong>From:</strong>
{% if message.from is defined %}
{{ message.getFrom()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif message.headers.has('from') %}
{{ message.headers.get('from').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</p>
<p>
<strong>To:</strong>
{% if message.to is defined %}
{{ message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif message.headers.has('to') %}
{{ message.headers.get('to').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</p>
{% for header in message.headers.all|filter(header => (header.name ?? '')|lower not in ['subject', 'from', 'to']) %}
<p class="mailer-message-header-secondary">{{ header.toString }}</p>
{% endfor %}
</div>
</div>
{% if message.attachments is defined and message.attachments %}
<div class="card-block">
{% set num_of_attachments = message.attachments|length %}
{% set total_attachments_size_in_bytes = message.attachments|reduce((total_size, attachment) => total_size + attachment.body|length, 0) %}
<p class="mailer-message-attachments-title">
{{ source('@WebProfiler/Icon/attachment.svg') }}
Attachments <span>({{ num_of_attachments }} file{{ num_of_attachments > 1 ? 's' }} / {{ _self.render_file_size_humanized(total_attachments_size_in_bytes) }})</span>
</p>
<ul class="mailer-message-attachments-list">
{% for attachment in message.attachments %}
<li>
{{ source('@WebProfiler/Icon/file.svg') }}
{% if attachment.filename|default %}
{{ attachment.filename }}
{% else %}
<em>(no filename)</em>
{% endif %}
({{ _self.render_file_size_humanized(attachment.body|length) }})
<a href="data:{{ attachment.contentType|default('application/octet-stream') }};base64,{{ collector.base64Encode(attachment.body) }}" download="{{ attachment.filename|default('attachment') }}">Download</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="card-block">
<div class="sf-tabs sf-tabs-sm">
{% if message.htmlBody is defined %}
{% set textBody = message.textBody %}
{% set htmlBody = message.htmlBody %}
<div class="tab {{ not textBody ? 'disabled' }} {{ textBody ? 'active' }}">
<h3 class="tab-title">Text content</h3>
<div class="tab-content">
{% if textBody %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{%- if message.textCharset() %}
{{- textBody|convert_encoding('UTF-8', message.textCharset()) }}
{%- else %}
{{- textBody }}
{%- endif -%}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The text body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% if htmlBody %}
<div class="tab">
<h3 class="tab-title">HTML preview</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px"><iframe src="data:text/html;charset=utf-8;base64,{{ collector.base64Encode(htmlBody) }}" style="height: 80vh;width: 100%;"></iframe>
</pre>
</div>
</div>
{% endif %}
<div class="tab {{ not htmlBody ? 'disabled' }} {{ not textBody and htmlBody ? 'active' }}">
<h3 class="tab-title">HTML content</h3>
<div class="tab-content">
{% if htmlBody %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{%- if message.htmlCharset() %}
{{- htmlBody|convert_encoding('UTF-8', message.htmlCharset()) }}
{%- else %}
{{- htmlBody }}
{%- endif -%}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The HTML body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% else %}
{% set body = message.body ? message.body.toString() : null %}
<div class="tab {{ not body ? 'disabled' }} {{ body ? 'active' }}">
<h3 class="tab-title">Content</h3>
<div class="tab-content">
{% if body %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{{- body }}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="tab">
<h3 class="tab-title">MIME parts</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.body().asDebugString() }}</pre>
</div>
</div>
<div class="tab">
<h3 class="tab-title">Raw Message</h3>
<div class="tab-content">
<a class="mailer-message-download-raw" href="data:application/octet-stream;base64,{{ collector.base64Encode(message.toString()) }}" download="email.eml">
{{ source('@WebProfiler/Icon/download.svg') }}
Download as EML file
</a>
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.toString() }}</pre>
</div>
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_file_size_humanized(bytes) %}
{%- if bytes < 1000 -%}
{{- bytes ~ ' bytes' -}}
{%- elseif bytes < 1000 ** 2 -%}
{{- (bytes / 1000)|number_format(2) ~ ' kB' -}}
{%- else -%}
{{- (bytes / 1000 ** 2)|number_format(2) ~ ' MB' -}}
{%- endif -%}
{% endmacro %}
{% endblock %}
@@ -0,0 +1,24 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% set icon %}
{% set status_color = (collector.memory / 1024 / 1024) > 50 ? 'yellow' %}
{{ source('@WebProfiler/Icon/memory.svg') }}
<span class="sf-toolbar-value">{{ '%.1f'|format(collector.memory / 1024 / 1024) }}</span>
<span class="sf-toolbar-label">MiB</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Peak memory usage</b>
<span>{{ '%.1f'|format(collector.memory / 1024 / 1024) }} MiB</span>
</div>
<div class="sf-toolbar-info-piece">
<b>PHP memory limit</b>
<span>{{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MiB'|format(collector.memoryLimit / 1024 / 1024) }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, name: 'time', status: status_color }) }}
{% endblock %}
@@ -0,0 +1,215 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
.message-item thead th { position: relative; cursor: pointer; user-select: none; padding-right: 35px; }
.message-item tbody tr td:first-child { width: 170px; }
.message-item .label { float: right; padding: 1px 5px; opacity: .75; margin-left: 5px; }
.message-item .toggle-button { position: absolute; right: 6px; top: 6px; opacity: .5; pointer-events: none; color: inherit; }
.message-item .icon svg { height: 24px; width: 24px; }
.message-item .icon-close svg { transform: rotate(180deg); }
.message-item .sf-toggle-off .icon-close, .sf-toggle-on .icon-open { display: none; }
.message-item .sf-toggle-off .icon-open, .sf-toggle-on .icon-close { display: block; }
.message-bus .badge.status-some-errors { line-height: 16px; border-bottom: 2px solid #B0413E; }
.message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; }
#collector-content .message-bus .trace {
border: var(--border);
background: var(--base-0);
padding: 10px;
margin: 0.5em 0;
overflow: auto;
}
#collector-content .message-bus .trace {
font-size: 12px;
}
#collector-content .message-bus .trace li {
margin-bottom: 0;
padding: 0;
}
#collector-content .message-bus .trace li.selected {
background: var(--highlight-selected-line);
}
</style>
{% endblock %}
{% block toolbar %}
{% if collector.messages|length > 0 %}
{% set status_color = collector.exceptionsCount ? 'red' %}
{% set icon %}
{{ source('@WebProfiler/Icon/messenger.svg') }}
<span class="sf-toolbar-value">{{ collector.messages|length }}</span>
{% endset %}
{% set text %}
{% for bus in collector.buses %}
{% set exceptionsCount = collector.exceptionsCount(bus) %}
<div class="sf-toolbar-info-piece">
<b>{{ bus }}</b>
<span
title="{{ exceptionsCount }} message(s) with exceptions"
class="sf-toolbar-status sf-toolbar-status-{{ exceptionsCount ? 'red' }}"
>
{{ collector.messages(bus)|length }}
</span>
</div>
{% endfor %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: 'messenger', status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label{{ collector.exceptionsCount ? ' label-status-error' }}{{ collector.messages is empty ? ' disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/messenger.svg') }}</span>
<strong>Messages</strong>
{% if collector.exceptionsCount > 0 %}
<span class="count">
<span>{{ collector.exceptionsCount }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>Messages</h2>
{% if collector.messages is empty %}
<div class="empty empty-panel">
<p>No messages have been collected.</p>
</div>
{% elseif 1 == collector.buses|length %}
<p class="text-muted">Ordered list of dispatched messages across all your buses</p>
{{ _self.render_bus_messages(collector.messages, true) }}
{% else %}
<div class="sf-tabs message-bus">
<div class="tab">
{% set messages = collector.messages %}
{% set exceptionsCount = collector.exceptionsCount %}
<h3 class="tab-title">All<span class="badge {{ exceptionsCount ? exceptionsCount == messages|length ? 'status-error' : 'status-some-errors' }}">{{ messages|length }}</span></h3>
<div class="tab-content">
<p class="text-muted">Ordered list of dispatched messages across all your buses</p>
{{ _self.render_bus_messages(messages, true) }}
</div>
</div>
{% for bus in collector.buses %}
<div class="tab message-bus">
{% set messages = collector.messages(bus) %}
{% set exceptionsCount = collector.exceptionsCount(bus) %}
<h3 class="tab-title">{{ bus }}<span class="badge {{ exceptionsCount ? exceptionsCount == messages|length ? 'status-error' : 'status-some-errors' }}">{{ messages|length }}</span></h3>
<div class="tab-content">
<p class="text-muted">Ordered list of messages dispatched on the <code>{{ bus }}</code> bus</p>
{{ _self.render_bus_messages(messages) }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}
{% macro render_bus_messages(messages, showBus = false) %}
{% set discr = random() %}
{% for dispatchCall in messages %}
<table class="message-item">
<thead>
<tr>
<th colspan="2" class="sf-toggle"
data-toggle-selector="#message-item-{{ discr }}-{{ loop.index0 }}-details"
data-toggle-initial="{{ loop.first ? 'display' }}"
>
<span class="dump-inline">{{ profiler_dump(dispatchCall.message.type) }}</span>
{% if dispatchCall.exception is defined %}
<span class="label status-error">exception</span>
{% endif %}
<button class="btn btn-link toggle-button" type="button">
<span class="icon icon-close">{{ source('@WebProfiler/Icon/chevron-down.svg') }}</span>
<span class="icon icon-open">{{ source('@WebProfiler/Icon/chevron-down.svg') }}</span>
</button>
</th>
</tr>
</thead>
<tbody id="message-item-{{ discr }}-{{ loop.index0 }}-details" class="sf-toggle-content">
<tr>
<th scope="row" class="font-normal">Caller</th>
<td class="message-bus-dispatch-caller">
In
{% set caller = dispatchCall.caller %}
{% if caller.line %}
{% set link = caller.file|file_link(caller.line) %}
{% if link %}
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
{% else %}
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
{% endif %}
{% else %}
{{ caller.name }}
{% endif %}
line <button type="button" class="btn-link text-small sf-toggle" data-toggle-selector="#sf-trace-{{ discr }}-{{ loop.index0 }}">{{ caller.line }}</button>
<div class="hidden" id="sf-trace-{{ discr }}-{{ loop.index0 }}">
<div class="trace">
{{ caller.file|file_excerpt(caller.line)|replace({
'#DD0000': 'var(--highlight-string)',
'#007700': 'var(--highlight-keyword)',
'#0000BB': 'var(--highlight-default)',
'#FF8000': 'var(--highlight-comment)'
})|raw }}
</div>
</div>
</td>
</tr>
{% if showBus %}
<tr>
<th scope="row" class="font-normal">Bus</th>
<td>{{ dispatchCall.bus }}</td>
</tr>
{% endif %}
<tr>
<th scope="row" class="font-normal">Message</th>
<td>{{ profiler_dump(dispatchCall.message.value, maxDepth=2) }}</td>
</tr>
<tr>
<th scope="row" class="font-normal">Envelope stamps <span class="block text-muted">when dispatching</span></th>
<td>
{% for item in dispatchCall.stamps %}
{{ profiler_dump(item) }}
{% else %}
<span class="text-muted font-normal">No items</span>
{% endfor %}
</td>
</tr>
{% if dispatchCall.stamps_after_dispatch is defined %}
<tr>
<th scope="row" class="font-normal">Envelope stamps <span class="block text-muted">after dispatch</span></th>
<td>
{% for item in dispatchCall.stamps_after_dispatch %}
{{ profiler_dump(item) }}
{% else %}
<span class="text-muted">No items</span>
{% endfor %}
</td>
</tr>
{% endif %}
{% if dispatchCall.exception is defined %}
<tr>
<td class="text-bold">Exception</td>
<td>
{{ profiler_dump(dispatchCall.exception.value, maxDepth=1) }}
</td>
</tr>
{% endif %}
</tbody>
</table>
{% endfor %}
{% endmacro %}
@@ -0,0 +1,168 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% set events = collector.events %}
{% if events.messages|length %}
{% set icon %}
{{ source('@WebProfiler/Icon/notifier.svg') }}
<span class="sf-toolbar-value">{{ events.messages|length }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Sent notifications</b>
<span class="sf-toolbar-status">{{ events.messages|length }}</span>
</div>
{% for transport in events.transports %}
<div class="sf-toolbar-info-piece">
<b>{{ transport ?: '<em>Empty Transport Name</em>' }}</b>
<span class="sf-toolbar-status">{{ events.messages(transport)|length }}</span>
</div>
{% endfor %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }}
{% endif %}
{% endblock %}
{% block head %}
{{ parent() }}
<style>
/* utility classes */
.m-t-0 { margin-top: 0 !important; }
.m-t-10 { margin-top: 10px !important; }
/* basic grid */
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.col {
flex-basis: 0;
flex-grow: 1;
max-width: 100%;
position: relative;
width: 100%;
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
}
.col-4 {
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
/* small tabs */
.sf-tabs-sm .tab-navigation li {
font-size: 14px;
padding: .3em .5em;
}
</style>
{% endblock %}
{% block menu %}
{% set events = collector.events %}
<span class="label {{ events.messages|length ? '' : 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/notifier.svg') }}</span>
<strong>Notifications</strong>
{% if events.messages|length > 0 %}
<span class="count">
<span>{{ events.messages|length }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
{% set events = collector.events %}
<h2>Notifications</h2>
{% if not events.messages|length %}
<div class="empty empty-panel">
<p>No notifications were sent.</p>
</div>
{% endif %}
<div class="metrics">
{% for transport in events.transports %}
<div class="metric">
<span class="value">{{ events.messages(transport)|length }}</span>
<span class="label">{{ transport }}</span>
</div>
{% endfor %}
</div>
{% for transport in events.transports %}
<h3>{{ transport ?: '<em>Empty Transport Name</em>' }}</h3>
<div class="card-block">
<div class="sf-tabs sf-tabs-sm">
{% for event in events.events(transport) %}
{% set message = event.message %}
<div class="tab">
<h3 class="tab-title">Message #{{ loop.index }} <small>({{ event.isQueued() ? 'queued' : 'sent' }})</small></h3>
<div class="tab-content">
<div class="card">
<div class="card-block">
<span class="label">Subject</span>
<h2 class="m-t-10">{{ message.getSubject() ?? '(empty)' }}</h2>
</div>
{% set notification = message.notification ?? null %}
{% if notification %}
<div class="card-block">
<div class="row">
<div class="col">
<span class="label">Content</span>
<pre class="prewrap">{{ notification.getContent() ?? '(empty)' }}</pre>
<span class="label">Importance</span>
<pre class="prewrap">{{ notification.getImportance() }}</pre>
</div>
</div>
</div>
{% endif %}
<div class="card-block">
<div class="sf-tabs sf-tabs-sm">
{% if notification %}
<div class="tab">
<h3 class="tab-title">Notification</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px">
{{- 'Subject: ' ~ notification.getSubject() }}<br/>
{{- 'Content: ' ~ notification.getContent() }}<br/>
{{- 'Importance: ' ~ notification.getImportance() }}<br/>
{{- 'Emoji: ' ~ (notification.getEmoji() is empty ? '(empty)' : notification.getEmoji()) }}<br/>
{{- 'Exception: ' ~ (notification.getException() ?? '(empty)') }}<br/>
{{- 'ExceptionAsString: ' ~ (notification.getExceptionAsString() is empty ? '(empty)' : notification.getExceptionAsString()) }}
</pre>
</div>
</div>
{% endif %}
<div class="tab">
<h3 class="tab-title">Message Options</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px">
{%- if message.getOptions() is null %}
{{- '(empty)' }}
{%- else %}
{{- message.getOptions().toArray()|json_encode(constant('JSON_PRETTY_PRINT')) }}
{%- endif %}
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
{% endblock %}
@@ -0,0 +1,431 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
.empty-query-post-files {
display: flex;
justify-content: space-between;
}
.empty-query-post-files > div {
flex: 1;
}
.empty-query-post-files > div + div {
margin-left: 30px;
}
.empty-query-post-files h3 {
margin-top: 0;
}
.empty-query-post-files .empty {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block toolbar %}
{% set request_handler %}
{{ _self.set_handler(collector.controller) }}
{% endset %}
{% if collector.redirect %}
{% set redirect_handler %}
{{ _self.set_handler(collector.redirect.controller, collector.redirect.route, 'GET' != collector.redirect.method ? collector.redirect.method) }}
{% endset %}
{% endif %}
{% if collector.forwardtoken %}
{% set forward_profile = profile.childByToken(collector.forwardtoken) %}
{% set forward_handler %}
{{ _self.set_handler(forward_profile ? forward_profile.collector('request').controller : 'n/a') }}
{% endset %}
{% endif %}
{% set request_status_code_color = (collector.statuscode >= 400) ? 'red' : (collector.statuscode >= 300) ? 'yellow' : 'green' %}
{% set icon %}
<span class="sf-toolbar-status sf-toolbar-status-{{ request_status_code_color }}">{{ collector.statuscode }}</span>
{% if collector.route %}
{% if collector.redirect %}<span class="sf-toolbar-request-icon">{{ source('@WebProfiler/Icon/redirect.svg') }}</span>{% endif %}
{% if collector.forwardtoken %}<span class="sf-toolbar-request-icon">{{ source('@WebProfiler/Icon/forward.svg') }}</span>{% endif %}
<span class="sf-toolbar-label">{{ 'GET' != collector.method ? collector.method }} @</span>
<span class="sf-toolbar-value sf-toolbar-info-piece-additional">{{ collector.route }}</span>
{% endif %}
{% endset %}
{% set text %}
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece">
<b>HTTP status</b>
<span>{{ collector.statuscode }} {{ collector.statustext }}</span>
</div>
{% if 'GET' != collector.method -%}
<div class="sf-toolbar-info-piece">
<b>Method</b>
<span>{{ collector.method }}</span>
</div>
{%- endif %}
<div class="sf-toolbar-info-piece">
<b>Controller</b>
<span>{{ request_handler }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Route name</b>
<span>{{ collector.route|default('n/a') }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Has session</b>
<span>{% if collector.sessionmetadata|length %}yes{% else %}no{% endif %}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Stateless Check</b>
<span>{% if collector.statelesscheck %}yes{% else %}no{% endif %}</span>
</div>
</div>
{% if redirect_handler is defined -%}
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece">
<b>
<span class="sf-toolbar-redirection-status sf-toolbar-status-yellow">{{ collector.redirect.status_code }}</span>
Redirect from
</b>
<span>
{{ redirect_handler }}
(<a href="{{ path('_profiler', { token: collector.redirect.token }) }}">{{ collector.redirect.token }}</a>)
</span>
</div>
</div>
{% endif %}
{% if forward_handler is defined %}
<div class="sf-toolbar-info-group">
<div class="sf-toolbar-info-piece">
<b>Forwarded to</b>
<span>
{{ forward_handler }}
(<a href="{{ path('_profiler', { token: collector.forwardtoken }) }}">{{ collector.forwardtoken }}</a>)
</span>
</div>
</div>
{% endif %}
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endblock %}
{% block menu %}
<span class="label">
<span class="icon">{{ source('@WebProfiler/Icon/request.svg') }}</span>
<strong>Request / Response</strong>
</span>
{% endblock %}
{% block panel %}
{% set controller_name = _self.set_handler(collector.controller) %}
<h2>
{{ 'n/a' in controller_name ? 'Request / Response' : controller_name }}
</h2>
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Request</h3>
<div class="tab-content">
{% set has_no_query_post_or_files = collector.requestquery.all is empty and collector.requestrequest.all is empty and collector.requestfiles is empty %}
{% if has_no_query_post_or_files %}
<div class="empty-query-post-files" style="display: flex; align-items: stretch">
<div>
<h3>GET Parameters</h3>
<div class="empty"><p>None</p></div>
</div>
<div>
<h3>POST Parameters</h3>
<div class="empty"><p>None</p></div>
</div>
<div>
<h3>Uploaded Files</h3>
<div class="empty"><p>None</p></div>
</div>
</div>
{% else %}
<h3>GET Parameters</h3>
{% if collector.requestquery.all is empty %}
<div class="empty">
<p>No GET parameters</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestquery, maxDepth: 1 }, with_context = false) }}
{% endif %}
<h3>POST Parameters</h3>
{% if collector.requestrequest.all is empty %}
<div class="empty">
<p>No POST parameters</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestrequest, maxDepth: 1 }, with_context = false) }}
{% endif %}
<h4>Uploaded Files</h4>
{% if collector.requestfiles is empty %}
<div class="empty">
<p>No files were uploaded</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestfiles, maxDepth: 1 }, with_context = false) }}
{% endif %}
{% endif %}
<h3>Request Attributes</h3>
{% if collector.requestattributes.all is empty %}
<div class="empty">
<p>No attributes</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestattributes }, with_context = false) }}
{% endif %}
<h3>Request Headers</h3>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}
<h3>Request Content</h3>
{% if collector.content == false %}
<div class="empty">
<p>Request content not available (it was retrieved as a resource).</p>
</div>
{% elseif collector.content %}
<div class="sf-tabs">
{% set prettyJson = collector.isJsonRequest ? collector.prettyJson : null %}
{% if prettyJson is not null %}
<div class="tab">
<h3 class="tab-title">Pretty</h3>
<div class="tab-content">
<div class="card" style="max-height: 500px; overflow-y: auto;">
<pre class="break-long-words">{{ prettyJson }}</pre>
</div>
</div>
</div>
{% endif %}
<div class="tab">
<h3 class="tab-title">Raw</h3>
<div class="tab-content">
<div class="card">
<pre class="break-long-words">{{ collector.content }}</pre>
</div>
</div>
</div>
</div>
{% else %}
<div class="empty">
<p>No content</p>
</div>
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Response</h3>
<div class="tab-content">
<h3>Response Headers</h3>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responseheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}
</div>
</div>
<div class="tab {{ collector.requestcookies.all is empty and collector.responsecookies.all is empty ? 'disabled' }}">
<h3 class="tab-title">Cookies</h3>
<div class="tab-content">
<h3>Request Cookies</h3>
{% if collector.requestcookies.all is empty %}
<div class="empty">
<p>No request cookies</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestcookies }, with_context = false) }}
{% endif %}
<h3>Response Cookies</h3>
{% if collector.responsecookies.all is empty %}
<div class="empty">
<p>No response cookies</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responsecookies }, with_context = true) }}
{% endif %}
</div>
</div>
<div class="tab {{ collector.sessionmetadata is empty ? 'disabled' }}">
<h3 class="tab-title">Session{% if collector.sessionusages is not empty %} <span class="badge">{{ collector.sessionusages|length }}</span>{% endif %}</h3>
<div class="tab-content">
<h3>Session Metadata</h3>
{% if collector.sessionmetadata is empty %}
<div class="empty">
<p>No session metadata</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.sessionmetadata }, with_context = false) }}
{% endif %}
<h3>Session Attributes</h3>
{% if collector.sessionattributes is empty %}
<div class="empty">
<p>No session attributes</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.sessionattributes, labels: ['Attribute', 'Value'] }, with_context = false) }}
{% endif %}
<h3>Session Usage</h3>
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.sessionusages|length }}</span>
<span class="label">Usages</span>
</div>
<div class="metric">
<span class="value">{{ source('@WebProfiler/Icon/' ~ (collector.statelesscheck ? 'yes' : 'no') ~ '.svg') }}</span>
<span class="label">Stateless check enabled</span>
</div>
</div>
{% if collector.sessionusages is empty %}
<div class="empty">
<p>Session not used.</p>
</div>
{% else %}
<table class="session_usages">
<thead>
<tr>
<th class="full-width">Usage</th>
</tr>
</thead>
<tbody>
{% for key, usage in collector.sessionusages %}
<tr>
<td class="font-normal">
{%- set link = usage.file|file_link(usage.line) %}
{%- if link %}<a href="{{ link }}" title="{{ usage.name }}">{% else %}<span title="{{ usage.name }}">{% endif %}
{{ usage.name }}
{%- if link %}</a>{% else %}</span>{% endif %}
<div class="text-small font-normal">
{% set usage_id = 'session-usage-trace-' ~ key %}
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ usage_id }}" data-toggle-alt-content="Hide trace">Show trace</a>
</div>
<div id="{{ usage_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(usage.trace, maxDepth=2) }}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
<div class="tab {{ collector.flashes is empty ? 'disabled' }}">
<h3 class="tab-title">Flashes</h3>
<div class="tab-content">
<h3>Flashes</h3>
{% if collector.flashes is empty %}
<div class="empty">
<p>No flash messages were created.</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.flashes }, with_context = false) }}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Server Parameters</h3>
<div class="tab-content">
<h3>Server Parameters</h3>
<h4>Defined in .env</h4>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.dotenvvars }, with_context = false) }}
<h4>Defined as regular env variables</h4>
{% set requestserver = [] %}
{% for key, value in collector.requestserver|filter((_, key) => key not in collector.dotenvvars.keys) %}
{% set requestserver = requestserver|merge({(key): value}) %}
{% endfor %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: requestserver }, with_context = false) }}
</div>
</div>
{% if profile.parent %}
<div class="tab">
<h3 class="tab-title">Parent Request</h3>
<div class="tab-content">
<h3>
<a href="{{ path('_profiler', { token: profile.parent.token }) }}">Return to parent request</a>
<small>(token = {{ profile.parent.token }})</small>
</h3>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: profile.parent.getcollector('request').requestattributes }, with_context = false) }}
</div>
</div>
{% endif %}
{% if profile.children|length %}
<div class="tab">
<h3 class="tab-title">Sub Requests <span class="badge">{{ profile.children|length }}</span></h3>
<div class="tab-content">
{% for child in profile.children %}
<h3>
{{ _self.set_handler(child.getcollector('request').controller) }}
<small>(token = <a href="{{ path('_profiler', { token: child.token }) }}">{{ child.token }}</a>)</small>
</h3>
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: child.getcollector('request').requestattributes }, with_context = false) }}
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endblock %}
{% macro set_handler(controller, route, method) %}
{% if controller.class is defined -%}
{%- if method|default(false) %}<span class="sf-toolbar-status sf-toolbar-redirection-method">{{ method }}</span>{% endif -%}
{%- set link = controller.file|file_link(controller.line) %}
{%- if link %}<a href="{{ link }}" title="{{ controller.class }}">{% else %}<span title="{{ controller.class }}">{% endif %}
{%- if route|default(false) -%}
@{{ route }}
{%- else -%}
{{- controller.class|abbr_class|striptags -}}
{{- controller.method ? ' :: ' ~ controller.method -}}
{%- endif -%}
{%- if link %}</a>{% else %}</span>{% endif %}
{%- else -%}
<span>{{ route|default(controller) }}</span>
{%- endif %}
{% endmacro %}
@@ -0,0 +1,14 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}{% endblock %}
{% block menu %}
<span class="label">
<span class="icon">{{ source('@WebProfiler/Icon/router.svg') }}</span>
<strong>Routing</strong>
</span>
{% endblock %}
{% block panel %}
{{ render(controller('web_profiler.controller.router::panelAction', { token: token })) }}
{% endblock %}
@@ -0,0 +1,370 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
#collector-content .sf-serializer {
margin-bottom: 2em;
}
#collector-content .sf-serializer .trace {
border: var(--border);
background: var(--page-background);
padding: 10px;
margin: 0.5em 0;
overflow: auto;
}
#collector-content .sf-serializer .trace {
font-size: 12px;
}
#collector-content .sf-serializer .trace li {
margin-bottom: 0;
padding: 0;
}
#collector-content .sf-serializer .trace li.selected {
background: var(--highlight-selected-line);
}
</style>
{% endblock %}
{% block toolbar %}
{% if collector.handledCount > 0 %}
{% set icon %}
{{ source('@WebProfiler/Icon/serializer.svg') }}
<span class="sf-toolbar-value">
{{ collector.handledCount }}
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Total calls</b>
<span class="sf-toolbar-status">{{ collector.handledCount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Total time</b>
<span>
{{ '%.2f'|format(collector.totalTime * 1000) }} <span class="unit">ms</span>
</span>
</div>
<div class="detailed-metrics">
<div>
<div class="sf-toolbar-info-piece">
<b>Serialize</b>
<span class="sf-toolbar-status">{{ collector.data.serialize|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Deserialize</b>
<span class="sf-toolbar-status">{{ collector.data.deserialize|length }}</span>
</div>
</div>
<div>
<div class="sf-toolbar-info-piece">
<b>Encode</b>
<span class="sf-toolbar-status">{{ collector.data.encode|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Decode</b>
<span class="sf-toolbar-status">{{ collector.data.decode|length }}</span>
</div>
</div>
<div>
<div class="sf-toolbar-info-piece">
<b>Normalize</b>
<span class="sf-toolbar-status">{{ collector.data.normalize|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Denormalize</b>
<span class="sf-toolbar-status">{{ collector.data.denormalize|length }}</span>
</div>
</div>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{ not collector.handledCount ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/serializer.svg') }}</span>
<strong>Serializer</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Serializer</h2>
<div class="sf-serializer sf-reset">
{% if not collector.handledCount %}
<div class="empty empty-panel">
<p>Nothing was handled by the serializer.</p>
</div>
{% else %}
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.handledCount }}</span>
<span class="label">Handled</span>
</div>
<div class="metric">
<span class="value">{{ '%.2f'|format(collector.totalTime * 1000) }} <span class="unit">ms</span></span>
<span class="label">Total time</span>
</div>
</div>
<div class="sf-tabs">
{% for serializer in collector.serializerNames %}
{{ _self.render_serializer_tab(collector, serializer) }}
{% endfor %}
</div>
{% endif %}
</div>
{% endblock %}
{% macro render_serializer_tab(collector, serializer) %}
<div class="tab">
<h3 class="tab-title">{{ serializer }} <span class="badge">{{ collector.handledCount(serializer) }}</h3>
<div class="tab-content">
<div class="sf-tabs">
{{ _self.render_serialize_tab(collector.data(serializer), true, serializer) }}
{{ _self.render_serialize_tab(collector.data(serializer), false, serializer) }}
{{ _self.render_normalize_tab(collector.data(serializer), true, serializer) }}
{{ _self.render_normalize_tab(collector.data(serializer), false, serializer) }}
{{ _self.render_encode_tab(collector.data(serializer), true, serializer) }}
{{ _self.render_encode_tab(collector.data(serializer), false, serializer) }}
</div>
</div>
</div>
{% endmacro %}
{% macro render_serialize_tab(collectorData, serialize, serializer) %}
{% set data = serialize ? collectorData.serialize : collectorData.deserialize %}
{% set cellPrefix = serialize ? 'serialize' : 'deserialize' %}
<div class="tab {{ not data ? 'disabled' }}">
<h3 class="tab-title">{{ serialize ? 'serialize' : 'deserialize' }} <span class="badge">{{ data|length }}</h3>
<div class="tab-content">
{% if not data|length %}
<div class="empty">
<p>Nothing was {{ serialize ? 'serialized' : 'deserialized' }}.</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th>Data</th>
<th>Context</th>
<th>Normalizer</th>
<th>Encoder</th>
<th>Time</th>
<th>Caller</th>
</tr>
</thead>
<tbody>
{% for item in data %}
<tr>
<td>{{ _self.render_data_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_context_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_normalizer_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_encoder_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_time_cell(item) }}</td>
<td>{{ _self.render_caller_cell(item, loop.index, cellPrefix, serializer) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endmacro %}
{% macro render_caller_cell(item, index, method, serializer) %}
{% if item.caller is defined %}
{% set trace_id = 'sf-trace-' ~ serializer ~ '-' ~ method ~ '-' ~ index %}
<span class="metadata">
{% set caller = item.caller %}
{% if caller.line %}
{% set link = caller.file|file_link(caller.line) %}
{% if link %}
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
{% else %}
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
{% endif %}
{% else %}
{{ caller.name }}
{% endif %}
line <a class="text-small sf-toggle" data-toggle-selector="#{{ trace_id }}">{{ caller.line }}</a>
</span>
<div class="sf-serializer-compact hidden" id="{{ trace_id }}">
<div class="trace">
{{ caller.file|file_excerpt(caller.line)|replace({
'#DD0000': 'var(--highlight-string)',
'#007700': 'var(--highlight-keyword)',
'#0000BB': 'var(--highlight-default)',
'#FF8000': 'var(--highlight-comment)'
})|raw }}
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_normalize_tab(collectorData, normalize, serializer) %}
{% set data = normalize ? collectorData.normalize : collectorData.denormalize %}
{% set cellPrefix = normalize ? 'normalize' : 'denormalize' %}
<div class="tab {{ not data ? 'disabled' }}">
<h3 class="tab-title">{{ normalize ? 'normalize' : 'denormalize' }} <span class="badge">{{ data|length }}</h3>
<div class="tab-content">
{% if not data|length %}
<div class="empty">
<p>Nothing was {{ normalize ? 'normalized' : 'denormalized' }}.</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th>Data</th>
<th>Context</th>
<th>Normalizer</th>
<th>Time</th>
<th>Caller</th>
</tr>
</thead>
<tbody>
{% for item in data %}
<tr>
<td>{{ _self.render_data_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_context_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_normalizer_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_time_cell(item) }}</td>
<td>{{ _self.render_caller_cell(item, loop.index, cellPrefix, serializer) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endmacro %}
{% macro render_encode_tab(collectorData, encode, serializer) %}
{% set data = encode ? collectorData.encode : collectorData.decode %}
{% set cellPrefix = encode ? 'encode' : 'decode' %}
<div class="tab {{ not data ? 'disabled' }}">
<h3 class="tab-title">{{ encode ? 'encode' : 'decode' }} <span class="badge">{{ data|length }}</h3>
<div class="tab-content">
{% if not data|length %}
<div class="empty">
<p>Nothing was {{ encode ? 'encoded' : 'decoded' }}.</p>
</div>
{% else %}
<table>
<thead>
<tr>
<th>Data</th>
<th>Context</th>
<th>Encoder</th>
<th>Time</th>
<th>Caller</th>
</tr>
</thead>
<tbody>
{% for item in data %}
<tr>
<td>{{ _self.render_data_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_context_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_encoder_cell(item, loop.index, cellPrefix, serializer) }}</td>
<td>{{ _self.render_time_cell(item) }}</td>
<td>{{ _self.render_caller_cell(item, loop.index, cellPrefix, serializer) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endmacro %}
{% macro render_data_cell(item, index, method, serializer) %}
{% set data_id = 'data-' ~ serializer ~ '-' ~ method ~ '-' ~ index %}
<span class="nowrap">{{ item.dataType }}</span>
<div>
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ data_id }}" data-toggle-alt-content="Hide contents">Show contents</a>
<div id="{{ data_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(item.data) }}
</div>
</div>
{% endmacro %}
{% macro render_context_cell(item, index, method, serializer) %}
{% set context_id = 'context-' ~ serializer ~ '-' ~ method ~ '-' ~ index %}
{% if item.type %}
<span class="nowrap">Type: {{ item.type }}</span>
<div class="nowrap">Format: {{ item.format ? item.format : 'none' }}</div>
{% else %}
<span class="nowrap">Format: {{ item.format ? item.format : 'none' }}</span>
{% endif %}
<div>
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide context">Show context</a>
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
{{ profiler_dump(item.context) }}
</div>
</div>
{% endmacro %}
{% macro render_normalizer_cell(item, index, method, serializer) %}
{% set nested_normalizers_id = 'nested-normalizers-' ~ serializer ~ '-' ~ method ~ '-' ~ index %}
{% if item.normalizer is defined %}
<span class="nowrap"><a href="{{ item.normalizer.file|file_link(item.normalizer.line) }}" title="{{ item.normalizer.file }}">{{ item.normalizer.class }}</a> ({{ '%.2f'|format(item.normalizer.time * 1000) }} ms)</span>
{% endif %}
{% if item.normalization|length > 1 %}
<div>
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ nested_normalizers_id }}" data-toggle-alt-content="Hide nested normalizers">Show nested normalizers</a>
<div id="{{ nested_normalizers_id }}" class="context sf-toggle-content sf-toggle-hidden">
<ul class="text-small" style="line-height:80%;margin-top:10px">
{% for normalizer in item.normalization %}
<li><span class="nowrap">x{{ normalizer.calls }} <a href="{{ normalizer.file|file_link(normalizer.line) }}" title="{{ normalizer.file }}">{{ normalizer.class }}</a> ({{ '%.2f'|format(normalizer.time * 1000) }} ms)</span></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_encoder_cell(item, index, method, serializer) %}
{% set nested_encoders_id = 'nested-encoders-' ~ serializer ~ '-' ~ method ~ '-' ~ index %}
{% if item.encoder is defined %}
<span class="nowrap"><a href="{{ item.encoder.file|file_link(item.encoder.line) }}" title="{{ item.encoder.file }}">{{ item.encoder.class }}</a> ({{ '%.2f'|format(item.encoder.time * 1000) }} ms)</span>
{% endif %}
{% if item.encoding|length > 1 %}
<div>
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ nested_encoders_id }}" data-toggle-alt-content="Hide nested encoders">Show nested encoders</a>
<div id="{{ nested_encoders_id }}" class="context sf-toggle-content sf-toggle-hidden">
<ul class="text-small" style="line-height:80%;margin-top:10px">
{% for encoder in item.encoding %}
<li><span class="nowrap">x{{ encoder.calls }} <a href="{{ encoder.file|file_link(encoder.line) }}" title="{{ encoder.file }}">{{ encoder.class }}</a> ({{ '%.2f'|format(encoder.time * 1000) }} ms)</span></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_time_cell(item) %}
<span class="nowrap">{{ '%.2f'|format(item.time * 1000) }} ms</span>
{% endmacro %}
@@ -0,0 +1,67 @@
/* Legend */
.sf-profiler-timeline .legends .timeline-category {
border: none;
background: none;
border-left: 1em solid transparent;
line-height: 1em;
margin: 0 1em 0 0;
padding: 0 0.5em;
display: none;
opacity: 0.5;
}
.sf-profiler-timeline .legends .timeline-category.active {
opacity: 1;
}
.sf-profiler-timeline .legends .timeline-category.present {
display: inline-block;
}
.timeline-graph {
margin: 1em 0;
width: 100%;
background-color: var(--table-background);
border: 1px solid var(--table-border-color);
}
/* Typography */
.timeline-graph .timeline-label {
font-family: var(--font-sans-serif);
font-size: 12px;
line-height: 12px;
font-weight: normal;
fill: var(--color-text);
}
.timeline-graph .timeline-label .timeline-sublabel {
margin-left: 1em;
fill: var(--color-muted);
}
.timeline-graph .timeline-subrequest,
.timeline-graph .timeline-border {
fill: none;
stroke: var(--table-border-color);
stroke-width: 1px;
}
.timeline-graph .timeline-subrequest {
fill: url(#subrequest);
fill-opacity: 0.5;
}
.timeline-subrequest-pattern {
fill: var(--gray-200);
}
.theme-dark .timeline-subrequest-pattern {
fill: var(--gray-600);
}
/* Timeline periods */
.timeline-graph .timeline-period {
stroke-width: 0;
}
@@ -0,0 +1,249 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
#timeline-control {
background: var(--table-background);
box-shadow: var(--shadow);
margin: 1em 0;
padding: 10px;
}
#timeline-control label {
font-weight: bold;
margin-right: 1em;
}
#timeline-control input {
background: var(--metric-value-background);
font-size: 16px;
padding: 4px;
text-align: right;
width: 5em;
}
#timeline-control .help {
margin-left: 1em;
}
.sf-profiler-timeline .legends {
font-size: 12px;
line-height: 1.5em;
}
.sf-profiler-timeline .legends button {
color: var(--color-text);
}
.sf-profiler-timeline + p.help {
margin-top: 0;
}
</style>
{% endblock %}
{% block toolbar %}
{% set has_time_events = collector.events|length > 0 %}
{% set total_time = has_time_events ? '%.0f'|format(collector.duration) : 'n/a' %}
{% set initialization_time = collector.events|length ? '%.0f'|format(collector.inittime) : 'n/a' %}
{% set status_color = has_time_events and collector.duration > 1000 ? 'yellow' %}
{% set icon %}
{{ source('@WebProfiler/Icon/time.svg') }}
<span class="sf-toolbar-value">{{ total_time }}</span>
<span class="sf-toolbar-label">ms</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Total time</b>
<span>{{ total_time }} ms</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Initialization time</b>
<span>{{ initialization_time }} ms</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endblock %}
{% block menu %}
<span class="label">
<span class="icon">{{ source('@WebProfiler/Icon/time.svg') }}</span>
<strong>Performance</strong>
</span>
{% endblock %}
{% block panel %}
{% set has_time_events = collector.events|length > 0 %}
<h2>Performance metrics</h2>
<div class="metrics">
<div class="metric-group">
<div class="metric">
<span class="value">{{ '%.0f'|format(collector.duration) }} <span class="unit">ms</span></span>
<span class="label">Total execution time</span>
</div>
<div class="metric">
<span class="value">{{ '%.0f'|format(collector.inittime) }} <span class="unit">ms</span></span>
<span class="label">Symfony initialization</span>
</div>
</div>
{% if profile.collectors.memory %}
<div class="metric-divider"></div>
<div class="metric">
<span class="value">{{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} <span class="unit">MiB</span></span>
<span class="label">Peak memory usage</span>
</div>
{% endif %}
{% if profile.children|length > 0 %}
<div class="metric-divider"></div>
<div class="metric-group">
<div class="metric">
<span class="value">{{ profile.children|length }}</span>
<span class="label">Sub-{{ profile_type|title }}{{ profile.children|length > 1 ? 's' }}</span>
</div>
{% set subrequests_time = has_time_events
? profile.children|reduce((total, child) => total + child.getcollector('time').events.__section__.duration, 0)
: 'n/a' %}
<div class="metric">
<span class="value">{{ subrequests_time }} <span class="unit">ms</span></span>
<span class="label">Sub-{{ profile_type|title }}{{ profile.children|length > 1 ? 's' }} time</span>
</div>
</div>
{% endif %}
</div>
<h2>Execution timeline</h2>
{% if not collector.isStopwatchInstalled() %}
<div class="empty empty-panel">
<p>The Stopwatch component is not installed. If you want to see timing events, run: <code>composer require symfony/stopwatch</code>.</p>
</div>
{% elseif collector.events is empty %}
<div class="empty">
<p>No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.</p>
</div>
{% else %}
{{ block('panelContent') }}
{% endif %}
{% endblock %}
{% block panelContent %}
<form id="timeline-control" action="" method="get">
<input type="hidden" name="panel" value="time">
<label for="threshold">Threshold</label>
<input type="number" name="threshold" id="threshold" value="1" min="0" placeholder="1.1"> ms
<span class="help">(timeline only displays events with a duration longer than this threshold)</span>
</form>
{% if profile.parent %}
<h3 class="dump-inline">
Sub-{{ profile_type|title }} {{ profiler_dump(profile.getcollector('request').requestattributes.get('_controller')) }}
<small>
{{ collector.events.__section__.duration }} ms
<a class="newline" href="{{ path('_profiler', { token: profile.parent.token, panel: 'time' }) }}">Return to parent {{ profile_type }}</a>
</small>
</h3>
{% elseif profile.children|length > 0 %}
<h3>
Main {{ profile_type|title }} <small>{{ collector.events.__section__.duration }} ms</small>
</h3>
{% endif %}
{{ _self.display_timeline(token, collector.events, collector.events.__section__.origin) }}
{% if profile.children|length %}
<p class="help">Note: sections with a striped background correspond to sub-{{ profile_type }}s.</p>
<h3>Sub-{{ profile_type }}s <small>({{ profile.children|length }})</small></h3>
{% for child in profile.children %}
{% set events = child.getcollector('time').events %}
<h4>
<a href="{{ path('_profiler', { token: child.token, panel: 'time' }) }}">{{ child.getcollector('request').identifier }}</a>
<small>{{ events.__section__.duration }} ms</small>
</h4>
{{ _self.display_timeline(child.token, events, collector.events.__section__.origin) }}
{% endfor %}
{% endif %}
<svg id="timeline-template" width="0" height="0">
<defs>
<pattern id="subrequest" class="timeline-subrequest-pattern" patternUnits="userSpaceOnUse" width="20" height="20" viewBox="0 0 40 40">
<path d="M0 40L40 0H20L0 20M40 40V20L20 40"/>
</pattern>
</defs>
</svg>
<style>
{{ include('@WebProfiler/Collector/time.css.twig') }}
</style>
<script>
{{ source('@WebProfiler/Collector/time.js') }}
</script>
{% endblock %}
{% macro dump_request_data(token, events, origin) %}
{% autoescape 'js' %}
{
id: "{{ token }}",
left: {{ "%F"|format(events.__section__.origin - origin) }},
end: "{{ '%F'|format(events.__section__.endtime) }}",
events: [ {{ _self.dump_events(events) }} ],
}
{% endautoescape %}
{% endmacro %}
{% macro dump_events(events) %}
{% autoescape 'js' %}
{% for name, event in events %}
{% if '__section__' != name %}
{
name: "{{ name }}",
category: "{{ event.category }}",
origin: {{ "%F"|format(event.origin) }},
starttime: {{ "%F"|format(event.starttime) }},
endtime: {{ "%F"|format(event.endtime) }},
duration: {{ "%F"|format(event.duration) }},
memory: {{ "%.1F"|format(event.memory / 1024 / 1024) }},
elements: {},
periods: [
{%- for period in event.periods -%}
{
start: {{ "%F"|format(period.starttime) }},
end: {{ "%F"|format(period.endtime) }},
duration: {{ "%F"|format(period.duration) }},
elements: {}
},
{%- endfor -%}
],
},
{% endif %}
{% endfor %}
{% endautoescape %}
{% endmacro %}
{% macro display_timeline(token, events, origin) %}
<div class="sf-profiler-timeline">
<div id="legend-{{ token }}" class="legends"></div>
<svg id="timeline-{{ token }}" class="timeline-graph"></svg>
<script>{% autoescape 'js' %}
window.addEventListener('load', function onLoad() {
const theme = new Theme();
new TimelineEngine(
theme,
new SvgRenderer(document.getElementById('timeline-{{ token }}')),
new Legend(document.getElementById('legend-{{ token }}'), theme),
document.getElementById('threshold'),
{{ _self.dump_request_data(token, events, origin) }}
);
});
{% endautoescape %}</script>
</div>
{% endmacro %}
@@ -0,0 +1,457 @@
'use strict';
class TimelineEngine {
/**
* @param {Theme} theme
* @param {Renderer} renderer
* @param {Legend} legend
* @param {Element} threshold
* @param {Object} request
* @param {Number} eventHeight
* @param {Number} horizontalMargin
*/
constructor(theme, renderer, legend, threshold, request, eventHeight = 36, horizontalMargin = 10) {
this.theme = theme;
this.renderer = renderer;
this.legend = legend;
this.threshold = threshold;
this.request = request;
this.scale = renderer.width / request.end;
this.eventHeight = eventHeight;
this.horizontalMargin = horizontalMargin;
this.labelY = Math.round(this.eventHeight * 0.48);
this.periodY = Math.round(this.eventHeight * 0.66);
this.FqcnMatcher = /\\([^\\]+)$/i;
this.origin = null;
this.createEventElements = this.createEventElements.bind(this);
this.createBackground = this.createBackground.bind(this);
this.createPeriod = this.createPeriod.bind(this);
this.render = this.render.bind(this);
this.renderEvent = this.renderEvent.bind(this);
this.renderPeriod = this.renderPeriod.bind(this);
this.onResize = this.onResize.bind(this);
this.isActive = this.isActive.bind(this);
this.threshold.addEventListener('change', this.render);
this.legend.addEventListener('change', this.render);
window.addEventListener('resize', this.onResize);
this.createElements();
this.render();
}
onResize() {
this.renderer.measure();
this.setScale(this.renderer.width / this.request.end);
}
setScale(scale) {
if (scale !== this.scale) {
this.scale = scale;
this.render();
}
}
createElements() {
this.origin = this.renderer.setFullVerticalLine(this.createBorder(), 0);
this.renderer.add(this.origin);
this.request.events
.filter(event => event.category === 'section')
.map(this.createBackground)
.forEach(this.renderer.add);
this.request.events
.map(this.createEventElements)
.forEach(this.renderer.add);
}
createBackground(event) {
const subrequest = event.name === '__section__.child';
const background = this.renderer.create('rect', subrequest ? 'timeline-subrequest' : 'timeline-border');
event.elements = Object.assign(event.elements || {}, { background });
return background;
}
createEventElements(event) {
const { name, category, duration, memory, periods } = event;
const border = this.renderer.setFullHorizontalLine(this.createBorder(), 0);
const lines = periods.map(period => this.createPeriod(period, category));
const label = this.createLabel(this.getShortName(name), duration, memory, periods[0]);
const title = this.renderer.createTitle(name);
const group = this.renderer.group([title, border, label].concat(lines), this.theme.getCategoryColor(event.category));
event.elements = Object.assign(event.elements || {}, { group, label, border });
this.legend.add(event.category)
return group;
}
createLabel(name, duration, memory, period) {
const label = this.renderer.createText(name, period.start * this.scale, this.labelY, 'timeline-label');
const sublabel = this.renderer.createTspan(` ${duration} ms / ${memory} MiB`, 'timeline-sublabel');
label.appendChild(sublabel);
return label;
}
createPeriod(period, category) {
const timeline = this.renderer.createPath(null, 'timeline-period', this.theme.getCategoryColor(category));
period.draw = category === 'section' ? this.renderer.setSectionLine : this.renderer.setPeriodLine;
period.elements = Object.assign(period.elements || {}, { timeline });
return timeline;
}
createBorder() {
return this.renderer.createPath(null, 'timeline-border');
}
isActive(event) {
const { duration, category } = event;
return duration >= this.threshold.value && this.legend.isActive(category);
}
render() {
const events = this.request.events.filter(this.isActive);
const width = this.renderer.width + this.horizontalMargin * 2;
const height = this.eventHeight * events.length;
// Set view box
this.renderer.setViewBox(-this.horizontalMargin, 0, width, height);
// Show 0ms origin
this.renderer.setFullVerticalLine(this.origin, 0);
// Render all events
this.request.events.forEach(event => this.renderEvent(event, events.indexOf(event)));
}
renderEvent(event, index) {
const { name, category, duration, memory, periods, elements } = event;
const { group, label, border, background } = elements;
const visible = index >= 0;
group.setAttribute('visibility', visible ? 'visible' : 'hidden');
if (background) {
background.setAttribute('visibility', visible ? 'visible' : 'hidden');
if (visible) {
const [min, max] = this.getEventLimits(event);
this.renderer.setFullRectangle(background, min * this.scale, max * this.scale);
}
}
if (visible) {
// Position the group
group.setAttribute('transform', `translate(0, ${index * this.eventHeight})`);
// Update top border
this.renderer.setFullHorizontalLine(border, 0);
// render label and ensure it doesn't escape the viewport
this.renderLabel(label, event);
// Update periods
periods.forEach(this.renderPeriod);
}
}
renderLabel(label, event) {
const width = this.getLabelWidth(label);
const [min, max] = this.getEventLimits(event);
const alignLeft = (min * this.scale) + width <= this.renderer.width;
label.setAttribute('x', (alignLeft ? min : max) * this.scale);
label.setAttribute('text-anchor', alignLeft ? 'start' : 'end');
}
renderPeriod(period) {
const { elements, start, duration } = period;
period.draw(elements.timeline, start * this.scale, this.periodY, Math.max(duration * this.scale, 1));
}
getLabelWidth(label) {
if (typeof label.width === 'undefined') {
label.width = label.getBBox().width;
}
return label.width;
}
getEventLimits(event) {
if (typeof event.limits === 'undefined') {
const { periods } = event;
event.limits = [
periods[0].start,
periods[periods.length - 1].end
];
}
return event.limits;
}
getShortName(name) {
const matches = this.FqcnMatcher.exec(name);
if (matches) {
return matches[1];
}
return name;
}
}
class Legend {
constructor(element, theme) {
this.element = element;
this.theme = theme;
this.toggle = this.toggle.bind(this);
this.createCategory = this.createCategory.bind(this);
this.categories = [];
this.theme.getDefaultCategories().forEach(this.createCategory);
}
add(category) {
this.get(category).classList.add('present');
}
createCategory(category) {
const element = document.createElement('button');
element.className = `timeline-category active`;
element.style.borderColor = this.theme.getCategoryColor(category);
element.innerText = category;
element.value = category;
element.type = 'button';
element.addEventListener('click', this.toggle);
this.element.appendChild(element);
this.categories.push(element);
return element;
}
toggle(event) {
event.target.classList.toggle('active');
this.emit('change');
}
isActive(category) {
return this.get(category).classList.contains('active');
}
get(category) {
return this.categories.find(element => element.value === category) || this.createCategory(category);
}
emit(name) {
this.element.dispatchEvent(new Event(name));
}
addEventListener(name, callback) {
this.element.addEventListener(name, callback);
}
removeEventListener(name, callback) {
this.element.removeEventListener(name, callback);
}
}
class SvgRenderer {
/**
* @param {SVGElement} element
*/
constructor(element) {
this.ns = 'http://www.w3.org/2000/svg';
this.width = null;
this.viewBox = {};
this.element = element;
this.add = this.add.bind(this);
this.setViewBox(0, 0, 0, 0);
this.measure();
}
setViewBox(x, y, width, height) {
this.viewBox = { x, y, width, height };
this.element.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
}
measure() {
this.width = this.element.getBoundingClientRect().width;
}
add(element) {
this.element.appendChild(element);
}
group(elements, className) {
const group = this.create('g', className);
elements.forEach(element => group.appendChild(element));
return group;
}
setHorizontalLine(element, x, y, width) {
element.setAttribute('d', `M${x},${y} h${width}`);
return element;
}
setVerticalLine(element, x, y, height) {
element.setAttribute('d', `M${x},${y} v${height}`);
return element;
}
setFullHorizontalLine(element, y) {
return this.setHorizontalLine(element, this.viewBox.x, y, this.viewBox.width);
}
setFullVerticalLine(element, x) {
return this.setVerticalLine(element, x, this.viewBox.y, this.viewBox.height);
}
setFullRectangle(element, min, max) {
element.setAttribute('x', min);
element.setAttribute('y', this.viewBox.y);
element.setAttribute('width', max - min);
element.setAttribute('height', this.viewBox.height);
}
setSectionLine(element, x, y, width, height = 4, markerSize = 6) {
const totalHeight = height + markerSize;
const maxMarkerWidth = Math.min(markerSize, width / 2);
const widthWithoutMarker = Math.max(0, width - (maxMarkerWidth * 2));
element.setAttribute('d', `M${x},${y + totalHeight} v${-totalHeight} h${width} v${totalHeight} l${-maxMarkerWidth} ${-markerSize} h${-widthWithoutMarker} Z`);
}
setPeriodLine(element, x, y, width, height = 4, markerWidth = 2, markerHeight = 4) {
const totalHeight = height + markerHeight;
const maxMarkerWidth = Math.min(markerWidth, width);
element.setAttribute('d', `M${x + maxMarkerWidth},${y + totalHeight} h${-maxMarkerWidth} v${-totalHeight} h${width} v${height} h${maxMarkerWidth-width}Z`);
}
createText(content, x, y, className) {
const element = this.create('text', className);
element.setAttribute('x', x);
element.setAttribute('y', y);
element.textContent = content;
return element;
}
createTspan(content, className) {
const element = this.create('tspan', className);
element.textContent = content;
return element;
}
createTitle(content) {
const element = this.create('title');
element.textContent = content;
return element;
}
createPath(path = null, className = null, color = null) {
const element = this.create('path', className);
if (path) {
element.setAttribute('d', path);
}
if (color) {
element.setAttribute('fill', color);
}
return element;
}
create(name, className = null) {
const element = document.createElementNS(this.ns, name);
if (className) {
element.setAttribute('class', className);
}
return element;
}
}
class Theme {
constructor(element) {
this.reservedCategoryColors = {
'default': '#737373',
'section': '#a3a3a3',
'event_listener': '#54aeff',
'template': '#4ac26b',
'doctrine': '#fd8c73',
'messenger_middleware': '#ff8182',
'controller.argument_value_resolver': '#c297ff',
'http_client': '#d4a72c',
};
this.customCategoryColors = [
'#d4a72c', // dark yellow
'#ffaba8', // light red
'#e6af05', // yellow
'#6fdd8b', // light green
'#76e3ea', // cyan
'#a475f9', // light purple
];
this.getCategoryColor = this.getCategoryColor.bind(this);
this.getDefaultCategories = this.getDefaultCategories.bind(this);
}
getDefaultCategories() {
return Object.keys(this.reservedCategoryColors);
}
getCategoryColor(category) {
return this.reservedCategoryColors[category] || this.getRandomColor(category);
}
getRandomColor(category) {
// instead of pure randomness, colors are assigned deterministically based on the
// category name, to ensure that each custom category always displays the same color
return this.customCategoryColors[this.hash(category) % this.customCategoryColors.length];
}
// copied from https://github.com/darkskyapp/string-hash
hash(string) {
var hash = 5381;
var i = string.length;
while(i) {
hash = (hash * 33) ^ string.charCodeAt(--i);
}
return hash >>> 0;
}
}
@@ -0,0 +1,227 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block toolbar %}
{% if collector.messages|length %}
{% set icon %}
{{ source('@WebProfiler/Icon/translation.svg') }}
{% set status_color = collector.countMissings ? 'red' : collector.countFallbacks ? 'yellow' %}
{% set error_count = collector.countMissings + collector.countFallbacks %}
<span class="sf-toolbar-value">{{ error_count ?: collector.countDefines }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Default locale</b>
<span class="sf-toolbar-status">
{{ collector.locale|default('-') }}
</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Missing messages</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countMissings ? 'red' }}">
{{ collector.countMissings }}
</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Fallback messages</b>
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countFallbacks ? 'yellow' }}">
{{ collector.countFallbacks }}
</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Defined messages</b>
<span class="sf-toolbar-status">{{ collector.countDefines }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label label-status-{{ collector.countMissings ? 'error' : collector.countFallbacks ? 'warning' }} {{ collector.messages is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/translation.svg') }}</span>
<strong>Translation</strong>
{% if collector.countMissings or collector.countFallbacks %}
{% set error_count = collector.countMissings + collector.countFallbacks %}
<span class="count">
<span>{{ error_count }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>Translation</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ collector.locale|default('-') }}</span>
<span class="label">Default locale</span>
</div>
<div class="metric">
<span class="value">{{ collector.fallbackLocales|join(', ')|default('-') }}</span>
<span class="label">Fallback locale{{ collector.fallbackLocales|length != 1 ? 's' }}</span>
</div>
</div>
<h2>Messages</h2>
{% if collector.messages is empty %}
<div class="empty empty-panel">
<p>No translations have been called.</p>
</div>
{% else %}
{% block messages %}
{# sort translation messages in groups #}
{% set messages_defined, messages_missing, messages_fallback = [], [], [] %}
{% for message in collector.messages %}
{% if message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_DEFINED') %}
{% set messages_defined = messages_defined|merge([message]) %}
{% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING') %}
{% set messages_missing = messages_missing|merge([message]) %}
{% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK') %}
{% set messages_fallback = messages_fallback|merge([message]) %}
{% endif %}
{% endfor %}
<div class="sf-tabs">
<div class="tab {{ collector.countMissings == 0 ? 'active' }}">
<h3 class="tab-title">Defined <span class="badge">{{ collector.countDefines }}</span></h3>
<div class="tab-content">
<p class="help">
These messages are correctly translated into the given locale.
</p>
{% if messages_defined is empty %}
<div class="empty">
<p>None of the used translation messages are defined for the given locale.</p>
</div>
{% else %}
{% block defined_messages %}
{{ _self.render_table(messages_defined) }}
{% endblock %}
{% endif %}
</div>
</div>
<div class="tab">
<h3 class="tab-title">Fallback <span class="badge {{ collector.countFallbacks ? 'status-warning' }}">{{ collector.countFallbacks }}</span></h3>
<div class="tab-content">
<p class="help">
These messages are not available for the given locale
but Symfony found them in the fallback locale catalog.
</p>
{% if messages_fallback is empty %}
<div class="empty">
<p>No fallback translation messages were used.</p>
</div>
{% else %}
{% block fallback_messages %}
{{ _self.render_table(messages_fallback, true) }}
{% endblock %}
{% endif %}
</div>
</div>
<div class="tab {{ collector.countMissings > 0 ? 'active' }}">
<h3 class="tab-title">Missing <span class="badge {{ collector.countMissings ? 'status-error' }}">{{ collector.countMissings }}</span></h3>
<div class="tab-content">
<p class="help">
These messages are not available for the given locale and cannot
be found in the fallback locales. Add them to the translation
catalogue to avoid Symfony outputting untranslated contents.
</p>
{% if messages_missing is empty %}
<div class="empty">
<p>There are no messages of this category.</p>
</div>
{% else %}
{% block missing_messages %}
{{ _self.render_table(messages_missing) }}
{% endblock %}
{% endif %}
</div>
</div>
</div>
{% endblock messages %}
{% endif %}
{% if collector.globalParameters|default([]) %}
<h2>Global parameters</h2>
<table>
<thead>
<tr>
<th>Message ID</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for id, value in collector.globalParameters %}
<tr>
<td class="font-normal text-small nowrap">{{ id }}</td>
<td class="font-normal text-small nowrap">{{ profiler_dump(value) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endblock %}
{% macro render_table(messages, is_fallback) %}
<table>
<thead>
<tr>
<th>Locale</th>
{% if is_fallback %}
<th>Fallback locale</th>
{% endif %}
<th>Domain</th>
<th>Times used</th>
<th>Message ID</th>
<th>Message Preview</th>
</tr>
</thead>
<tbody>
{% for message in messages %}
<tr>
<td class="font-normal text-small nowrap">{{ message.locale }}</td>
{% if is_fallback %}
<td class="font-normal text-small nowrap">{{ message.fallbackLocale|default('-') }}</td>
{% endif %}
<td class="font-normal text-small text-bold nowrap">{{ message.domain }}</td>
<td class="font-normal text-small nowrap">{{ message.count }}</td>
<td>
<span class="{{ message.id|length < 64 ? 'nowrap' }}">{{ message.id }}</span>
{% if message.transChoiceNumber is not null %}
<small class="newline">(pluralization is used)</small>
{% endif %}
{% if message.parameters|length > 0 %}
<button class="btn-link newline text-small sf-toggle" data-toggle-selector="#parameters-{{ loop.index }}" data-toggle-alt-content="Hide parameters">Show parameters</button>
<div id="parameters-{{ loop.index }}" class="hidden">
{% for parameters in message.parameters %}
{{ profiler_dump(parameters, maxDepth=1) }}
{% endfor %}
</div>
{% endif %}
</td>
<td class="prewrap">{{ message.translation }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endmacro %}
@@ -0,0 +1,168 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
#twig-dump pre {
font-size: var(--font-size-monospace);
line-height: 1.7;
background-color: var(--page-background);
border: var(--border);
border-radius: 6px;
padding: 15px;
box-shadow: 0 0 1px rgba(128, 128, 128, .2);
}
#twig-dump span {
border-radius: 2px;
padding: 1px 2px;
}
#twig-dump .status-error { background: transparent; color: var(--color-error); }
#twig-dump .status-warning { background: rgba(240, 181, 24, 0.3); }
#twig-dump .status-success { background: rgba(100, 189, 99, 0.2); }
#twig-dump .status-info { background: var(--info-background); }
.theme-dark #twig-dump .status-warning { color: var(--yellow-200); }
.theme-dark #twig-dump .status-success { color: var(--green-200); }
#twig-table tbody td {
position: relative;
}
#twig-table tbody td div {
margin: 0;
}
#twig-table .template-file-path {
color: var(--color-muted);
display: block;
}
</style>
{% endblock %}
{% block toolbar %}
{% set time = collector.templatecount ? '%0.0f'|format(collector.time) : 'n/a' %}
{% set icon %}
{{ source('@WebProfiler/Icon/twig.svg') }}
<span class="sf-toolbar-value">{{ time }}</span>
<span class="sf-toolbar-label">ms</span>
{% endset %}
{% set text %}
{% set template = collector.templates|keys|first %}
{% set file = collector.templatePaths[template]|default(false) %}
{% set link = file ? file|file_link(1) : false %}
<div class="sf-toolbar-info-piece">
<b>Entry View</b>
<span>
{% if link %}
<a href="{{ link }}" title="{{ file }}">
{{ template }}
</a>
{% else %}
{{ template }}
{% endif %}
</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Render Time</b>
<span>{{ time }} ms</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Template Calls</b>
<span class="sf-toolbar-status">{{ collector.templatecount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Block Calls</b>
<span class="sf-toolbar-status">{{ collector.blockcount }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Macro Calls</b>
<span class="sf-toolbar-status">{{ collector.macrocount }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endblock %}
{% block menu %}
<span class="label {{ 0 == collector.templateCount ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/twig.svg') }}</span>
<strong>Twig</strong>
</span>
{% endblock %}
{% block panel %}
{% if collector.templatecount == 0 %}
<h2>Twig</h2>
<div class="empty empty-panel">
<p>No Twig templates were rendered.</p>
</div>
{% else %}
<h2>Twig Metrics</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ '%0.0f'|format(collector.time) }} <span class="unit">ms</span></span>
<span class="label">Render time</span>
</div>
<div class="metric-divider"></div>
<div class="metric-group">
<div class="metric">
<span class="value">{{ collector.templatecount }}</span>
<span class="label">Template calls</span>
</div>
<div class="metric">
<span class="value">{{ collector.blockcount }}</span>
<span class="label">Block calls</span>
</div>
<div class="metric">
<span class="value">{{ collector.macrocount }}</span>
<span class="label">Macro calls</span>
</div>
</div>
</div>
<p class="help">
Render time includes sub-requests rendering time (if any).
</p>
<h2>Rendered Templates</h2>
<table id="twig-table">
<thead>
<tr>
<th scope="col">Template Name &amp; Path</th>
<th class="num-col" scope="col">Render Count</th>
</tr>
</thead>
<tbody>
{% for template, count in collector.templates %}
<tr>
{% set file = collector.templatePaths[template]|default(false) %}
{% set link = file ? file|file_link(1) : false %}
<td class="font-normal">
{% if link %}
<a href="{{ link }}" title="{{ file }}" class="stretched-link">
{{ template }}
<span class="template-file-path">{{ file|file_relative|default(file) }}</span>
</a>
{% else %}
{{ template }}
{% endif %}
</td>
<td class="font-normal num-col">{{ count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h2>Rendering Call Graph</h2>
<div id="twig-dump">
{{ collector.htmlcallgraph }}
</div>
{% endif %}
{% endblock %}
@@ -0,0 +1,132 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block head %}
{{ parent() }}
<style>
#collector-content .sf-validator {
margin-bottom: 2em;
}
#collector-content .sf-validator .sf-validator-context,
#collector-content .sf-validator .trace {
border: var(--border);
background: var(--base-0);
padding: 10px;
margin: 0.5em 0;
overflow: auto;
}
#collector-content .sf-validator .trace {
font-size: 12px;
}
#collector-content .sf-validator .trace li {
margin-bottom: 0;
padding: 0;
}
#collector-content .sf-validator .trace li.selected {
background: var(--highlight-selected-line);
}
</style>
{% endblock %}
{% block toolbar %}
{% if collector.violationsCount > 0 or collector.calls|length %}
{% set status_color = collector.violationsCount ? 'red' %}
{% set icon %}
{{ source('@WebProfiler/Icon/validator.svg') }}
<span class="sf-toolbar-value">
{{ collector.violationsCount ?: collector.calls|length }}
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Validator calls</b>
<span class="sf-toolbar-status">{{ collector.calls|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Number of violations</b>
<span class="sf-toolbar-status {{- collector.violationsCount > 0 ? ' sf-toolbar-status-red' }}">{{ collector.violationsCount }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{- collector.violationsCount ? ' label-status-error' }} {{ collector.calls is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/validator.svg') }}</span>
<strong>Validator</strong>
{% if collector.violationsCount > 0 %}
<span class="count">
<span>{{ collector.violationsCount }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
<h2>Validator calls</h2>
{% for call in collector.calls %}
<div class="sf-validator sf-reset">
<span class="metadata">In
{% set caller = call.caller %}
{% if caller.line %}
{% set link = caller.file|file_link(caller.line) %}
{% if link %}
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
{% else %}
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
{% endif %}
{% else %}
{{ caller.name }}
{% endif %}
line <button type="button" class="btn-link text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ caller.line }}</button> (<button type="button" class="btn-link text-small sf-toggle" data-toggle-selector="#sf-context-{{ loop.index0 }}">context</button>):
</span>
<div class="sf-validator-compact hidden" id="sf-trace-{{ loop.index0 }}">
<div class="trace">
{{ caller.file|file_excerpt(caller.line)|replace({
'#DD0000': 'var(--highlight-string)',
'#007700': 'var(--highlight-keyword)',
'#0000BB': 'var(--highlight-default)',
'#FF8000': 'var(--highlight-comment)'
})|raw }}
</div>
</div>
<div class="sf-validator-compact hidden sf-validator-context" id="sf-context-{{ loop.index0 }}">
{{ profiler_dump(call.context, maxDepth=1) }}
</div>
{% if call.violations|length %}
<table>
<thead>
<tr>
<th>Path</th>
<th>Message</th>
<th>Invalid value</th>
<th>Violation</th>
</tr>
</thead>
{% for violation in call.violations %}
<tr>
<td>{{ violation.propertyPath }}</td>
<td>{{ violation.message }}</td>
<td>{{ profiler_dump(violation.seek('invalidValue')) }}</td>
<td>{{ profiler_dump(violation) }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No violations
{% endif %}
</div>
{% else %}
<div class="empty empty-panel">
<p>No calls to the validator were collected.</p>
</div>
{% endfor %}
{% endblock %}
@@ -0,0 +1,359 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
:root {
--workflow-dialog-background: var(--gray-200);
}
.theme-dark {
--workflow-dialog-background: var(--gray-900);
}
dialog {
background: var(--workflow-dialog-background);
border: none;
border-radius: 6px;
box-shadow: var(--settings-modal-shadow);
max-width: 94%;
width: 1200px;
}
dialog::backdrop {
background: linear-gradient(
45deg,
rgb(18, 18, 20, 0.4),
rgb(17, 17, 20, 0.8)
);
}
dialog[open] {
animation: scale 0.3s ease normal;
}
dialog[open]::backdrop {
animation: backdrop 0.3s ease normal;
}
dialog.hide {
animation-direction: reverse;
}
dialog h2 {
box-shadow: none !important;
color: var(--page-color);
font-size: 18px;
margin: 0 0 .5em;
}
dialog i.cancel {
cursor: pointer;
padding: 0 5px;
float: right;
}
dialog table {
background: var(--page-background);
border-collapse: collapse;
border-radius: 4px;
box-shadow: inset 0 0 0 1px var(--table-border-color);
color: var(--page-color);
margin: 0 0 1em 0;
margin-bottom: 1em;
padding: 0;
}
dialog table tr {
border: 1px solid var(--table-border-color);
padding: .35em;
}
dialog table th,
dialog table td {
border: 1px solid var(--table-border-color);
padding: .625em;
text-align: left;
word-wrap: break-word;
}
dialog table thead th {
background: var(--table-header);
border-top-left-radius: 4px;
border-top-right-radius: 4px;
font-size: .85em;
}
dialog menu {
display: none;
}
@keyframes scale {
from { transform: scale(0); }
to { transform: scale(1); }
}
@keyframes backdrop {
from { opacity: 0; }
to { opacity: 1; }
}
</style>
{% endblock %}
{% block toolbar %}
{% if collector.callsCount > 0 %}
{% set icon %}
{{ source('@WebProfiler/Icon/workflow.svg') }}
<span class="sf-toolbar-value">{{ collector.callsCount }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Workflow Calls</b>
<span>{{ collector.callsCount }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
<span class="label {{ collector.workflows|length == 0 ? 'disabled' }}">
<span class="icon">
{{ source('@WebProfiler/Icon/workflow.svg') }}
</span>
<strong>Workflow</strong>
</span>
{% endblock %}
{% block panel %}
<h2>Workflow</h2>
{% if collector.workflows|length == 0 %}
<div class="empty empty-panel">
<p>There are no workflows configured.</p>
</div>
{% else %}
<script>
{{ source('@WebProfiler/Script/Mermaid/mermaid-flowchart-v2.min.js') }}
const isDarkMode = document.querySelector('body').classList.contains('theme-dark');
mermaid.initialize({
flowchart: {
useMaxWidth: true,
},
securityLevel: 'loose',
theme: 'base',
themeVariables: {
darkMode: isDarkMode,
fontFamily: 'var(--font-family-system)',
fontSize: 'var(--font-size-body)',
// the properties below don't support CSS variables
primaryColor: isDarkMode ? 'lightsteelblue' : 'aliceblue',
primaryTextColor: isDarkMode ? '#000' : '#000',
primaryBorderColor: isDarkMode ? 'steelblue' : 'lightsteelblue',
lineColor: isDarkMode ? '#939393' : '#d4d4d4',
secondaryColor: isDarkMode ? 'lightyellow' : 'lightyellow',
tertiaryColor: isDarkMode ? 'lightSalmon' : 'lightSalmon',
}
});
{% for name, data in collector.workflows %}
window.showNodeDetails{{ collector.hash(name) }} = function (node) {
const map = {{ data.listeners|json_encode|raw }};
showNodeDetails(node, map);
};
{% endfor %}
const showNodeDetails = function (node, map) {
const dialog = document.getElementById('detailsDialog');
dialog.querySelector('tbody').innerHTML = '';
for (const [eventName, listeners] of Object.entries(map[node])) {
listeners.forEach(listener => {
const row = document.createElement('tr');
const eventNameCode = document.createElement('code');
eventNameCode.textContent = eventName;
const eventNameCell = document.createElement('td');
eventNameCell.appendChild(eventNameCode);
row.appendChild(eventNameCell);
const listenerDetailsCell = document.createElement('td');
row.appendChild(listenerDetailsCell);
let listenerDetails;
const listenerDetailsCode = document.createElement('code');
listenerDetailsCode.textContent = listener.title;
if (listener.file) {
const link = document.createElement('a');
link.href = listener.file;
link.appendChild(listenerDetailsCode);
listenerDetails = link;
} else {
listenerDetails = listenerDetailsCode;
}
listenerDetailsCell.appendChild(listenerDetails);
if (typeof listener.guardExpressions === 'object') {
listenerDetailsCell.appendChild(document.createElement('br'));
const guardExpressionsWrapper = document.createElement('span');
guardExpressionsWrapper.appendChild(document.createTextNode('guard expressions: '));
listener.guardExpressions.forEach((expression, index) => {
if (index > 0) {
guardExpressionsWrapper.appendChild(document.createTextNode(', '));
}
const expressionCode = document.createElement('code');
expressionCode.textContent = expression;
guardExpressionsWrapper.appendChild(expressionCode);
});
listenerDetailsCell.appendChild(guardExpressionsWrapper);
}
dialog.querySelector('tbody').appendChild(row);
});
};
if (dialog.dataset.processed) {
dialog.showModal();
return;
}
dialog.addEventListener('click', (e) => {
const rect = dialog.getBoundingClientRect();
const inDialog =
rect.top <= e.clientY &&
e.clientY <= rect.top + rect.height &&
rect.left <= e.clientX &&
e.clientX <= rect.left + rect.width;
!inDialog && dialog.close();
});
dialog.querySelectorAll('.cancel').forEach(elt => {
elt.addEventListener('click', () => dialog.close());
});
dialog.showModal();
dialog.dataset.processed = true;
};
// We do not load all mermaid diagrams at once, but only when the tab is opened
// This is because mermaid diagrams are in a tab, and cannot be renderer with a
// "good size" if they are not visible
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.tab').forEach((el) => {
const observer = new MutationObserver(() => {
if (!el.classList.contains('block')) {
return;
}
const mEl = el.querySelector('.sf-mermaid');
if (mEl.dataset.processed) {
return;
}
mermaid.run({
nodes: [mEl],
});
});
observer.observe(el, { attributeFilter: ['class'] });
});
});
</script>
<div class="sf-tabs js-tabs">
{% for name, data in collector.workflows %}
<div class="tab">
<h2 class="tab-title">{{ name }}{% if data.calls|length %} ({{ data.calls|length }}){% endif %}</h2>
<div class="tab-content">
<h3>Definition</h3>
<pre class="sf-mermaid">
{{ data.dump|raw }}
{% for nodeId, events in data.listeners %}
click {{ nodeId }} showNodeDetails{{ collector.hash(name) }}
{% endfor %}
</pre>
<a href="{{ collector.buildMermaidLiveLink(name) }}">View on mermaid.live</a>
<h3>Calls</h3>
<table>
<thead>
<tr>
<th>#</th>
<th>Call</th>
<th>Args</th>
<th>Return</th>
<th>Exception</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
{% for call in data.calls %}
<tr>
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
<td>
<code>{{ call.method }}()</code>
{% if call.previousMarking ?? null %}
<hr />
Previous marking:
{{ profiler_dump(call.previousMarking) }}
{% endif %}
</td>
<td>
{{ profiler_dump(call.args) }}
</td>
<td>
{% if call.return is defined %}
{% if call.return is same as true %}
<code>true</code>
{% elseif call.return is same as false %}
<code>false</code>
{% else %}
{{ profiler_dump(call.return) }}
{% endif %}
{% endif %}
</td>
<td>
{% if call.exception is defined %}
{{ profiler_dump(call.exception) }}
{% endif %}
</td>
<td>
{{ '%0.2f ms'|format(call.duration) }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<dialog id="detailsDialog">
<h2>
Event listeners
<i class="cancel">×</i>
</h2>
<table>
<thead>
<tr>
<th>event</th>
<th>listener</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<menu>
<small><i>⌨</i> <kbd>esc</kbd></small>
<button class="btn btn-sm cancel">Close</button>
</menu>
</dialog>
{% endblock %}
@@ -0,0 +1,26 @@
Icons are from "Tabler Icons" (https://github.com/tabler/tabler-icons), a set of
free MIT-licensed high-quality SVG icons.
-----
MIT License
Copyright (c) 2020-2022 Paweł Kuna
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-arrows-down-up" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="17" y1="3" x2="17" y2="21"></line>
<path d="M10 18l-3 3l-3 -3"></path>
<line x1="7" y1="21" x2="7" y2="3"></line>
<path d="M20 6l-3 -3l-3 3"></path>
</svg>

After

Width:  |  Height:  |  Size: 472 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-alert-circle" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="9"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>

After

Width:  |  Height:  |  Size: 441 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-paperclip" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M15 7l-6.5 6.5a1.5 1.5 0 0 0 3 3l6.5 -6.5a3 3 0 0 0 -6 -6l-6.5 6.5a4.5 4.5 0 0 0 9 9l6.5 -6.5"></path>
</svg>

After

Width:  |  Height:  |  Size: 408 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-stack-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Cache</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<polyline points="12 4 4 8 12 12 20 8 12 4"></polyline>
<polyline points="4 12 12 16 20 12"></polyline>
<polyline points="4 16 12 20 20 16"></polyline>
</svg>

After

Width:  |  Height:  |  Size: 490 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-chevron-down" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 9l6 6l6 -6"></path>
</svg>

After

Width:  |  Height:  |  Size: 331 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>

After

Width:  |  Height:  |  Size: 380 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-terminal-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M8 9l3 3l-3 3"></path>
<line x1="13" y1="15" x2="16" y2="15"></line>
<rect x="3" y="4" width="18" height="16" rx="2"></rect>
</svg>

After

Width:  |  Height:  |  Size: 437 B

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-adjustments" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Config</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="6" cy="10" r="2"></circle>
<line x1="6" y1="4" x2="6" y2="8"></line>
<line x1="6" y1="12" x2="6" y2="20"></line>
<circle cx="12" cy="16" r="2"></circle>
<line x1="12" y1="4" x2="12" y2="14"></line>
<line x1="12" y1="18" x2="12" y2="20"></line>
<circle cx="18" cy="7" r="2"></circle>
<line x1="18" y1="4" x2="18" y2="5"></line>
<line x1="18" y1="9" x2="18" y2="20"></line>
</svg>

After

Width:  |  Height:  |  Size: 751 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-download" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M4 17v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-2"></path>
<polyline points="7 11 12 16 17 11"></polyline>
<line x1="12" y1="4" x2="12" y2="16"></line>
</svg>

After

Width:  |  Height:  |  Size: 457 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-access-point" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="12" y1="12" x2="12" y2="12.01"></line>
<path d="M14.828 9.172a4 4 0 0 1 0 5.656"></path>
<path d="M17.657 6.343a8 8 0 0 1 0 11.314"></path>
<path d="M9.168 14.828a4 4 0 0 1 0 -5.656"></path>
<path d="M6.337 17.657a8 8 0 0 1 0 -11.314"></path>
</svg>

After

Width:  |  Height:  |  Size: 568 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-ghost" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Exception</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5 11a7 7 0 0 1 14 0v7a1.78 1.78 0 0 1 -3.1 1.4a1.65 1.65 0 0 0 -2.6 0a1.65 1.65 0 0 1 -2.6 0a1.65 1.65 0 0 0 -2.6 0a1.78 1.78 0 0 1 -3.1 -1.4v-7"></path>
<line x1="10" y1="10" x2="10.01" y2="10"></line>
<line x1="14" y1="10" x2="14.01" y2="10"></line>
<path d="M10 14a3.5 3.5 0 0 0 4 0"></path>
</svg>

After

Width:  |  Height:  |  Size: 649 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-file" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 426 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-filter" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5.5 5h13a1 1 0 0 1 .5 1.5l-5 5.5l0 7l-4 -3l0 -4l-5 -5.5a1 1 0 0 1 .5 -1.5"></path>
</svg>

After

Width:  |  Height:  |  Size: 386 B

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-forms" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Cache</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3a3 3 0 0 0 -3 3v12a3 3 0 0 0 3 3"></path>
<path d="M6 3a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3"></path>
<path d="M13 7h7a1 1 0 0 1 1 1v8a1 1 0 0 1 -1 1h-7"></path>
<path d="M5 7h-1a1 1 0 0 0 -1 1v8a1 1 0 0 0 1 1h1"></path>
<path d="M17 12h.01"></path>
<path d="M13 12h.01"></path>
</svg>

After

Width:  |  Height:  |  Size: 636 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-arrow-narrow-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="5" y1="12" x2="19" y2="12"></line>
<line x1="15" y1="16" x2="19" y2="12"></line>
<line x1="15" y1="8" x2="19" y2="12"></line>
</svg>

After

Width:  |  Height:  |  Size: 449 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-affiliate" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5.931 6.936l1.275 4.249m5.607 5.609l4.251 1.275"></path>
<path d="M11.683 12.317l5.759 -5.759"></path>
<circle cx="5.5" cy="5.5" r="1.5"></circle>
<circle cx="18.5" cy="5.5" r="1.5"></circle>
<circle cx="18.5" cy="18.5" r="1.5"></circle>
<circle cx="8.5" cy="15.5" r="4.5"></circle>
</svg>

After

Width:  |  Height:  |  Size: 609 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-file-alert" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Logger</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
<line x1="12" y1="11" x2="12" y2="14"></line>
</svg>

After

Width:  |  Height:  |  Size: 572 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-mail" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<rect x="3" y="5" width="18" height="14" rx="2"></rect>
<polyline points="3 7 12 13 21 7"></polyline>
</svg>

After

Width:  |  Height:  |  Size: 398 B

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-cpu" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Memory</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<rect x="5" y="5" width="14" height="14" rx="1"></rect>
<path d="M9 9h6v6h-6z"></path>
<path d="M3 10h2"></path>
<path d="M3 14h2"></path>
<path d="M10 3v2"></path>
<path d="M14 3v2"></path>
<path d="M21 10h-2"></path>
<path d="M21 14h-2"></path>
<path d="M14 21v-2"></path>
<path d="M10 21v-2"></path>
</svg>

After

Width:  |  Height:  |  Size: 666 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-menu-2" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Menu</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="4" y1="6" x2="20" y2="6"></line>
<line x1="4" y1="12" x2="20" y2="12"></line>
<line x1="4" y1="18" x2="20" y2="18"></line>
</svg>

After

Width:  |  Height:  |  Size: 469 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-subtask" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="6" y1="9" x2="12" y2="9"></line>
<line x1="4" y1="5" x2="8" y2="5"></line>
<path d="M6 5v11a1 1 0 0 0 1 1h5"></path>
<rect x="12" y="7" width="8" height="4" rx="1"></rect>
<rect x="12" y="15" width="8" height="4" rx="1"></rect>
</svg>

After

Width:  |  Height:  |  Size: 548 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>

After

Width:  |  Height:  |  Size: 378 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-bell-ringing" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M10 5a2 2 0 0 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6"></path>
<path d="M9 17v1a3 3 0 0 0 6 0v-1"></path>
<path d="M21 6.727a11.05 11.05 0 0 0 -2.794 -3.727"></path>
<path d="M3 6.727a11.05 11.05 0 0 1 2.792 -3.727"></path>
</svg>

After

Width:  |  Height:  |  Size: 577 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-corner-down-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Redirect</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 6v6a3 3 0 0 0 3 3h10l-4 -4m0 8l4 -4"></path>
</svg>

After

Width:  |  Height:  |  Size: 400 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-arrow-back-up" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 13l-4 -4l4 -4m-4 4h11a4 4 0 0 1 0 8h-1"></path>
</svg>

After

Width:  |  Height:  |  Size: 360 B

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-browser" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<rect x="4" y="4" width="16" height="16" rx="1"></rect>
<line x1="4" y1="8" x2="20" y2="8"></line>
<line x1="8" y1="4" x2="8" y2="8"></line>
</svg>

After

Width:  |  Height:  |  Size: 443 B

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-directions" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 21v-4"></path>
<path d="M12 13v-4"></path>
<path d="M12 5v-2"></path>
<path d="M10 21h4"></path>
<path d="M8 5v4h11l2 -2l-2 -2z"></path>
<path d="M14 13v4h-8l-2 -2l2 -2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 509 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="10" cy="10" r="7"></circle>
<line x1="21" y1="21" x2="15" y2="15"></line>
</svg>

After

Width:  |  Height:  |  Size: 383 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-arrows-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<line x1="21" y1="17" x2="3" y2="17"></line>
<path d="M18 4l3 3l-3 3"></path>
<path d="M18 20l3 -3l-3 -3"></path>
<line x1="21" y1="7" x2="3" y2="7"></line>
</svg>

After

Width:  |  Height:  |  Size: 458 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-moon" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>

After

Width:  |  Height:  |  Size: 377 B

@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-sun-high" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M14.828 14.828a4 4 0 1 0 -5.656 -5.656a4 4 0 0 0 5.656 5.656z" />
<path d="M6.343 17.657l-1.414 1.414" />
<path d="M6.343 6.343l-1.414 -1.414" />
<path d="M17.657 6.343l1.414 -1.414" />
<path d="M17.657 17.657l1.414 1.414" />
<path d="M4 12h-2" />
<path d="M12 4v-2" />
<path d="M20 12h2" />
<path d="M12 20v2" />
</svg>

After

Width:  |  Height:  |  Size: 644 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-device-laptop" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<line x1="3" y1="19" x2="21" y2="19" />
<rect x="5" y="6" width="14" height="10" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 389 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-autofit-width" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M4 12v-6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v6" />
<path d="M10 18h-7" />
<path d="M21 18h-7" />
<path d="M6 15l-3 3l3 3" />
<path d="M18 15l3 3l-3 3" />
</svg>

After

Width:  |  Height:  |  Size: 483 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-rectangle" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<rect x="3" y="5" width="18" height="14" rx="2" />
</svg>

After

Width:  |  Height:  |  Size: 349 B

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-settings" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>

After

Width:  |  Height:  |  Size: 881 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Symfony" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .9C5.8.9.9 5.8.9 12a11 11 0 1 0 22.2 0A11 11 0 0 0 12 .9zm6.5 6c-.6 0-.9-.3-.9-.8 0-.2 0-.4.2-.6l.2-.4c0-.3-.5-.4-.6-.4-1.8.1-2.3 2.5-2.7 4.4l-.2 1c1 .2 1.8 0 2.2-.3.6-.4-.2-.7-.1-1.2.1-.3.5-.5.7-.6.5 0 .7.5.7.9 0 .7-1 1.8-3 1.8l-.6-.1-.6 2.4c-.4 1.6-.8 3.8-2.4 5.7-1.4 1.7-2.9 1.9-3.5 1.9-1.2 0-1.9-.6-2-1.5 0-.8.7-1.3 1.2-1.3.6 0 1.1.5 1.1 1s-.2.6-.4.6c-.1.1-.3.2-.3.4 0 .1.1.3.4.3.5 0 .8-.3 1.1-.5 1.2-.9 1.6-2.7 2.2-5.7l.1-.7.7-3.2c-.8-.6-1.3-1.4-2.4-1.7-.6-.1-1.1.1-1.5.5-.4.5-.2 1.1.2 1.5l.7.6c.7.8 1.2 1.6 1 2.5-.3 1.5-2 2.6-4 1.9-1.8-.6-2-1.8-1.8-2.5.2-.6.6-.7 1.1-.6.5.2.6.7.6 1.2l-.1.3c-.2.1-.3.3-.3.4-.1.4.4.6.7.7.7.3 1.6-.2 1.8-.8a1 1 0 0 0-.4-1.1l-.7-.8c-.4-.4-1.1-1.4-.7-2.6.1-.5.4-.9.7-1.3a4 4 0 0 1 2.8-.6c1.2.4 1.8 1.1 2.6 1.8.5-1.2 1-2.4 1.8-3.5.9-.9 1.9-1.6 3.1-1.7 1.3.2 2.2.7 2.2 1.6 0 .4-.2 1.1-.9 1.1z"/></svg>

After

Width:  |  Height:  |  Size: 982 B

@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-chart-infographic" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Time</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="7" cy="7" r="4"></circle>
<path d="M7 3v4h4"></path>
<line x1="9" y1="17" x2="9" y2="21"></line>
<line x1="17" y1="14" x2="17" y2="21"></line>
<line x1="13" y1="13" x2="13" y2="21"></line>
<line x1="21" y1="12" x2="21" y2="21"></line>
</svg>

After

Width:  |  Height:  |  Size: 606 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-language-hiragana" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M4 5h7"></path>
<path d="M7 4c0 4.846 0 7 .5 8"></path>
<path d="M10 8.5c0 2.286 -2 4.5 -3.5 4.5s-2.5 -1.135 -2.5 -2c0 -2 1 -3 3 -3s5 .57 5 2.857c0 1.524 -.667 2.571 -2 3.143"></path>
<path d="M12 20l4 -9l4 9"></path>
<path d="M19.1 18h-6.2"></path>
</svg>

After

Width:  |  Height:  |  Size: 579 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-seeding" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" role="img">
<title>Twig</title>
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 10a6 6 0 0 0 -6 -6h-3v2a6 6 0 0 0 6 6h3"></path>
<path d="M12 14a6 6 0 0 1 6 -6h3v1a6 6 0 0 1 -6 6h-3"></path>
<line x1="12" y1="20" x2="12" y2="10"></line>
</svg>

After

Width:  |  Height:  |  Size: 507 B

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-checklist" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M9.615 20h-2.615a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h8a2 2 0 0 1 2 2v8"></path>
<path d="M14 19l2 2l4 -4"></path>
<path d="M9 8h4"></path>
<path d="M9 12h2"></path>
</svg>

After

Width:  |  Height:  |  Size: 481 B

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 18m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path>
<path d="M7 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path>
<path d="M17 6m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0"></path>
<path d="M7 8v2a2 2 0 0 0 2 2h6a2 2 0 0 0 2 -2v-2"></path>
<path d="M12 12l0 4"></path>
</svg>

After

Width:  |  Height:  |  Size: 527 B

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-check" width="24" height="24" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M5 12l5 5l10 -10"></path>
</svg>

After

Width:  |  Height:  |  Size: 325 B

@@ -0,0 +1,50 @@
{% set status_code = profile.statuscode|default(0) %}
{% set interrupted = command_collector is same as false ? null : command_collector.interruptedBySignal %}
{% set css_class = status_code == 113 or interrupted is not null ? 'status-warning' : status_code > 0 ? 'status-error' : 'status-success' %}
<div class="terminal status {{ css_class }}">
<div class="container">
<h2>
<span class="status-request-method">
{{ profile.method|upper }}
</span>
<span class="status-command">
{{ profile.url }}
</span>
</h2>
<dl class="metadata">
{% if interrupted %}
<span class="status-response-status-code">Interrupted</span>
<dt>Signal</dt>
<dd class="status-response-status-text">{{ interrupted }}</dd>
<dt>Exit code</dt>
<dd class="status-response-status-text">{{ status_code }}</dd>
{% elseif status_code == 0 %}
<span class="status-response-status-code">Success</span>
{% elseif status_code > 0 %}
<span class="status-response-status-code">Error</span>
<dt>Exit code</dt>
<dd class="status-response-status-text"><span class="status-response-status-code">{{ status_code }}</span></dd>
{% endif %}
{% if request_collector.requestserver.has('SYMFONY_CLI_BINARY_NAME') %}
<dt>Symfony CLI</dt>
<dd>v{{ request_collector.requestserver.get('SYMFONY_CLI_VERSION') }}</dd>
{% endif %}
<dt>Application</dt>
<dd>
<a href="{{ path('_profiler_search_results', { token: token, limit: 10, ip: profile.ip, type: 'command' }) }}">{{ profile.ip }}</a>
</dd>
<dt>Profiled on</dt>
<dd><time data-convert-to-user-timezone data-render-as-datetime datetime="{{ profile.time|date('c') }}">{{ profile.time|date('r') }}</time></dd>
<dt>Token</dt>
<dd>{{ profile.token }}</dd>
</dl>
</div>
</div>
@@ -0,0 +1,99 @@
{% set status_code = request_collector ? request_collector.statuscode|default(0) : 0 %}
{% set css_class = status_code > 399 ? 'status-error' : status_code > 299 ? 'status-warning' : 'status-success' %}
{% if request_collector and request_collector.redirect %}
{% set redirect = request_collector.redirect %}
{% set link_to_source_code = redirect.controller.class is defined ? redirect.controller.file|file_link(redirect.controller.line) %}
{% set redirect_route_name = '@' ~ redirect.route %}
<div class="status status-compact status-warning">
<span class="icon icon-redirect">{{ source('@WebProfiler/Icon/redirect.svg') }}</span>
<span class="status-response-status-code">{{ redirect.status_code }}</span> redirect from
<span class="status-request-method">{{ redirect.method }}</span>
{% if link_to_source_code %}
<a href="{{ link_to_source_code }}" title="{{ redirect.controller.file }}">{{ redirect_route_name }}</a>
{% else %}
{{ redirect_route_name }}
{% endif %}
(<a href="{{ path('_profiler', { token: redirect.token, panel: request.query.get('panel', 'request') }) }}">{{ redirect.token }}</a>)
</div>
{% endif %}
<div class="status {{ css_class }}">
{% if status_code > 399 %}
<p class="status-error-details">
<span class="icon">{{ source('@WebProfiler/Icon/alert-circle.svg') }}</span>
<span class="status-response-status-code">Error {{ status_code }}</span>
<span class="status-response-status-text">{{ request_collector.statusText }}</span>
</p>
{% endif %}
<h2>
<span class="status-request-method">
{{ profile.method|upper }}
</span>
{% set profile_title = profile.url|length < 160 ? profile.url : profile.url[:160] ~ '…' %}
{% if profile.method|upper in ['GET', 'HEAD'] %}
<a href="{{ profile.url }}">{{ profile_title }}</a>
{% else %}
{{ profile_title }}
{% endif %}
</h2>
<dl class="metadata">
{% if status_code < 400 %}
<dt>Response</dt>
<dd>
<span class="status-response-status-code">{{ status_code }}</span>
<span class="status-response-status-text">{{ request_collector.statusText }}</span>
</dd>
{% endif %}
{% set referer = request_collector ? request_collector.requestheaders.get('referer') : null %}
{% if referer %}
<dt></dt>
<dd>
<span class="icon icon-referer">{{ source('@WebProfiler/Icon/referrer.svg') }}</span>
<a href="{{ referer }}" class="referer">Browse referrer URL</a>
</dd>
{% endif %}
<dt>IP</dt>
<dd>
<a href="{{ path('_profiler_search_results', { token: token, limit: 10, ip: profile.ip }) }}">{{ profile.ip }}</a>
</dd>
<dt>Profiled on</dt>
<dd><time data-convert-to-user-timezone data-render-as-datetime datetime="{{ profile.time|date('c') }}">{{ profile.time|date('r') }}</time></dd>
<dt>Token</dt>
<dd>{{ profile.token }}</dd>
</dl>
</div>
{% if request_collector and request_collector.forwardtoken -%}
{% set forward_profile = profile.childByToken(request_collector.forwardtoken) %}
{% set controller = forward_profile ? forward_profile.collector('request').controller : 'n/a' %}
<div class="status status-compact status-compact-forward">
<span class="icon icon-forward">{{ source('@WebProfiler/Icon/forward.svg') }}</span>
Forwarded to
{% set link = controller.file is defined ? controller.file|file_link(controller.line) : null -%}
{%- if link %}<a href="{{ link }}" title="{{ controller.file }}">{% endif -%}
{% if controller.class is defined %}
{{- controller.class|abbr_class|striptags -}}
{{- controller.method ? ' :: ' ~ controller.method -}}
{% else %}
{{- controller -}}
{% endif %}
{%- if link %}</a>{% endif %}
(<a href="{{ path('_profiler', { token: request_collector.forwardtoken }) }}">{{ request_collector.forwardtoken }}</a>)
</div>
{%- endif %}
@@ -0,0 +1 @@
{% block panel '' %}
@@ -0,0 +1,22 @@
<div class="table-with-search-field">
<table class="{{ class|default('') }}">
<thead>
<tr>
<th scope="col" class="key">{{ labels is defined ? labels[0] : 'Key' }}</th>
<th scope="col">{{ labels is defined ? labels[1] : 'Value' }}</th>
</tr>
</thead>
<tbody>
{% for key in bag.keys|sort %}
<tr>
<th>{{ key }}</th>
<td>{{ profiler_dump(bag.get(key), maxDepth=maxDepth|default(0)) }}</td>
</tr>
{% else %}
<tr>
<td colspan="2">(no data)</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="{{ _charset }}" />
<meta name="robots" content="noindex,nofollow" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="view-transition" content="same-origin">
<title>{% block title %}Symfony Profiler{% endblock %}</title>
{% set request_collector = profile is defined ? profile.collectors.request|default(null) : null %}
{% set status_code = request_collector is not null ? request_collector.statuscode|default(0) : 0 %}
{% set favicon_color = status_code > 399 ? 'b41939' : status_code > 299 ? 'af8503' : '000000' %}
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg viewBox='0 0 600 600' xmlns='http://www.w3.org/2000/svg' fill-rule='evenodd' clip-rule='evenodd' stroke-linejoin='round' stroke-miterlimit='2'%3E%3Cstyle%3E%23circle %7B fill: %23{{ favicon_color }}; %7D %23sf %7B fill: %23ffffff; %7D%3C/style%3E%3Cpath fill='none' d='M0 0h600v600H0z'/%3E%3CclipPath id='a'%3E%3Cpath d='M0 0h600v600H0z'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23a)'%3E%3Cpath id='circle' d='M599.985 299.974c0 165.696-134.307 300.024-300.003 300.024C134.302 599.998 0 465.67 0 299.974 0 134.304 134.302-.002 299.982-.002c165.696 0 300.003 134.307 300.003 299.976z' fill-rule='nonzero'/%3E%3Cpath id='sf' d='M431.154 110.993c-30.474 1.043-57.08 17.866-76.884 41.076-21.926 25.49-36.508 55.696-47.03 86.55-18.791-15.416-33.282-35.364-63.457-44.04-23.311-6.702-47.794-3.948-70.314 12.833-10.667 7.965-18.016 19.995-21.51 31.34-9.05 29.416 9.506 55.61 17.942 65.004l18.444 19.743c3.792 3.879 12.95 13.983 8.467 28.458-4.82 15.764-23.809 25.938-43.285 19.958-8.703-2.67-21.199-9.147-18.396-18.257 1.145-3.739 3.82-6.553 5.264-9.74 1.305-2.788 1.941-4.858 2.337-6.099 3.557-11.602-1.31-26.714-13.747-30.56-11.613-3.562-23.488-.738-28.094 14.202-5.22 16.979 2.905 47.795 46.436 61.206 51 15.694 94.13-12.084 100.249-48.287 3.857-22.675-6.392-39.536-25.147-61.2l-15.293-16.92c-9.254-9.248-12.437-25.018-2.856-37.134 8.093-10.233 19.6-14.581 38.476-9.457 27.543 7.468 39.809 26.58 60.285 41.996-8.44 27.741-13.977 55.584-18.973 80.548l-3.07 18.626c-14.636 76.766-25.816 118.939-54.856 143.144-5.858 4.167-14.218 10.399-26.821 10.843-6.622.203-8.757-4.355-8.847-6.344-.15-4.628 3.755-6.756 6.349-8.837 3.889-2.124 9.757-5.633 9.356-16.882-.423-13.293-11.431-24.815-27.35-24.286-11.919.402-30.09 11.608-29.4 32.149.701 21.22 20.472 37.118 50.288 36.107 15.935-.535 51.528-7.018 86.592-48.699 40.82-47.8 52.235-102.576 60.82-142.673l9.591-52.946a177.574 177.574 0 0017.209 1.22c50.844 1.075 76.257-25.249 76.653-44.41.257-11.591-7.6-23.011-18.61-22.739-7.863.22-17.759 5.473-20.123 16.353-2.332 10.671 16.17 20.316 1.712 29.704-10.27 6.643-28.683 11.319-54.615 7.526l4.712-26.061c9.623-49.416 21.493-110.193 66.528-111.68 3.284-.155 15.282.139 15.56 8.088.08 2.637-.582 3.332-3.68 9.393-3.166 4.729-4.36 8.773-4.204 13.394.433 12.608 10.024 20.91 23.916 20.429 18.572-.626 23.906-18.7 23.6-27.998-.759-21.846-23.776-35.647-54.224-34.641z' fill-rule='nonzero'/%3E%3C/g%3E%3C/svg%3E"/>
{% block head %}
{% block stylesheets %}
<style{% if csp_style_nonce is defined and csp_style_nonce %} nonce="{{ csp_style_nonce }}"{% endif %}>
{{ include('@WebProfiler/Profiler/profiler.css.twig') }}
</style>
{% endblock %}
{% block javascripts %}
{% endblock %}
{% endblock %}
</head>
<body>
<script{% if csp_script_nonce is defined and csp_script_nonce %} nonce="{{ csp_script_nonce }}"{% endif %}>
if (null === localStorage.getItem('symfony/profiler/theme') || 'theme-auto' === localStorage.getItem('symfony/profiler/theme')) {
document.body.classList.add((matchMedia('(prefers-color-scheme: dark)').matches ? 'theme-dark' : 'theme-light'));
// needed to respond dynamically to OS changes without having to refresh the page
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add(e.matches ? 'theme-dark' : 'theme-light');
});
} else {
document.body.classList.add(localStorage.getItem('symfony/profiler/theme'));
}
document.body.classList.add(localStorage.getItem('symfony/profiler/width') || 'width-normal');
document.body.classList.add(
(navigator.appVersion.indexOf('Win') !== -1) ? 'windows' : (navigator.appVersion.indexOf('Mac') !== -1) ? 'macos' : 'linux'
);
</script>
{% block body '' %}
</body>
</html>
@@ -0,0 +1,280 @@
{# This file is partially duplicated in src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js.
If you make any change in this file, verify the same change is needed in the other file. #}
{# CAUTION: the contents of this file are processed by Twig before loading
them as JavaScript source code. Always use '/*' comments instead
of '//' comments to avoid impossible-to-debug side-effects #}
<script{% if csp_script_nonce is defined and csp_script_nonce %} nonce="{{ csp_script_nonce }}"{% endif %}>
window.addEventListener('DOMContentLoaded', () => {
new SymfonyProfiler();
});
class SymfonyProfiler {
constructor() {
this.#createTabs();
this.#createTableSearchFields();
this.#createToggles();
this.#createCopyToClipboard();
this.#convertDateTimesToUserTimezone();
}
#createTabs() {
/* the accessibility options of this component have been defined according to: */
/* www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-manual.html */
const tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])');
/* create the tab navigation for each group of tabs */
tabGroups.forEach((tabGroup, i) => {
const tabs = tabGroup.querySelectorAll(':scope > .tab');
const tabNavigation = document.createElement('div');
tabNavigation.classList.add('tab-navigation');
tabNavigation.setAttribute('role', 'tablist');
let selectedTabId = `tab-${i}-0`; /* select the first tab by default */
tabs.forEach((tab, j) => {
const tabId = `tab-${i}-${j}`;
const tabTitle = tab.querySelector('.tab-title').innerHTML;
const tabNavigationItem = document.createElement('button');
tabNavigationItem.classList.add('tab-control');
tabNavigationItem.setAttribute('data-tab-id', tabId);
tabNavigationItem.setAttribute('role', 'tab');
tabNavigationItem.setAttribute('aria-controls', tabId);
if (tab.classList.contains('active')) { selectedTabId = tabId; }
if (tab.classList.contains('disabled')) {
tabNavigationItem.classList.add('disabled');
}
tabNavigationItem.innerHTML = tabTitle;
tabNavigation.appendChild(tabNavigationItem);
const tabContent = tab.querySelector('.tab-content');
tabContent.parentElement.setAttribute('id', tabId);
});
tabGroup.insertBefore(tabNavigation, tabGroup.firstChild);
document.querySelector('[data-tab-id="' + selectedTabId + '"]').classList.add('active');
});
/* display the active tab and add the 'click' event listeners */
tabGroups.forEach((tabGroup) => {
const tabs = tabGroup.querySelectorAll(':scope > .tab-navigation .tab-control');
tabs.forEach((tab) => {
const tabId = tab.getAttribute('data-tab-id');
const tabPanel = document.getElementById(tabId);
tabPanel.setAttribute('role', 'tabpanel');
tabPanel.setAttribute('aria-labelledby', tabId);
tabPanel.querySelector('.tab-title').className = 'hidden';
if (tab.classList.contains('active')) {
tabPanel.className = 'block';
tab.setAttribute('aria-selected', 'true');
tab.removeAttribute('tabindex');
} else {
tabPanel.className = 'hidden';
tab.removeAttribute('aria-selected');
tab.setAttribute('tabindex', '-1');
}
tab.addEventListener('click', function(e) {
let activeTab = e.target || e.srcElement;
/* needed because when the tab contains HTML contents, user can click */
/* on any of those elements instead of their parent '<button>' element */
while ('button' !== activeTab.tagName.toLowerCase()) {
activeTab = activeTab.parentNode;
}
/* get the full list of tabs through the parent of the active tab element */
const tabs = Array.from(activeTab.parentNode.children);
tabs.forEach((tab) => {
const tabId = tab.getAttribute('data-tab-id');
document.getElementById(tabId).className = 'hidden';
tab.classList.remove('active');
tab.removeAttribute('aria-selected');
tab.setAttribute('tabindex', '-1');
});
activeTab.classList.add('active');
activeTab.setAttribute('aria-selected', 'true');
activeTab.removeAttribute('tabindex');
const activeTabId = activeTab.getAttribute('data-tab-id');
document.getElementById(activeTabId).className = 'block';
});
});
tabGroup.setAttribute('data-processed', 'true');
});
}
#createTableSearchFields() {
document.querySelectorAll('div.table-with-search-field').forEach((tableWrapper, i) => {
const searchField = document.createElement('input');
searchField.type = 'search';
searchField.placeholder = 'search...';
searchField.id = `table-search-field-${i}`;
searchField.classList.add(`table-search-field-input`);
searchField.autocapitalize = 'off';
searchField.autocomplete = 'off';
searchField.autocorrect = 'off';
tableWrapper.insertBefore(searchField, tableWrapper.firstChild);
const labelField = document.createElement('label');
labelField.htmlFor = `table-search-field-${i}`;
labelField.classList.add(`table-search-field-label`);
labelField.textContent = 'Search inside the contents of the table';
tableWrapper.insertBefore(labelField, tableWrapper.firstChild);
searchField.addEventListener('input', () => {
const query = searchField.value.toLowerCase();
let allRowsAreHidden = true;
tableWrapper.querySelectorAll('tbody tr').forEach((row) => {
const rowMatchesQuery = row.textContent.toLowerCase().includes(query);
row.style.display = rowMatchesQuery ? '' : 'none';
if (rowMatchesQuery) {
allRowsAreHidden = false;
}
});
/* if there are no results and all rows are hidden, show a message to avoid confusion */
const noResultsElement = tableWrapper.querySelector('.no-results-message');
if (allRowsAreHidden) {
if (null === noResultsElement) {
const noResultsElement = document.createElement('p');
noResultsElement.textContent = 'No results found.';
noResultsElement.classList.add('no-results-message');
tableWrapper.appendChild(noResultsElement);
} else {
noResultsElement.style.display = '';
}
} else {
if (null !== noResultsElement) {
noResultsElement.style.display = 'none';
}
}
});
});
}
#createToggles() {
const toggles = document.querySelectorAll('.sf-toggle:not([data-processed=true])');
toggles.forEach((toggle) => {
const elementSelector = toggle.getAttribute('data-toggle-selector');
const element = document.querySelector(elementSelector);
element.classList.add('sf-toggle-content');
if (toggle.hasAttribute('data-toggle-initial') && 'display' === toggle.getAttribute('data-toggle-initial')) {
toggle.classList.add('sf-toggle-on');
element.classList.add('sf-toggle-visible');
} else {
toggle.classList.add('sf-toggle-off');
element.classList.add('sf-toggle-hidden');
}
toggle.addEventListener('click', (e) => {
const toggle = e.currentTarget;
if (e.target.closest('a, .sf-toggle') !== toggle) {
return;
}
e.preventDefault();
if ('' !== window.getSelection().toString()) {
/* Don't do anything on text selection */
return;
}
const element = document.querySelector(toggle.getAttribute('data-toggle-selector'));
toggle.classList.toggle('sf-toggle-on');
toggle.classList.toggle('sf-toggle-off');
element.classList.toggle('sf-toggle-hidden');
element.classList.toggle('sf-toggle-visible');
/* the toggle doesn't change its contents when clicking on it */
if (!toggle.hasAttribute('data-toggle-alt-content')) {
return;
}
if (!toggle.hasAttribute('data-toggle-original-content')) {
toggle.setAttribute('data-toggle-original-content', toggle.innerHTML);
}
const currentContent = toggle.innerHTML;
const originalContent = toggle.getAttribute('data-toggle-original-content');
const altContent = toggle.getAttribute('data-toggle-alt-content');
toggle.innerHTML = currentContent !== altContent ? altContent : originalContent;
});
toggle.setAttribute('data-processed', 'true');
});
}
#createCopyToClipboard() {
if (!navigator.clipboard) {
return;
}
const copyToClipboardElements = document.querySelectorAll('[data-clipboard-text]');
copyToClipboardElements.forEach((copyToClipboardElement) => {
copyToClipboardElement.classList.remove('hidden');
copyToClipboardElement.addEventListener('click', (e) => {
/* Prevents from disallowing clicks on "copy to clipboard" elements inside toggles */
e.stopPropagation();
navigator.clipboard.writeText(copyToClipboardElement.getAttribute('data-clipboard-text'));
let oldContent = copyToClipboardElement.textContent;
copyToClipboardElement.textContent = `✅ Copied!`;
copyToClipboardElement.disabled = true;
setTimeout(() => {
copyToClipboardElement.textContent = oldContent;
copyToClipboardElement.disabled = false;
}, 7000);
});
});
}
#convertDateTimesToUserTimezone() {
const userTimezoneName = Intl.DateTimeFormat().resolvedOptions().timeZone;
document.querySelectorAll('time[data-convert-to-user-timezone]').forEach((timeElement) => {
const iso8601Datetime = timeElement.getAttribute('datetime');
const dateInUserTimezone = new Date(iso8601Datetime);
let options = {};
if (timeElement.hasAttribute('data-render-as-datetime')) {
options = {
year: 'numeric', month: 'long', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric'
};
} else if (timeElement.hasAttribute('data-render-as-date')) {
options = { year: 'numeric', month: 'long', day: 'numeric' };
} else if (timeElement.hasAttribute('data-render-as-time')) {
options = { hour: 'numeric', minute: 'numeric', second: 'numeric' };
}
if (timeElement.hasAttribute('data-render-with-millisecond-precision')) {
options.fractionalSecondDigits = 3;
}
/* dates/times are always rendered in English to match the rest of the Profiler interface */
timeElement.textContent = dateInUserTimezone.toLocaleString('en', options);
if (undefined !== userTimezoneName) {
const existingTitle = timeElement.getAttribute('title');
const newTitle = null === existingTitle
? `Date/times shown in your timezone: ${userTimezoneName}`
: existingTitle + ` (date/times shown in your timezone: ${userTimezoneName})`;
timeElement.setAttribute('title', newTitle);
}
});
}
}
</script>
@@ -0,0 +1,25 @@
{% block toolbar %}
{% set icon %}
{{ source('@WebProfiler/Icon/symfony.svg') }}
<span class="sf-toolbar-value sf-toolbar-ajax-request-counter">
Loading&hellip;
</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Loading the web debug toolbar&hellip;</b>
</div>
<div class="sf-toolbar-info-piece">
Attempt #<span id="sfLoadCounter-{{ token }}"></span>
</div>
<div class="sf-toolbar-info-piece">
<b>
<button class="sf-cancel-button" type="button" id="sfLoadCancel-{{ token }}" title="Cancel loading">Cancel</button>
</b>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
{% endblock %}
@@ -0,0 +1,12 @@
<div id="header">
<h1><a href="{{ path('_profiler_home') }}">{{ source('@WebProfiler/Icon/symfony.svg') }} Symfony Profiler</a></h1>
<div class="search">
<form method="get" action="https://symfony.com/search" target="_blank">
<div class="form-row">
<input name="q" id="search-id" type="search" placeholder="search on symfony.com" aria-label="Search on symfony.com">
<button type="submit" class="visually-hidden">Search</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,22 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% set messages = {
'no_token' : {
status: 'error',
title: (token|default('') == 'latest') ? 'There are no profiles' : 'Token not found',
message: (token|default('') == 'latest') ? 'No profiles found.' : 'Token "' ~ token|default('') ~ '" not found.'
}
} %}
{% block summary %}
<div class="status status-{{ messages[about].status }}">
<div class="container">
<h2>{{ messages[about].status|title }}</h2>
</div>
</div>
{% endblock %}
{% block panel %}
<h2>{{ messages[about].title }}</h2>
<p>{{ messages[about].message }}</p>
{% endblock %}
@@ -0,0 +1,76 @@
{% extends '@WebProfiler/Profiler/base.html.twig' %}
{% block body %}
<div class="container">
{{ include('@WebProfiler/Profiler/header.html.twig', {profile_type: profile_type}, with_context = false) }}
<div id="summary">
{% block summary %}
{% if profile is defined %}
{% set request_collector = profile.collectors.request|default(false) %}
{{ include('@WebProfiler/Profiler/_%s_summary.html.twig'|format(profile_type), {
profile: profile,
command_collector: profile.collectors.command|default(false) ,
request_collector: request_collector,
request: request,
token: token
}, with_context=false) }}
{% endif %}
{% endblock %}
</div>
<div id="content">
<div id="main">
<div id="sidebar">
{% block sidebar %}
<div id="sidebar-contents">
<div id="sidebar-shortcuts">
{% block sidebar_shortcuts_links %}
<div class="shortcuts">
<a class="btn btn-link" href="{{ path('_profiler_search', { limit: 10, type: profile_type }) }}">{{ source('@WebProfiler/Icon/search.svg') }} Search profiles</a>
<a class="btn btn-link" href="{{ path('_profiler', { token: 'latest', type: profile_type }|merge(request.query.all)) }}">Latest</a>
</div>
{% endblock sidebar_shortcuts_links %}
</div>
{% if templates is defined %}
<ul id="menu-profiler">
{% if 'request' is same as(profile_type) %}
{% set excludes = ['command'] %}
{% elseif 'command' is same as(profile_type) %}
{% set excludes = ['request', 'router'] %}
{% endif %}
{% for name, template in templates|filter((t, n) => n not in excludes) %}
{% set menu -%}
{%- if block('menu', template) is defined -%}
{% with { collector: profile.getcollector(name), profiler_markup_version: profiler_markup_version } %}
{{- block('menu', template) -}}
{% endwith %}
{%- endif -%}
{%- endset %}
{% if menu is not empty %}
<li class="{{ name }} {{ name == panel ? 'selected' }}">
<a href="{{ path('_profiler', { token: token, panel: name, type: profile_type }) }}">{{ menu|raw }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
{{ include('@WebProfiler/Profiler/settings.html.twig') }}
{% endblock sidebar %}
</div>
<div id="collector-wrapper">
<div id="collector-content">
{{ include('@WebProfiler/Profiler/base_js.html.twig') }}
{% block panel '' %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,80 @@
#header {
margin-bottom: 30px;
}
#source {
background: var(--page-background);
border: 1px solid var(--menu-border-color);
box-shadow: 0 0 0 5px var(--page-background);
border-radius: 6px;
margin: 0 30px 45px 0;
max-width: 960px;
padding: 15px 20px;
}
.width-full #source {
max-width: unset;
width: 100%;
}
#source code {
font-size: 15px;
}
#source .source-file-name {
border-bottom: 1px solid var(--table-border-color);
font-size: 18px;
font-weight: 500;
margin: 0 0 15px 0;
padding: 0 0 15px;
}
#source .source-file-name small {
color: var(--color-muted);
}
#source .source-content {
overflow-x: auto;
}
#source .source-content ol {
margin: 0;
}
#source .source-content ol li {
margin: 0 0 2px 0;
padding-left: 5px;
white-space: preserve nowrap;
}
#source .source-content ol li::marker {
color: var(--color-muted);
font-family: var(--font-family-monospace);
padding-right: 5px;
}
#source .source-content li.selected {
background: var(--yellow-100);
border-radius: 4px;
}
.theme-dark #source .source-content li.selected {
background: var(--gray-600);
}
#source .source-content li.selected::marker {
color: var(--color-text);
font-weight: bold;
}
#source span[style="color: #FF8000"] { color: var(--highlight-comment) !important; }
#source span[style="color: #007700"] { color: var(--highlight-keyword) !important; }
#source span[style="color: #0000BB"] { color: var(--color-text) !important; }
#source span[style="color: #DD0000"] { color: var(--highlight-string) !important; }
.file-metadata dt {
color: var(--header-metadata-key);
display: block;
font-weight: bold;
}
.file-metadata dd {
color: var(--header-metadata-value);
margin: 5px 0 20px;
/* needed to break the long file paths */
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
}
@@ -0,0 +1,64 @@
{% extends '@WebProfiler/Profiler/base.html.twig' %}
{% block head %}
<style>
{{ include('@WebProfiler/Profiler/profiler.css.twig') }}
{{ include('@WebProfiler/Profiler/open.css.twig') }}
</style>
{% endblock %}
{% block body %}
<div class="container">
{{ include('@WebProfiler/Profiler/header.html.twig', with_context = false) }}
{% set source = file_info.pathname|file_excerpt(line, -1) %}
<div id="content">
<div id="main">
<div id="source">
<h1 class="source-file-name">{{ file }}{% if 0 < line %} <small>line {{ line }}</small>{% endif %}</h1>
<div class="source-content">
{% if source is null %}
<p class="empty empty-panel">The file is not readable.</p>
{% else %}
{{ source|raw }}
{% endif %}
</div>
</div>
<div id="sidebar">
<dl class="file-metadata">
<dt>Filepath:</dt>
<dd>{{ file_info.pathname }}</dd>
<dt>Last modified:</dt>
<dd>{{ file_info.mTime|date }}</dd>
<dt>Size:</dt>
{% set file_size_in_kb = file_info.size / 1024 %}
{% set file_num_lines = source|split("\n")|length - 1 %}
<dd>
{{ file_size_in_kb < 1 ? file_info.size ~ ' bytes' : file_size_in_kb|number_format(0) ~ ' KB' }}
/ {{ file_num_lines }} lines
</dd>
</dl>
<a class="doc-link" href="https://symfony.com/doc/{{ constant('Symfony\\Component\\HttpKernel\\Kernel::VERSION') }}/reference/configuration/framework.html#ide" rel="help">Open this file in your IDE?</a>
</div>
</div>
</div>
</div>
<script>
window.addEventListener('load', function () {
const selectedLineElement = document.querySelector('.source-content li.selected');
if (null === selectedLineElement) {
return;
}
const selectedLineYCoordinate = selectedLineElement.getBoundingClientRect().y;
console.log(selectedLineYCoordinate);
window.scrollTo({ top: selectedLineYCoordinate - 20, left: 0, behavior: 'smooth' });
});
</script>
{% endblock %}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,138 @@
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% macro profile_search_filter(request, result, property) %}
{%- if request.hasSession -%}
<a href="{{ path('_profiler_search_results', request.query.all|merge({token: result.token})|merge({ (property): result[property] })) }}" title="Search"><span title="Search" class="sf-icon sf-search">{{ source('@WebProfiler/Icon/search.svg') }}</span></a>
{%- endif -%}
{% endmacro %}
{% block head %}
{{ parent() }}
<style>
#search-results td {
font-family: var(--font-family-system);
vertical-align: middle;
}
#search-results .sf-search {
visibility: hidden;
margin-left: 2px;
}
#search-results tr:hover .sf-search {
visibility: visible;
}
#search-results .sf-search svg {
stroke-width: 3;
}
</style>
{% endblock %}
{% block summary %}
<div class="status">
<h2>Profile Search</h2>
</div>
{% endblock %}
{% block sidebar_search_css_class %}{% endblock %}
{% block sidebar_shortcuts_links %}
{{ parent() }}
{{ render(controller('web_profiler.controller.profiler::searchBarAction', query={type: profile_type }|merge(request.query.all))) }}
{% endblock %}
{% block panel %}
<div class="sf-tabs" data-processed="true">
<div class="tab-navigation" role="tablist">
<button class="tab-control {{ 'request' == profile_type ? 'active' }}" role="tab" {{ 'request' == profile_type ? 'aria-selected="true"' : 'tabindex="-1"' }} >
<a href="{{ path('_profiler_search_results', {token: 'empty', limit: 10}) }}">
HTTP Requests
</a>
</button>
<button class="tab-control {{ 'command' == profile_type ? 'active' }}" role="tab" {{ 'command' == profile_type ? 'aria-selected="true"' : 'tabindex="-1"' }}>
<a href="{{ path('_profiler_search_results', {token: 'empty', limit: 10, type: 'command'}) }}">
Console Commands
</a>
</button>
</div>
</div>
<h2>{{ tokens ? tokens|length : 'No' }} results found</h2>
{% if tokens %}
<table id="search-results">
<thead>
<tr>
<th scope="col" class="text-center">
{% if 'command' == profile_type %}
Exit code
{% else %}
Status
{% endif %}
</th>
<th scope="col">
{% if 'command' == profile_type %}
Application
{% else %}
IP
{% endif %}
</th>
<th scope="col">
{% if 'command' == profile_type %}
Mode
{% else %}
Method
{% endif %}
</th>
<th scope="col">
{% if 'command' == profile_type %}
Command
{% else %}
URL
{% endif %}
</th>
<th scope="col">Time</th>
<th scope="col">Token</th>
</tr>
</thead>
<tbody>
{% for result in tokens %}
{% if 'command' == profile_type %}
{% set css_class = result.status_code == 113 ? 'status-warning' : result.status_code > 0 ? 'status-error' : 'status-success' %}
{% else %}
{% set css_class = result.status_code|default(0) > 399 ? 'status-error' : result.status_code|default(0) > 299 ? 'status-warning' : 'status-success' %}
{% endif %}
<tr>
<td class="text-center">
<span class="label {{ css_class }}">{{ result.status_code|default('n/a') }}</span>
</td>
<td>
<span class="nowrap">{{ result.ip }} {{ _self.profile_search_filter(request, result, 'ip') }}</span>
</td>
<td>
<span class="nowrap">{{ result.method }} {{ _self.profile_search_filter(request, result, 'method') }}</span>
</td>
<td class="break-long-words">
{{ result.url }}
{{ _self.profile_search_filter(request, result, 'url') }}
</td>
<td class="text-small">
<time data-convert-to-user-timezone data-render-as-date datetime="{{ result.time|date('c') }}">
{{ result.time|date('d-M-Y') }}
</time>
<time class="newline" data-convert-to-user-timezone data-render-as-time datetime="{{ result.time|date('c') }}">
{{ result.time|date('H:i:s') }}
</time>
</td>
<td class="nowrap"><a href="{{ path('_profiler', { token: result.token }) }}">{{ result.token }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="empty empty-panel">
<p>The query returned no result.</p>
</div>
{% endif %}
{% endblock %}
@@ -0,0 +1,93 @@
<div id="sidebar-search">
<form action="{{ path('_profiler_search') }}" method="get">
<div class="form-group">
<label for="ip">
{% if 'command' == profile_type %}
Application
{% else %}
IP
{% endif %}
</label>
<input type="text" name="ip" id="ip" value="{{ ip }}">
</div>
<div class="form-group-row">
<div class="form-group">
<label for="method">
{% if 'command' == profile_type %}
Mode
{% else %}
Method
{% endif %}
</label>
<select name="method" id="method">
<option value="">Any</option>
{% if 'command' == profile_type %}
{% set methods = ['BATCH', 'INTERACTIVE'] %}
{% else %}
{% set methods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'] %}
{% endif %}
{% for m in methods %}
<option {{ m == method ? 'selected="selected"' }}>{{ m }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="status_code">
{% if 'command' == profile_type %}
Exit code
{% set min_and_max = 'min=%d max=%d'|format(0, 255) %}
{% else %}
Status
{% set min_and_max = 'min=%d max=%d'|format(100, 599) %}
{% endif %}
</label>
<input type="number" name="status_code" id="status_code" {{ min_and_max }} value="{{ status_code }}">
</div>
</div>
<div class="form-group">
<label for="url">
{% if 'command' == profile_type %}
Command
{% else %}
URL
{% endif %}
</label>
<input type="text" name="url" id="url" value="{{ url }}">
</div>
<div class="form-group">
<label for="token">Token</label>
<input type="text" name="token" id="token" size="8" value="{{ token }}">
</div>
<div class="form-group">
<label for="start">From</label>
<input type="date" name="start" id="start" value="{{ start }}">
</div>
<div class="form-group">
<label for="end">Until</label>
<input type="date" name="end" id="end" value="{{ end }}">
</div>
<div class="form-group-row form-group-row-search-button">
<div class="form-group">
<label for="limit">Results</label>
<select name="limit" id="limit">
{% for l in [10, 50, 100] %}
<option {{ l == limit ? 'selected="selected"' }}>{{ l }}</option>
{% endfor %}
</select>
</div>
<input type="hidden" name="type" value="{{ profile_type }}">
<div class="form-group">
<button type="submit" class="btn btn-sm">Search</button>
</div>
</div>
</form>
</div>
@@ -0,0 +1,316 @@
<style>
:root {
--settings-modal-shadow: 0 0 0 1px var(--gray-400), 5px 5px 20px 0 var(--gray-800);
--settings-modal-header-background: var(--gray-200);
--settings-modal-content-background: var(--gray-100);
--settings-option-background: var(--page-background);
--settings-option-border-color: var(--gray-300);
--settings-option-color: var(--color-text);
--settings-option-icon-color: var(--gray-400);
--settings-option-active-border-color: #3b82f6;
--settings-option-active-background: #eff6ff;
--settings-option-active-color: var(--color-text);
--settings-option-active-icon-color: var(--gray-400);
}
.theme-dark {
--settings-modal-shadow: 0 0 0 1px var(--gray-600), 5px 5px 10px 0 var(--gray-900);
--settings-modal-header-background: var(--gray-800);
--settings-modal-content-background: var(--gray-700);
--settings-option-background: transparent;
--settings-option-border-color: var(--gray-500);
--settings-option-color: var(--color-text);
--settings-option-icon-color: var(--gray-300);
--settings-option-active-border-color: #93c5fd;
--settings-option-active-background: var(--gray-700);
--settings-option-active-color: var(--color-text);
--settings-option-active-icon-color: #93c5fd;
}
#open-settings {
color: var(--color-muted);
align-items: center;
display: flex;
margin: 10px 0 5px;
}
#open-settings .icon {
margin-right: 4px;
}
#open-settings .icon, #open-settings svg {
height: 18px;
width: 18px;
}
.modal-wrap {
-webkit-transition-duration: 0.3s;
-webkit-transition-property: opacity, visibility;
-webkit-transition-timing-function: ease-in-out;
align-items: center;
background: rgba(0, 0, 0, 0.70);
display: flex;
backdrop-filter: blur(2px);
height: 100%;
justify-content: center;
left: 0;
opacity: 0;
overflow: auto;
position: fixed;
top: 0;
transition-duration: 0.3s;
transition-property: opacity, visibility;
transition-timing-function: ease-in-out;
visibility: hidden;
width: 100%;
z-index: 100000;
}
.modal-wrap.visible {
opacity: 1;
visibility: visible;
}
.modal-wrap .modal-container {
border-radius: 6px;
box-shadow: var(--settings-modal-shadow);
color: var(--color-text);
margin: 1em;
max-width: 94%;
width: 600px;
}
.modal-wrap .modal-header {
align-items: center;
background: var(--settings-modal-header-background);
border-top-left-radius: 6px;
border-top-right-radius: 6px;
display: flex;
justify-content: space-between;
padding: 15px 30px;
}
.modal-wrap .modal-header h3 {
margin: 0;
}
.modal-wrap .modal-header .close-modal {
background: transparent;
border: 0;
color: var(--color-muted);
cursor: pointer;
font-size: 28px;
line-height: 1;
}
.modal-wrap .modal-header .close-modal:hover { opacity: 1; }
.modal-wrap .modal-content {
background: var(--settings-modal-content-background);
border-bottom-left-radius: 6px;
border-bottom-right-radius: 6px;
margin: 0;
padding: 15px 30px;
z-index: 100000;
}
.modal-content h4 {
font-size: 18px;
margin: 0 0 10px;
}
.modal-content .settings-group + h4 {
margin-top: 30px;
}
.modal-content .settings-group {
border: 1px solid var(--settings-option-border-color);
border-radius: 4px;
display: flex;
margin-bottom: 15px;
}
.modal-content .settings-group label {
cursor: pointer;
display: flex;
flex: 1;
font-size: 16px;
margin: 0;
}
.modal-content .settings-group label input {
position: absolute;
clip: rect(0, 0, 0, 0);
pointer-events: none;
opacity: 0;
}
.modal-content .settings-group:has(input:focus-visible) {
outline: 2px dotted var(--settings-option-active-border-color);
outline-offset: 2px;
}
.modal-content .settings-group label input:checked + p {
box-shadow: inset 0 0 0 2px var(--settings-option-active-border-color);
background-color: var(--settings-option-active-background);
color: var(--settings-option-active-color);
}
.modal-content .settings-group label input:checked + p svg {
color: var(--settings-option-active-icon-color);
}
.modal-content .settings-group label p {
align-items: center;
background: var(--settings-option-background);
color: var(--settings-option-color);
flex: 1;
font-size: 14px;
margin: 0;
padding: 10px 15px;
text-align: center;
}
.modal-content .settings-group label:first-child p {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.modal-content .settings-group label:last-child p {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.modal-content .settings-group label + label p {
border-left: 1px solid var(--settings-option-border-color);
}
.modal-content label p span {
display: block;
font-size: 16px;
}
.modal-content label p abbr {
border: 0;
text-decoration: none;
}
.modal-content label svg {
color: var(--settings-option-icon-color);
height: 36px;
width: 36px;
}
@media (max-width: 768px) {
#open-settings {
color: transparent;
}
#sidebar:hover #open-settings, #sidebar.expanded #open-settings {
color: var(--color-muted);
}
}
</style>
<a href="#" id="open-settings">
<span class="icon">{{ source('@WebProfiler/Icon/settings.svg') }}</span>
Profiler settings
</a>
<div class="modal-wrap" id="profiler-settings">
<div class="modal-container">
<div class="modal-header">
<h3>Configuration Settings</h3>
<button aria-label="Close" class="close-modal">&times;</button>
</div>
<div class="modal-content">
<h4>Theme</h4>
<div class="settings-group">
<label for="settings-theme-auto">
<input class="config-option" type="radio" name="theme" value="auto" id="settings-theme-auto">
<p>
{{ source('@WebProfiler/Icon/settings-theme-system.svg') }}
<span>System / OS</span>
</p>
</label>
<label for="settings-theme-light">
<input class="config-option" type="radio" name="theme" value="light" id="settings-theme-light">
<p>
{{ source('@WebProfiler/Icon/settings-theme-light.svg') }}
<span>Light</span>
</p>
</label>
<label for="settings-theme-dark">
<input class="config-option" type="radio" name="theme" value="dark" id="settings-theme-dark">
<p>
{{ source('@WebProfiler/Icon/settings-theme-dark.svg') }}
<span>Dark</span>
</p>
</label>
</div>
<h4>Page Width</h4>
<div class="settings-group">
<label for="settings-width-normal">
<input class="config-option" type="radio" name="width" value="normal" id="settings-width-normal">
<p>
{{ source('@WebProfiler/Icon/settings-width-fixed.svg') }}
<span>Fixed width</span>
</p>
</label>
<label for="settings-width-full">
<input class="config-option" type="radio" name="width" value="full" id="settings-width-full">
<p>
{{ source('@WebProfiler/Icon/settings-width-fitted.svg') }}
<span>Fit to window</span>
</p>
</label>
</div>
</div>
</div>
</div>
<script>
(function() {
const configOptions = document.querySelectorAll('.config-option');
[...configOptions].forEach(option => {
option.addEventListener('change', function (event) {
const optionName = option.name;
const optionValue = option.value;
const settingName = 'symfony/profiler/' + optionName;
const settingValue = optionName + '-' + optionValue;
localStorage.setItem(settingName, settingValue);
document.body.classList.forEach((cssClass) => {
if (cssClass.startsWith(optionName)) {
document.body.classList.remove(cssClass);
}
});
const resolvedSettingValue = 'theme-auto' === settingValue
? (matchMedia('(prefers-color-scheme: dark)').matches ? 'theme-dark' : 'theme-light')
: settingValue;
document.body.classList.add(resolvedSettingValue);
if (resolvedSettingValue.startsWith('theme-')) {
document.body.style.colorScheme = resolvedSettingValue.endsWith('-light') ? 'light' : 'dark';
}
});
});
const openModalButton = document.getElementById('open-settings');
const modalWindow = document.getElementById('profiler-settings');
const closeModalButton = document.getElementsByClassName('close-modal')[0];
const modalWrapper = document.getElementsByClassName('modal-wrap')[0];
const closeModal = () => {
modalWindow.classList.remove('visible');
setTimeout(() => openModalButton.focus(), 30);
};
openModalButton.addEventListener('click', function(event) {
document.getElementById('settings-' + (localStorage.getItem('symfony/profiler/theme') || 'theme-auto')).checked = 'checked';
document.getElementById('settings-' + (localStorage.getItem('symfony/profiler/width') || 'width-normal')).checked = 'checked';
modalWindow.classList.toggle('visible');
setTimeout(() => closeModalButton.focus(), 30);
event.preventDefault();
});
closeModalButton.addEventListener('click', closeModal);
modalWrapper.addEventListener('click', function(event) {
if (event.target == event.currentTarget) {
closeModal();
}
});
modalWrapper.addEventListener('keydown', function(event) {
if (event.key === 'Esc' || event.key === 'Escape') {
closeModal();
}
});
})();
</script>
@@ -0,0 +1,18 @@
<div class="table-with-search-field">
<table class="{{ class|default('') }}">
<thead>
<tr>
<th scope="col" class="key">{{ labels is defined ? labels[0] : 'Key' }}</th>
<th scope="col">{{ labels is defined ? labels[1] : 'Value' }}</th>
</tr>
</thead>
<tbody>
{% for key in data|keys|sort %}
<tr>
<th scope="row">{{ key }}</th>
<td>{{ profiler_dump(data[key]) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
@@ -0,0 +1,679 @@
.sf-toolbarreset {
--sf-toolbar-font-family-system: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--sf-toolbar-font-family-monospace: "Ubuntu Mono", "JetBrains Mono", ui-monospace, "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas,"Liberation Mono", "Courier New", monospace;
--sf-toolbar-white: #fff;
--sf-toolbar-black: #000;
--sf-toolbar-gray-50: #fafafa;
--sf-toolbar-gray-100: #f5f5f5;
--sf-toolbar-gray-200: #e5e5e5;
--sf-toolbar-gray-300: #d4d4d4;
--sf-toolbar-gray-400: #a3a3a3;
--sf-toolbar-gray-500: #737373;
--sf-toolbar-gray-600: #525252;
--sf-toolbar-gray-700: #404040;
--sf-toolbar-gray-800: #262626;
--sf-toolbar-gray-900: #171717;
--sf-toolbar-red-50: #FEFBFC;
--sf-toolbar-red-100: #FCE9ED;
--sf-toolbar-red-200: #F5B8C5;
--sf-toolbar-red-300: #EF869C;
--sf-toolbar-red-400: #E85574;
--sf-toolbar-red-500: #E1244B;
--sf-toolbar-red-600: #B41939;
--sf-toolbar-red-700: #83122A;
--sf-toolbar-red-800: #510B1A;
--sf-toolbar-red-900: #20040A;
--sf-toolbar-yellow-50: #fef7e1;
--sf-toolbar-yellow-100: #fef2cd;
--sf-toolbar-yellow-200: #fde496;
--sf-toolbar-yellow-300: #fcd55f;
--sf-toolbar-yellow-400: #fbc728;
--sf-toolbar-yellow-500: #e6af05;
--sf-toolbar-yellow-600: #af8503;
--sf-toolbar-yellow-700: #785b02;
--sf-toolbar-yellow-800: #413101;
--sf-toolbar-yellow-900: #0a0800;
--sf-toolbar-green-50: #eff5f5;
--sf-toolbar-green-100: #deeaea;
--sf-toolbar-green-200: #bbd5d5;
--sf-toolbar-green-300: #99bfbf;
--sf-toolbar-green-400: #76a9a9;
--sf-toolbar-green-500: #598e8e;
--sf-toolbar-green-600: #436c6c;
--sf-toolbar-green-700: #2e4949;
--sf-toolbar-green-800: #182727;
--sf-toolbar-green-900: #030404;
}
.sf-toolbar-clearer {
clear: both;
height: 36px;
}
.sf-toolbarreset *:not(svg rect) {
box-sizing: content-box;
vertical-align: baseline;
letter-spacing: normal;
width: auto;
}
.sf-toolbarreset {
background-color: var(--sf-toolbar-gray-800);
bottom: 0;
box-shadow: inset 0 1px 0 var(--sf-toolbar-black), 0 -1px 0 rgba(0, 0, 0, 0.5);
color: var(--sf-toolbar-gray-200);
font: 11px var(--sf-toolbar-font-family-system);
left: 0;
margin: 0;
padding: 0 36px 0 0;
position: fixed;
right: 0;
text-align: left;
text-transform: none;
z-index: 99999;
direction: ltr;
/* neutralize the aliasing defined by external CSS styles */
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
}
.sf-toolbarreset abbr {
border: dashed var(--sf-toolbar-gray-500);
border-width: 0 0 1px;
}
.sf-toolbarreset svg,
.sf-toolbarreset img {
height: 20px;
width: 20px;
display: inline-block;
}
.sf-toolbarreset .sf-cancel-button {
color: var(--sf-toolbar-gray-700);
}
.sf-toolbarreset .sf-toolbar-toggle-button {
background: var(--sf-toolbar-gray-800);
color: var(--sf-toolbar-gray-300);
display: block;
position: absolute;
top: 1px;
right: 0;
width: 36px;
height: 35px;
cursor: pointer;
text-align: center;
border: none;
margin: 0;
padding: 0;
outline: none;
}
.sf-toolbarreset .sf-toolbar-toggle-button:hover {
background: var(--sf-toolbar-gray-700);
}
.sf-toolbar.sf-toolbar-closed .sf-toolbar-clearer {
display: none;
}
.sf-toolbar.sf-toolbar-closed .sf-toolbarreset .sf-toolbar-block {
display: none;
}
.sf-toolbar.sf-toolbar-closed .sf-toolbarreset .sf-toolbar-toggle-button {
top: -37px;
}
.sf-toolbar .sf-toolbar-toggle-button i {
display: block;
height: 35px;
place-content: center;
}
.sf-toolbar.sf-toolbar-opened .sf-toolbar-toggle-button .sf-toolbar-icon-closed {
display: none;
}
.sf-toolbar.sf-toolbar-opened .sf-toolbar-toggle-button .sf-toolbar-icon-opened {
display: block;
}
.sf-toolbar.sf-toolbar-closed .sf-toolbar-toggle-button .sf-toolbar-icon-closed {
display: block;
}
.sf-toolbar.sf-toolbar-closed .sf-toolbar-toggle-button .sf-toolbar-icon-opened {
display: none;
}
.sf-toolbar.sf-toolbar-closed .sf-toolbarreset .sf-toolbar-toggle-button {
border-top: 2px solid var(--sf-toolbar-gray-800);
}
.sf-toolbar-block {
cursor: default;
display: block;
float: left;
height: 36px;
margin-right: 0;
position: relative;
white-space: nowrap;
max-width: 15%;
}
.sf-toolbar-block > a,
.sf-toolbar-block > a:hover {
display: block;
text-decoration: none;
background-color: transparent;
color: inherit;
}
.sf-toolbar-block span {
display: inline-block;
}
.sf-toolbar-block .sf-toolbar-value {
color: var(--sf-toolbar-gray-100);
font-size: 13px;
line-height: 36px;
padding: 0;
}
.sf-toolbar-block .sf-toolbar-label,
.sf-toolbar-block .sf-toolbar-class-separator {
color: var(--sf-toolbar-gray-400);
font-size: 12px;
margin-left: 2px;
}
.sf-toolbar-block .sf-toolbar-info {
border-collapse: collapse;
display: table;
z-index: 100000;
}
.sf-toolbar-block hr {
border-top: 1px solid var(--sf-toolbar-gray-500);
margin: 4px 0;
padding-top: 4px;
}
.sf-toolbar-block .sf-toolbar-info-piece {
/* this 'border-bottom' trick is needed because 'margin-bottom' doesn't work for table rows */
border-bottom: solid transparent 3px;
display: table-row;
}
.sf-toolbar-block .sf-toolbar-info-piece-additional,
.sf-toolbar-block .sf-toolbar-info-piece-additional-detail {
display: none;
}
.sf-toolbar-block .sf-toolbar-info-group {
margin-bottom: 4px;
padding-bottom: 2px;
border-bottom: 1px solid #333333;
}
.sf-toolbar-block .sf-toolbar-info-group:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status {
border-radius: 4px;
padding: 2px 5px;
margin-bottom: 0;
}
.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-status + .sf-toolbar-status {
margin-left: 4px;
}
.sf-toolbar-block .sf-toolbar-info-piece:last-child {
margin-bottom: 0;
}
div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece a {
color: #99CDD8;
text-decoration: underline;
}
div.sf-toolbar .sf-toolbar-block a:hover {
text-decoration: none;
}
.sf-toolbar-block .sf-toolbar-info-piece b {
color: var(--sf-toolbar-gray-400);
display: table-cell;
font-size: 11px;
padding: 4px 8px 4px 0;
}
.sf-toolbar-block:not(.sf-toolbar-block-dump) .sf-toolbar-info-piece span {
color: var(--sf-toolbar-gray-100);
}
.sf-toolbar-block .sf-toolbar-info-piece span {
font-size: 12px;
}
div.sf-toolbar .sf-toolbar-block .sf-toolbar-info-piece.sf-toolbar-info-php-ext a {
text-decoration: none;
}
.sf-toolbar-block .sf-toolbar-info {
background-color: var(--sf-toolbar-gray-700);
border-radius: 4px;
border-bottom-left-radius: 0;
bottom: 36px;
color: var(--sf-toolbar-gray-100);
display: none;
padding: 9px 0;
position: absolute;
}
.sf-toolbar-block.sf-toolbar-block-right .sf-toolbar-info {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0;
}
.sf-toolbar-block .sf-toolbar-info:empty {
visibility: hidden;
}
.sf-toolbar-block .sf-toolbar-status {
display: inline-block;
color: var(--sf-toolbar-white);
background-color: var(--sf-toolbar-gray-600);
padding: 3px 6px;
margin: 0 4px;
min-width: 15px;
min-height: 13px;
text-align: center;
}
.sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-green,
.sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-green {
background-color: #059669;
color: var(--white);
}
.sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-red,
.sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-red {
background-color: var(--sf-toolbar-red-500);
color: var(--sf-toolbar-red-50);
}
.sf-toolbar-block .sf-toolbar-status.sf-toolbar-status-yellow,
.sf-toolbar-block .sf-toolbar-info .sf-toolbar-status.sf-toolbar-status-yellow {
background-color: var(--sf-toolbar-yellow-300);
color: var(--sf-toolbar-yellow-800);
}
.sf-toolbar-block.sf-toolbar-status-green::before,
.sf-toolbar-block.sf-toolbar-status-red::before,
.sf-toolbar-block.sf-toolbar-status-yellow::before {
background: var(--sf-toolbar-yellow-400);
border-radius: 6px;
content: '';
position: absolute;
bottom: 1px;
left: 0;
width: 98%;
height: 3px;
z-index: 10005;
}
.sf-toolbar-block.sf-toolbar-status-red::before {
background: var(--sf-toolbar-red-400);
}
.sf-toolbar-block.sf-toolbar-status-green::before {
background: var(--sf-toolbar-green-400);
}
.sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-green::before,
.sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-red::before,
.sf-toolbar-block-request.sf-toolbar-block.sf-toolbar-status-yellow::before {
display: none;
}
.sf-toolbar-block-request .sf-toolbar-status {
border-radius: 6px;
color: #fff;
display: inline-block;
flex-shrink: 0;
font-size: 13px;
font-weight: 500;
padding: 4px 8px;
}
.sf-toolbar-block-request .sf-toolbar-info-piece a {
background-color: transparent;
text-decoration: none;
}
.sf-toolbar-block-request .sf-toolbar-info-piece a:hover {
text-decoration: underline;
}
.sf-toolbar-block-request .sf-toolbar-redirection-status {
font-weight: normal;
padding: 2px 4px;
line-height: 18px;
}
.sf-toolbar-block.sf-toolbar-block-request .sf-toolbar-redirection-status.sf-toolbar-status-yellow {
background-color: var(--sf-toolbar-yellow-300);
border-radius: 4px;
color: var(--sf-toolbar-yellow-800);
padding: 1px 4px;
}
.sf-toolbar-block.sf-toolbar-block-request .sf-toolbar-info-piece .sf-toolbar-redirection-method {
background: transparent;
color: var(--sf-toolbar-gray-300);
border: 1px solid var(--sf-toolbar-gray-400);
padding: 1px 4px;
}
.sf-toolbar-block-request .sf-toolbar-info-piece span.sf-toolbar-redirection-method {
font-size: 12px;
height: 17px;
line-height: 17px;
margin-right: 5px;
}
.sf-toolbar-block-request .sf-toolbar-request-icon svg {
stroke-width: 3px;
}
.sf-toolbar-block-ajax .sf-toolbar-icon {
cursor: pointer;
}
.sf-toolbar-status-green .sf-toolbar-label,
.sf-toolbar-status-yellow .sf-toolbar-label,
.sf-toolbar-status-red .sf-toolbar-label {
color: var(--sf-toolbar-white);
}
.sf-toolbar-block-config svg path,
.sf-toolbar-block-config svg .sf-svg-path {
fill: var(--sf-toolbar-white);
}
.sf-toolbar-block .sf-toolbar-icon {
color: var(--sf-toolbar-gray-300);
align-items: center;
display: flex;
height: 36px;
padding: 0 7px;
overflow: hidden;
text-overflow: ellipsis;
}
.sf-toolbar-block:hover .sf-toolbar-icon {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
box-shadow: 1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black);
}
.sf-toolbar-block.sf-toolbar-block-right:hover .sf-toolbar-icon {
box-shadow: -1px 0 0 var(--sf-toolbar-black), inset 0 -1px 0 var(--sf-toolbar-black);
}
.sf-toolbar-block-request .sf-toolbar-icon {
padding-left: 0;
padding-right: 0;
}
.sf-toolbar-block .sf-toolbar-icon img,
.sf-toolbar-block .sf-toolbar-icon svg {
border-width: 0;
}
.sf-toolbar-block .sf-toolbar-icon img + span,
.sf-toolbar-block .sf-toolbar-icon svg + span {
margin-left: 4px;
}
.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-value,
.sf-toolbar-block.sf-toolbar-block-sf-cli .sf-toolbar-value {
margin-left: 5px;
}
.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label,
.sf-toolbar-block.sf-toolbar-block-sf-cli .sf-toolbar-label {
margin-left: 0;
}
.sf-toolbar-block:hover,
.sf-toolbar-block.hover {
position: relative;
}
.sf-toolbar-block:hover .sf-toolbar-icon,
.sf-toolbar-block.hover .sf-toolbar-icon {
background-color: var(--sf-toolbar-gray-700);
position: relative;
z-index: 10002;
}
.sf-toolbar-block-ajax.hover .sf-toolbar-info {
z-index: 10001;
}
.sf-toolbar-block:hover .sf-toolbar-info,
.sf-toolbar-block.hover .sf-toolbar-info {
display: block;
padding: 10px;
max-width: 525px;
max-height: 480px;
word-wrap: break-word;
overflow: hidden;
overflow-y: auto;
}
.sf-toolbar-info-piece b.sf-toolbar-ajax-info {
color: var(--sf-toolbar-gray-100);
}
.sf-toolbar-ajax-requests {
border: 1px solid var(--sf-toolbar-gray-500);
font-variant: tabular-nums;
margin: 5px 0 0;
width: 100%;
}
.sf-toolbar-ajax-requests td {
background-color: var(--sf-toolbar-gray-700);
border: 1px solid var(--sf-toolbar-gray-500);
color: var(--sf-toolbar-gray-100);
font-size: 12px;
padding: 4px;
vertical-align: middle;
}
.sf-toolbar-ajax-requests thead {
border: 0;
}
.sf-toolbar-ajax-requests th {
background-color: var(--sf-toolbar-gray-800);
border: 1px solid var(--sf-toolbar-gray-500);
color: var(--sf-toolbar-gray-200);
font-size: 11px;
padding: 4px;
}
.sf-ajax-request-url {
max-width: 250px;
line-height: 9px;
overflow: hidden;
text-overflow: ellipsis;
}
.sf-toolbar-ajax-requests .sf-ajax-request-url a {
text-decoration: none;
}
.sf-toolbar-ajax-requests .sf-ajax-request-url a:hover {
text-decoration: underline;
}
.sf-ajax-request-duration {
text-align: right;
}
.sf-toolbar-block .sf-toolbar-info-piece .sf-toolbar-ajax-requests .sf-toolbar-status {
font-size: 11px;
padding: 1px 3px;
}
.sf-ajax-request-loading {
animation: sf-blink .5s ease-in-out infinite;
}
@keyframes sf-blink {
0% { background: var(--sf-toolbar-gray-800); }
50% { background: var(--sf-toolbar-gray-700); }
100% { background: var(--sf-toolbar-gray-800); }
}
.sf-toolbar-block.sf-toolbar-block-dump .sf-toolbar-info {
max-width: none;
width: 100%;
position: fixed;
box-sizing: border-box;
left: 0;
}
.sf-toolbar-block-dump pre.sf-dump {
background-color: var(--sf-toolbar-gray-800);
border-color: var(--sf-toolbar-gray-500);
border-radius: 0;
margin: 6px 0 12px 0;
}
.sf-toolbar-block-dump pre.sf-dump:last-child {
margin-bottom: 0;
}
.sf-toolbar-block-dump pre.sf-dump .sf-dump-search-wrapper {
margin-bottom: 5px;
}
.sf-toolbar-block-dump pre.sf-dump span.sf-dump-search-count {
color: #333;
font-size: 12px;
}
.sf-toolbar-block-dump .sf-toolbar-info-piece {
display: block;
}
.sf-toolbar-block-dump .sf-toolbar-info-piece .sf-toolbar-file-line {
color: var(--sf-toolbar-gray-400);
margin-left: 4px;
}
.sf-toolbar-block-dump .sf-toolbar-info img {
display: none;
}
.sf-toolbar-block-serializer .detailed-metrics {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 15px;
margin-top: 15px;
}
/* Responsive Design */
.sf-toolbar-icon .sf-toolbar-label,
.sf-toolbar-icon .sf-toolbar-value {
display: none;
}
.sf-toolbar-block-config .sf-toolbar-icon .sf-toolbar-label,
.sf-cli .sf-toolbar-icon .sf-toolbar-label {
display: inline-block;
}
/* Legacy Design - these styles are maintained to make old panels look
a bit better on the new toolbar */
.sf-toolbar-block .sf-toolbar-info-piece-additional-detail {
color: var(--sf-toolbar-gray-400);
font-size: 12px;
}
.sf-toolbar-status-green .sf-toolbar-info-piece-additional-detail,
.sf-toolbar-status-yellow .sf-toolbar-info-piece-additional-detail,
.sf-toolbar-status-red .sf-toolbar-info-piece-additional-detail {
color: var(--sf-toolbar-white);
}
@media (min-width: 768px) {
.sf-toolbar-icon .sf-toolbar-label,
.sf-toolbar-icon .sf-toolbar-value {
display: inline;
}
.sf-toolbar-block-time .sf-toolbar-icon svg,
.sf-toolbar-block-memory .sf-toolbar-icon svg {
display: none;
}
.sf-toolbar-block-time .sf-toolbar-icon svg + span,
.sf-toolbar-block-memory .sf-toolbar-icon svg + span {
margin-left: 0;
}
.sf-toolbar-block .sf-toolbar-icon {
padding: 0 10px;
}
.sf-toolbar-block-time .sf-toolbar-icon {
padding-right: 5px;
}
.sf-toolbar-block-memory .sf-toolbar-icon {
padding-left: 5px;
}
.sf-toolbar-block-request .sf-toolbar-icon {
display: flex;
align-items: center;
padding-left: 0;
padding-right: 0;
}
.sf-toolbar-block-request .sf-toolbar-label {
margin-left: 4px;
margin-right: 1px;
}
.sf-toolbar-block-request .sf-toolbar-status + .sf-toolbar-request-icon {
display: inline-flex;
margin-left: 5px;
}
.sf-toolbar-block-request .sf-toolbar-icon .sf-toolbar-request-icon + .sf-toolbar-label {
margin-left: 0;
}
.sf-toolbar-block-request .sf-toolbar-label + .sf-toolbar-value {
margin-right: 5px;
}
.sf-toolbar-block-request:hover .sf-toolbar-info {
max-width: none;
}
.sf-toolbar-block .sf-toolbar-info-piece b {
font-size: 12px;
}
.sf-toolbar-block .sf-toolbar-info-piece span {
font-size: 13px;
}
.sf-toolbar-block-right {
float: right;
margin-left: 0;
margin-right: 0;
}
.sf-toolbarreset .sf-toolbar-block.sf-toolbar-block-right:not(.sf-toolbar-block-sf-cli) .sf-toolbar-info {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0;
}
}
@media (min-width: 1024px) {
.sf-toolbar-block .sf-toolbar-info-piece-additional,
.sf-toolbar-block .sf-toolbar-info-piece-additional-detail {
display: inline;
}
.sf-toolbar-block .sf-toolbar-info-piece-additional:empty,
.sf-toolbar-block .sf-toolbar-info-piece-additional-detail:empty {
display: none;
}
}
/***** Error Toolbar *****/
.sf-error-toolbar .sf-toolbarreset {
background: var(--sf-toolbar-gray-800);
color: var(--sf-toolbar-gray-100);
font: 13px/36px var(--sf-toolbar-font-family-system);
height: 36px;
padding: 0 15px;
text-align: left;
}
.sf-error-toolbar .sf-toolbarreset svg {
height: auto;
}
.sf-error-toolbar .sf-toolbarreset a {
color: #99cdd8;
margin-left: 5px;
text-decoration: underline;
}
.sf-error-toolbar .sf-toolbarreset a:hover {
text-decoration: none;
}
.sf-error-toolbar .sf-toolbarreset .sf-toolbar-icon {
float: left;
padding: 5px 0;
margin-right: 10px;
}
.sf-full-stack {
left: 0px;
font-size: 12px;
}
/***** Media query print: Do not print the Toolbar. *****/
@media print {
.sf-toolbar {
display: none !important;
}
}
@@ -0,0 +1,39 @@
<div id="sfToolbarClearer-{{ token }}" class="sf-toolbar-clearer"></div>
<div id="sfToolbarMainContent-{{ token }}" class="sf-toolbarreset notranslate clear-fix" data-no-turbolink data-turbo="false">
{% for name, template in templates %}
{% if block('toolbar', template) is defined %}
{% with {
collector: profile ? profile.getcollector(name) : null,
profiler_url: profiler_url,
token: token ?? (profile ? profile.token : null),
name: name,
profiler_markup_version: profiler_markup_version,
csp_script_nonce: csp_script_nonce,
csp_style_nonce: csp_style_nonce
} %}
{{ block('toolbar', template) }}
{% endwith %}
{% endif %}
{% endfor %}
{% if full_stack %}
<div class="sf-full-stack sf-toolbar-block sf-toolbar-block-full-stack sf-toolbar-status-red sf-toolbar-block-right">
<div class="sf-toolbar-icon">
<span class="sf-toolbar-value">Using symfony/symfony is NOT supported</span>
</div>
<div class="sf-toolbar-info sf-toolbar-status-red">
<p>This project is using Symfony via the "symfony/symfony" package.</p>
<p>This is NOT supported anymore since Symfony 4.0.</p>
<p>Even if it seems to work well, it has some important limitations with no workarounds.</p>
<p>Using this package also makes your project slower.</p>
<strong>Please, stop using this package and replace it with individual packages instead.</strong>
</div>
<div></div>
</div>
{% endif %}
<button class="sf-toolbar-toggle-button" type="button" id="sfToolbarToggleButton-{{ token }}" accesskey="D" aria-expanded="true" aria-controls="sfToolbarMainContent-{{ token }}">
<i class="sf-toolbar-icon-opened" title="Close Toolbar">{{ source('@WebProfiler/Icon/close.svg') }}</i>
<i class="sf-toolbar-icon-closed" title="Open Toolbar">{{ source('@WebProfiler/Icon/symfony.svg') }}</i>
</button>
</div>
@@ -0,0 +1,6 @@
<div class="sf-toolbar-block sf-toolbar-block-{{ name }} sf-toolbar-status-{{ status|default('normal') }} {{ additional_classes|default('') }}" {{ block_attrs|default('')|raw }}>
{% if link is not defined or link %}<a href="{{ url('_profiler', { token: token, panel: name }) }}">{% endif %}
<div class="sf-toolbar-icon">{{ icon|default('') }}</div>
{% if link|default(false) %}</a>{% endif %}
<div class="sf-toolbar-info">{{ text|default('') }}</div>
</div>
@@ -0,0 +1,632 @@
<!-- START of Symfony Web Debug Toolbar -->
<div id="sfwdt{{ token }}" class="sf-toolbar sf-toolbar-opened" role="region" aria-label="Symfony Web Debug Toolbar">
{{ include('@WebProfiler/Profiler/toolbar.html.twig', {
templates: {
'request': '@WebProfiler/Profiler/cancel.html.twig'
},
profile: null,
profiler_url: url('_profiler', {token: token}),
profiler_markup_version: 3,
}) }}
</div>
<link rel="stylesheet"{% if csp_style_nonce %} nonce="{{ csp_style_nonce }}"{% endif %} href="{{ url('_wdt_stylesheet') }}" />
{# CAUTION: the contents of this file are processed by Twig before loading
them as JavaScript source code. Always use '/*' comments instead
of '//' comments to avoid impossible-to-debug side-effects #}
<script{% if csp_script_nonce is defined and csp_script_nonce %} nonce="{{ csp_script_nonce }}"{% endif %}>/*<![CDATA[*/
if (typeof Sfjs === 'undefined' || typeof Sfjs.loadToolbar === 'undefined') {
Sfjs = (function() {
"use strict";
if ('classList' in document.documentElement) {
var hasClass = function (el, cssClass) { return el.classList.contains(cssClass); };
var removeClass = function(el, cssClass) { el.classList.remove(cssClass); };
var addClass = function(el, cssClass) { el.classList.add(cssClass); };
var toggleClass = function(el, cssClass) { el.classList.toggle(cssClass); };
} else {
var hasClass = function (el, cssClass) { return el.className.match(new RegExp('\\b' + cssClass + '\\b')); };
var removeClass = function(el, cssClass) { el.className = el.className.replace(new RegExp('\\b' + cssClass + '\\b'), ' '); };
var addClass = function(el, cssClass) { if (!hasClass(el, cssClass)) { el.className += " " + cssClass; } };
var toggleClass = function(el, cssClass) { hasClass(el, cssClass) ? removeClass(el, cssClass) : addClass(el, cssClass); };
}
var noop = function() {};
var profilerStorageKey = 'symfony/profiler/';
var addEventListener;
var el = document.createElement('div');
if (!('addEventListener' in el)) {
addEventListener = function (element, eventName, callback) {
element.attachEvent('on' + eventName, callback);
};
} else {
addEventListener = function (element, eventName, callback) {
element.addEventListener(eventName, callback, false);
};
}
var request = function(url, onSuccess, onError, payload, options, tries) {
url = new URL(url);
url.searchParams.set('XDEBUG_IGNORE', '1');
url = url.toString();
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
options = options || {};
options.retry = options.retry || false;
tries = tries || 1;
/* this delays for 125, 375, 625, 875, and 1000, ... */
var delay = tries < 5 ? (tries - 0.5) * 250 : 1000;
xhr.open(options.method || 'GET', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(state) {
if (4 !== xhr.readyState) {
return null;
}
if (xhr.status == 404 && options.retry && !options.stop) {
setTimeout(function() {
if (options.stop) {
return;
}
request(url, onSuccess, onError, payload, options, tries + 1);
}, delay);
return null;
}
if (200 === xhr.status) {
(onSuccess || noop)(xhr);
} else {
(onError || noop)(xhr);
}
};
if (options.onSend) {
options.onSend(tries);
}
xhr.send(payload || '');
};
var getPreference = function(name) {
if (!window.localStorage) {
return null;
}
return localStorage.getItem(profilerStorageKey + name);
};
var setPreference = function(name, value) {
if (!window.localStorage) {
return null;
}
localStorage.setItem(profilerStorageKey + name, value);
};
var requestStack = [];
var extractHeaders = function(xhr, stackElement) {
/* Here we avoid to call xhr.getResponseHeader in order to */
/* prevent polluting the console with CORS security errors */
var allHeaders = xhr.getAllResponseHeaders();
var ret;
if (ret = allHeaders.match(/^x-debug-token:\s+(.*)$/im)) {
stackElement.profile = ret[1];
}
if (ret = allHeaders.match(/^x-debug-token-link:\s+(.*)$/im)) {
stackElement.profilerUrl = ret[1];
}
if (ret = allHeaders.match(/^Symfony-Debug-Toolbar-Replace:\s+(.*)$/im)) {
stackElement.toolbarReplaceFinished = false;
stackElement.toolbarReplace = '1' === ret[1];
}
};
var successStreak = 4;
var pendingRequests = 0;
var renderAjaxRequests = function() {
var requestCounter = document.querySelector('.sf-toolbar-ajax-request-counter');
if (!requestCounter) {
return;
}
requestCounter.textContent = requestStack.length;
var infoSpan = document.querySelector(".sf-toolbar-ajax-info");
if (infoSpan) {
infoSpan.textContent = requestStack.length + ' AJAX request' + (requestStack.length !== 1 ? 's' : '');
}
var ajaxToolbarPanel = document.querySelector('.sf-toolbar-block-ajax');
if (requestStack.length) {
ajaxToolbarPanel.style.display = '';
} else {
ajaxToolbarPanel.style.display = 'none';
}
if (pendingRequests > 0) {
addClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
} else if (successStreak < 4) {
addClass(ajaxToolbarPanel, 'sf-toolbar-status-red');
removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
} else {
removeClass(ajaxToolbarPanel, 'sf-ajax-request-loading');
removeClass(ajaxToolbarPanel, 'sf-toolbar-status-red');
}
};
var startAjaxRequest = function(index) {
var tbody = document.querySelector('.sf-toolbar-ajax-request-list');
if (!tbody) {
return;
}
var nbOfAjaxRequest = tbody.rows.length;
if (nbOfAjaxRequest >= 100) {
tbody.deleteRow(0);
}
var request = requestStack[index];
pendingRequests++;
var row = document.createElement('tr');
request.DOMNode = row;
var requestNumberCell = document.createElement('td');
requestNumberCell.textContent = index + 1;
row.appendChild(requestNumberCell);
var profilerCell = document.createElement('td');
profilerCell.textContent = 'n/a';
row.appendChild(profilerCell);
var methodCell = document.createElement('td');
methodCell.textContent = request.method;
row.appendChild(methodCell);
var typeCell = document.createElement('td');
typeCell.textContent = request.type;
row.appendChild(typeCell);
var statusCodeCell = document.createElement('td');
var statusCode = document.createElement('span');
statusCode.textContent = 'n/a';
statusCodeCell.appendChild(statusCode);
row.appendChild(statusCodeCell);
var pathCell = document.createElement('td');
pathCell.className = 'sf-ajax-request-url';
if ('GET' === request.method) {
var pathLink = document.createElement('a');
pathLink.setAttribute('href', request.url);
pathLink.textContent = request.url;
pathCell.appendChild(pathLink);
} else {
pathCell.textContent = request.url;
}
pathCell.setAttribute('title', request.url);
row.appendChild(pathCell);
var durationCell = document.createElement('td');
durationCell.className = 'sf-ajax-request-duration';
durationCell.textContent = 'n/a';
row.appendChild(durationCell);
request.liveDurationHandle = setInterval(function() {
durationCell.textContent = (new Date() - request.start) + ' ms';
}, 100);
row.className = 'sf-ajax-request sf-ajax-request-loading';
tbody.insertBefore(row, null);
var toolbarInfo = document.querySelector('.sf-toolbar-block-ajax .sf-toolbar-info');
toolbarInfo.scrollTop = toolbarInfo.scrollHeight;
renderAjaxRequests();
};
var finishAjaxRequest = function(index) {
var request = requestStack[index];
clearInterval(request.liveDurationHandle);
if (!request.DOMNode) {
return;
}
if (request.toolbarReplace && !request.toolbarReplaceFinished && request.profile) {
/* Flag as complete because finishAjaxRequest can be called multiple times. */
request.toolbarReplaceFinished = true;
/* Search up through the DOM to find the toolbar's container ID. */
for (var elem = request.DOMNode; elem && elem !== document; elem = elem.parentNode) {
if (elem.id.match(/^sfwdt/)) {
Sfjs.loadToolbar(elem.id.replace(/^sfwdt/, ''), request.profile);
break;
}
}
}
pendingRequests--;
var row = request.DOMNode;
/* Unpack the children from the row */
var profilerCell = row.children[1];
var methodCell = row.children[2];
var statusCodeCell = row.children[4];
var statusCodeElem = statusCodeCell.children[0];
var durationCell = row.children[6];
if (request.error) {
row.className = 'sf-ajax-request sf-ajax-request-error';
methodCell.className = 'sf-ajax-request-error';
successStreak = 0;
} else {
row.className = 'sf-ajax-request sf-ajax-request-ok';
successStreak++;
}
if (request.statusCode) {
if (request.statusCode < 300) {
statusCodeElem.setAttribute('class', 'sf-toolbar-status');
} else if (request.statusCode < 400) {
statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-yellow');
} else {
statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red');
}
statusCodeElem.textContent = request.statusCode;
} else {
statusCodeElem.setAttribute('class', 'sf-toolbar-status sf-toolbar-status-red');
}
if (request.duration) {
durationCell.textContent = request.duration + ' ms';
}
if (request.profilerUrl) {
profilerCell.textContent = '';
var profilerLink = document.createElement('a');
profilerLink.setAttribute('href', request.profilerUrl);
profilerLink.textContent = request.profile;
profilerCell.appendChild(profilerLink);
}
renderAjaxRequests();
};
{% if excluded_ajax_paths is defined %}
if (window.fetch && window.fetch.polyfill === undefined) {
var oldFetch = window.fetch;
window.fetch = function () {
var promise = oldFetch.apply(this, arguments);
var url = arguments[0];
var params = arguments[1];
var paramType = Object.prototype.toString.call(arguments[0]);
if (paramType === '[object Request]') {
url = arguments[0].url;
params = {
method: arguments[0].method,
credentials: arguments[0].credentials,
headers: arguments[0].headers,
mode: arguments[0].mode,
redirect: arguments[0].redirect
};
} else {
url = String(url);
}
if (!url.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
var method = 'GET';
if (params && params.method !== undefined) {
method = params.method;
}
var stackElement = {
error: false,
url: url,
method: method,
type: 'fetch',
start: new Date()
};
var idx = requestStack.push(stackElement) - 1;
promise.then(function (r) {
stackElement.duration = new Date() - stackElement.start;
stackElement.error = r.status < 200 || r.status >= 400;
stackElement.statusCode = r.status;
stackElement.profile = r.headers.get('x-debug-token');
stackElement.profilerUrl = r.headers.get('x-debug-token-link');
stackElement.toolbarReplaceFinished = false;
stackElement.toolbarReplace = '1' === r.headers.get('Symfony-Debug-Toolbar-Replace');
finishAjaxRequest(idx);
}, function (e){
stackElement.error = true;
finishAjaxRequest(idx);
});
startAjaxRequest(idx);
}
return promise;
};
}
if (window.XMLHttpRequest && XMLHttpRequest.prototype.addEventListener) {
var proxied = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
var self = this;
/* prevent logging AJAX calls to static and inline files, like templates */
var path = url;
if (url.slice(0, 1) === '/') {
if (0 === url.indexOf('{{ request.basePath|e('js') }}')) {
path = url.slice({{ request.basePath|length }});
}
}
else if (0 === url.indexOf('{{ (request.schemeAndHttpHost ~ request.basePath)|e('js') }}')) {
path = url.slice({{ (request.schemeAndHttpHost ~ request.basePath)|length }});
}
if (!path.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
var stackElement = {
error: false,
url: url,
method: method,
type: 'xhr',
start: new Date()
};
var idx = requestStack.push(stackElement) - 1;
this.addEventListener('readystatechange', function() {
if (self.readyState == 4) {
stackElement.duration = new Date() - stackElement.start;
stackElement.error = self.status < 200 || self.status >= 400;
stackElement.statusCode = self.status;
extractHeaders(self, stackElement);
finishAjaxRequest(idx);
}
}, false);
startAjaxRequest(idx);
}
proxied.apply(this, Array.prototype.slice.call(arguments));
};
}
{% endif %}
return {
hasClass: hasClass,
removeClass: removeClass,
addClass: addClass,
toggleClass: toggleClass,
getPreference: getPreference,
setPreference: setPreference,
addEventListener: addEventListener,
request: request,
renderAjaxRequests: renderAjaxRequests,
getSfwdt: function(token) {
return document.getElementById('sfwdt' + token);
},
load: function(selector, url, onSuccess, onError, options) {
var el = document.getElementById(selector);
if (el && el.getAttribute('data-sfurl') !== url) {
request(
url,
function(xhr) {
el.innerHTML = xhr.responseText;
el.setAttribute('data-sfurl', url);
removeClass(el, 'loading');
var pending = pendingRequests;
for (var i = 0; i < requestStack.length; i++) {
startAjaxRequest(i);
if (requestStack[i].duration) {
finishAjaxRequest(i);
}
}
/* Revert the pending state in case there was a start called without a finish above. */
pendingRequests = pending;
(onSuccess || noop)(xhr, el);
},
function(xhr) { (onError || noop)(xhr, el); },
'',
options
);
}
return this;
},
showToolbar: function(token) {
var sfwdt = this.getSfwdt(token);
if ('closed' === getPreference('toolbar/displayState')) {
addClass(sfwdt, 'sf-toolbar-closed');
removeClass(sfwdt, 'sf-toolbar-opened');
} else {
addClass(sfwdt, 'sf-toolbar-opened');
removeClass(sfwdt, 'sf-toolbar-closed');
}
},
hideToolbar: function(token) {
var sfwdt = this.getSfwdt(token);
addClass(sfwdt, 'sf-toolbar-closed');
removeClass(sfwdt, 'sf-toolbar-opened');
},
initToolbar: function(token) {
this.showToolbar(token);
var toggleButton = document.querySelector(`#sfToolbarToggleButton-${token}`);
addEventListener(toggleButton, 'click', function (event) {
event.preventDefault();
const newState = 'opened' === getPreference('toolbar/displayState') ? 'closed' : 'opened';
setPreference('toolbar/displayState', newState);
'opened' === newState ? Sfjs.showToolbar(token) : Sfjs.hideToolbar(token);
});
},
loadToolbar: function(token, newToken) {
var that = this;
var triesCounter = document.getElementById('sfLoadCounter-' + token);
var options = {
retry: true,
onSend: function (count) {
if (count === 3) {
that.initToolbar(token);
}
if (triesCounter) {
triesCounter.textContent = count;
}
},
};
var cancelButton = document.getElementById('sfLoadCancel-' + token);
if (cancelButton) {
addEventListener(cancelButton, 'click', function (event) {
event.preventDefault();
options.stop = true;
that.hideToolbar(token);
});
}
newToken = (newToken || token);
this.load(
'sfwdt' + token,
'{{ url("_wdt", { "token": "xxxxxx" })|escape('js') }}'.replace(/xxxxxx/, newToken),
function(xhr, el) {
var toolbarContent = document.getElementById('sfToolbarMainContent-' + newToken);
/* Do nothing in the edge case where the toolbar has already been replaced with a new one */
if (!toolbarContent) {
return;
}
/* Replace the ID, it has to match the new token */
toolbarContent.parentElement.id = 'sfwdt' + newToken;
/* Evaluate in global scope scripts embedded inside the toolbar */
var i, scripts = [].slice.call(el.querySelectorAll('script'));
for (i = 0; i < scripts.length; ++i) {
if (scripts[i].firstChild) {
eval.call({}, scripts[i].firstChild.nodeValue);
}
}
el.style.display = -1 !== xhr.responseText.indexOf('sf-toolbarreset') ? 'block' : 'none';
if (el.style.display == 'none') {
return;
}
that.initToolbar(newToken);
/* Handle toolbar-info position */
var toolbarBlocks = [].slice.call(el.querySelectorAll('.sf-toolbar-block'));
for (i = 0; i < toolbarBlocks.length; ++i) {
toolbarBlocks[i].onmouseover = function () {
var toolbarInfo = this.querySelectorAll('.sf-toolbar-info')[0];
var pageWidth = document.body.clientWidth;
var elementWidth = toolbarInfo.offsetWidth;
var leftValue = (elementWidth + this.offsetLeft) - pageWidth;
var rightValue = (elementWidth + (pageWidth - this.offsetLeft)) - pageWidth;
/* Reset right and left value, useful on window resize */
toolbarInfo.style.right = '';
toolbarInfo.style.left = '';
if (elementWidth > pageWidth) {
toolbarInfo.style.left = 0;
}
else if (leftValue > 0 && rightValue > 0) {
toolbarInfo.style.right = (rightValue * -1) + 'px';
} else if (leftValue < 0) {
toolbarInfo.style.left = 0;
} else {
toolbarInfo.style.right = '0px';
}
};
}
renderAjaxRequests();
addEventListener(document.querySelector('.sf-toolbar-ajax-clear'), 'click', function() {
requestStack = [];
renderAjaxRequests();
successStreak = 4;
document.querySelector('.sf-toolbar-ajax-request-list').innerHTML = '';
});
addEventListener(document.querySelector('.sf-toolbar-block-ajax'), 'mouseenter', function (event) {
var elem = document.querySelector('.sf-toolbar-block-ajax .sf-toolbar-info');
elem.scrollTop = elem.scrollHeight;
});
addEventListener(document.querySelector('.sf-toolbar-block-ajax > .sf-toolbar-icon'), 'click', function (event) {
event.preventDefault();
toggleClass(this.parentNode, 'hover');
});
var dumpInfo = document.querySelector('.sf-toolbar-block-dump .sf-toolbar-info');
if (null !== dumpInfo) {
addEventListener(dumpInfo, 'sfbeforedumpcollapse', function () {
dumpInfo.style.minHeight = dumpInfo.getBoundingClientRect().height+'px';
});
addEventListener(dumpInfo, 'mouseleave', function () {
dumpInfo.style.minHeight = '';
});
}
},
function(xhr) {
if (xhr.status !== 0 && !options.stop) {
var sfwdt = that.getSfwdt(token);
sfwdt.innerHTML = '\
<div class="sf-toolbarreset notranslate">\
<div class="sf-toolbar-icon"><svg width="26" height="28" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" viewBox="0 0 26 28" enable-background="new 0 0 26 28" xml:space="preserve"><path fill="#FFFFFF" d="M13 0C5.8 0 0 5.8 0 13c0 7.2 5.8 13 13 13c7.2 0 13-5.8 13-13C26 5.8 20.2 0 13 0z M20 7.5 c-0.6 0-1-0.3-1-0.9c0-0.2 0-0.4 0.2-0.6c0.1-0.3 0.2-0.3 0.2-0.4c0-0.3-0.5-0.4-0.7-0.4c-2 0.1-2.5 2.7-2.9 4.8l-0.2 1.1 c1.1 0.2 1.9 0 2.4-0.3c0.6-0.4-0.2-0.8-0.1-1.3C18 9.2 18.4 9 18.7 8.9c0.5 0 0.8 0.5 0.8 1c0 0.8-1.1 2-3.3 1.9 c-0.3 0-0.5 0-0.7-0.1L15 14.1c-0.4 1.7-0.9 4.1-2.6 6.2c-1.5 1.8-3.1 2.1-3.8 2.1c-1.3 0-2.1-0.6-2.2-1.6c0-0.9 0.8-1.4 1.3-1.4 c0.7 0 1.2 0.5 1.2 1.1c0 0.5-0.2 0.6-0.4 0.7c-0.1 0.1-0.3 0.2-0.3 0.4c0 0.1 0.1 0.3 0.4 0.3c0.5 0 0.9-0.3 1.2-0.5 c1.3-1 1.7-2.9 2.4-6.2l0.1-0.8c0.2-1.1 0.5-2.3 0.8-3.5c-0.9-0.7-1.4-1.5-2.6-1.8c-0.8-0.2-1.3 0-1.7 0.4C8.4 10 8.6 10.7 9 11.1 l0.7 0.7c0.8 0.9 1.3 1.7 1.1 2.7c-0.3 1.6-2.1 2.8-4.3 2.1c-1.9-0.6-2.2-1.9-2-2.7c0.2-0.6 0.7-0.8 1.2-0.6 c0.5 0.2 0.7 0.8 0.6 1.3c0 0.1 0 0.1-0.1 0.3C6 15 5.9 15.2 5.9 15.3c-0.1 0.4 0.4 0.7 0.8 0.8c0.8 0.3 1.7-0.2 1.9-0.9 c0.2-0.6-0.2-1.1-0.4-1.2l-0.8-0.9c-0.4-0.4-1.2-1.5-0.8-2.8c0.2-0.5 0.5-1 0.9-1.4c1-0.7 2-0.8 3-0.6c1.3 0.4 1.9 1.2 2.8 1.9 c0.5-1.3 1.1-2.6 2-3.8c0.9-1 2-1.7 3.3-1.8C20 4.8 21 5.4 21 6.3C21 6.7 20.8 7.5 20 7.5z"/></svg></div>\
An error occurred while loading the web debug toolbar. <a href="{{ url("_profiler_home")|escape('js') }}' + newToken + '">Open the web profiler.</a>\
</div>\
';
sfwdt.setAttribute('class', 'sf-toolbar sf-error-toolbar');
}
},
options
);
return this;
},
toggle: function(selector, elOn, elOff) {
var tmp = elOn.style.display,
el = document.getElementById(selector);
elOn.style.display = elOff.style.display;
elOff.style.display = tmp;
if (el) {
el.style.display = 'none' === tmp ? 'none' : 'block';
}
return this;
},
};
})();
}
Sfjs.loadToolbar('{{ token }}');
/*]]>*/</script>
<!-- END of Symfony Web Debug Toolbar -->
@@ -0,0 +1,54 @@
{% extends '@WebProfiler/Profiler/base.html.twig' %}
{% block title 'Redirection Intercepted' %}
{% block head %}
{{ parent() }}
<style>
.sf-redirection-details {
background: var(--page-background);
box-shadow: inset 0 0 0 1px var(--menu-border-color), 0 0 0 5px var(--page-background);
border-radius: 6px;
margin: 45px auto 30px;
max-width: 800px;
padding: 30px 45px;
}
.sf-redirection-details h1 {
font-size: 21px;
font-weight: bold;
margin: 0 0 10px;
}
.sf-redirection-details p {
margin-top: 0;
}
.sf-redirection-details .sf-redirection-help {
color: var(--color-muted);
font-size: 14px;
margin: 45px 0 0;
}
</style>
{% endblock %}
{% block body %}
<div class="container">
{{ include('@WebProfiler/Profiler/header.html.twig', with_context = false) }}
<div class="sf-reset sf-redirection-details">
<div class="block-exception">
<h1>Redirection Intercepted</h1>
{% set absolute_url = absolute_url(location) %}
<p>This request redirects to <strong>{{ absolute_url }}</strong></p>
<p><a class="btn" href="{{ absolute_url }}">Follow redirect</a></p>
<p class="sf-redirection-help">
The redirect was intercepted by the Symfony Web Debug toolbar to help debugging.
For more information, see the "intercept-redirects" option of the Profiler.
</p>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,71 @@
<h2>Routing</h2>
<div class="metrics">
<div class="metric">
<span class="value">{{ request.route ?: '(none)' }}</span>
<span class="label">Matched route</span>
</div>
</div>
{% if request.route %}
<h3>Route Parameters</h3>
{% if request.routeParams is empty %}
<div class="empty">
<p>No parameters.</p>
</div>
{% else %}
{{ include('@WebProfiler/Profiler/table.html.twig', { data: request.routeParams, labels: ['Name', 'Value'] }, with_context = false) }}
{% endif %}
{% endif %}
{% if router.redirect %}
<h3>Route Redirection</h3>
<p>This page redirects to:</p>
<div class="card break-long-words">
{{ router.targetUrl }}
{% if router.targetRoute %}<span class="text-muted">(route: "{{ router.targetRoute }}")</span>{% endif %}
</div>
{% endif %}
<h3>Route Matching Logs</h3>
<div class="card">
<strong>Path to match:</strong> <code>{{ request.pathinfo }}</code>
</div>
<table id="router-logs">
<thead>
<tr>
<th>#</th>
<th>Route name</th>
<th>Path</th>
<th>Log</th>
</tr>
</thead>
<tbody>
{% for trace in traces %}
<tr class="{{ trace.level == 1 ? 'status-warning' : trace.level == 2 ? 'status-success' }}">
<td class="font-normal text-muted nowrap">{{ loop.index }}</td>
<td class="break-long-words">{{ trace.name }}</td>
<td class="break-long-words">{{ trace.path }}</td>
<td class="break-long-words font-normal">
{% if trace.level == 1 %}
Path almost matches, but
<span class="newline">{{ trace.log }}</span>
{% elseif trace.level == 2 %}
{{ trace.log }}
{% else %}
Path does not match
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<p class="help">
Note: These matching logs are based on the current router configuration,
which might differ from the configuration used when profiling this request.
</p>
@@ -0,0 +1,483 @@
(function(Mi,ya){typeof exports=="object"&&typeof module<"u"?module.exports=ya():typeof define=="function"&&define.amd?define(ya):(Mi=typeof globalThis<"u"?globalThis:Mi||self,Mi.mermaid=ya())})(this,function(){"use strict";function Mi(t){for(var e=[],r=1;r<arguments.length;r++)e[r-1]=arguments[r];var n=Array.from(typeof t=="string"?[t]:t);n[n.length-1]=n[n.length-1].replace(/\r?\n([\t ]*)$/,"");var i=n.reduce(function(o,l){var u=l.match(/\n([\t ]+|(?!\s).)/g);return u?o.concat(u.map(function(c){var h,f;return(f=(h=c.match(/[\t ]/g))===null||h===void 0?void 0:h.length)!==null&&f!==void 0?f:0})):o},[]);if(i.length){var a=new RegExp(`
[ ]{`+Math.min.apply(Math,i)+"}","g");n=n.map(function(o){return o.replace(a,`
`)})}n[0]=n[0].replace(/^\r?\n/,"");var s=n[0];return e.forEach(function(o,l){var u=s.match(/(?:^|\n)( *)$/),c=u?u[1]:"",h=o;typeof o=="string"&&o.includes(`
`)&&(h=String(o).split(`
`).map(function(f,p){return p===0?f:""+c+f}).join(`
`)),s+=h+n[l+1]}),s}var ya=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Pm(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var kc={exports:{}};(function(t,e){(function(r,n){t.exports=n()})(ya,function(){var r=1e3,n=6e4,i=36e5,a="millisecond",s="second",o="minute",l="hour",u="day",c="week",h="month",f="quarter",p="year",y="date",b="Invalid Date",A=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,_=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(X){var $=["th","st","nd","rd"],U=X%100;return"["+X+($[(U-20)%10]||$[U]||$[0])+"]"}},I=function(X,$,U){var et=String(X);return!et||et.length>=$?X:""+Array($+1-et.length).join(U)+X},V={s:I,z:function(X){var $=-X.utcOffset(),U=Math.abs($),et=Math.floor(U/60),K=U%60;return($<=0?"+":"-")+I(et,2,"0")+":"+I(K,2,"0")},m:function X($,U){if($.date()<U.date())return-X(U,$);var et=12*(U.year()-$.year())+(U.month()-$.month()),K=$.clone().add(et,h),W=U-K<0,v=$.clone().add(et+(W?-1:1),h);return+(-(et+(U-K)/(W?K-v:v-K))||0)},a:function(X){return X<0?Math.ceil(X)||0:Math.floor(X)},p:function(X){return{M:h,y:p,w:c,d:u,D:y,h:l,m:o,s,ms:a,Q:f}[X]||String(X||"").toLowerCase().replace(/s$/,"")},u:function(X){return X===void 0}},N="en",L={};L[N]=M;var q="$isDayjsObject",G=function(X){return X instanceof P||!(!X||!X[q])},Y=function X($,U,et){var K;if(!$)return N;if(typeof $=="string"){var W=$.toLowerCase();L[W]&&(K=W),U&&(L[W]=U,K=W);var v=$.split("-");if(!K&&v.length>1)return X(v[0])}else{var st=$.name;L[st]=$,K=st}return!et&&K&&(N=K),K||!et&&N},J=function(X,$){if(G(X))return X.clone();var U=typeof $=="object"?$:{};return U.date=X,U.args=arguments,new P(U)},O=V;O.l=Y,O.i=G,O.w=function(X,$){return J(X,{locale:$.$L,utc:$.$u,x:$.$x,$offset:$.$offset})};var P=function(){function X(U){this.$L=Y(U.locale,null,!0),this.parse(U),this.$x=this.$x||U.x||{},this[q]=!0}var $=X.prototype;return $.parse=function(U){this.$d=function(et){var K=et.date,W=et.utc;if(K===null)return new Date(NaN);if(O.u(K))return new Date;if(K instanceof Date)return new Date(K);if(typeof K=="string"&&!/Z$/i.test(K)){var v=K.match(A);if(v){var st=v[2]-1||0,dt=(v[7]||"0").substring(0,3);return W?new Date(Date.UTC(v[1],st,v[3]||1,v[4]||0,v[5]||0,v[6]||0,dt)):new Date(v[1],st,v[3]||1,v[4]||0,v[5]||0,v[6]||0,dt)}}return new Date(K)}(U),this.init()},$.init=function(){var U=this.$d;this.$y=U.getFullYear(),this.$M=U.getMonth(),this.$D=U.getDate(),this.$W=U.getDay(),this.$H=U.getHours(),this.$m=U.getMinutes(),this.$s=U.getSeconds(),this.$ms=U.getMilliseconds()},$.$utils=function(){return O},$.isValid=function(){return this.$d.toString()!==b},$.isSame=function(U,et){var K=J(U);return this.startOf(et)<=K&&K<=this.endOf(et)},$.isAfter=function(U,et){return J(U)<this.startOf(et)},$.isBefore=function(U,et){return this.endOf(et)<J(U)},$.$g=function(U,et,K){return O.u(U)?this[et]:this.set(K,U)},$.unix=function(){return Math.floor(this.valueOf()/1e3)},$.valueOf=function(){return this.$d.getTime()},$.startOf=function(U,et){var K=this,W=!!O.u(et)||et,v=O.p(U),st=function(jt,Ft){var Yt=O.w(K.$u?Date.UTC(K.$y,Ft,jt):new Date(K.$y,Ft,jt),K);return W?Yt:Yt.endOf(u)},dt=function(jt,Ft){return O.w(K.toDate()[jt].apply(K.toDate("s"),(W?[0,0,0,0]:[23,59,59,999]).slice(Ft)),K)},w=this.$W,St=this.$M,zt=this.$D,Ot="set"+(this.$u?"UTC":"");switch(v){case p:return W?st(1,0):st(31,11);case h:return W?st(1,St):st(0,St+1);case c:var Ht=this.$locale().weekStart||0,Wt=(w<Ht?w+7:w)-Ht;return st(W?zt-Wt:zt+(6-Wt),St);case u:case y:return dt(Ot+"Hours",0);case l:return dt(Ot+"Minutes",1);case o:return dt(Ot+"Seconds",2);case s:return dt(Ot+"Milliseconds",3);default:return this.clone()}},$.endOf=function(U){return this.startOf(U,!1)},$.$set=function(U,et){var K,W=O.p(U),v="set"+(this.$u?"UTC":""),st=(K={},K[u]=v+"Date",K[y]=v+"Date",K[h]=v+"Month",K[p]=v+"FullYear",K[l]=v+"Hours",K[o]=v+"Minutes",K[s]=v+"Seconds",K[a]=v+"Milliseconds",K)[W],dt=W===u?this.$D+(et-this.$W):et;if(W===h||W===p){var w=this.clone().set(y,1);w.$d[st](dt),w.init(),this.$d=w.set(y,Math.min(this.$D,w.daysInMonth())).$d}else st&&this.$d[st](dt);return this.init(),this},$.set=function(U,et){return this.clone().$set(U,et)},$.get=function(U){return this[O.p(U)]()},$.add=function(U,et){var K,W=this;U=Number(U);var v=O.p(et),st=function(St){var zt=J(W);return O.w(zt.date(zt.date()+Math.round(St*U)),W)};if(v===h)return this.set(h,this.$M+U);if(v===p)return this.set(p,this.$y+U);if(v===u)return st(1);if(v===c)return st(7);var dt=(K={},K[o]=n,K[l]=i,K[s]=r,K)[v]||1,w=this.$d.getTime()+U*dt;return O.w(w,this)},$.subtract=function(U,et){return this.add(-1*U,et)},$.format=function(U){var et=this,K=this.$Line truncated
`+Pe),Ft&&qs([O,P,ft],Kr=>{Pe=Fa(Pe,Kr," ")}),I&&Kt?I.createHTML(Pe):Pe},e.setConfig=function(){let ht=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};Ei(ht),ye=!0},e.clearConfig=function(){zn=null,ye=!1},e.isValidAttribute=function(ht,F,Q){zn||Ei({});const ct=Ut(ht),Xt=Ut(F);return Er(ct,Xt,Q)},e.addHook=function(ht,F){typeof F=="function"&&(J[ht]=J[ht]||[],Ea(J[ht],F))},e.removeHook=function(ht){if(J[ht])return zh(J[ht])},e.removeHooks=function(ht){J[ht]&&(J[ht]=[])},e.removeAllHooks=function(){J={}},e}var Ni=Vh();const Ma=/<br\s*\/?>/gi,Yg=t=>t?Gh(t).replace(/\\n/g,"#br#").split("#br#"):[""],Xg=(()=>{let t=!1;return()=>{t||(Kg(),t=!0)}})();function Kg(){const t="data-temp-href-target";Ni.addHook("beforeSanitizeAttributes",e=>{e.tagName==="A"&&e.hasAttribute("target")&&e.setAttribute(t,e.getAttribute("target")||"")}),Ni.addHook("afterSanitizeAttributes",e=>{e.tagName==="A"&&e.hasAttribute(t)&&(e.setAttribute("target",e.getAttribute(t)||""),e.removeAttribute(t),e.getAttribute("target")==="_blank"&&e.setAttribute("rel","noopener"))})}const Wh=t=>(Xg(),Ni.sanitize(t)),Uh=(t,e)=>{var r;if(((r=e.flowchart)==null?void 0:r.htmlLabels)!==!1){const n=e.securityLevel;n==="antiscript"||n==="strict"?t=Wh(t):n!=="loose"&&(t=Gh(t),t=t.replace(/</g,"&lt;").replace(/>/g,"&gt;"),t=t.replace(/=/g,"&equals;"),t=t6(t))}return t},li=(t,e)=>t&&(e.dompurifyConfig?t=Ni.sanitize(Uh(t,e),e.dompurifyConfig).toString():t=Ni.sanitize(Uh(t,e),{FORBID_TAGS:["style"]}).toString(),t),Zg=(t,e)=>typeof t=="string"?li(t,e):t.flat().map(r=>li(r,e)),Qg=t=>Ma.test(t),Jg=t=>t.split(Ma),t6=t=>t.replace(/#br#/g,"<br/>"),Gh=t=>t.replace(Ma,"#br#"),e6=t=>{let e="";return t&&(e=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,e=e.replaceAll(/\(/g,"\\("),e=e.replaceAll(/\)/g,"\\)")),e},De=t=>!(t===!1||["false","null","0"].includes(String(t).trim().toLowerCase())),r6=function(...t){const e=t.filter(r=>!isNaN(r));return Math.max(...e)},n6=function(...t){const e=t.filter(r=>!isNaN(r));return Math.min(...e)},jh=()=>window.MathMLElement!==void 0,ol=/\$\$(.*)\$\$/g,Yh=t=>{var e;return(((e=t.match(ol))==null?void 0:e.length)??0)>0},Xh=async(t,e)=>{if(!Yh(t))return t;if(!jh()&&!e.legacyMathML)return t.replace(ol,"MathML is unsupported in this environment.");const{default:r}=await Promise.resolve().then(()=>cL);return t.split(Ma).map(n=>Yh(n)?`
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
${n}
</div>
`:`<div>${n}</div>`).join("").replace(ol,(n,i)=>r.renderToString(i,{throwOnError:!0,displayMode:!0,output:jh()?"mathml":"htmlAndMathml"}).replace(/\n/g," ").replace(/<annotation.*<\/annotation>/g,""))},Ri={getRows:Yg,sanitizeText:li,sanitizeTextOrArray:Zg,hasBreaks:Qg,splitBreaks:Jg,lineBreakRegex:Ma,removeScript:Wh,getUrl:e6,evaluate:De,getMax:r6,getMin:n6},Ws={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:t=>t>=255?255:t<0?0:t,g:t=>t>=255?255:t<0?0:t,b:t=>t>=255?255:t<0?0:t,h:t=>t%360,s:t=>t>=100?100:t<0?0:t,l:t=>t>=100?100:t<0?0:t,a:t=>t>=1?1:t<0?0:t},toLinear:t=>{const e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:(t,e,r)=>(r<0&&(r+=1),r>1&&(r-=1),r<1/6?t+(e-t)*6*r:r<1/2?e:r<2/3?t+(e-t)*(2/3-r)*6:t),hsl2rgb:({h:t,s:e,l:r},n)=>{if(!e)return r*2.55;t/=360,e/=100,r/=100;const i=r<.5?r*(1+e):r+e-r*e,a=2*r-i;switch(n){case"r":return Ws.hue2rgb(a,i,t+1/3)*255;case"g":return Ws.hue2rgb(a,i,t)*255;case"b":return Ws.hue2rgb(a,i,t-1/3)*255}},rgb2hsl:({r:t,g:e,b:r},n)=>{t/=255,e/=255,r/=255;const i=Math.max(t,e,r),a=Math.min(t,e,r),s=(i+a)/2;if(n==="l")return s*100;if(i===a)return 0;const o=i-a,l=s>.5?o/(2-i-a):o/(i+a);if(n==="s")return l*100;switch(i){case t:return((e-r)/o+(e<r?6:0))*60;case e:return((r-t)/o+2)*60;case r:return((t-e)/o+4)*60;default:return-1}}},wt={channel:Ws,lang:{clamp:(t,e,r)=>e>r?Math.min(e,Math.max(r,t)):Math.min(r,Math.max(e,t)),round:t=>Math.round(t*1e10)/1e10},unit:{dec2hex:t=>{const e=Math.round(t).toString(16);return e.length>1?e:`0${e}`}}},$n={};for(let t=0;t<=255;t++)$n[t]=wt.unit.dec2hex(t);const ze={ALL:0,RGB:1,HSL:2};class i6{constructor(){this.type=ze.ALL}get(){return this.type}set(e){if(this.type&&this.type!==e)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=e}reset(){this.type=ze.ALL}is(e){return this.type===e}}const a6=i6;class s6{constructor(e,r){this.color=r,this.changed=!1,this.data=e,this.type=new a6}set(e,r){return this.color=r,this.changed=!1,this.data=e,this.type.type=ze.ALL,this}_ensureHSL(){const e=this.data,{h:r,s:n,l:i}=e;r===void 0&&(e.h=wt.channel.rgb2hsl(e,"h")),n===void 0&&(e.s=wt.channel.rgb2hsl(e,"s")),i===void 0&&(e.l=wt.channel.rgb2hsl(e,"l"))}_ensureRGB(){const e=this.data,{r,g:n,b:i}=e;r===void 0&&(e.r=wt.channel.hsl2rgb(e,"r")),n===void 0&&(e.g=wt.channel.hsl2rgb(e,"g")),i===void 0&&(e.b=wt.channel.hsl2rgb(e,"b"))}get r(){const e=this.data,r=e.r;return!this.type.is(ze.HSL)&&r!==void 0?r:(this._ensureHSL(),wt.channel.hsl2rgb(e,"r"))}get g(){const e=this.data,r=e.g;return!this.type.is(ze.HSL)&&r!==void 0?r:(this._ensureHSL(),wt.channel.hsl2rgb(e,"g"))}get b(){const e=this.data,r=e.b;return!this.type.is(ze.HSL)&&r!==void 0?r:(this._ensureHSL(),wt.channel.hsl2rgb(e,"b"))}get h(){const e=this.data,r=e.h;return!this.type.is(ze.RGB)&&r!==void 0?r:(this._ensureRGB(),wt.channel.rgb2hsl(e,"h"))}get s(){const e=this.data,r=e.s;return!this.type.is(ze.RGB)&&r!==void 0?r:(this._ensureRGB(),wt.channel.rgb2hsl(e,"s"))}get l(){const e=this.data,r=e.l;return!this.type.is(ze.RGB)&&r!==void 0?r:(this._ensureRGB(),wt.channel.rgb2hsl(e,"l"))}get a(){return this.data.a}set r(e){this.type.set(ze.RGB),this.changed=!0,this.data.r=e}set g(e){this.type.set(ze.RGB),this.changed=!0,this.data.g=e}set b(e){this.type.set(ze.RGB),this.changed=!0,this.data.b=e}set h(e){this.type.set(ze.HSL),this.changed=!0,this.data.h=e}set s(e){this.type.set(ze.HSL),this.changed=!0,this.data.s=e}set l(e){this.type.set(ze.HSL),this.changed=!0,this.data.l=e}set a(e){this.changed=!0,this.data.a=e}}const o6=s6,Us=new o6({r:0,g:0,b:0,a:0},"transparent"),Kh={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:t=>{if(t.charCodeAt(0)!==35)return;const e=t.match(Kh.re);if(!e)return;const r=e[1],n=parseInt(r,16),i=r.length,a=i%4===0,s=i>4,o=s?1:17,l=s?8:4,u=a?0:-1,c=s?255:15;return Us.set({r:(n>>l*(u+3)&c)*o,g:(n>>l*(u+2)&c)*o,b:(n>>l*(u+1)&c)*o,a:a?(n&c)*o/255:1},t)},stringify:t=>{const{r:e,g:r,b:n,a:i}=t;return i<1?`#${$n[Math.round(e)]}${$n[Math.round(r)]}${$n[Math.round(n)]}${$n[Math.round(i*255)]}`:`#${$n[Math.round(e)]}${$n[Math.round(r)]}${$n[Math.round(n)]}`}},Da=Kh,Gs={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:t=>{const e=t.match(Gs.hueRe);if(e){const[,r,n]=e;switch(n){case"grad":return wt.channel.clamp.h(parseFloat(r)*.9);case"rad":return wt.channel.clamp.h(parseFloat(r)*180/Math.PI);case"turn":return wt.channel.clamp.h(parseFloat(r)*360)}}return wt.channel.clamp.h(parseFloat(t))},parse:t=>{const e=t.charCodeAt(0);if(e!==104&&e!==72)return;const r=t.match(Gs.re);if(!r)return;const[,n,i,a,s,o]=r;return Us.set({h:Gs._hue2deg(n),s:wt.channel.clamp.s(parseFloat(i)),l:wt.channel.clamp.l(parseFloat(a)),a:s?wt.channel.clamp.a(o?pLine truncated
`);for(const[r,{detector:n}]of Object.entries(qi))if(n(t,e))return r;throw new i1(`No diagram type detected matching given configuration for text: ${t}`)},S6=(...t)=>{for(const{id:e,detector:r,loader:n}of t)a1(e,r,n)},a1=(t,e,r)=>{qi[t]?E.error(`Detector with key ${t} already exists`):qi[t]={detector:e,loader:r},E.debug(`Detector with key ${t} added${r?" with loader":""}`)},T6=t=>qi[t].loader,ll=(t,e,{depth:r=2,clobber:n=!1}={})=>{const i={depth:r,clobber:n};return Array.isArray(e)&&!Array.isArray(t)?(e.forEach(a=>ll(t,a,i)),t):Array.isArray(e)&&Array.isArray(t)?(e.forEach(a=>{t.includes(a)||t.push(a)}),t):t===void 0||r<=0?t!=null&&typeof t=="object"&&typeof e=="object"?Object.assign(t,e):e:(e!==void 0&&typeof t=="object"&&typeof e=="object"&&Object.keys(e).forEach(a=>{typeof e[a]=="object"&&(t[a]===void 0||typeof t[a]=="object")?(t[a]===void 0&&(t[a]=Array.isArray(e[a])?[]:{}),t[a]=ll(t[a],e[a],{depth:r-1,clobber:n})):(n||typeof t[a]!="object"&&typeof e[a]!="object")&&(t[a]=e[a])}),t)},Oe=ll;var A6=typeof global=="object"&&global&&global.Object===Object&&global;const s1=A6;var B6=typeof self=="object"&&self&&self.Object===Object&&self,E6=s1||B6||Function("return this")();const Pr=E6;var F6=Pr.Symbol;const xr=F6;var o1=Object.prototype,L6=o1.hasOwnProperty,M6=o1.toString,Oa=xr?xr.toStringTag:void 0;function D6(t){var e=L6.call(t,Oa),r=t[Oa];try{t[Oa]=void 0;var n=!0}catch{}var i=M6.call(t);return n&&(e?t[Oa]=r:delete t[Oa]),i}var I6=Object.prototype,z6=I6.toString;function O6(t){return z6.call(t)}var N6="[object Null]",R6="[object Undefined]",l1=xr?xr.toStringTag:void 0;function ui(t){return t==null?t===void 0?R6:N6:l1&&l1 in Object(t)?D6(t):O6(t)}function hr(t){var e=typeof t;return t!=null&&(e=="object"||e=="function")}var P6="[object AsyncFunction]",q6="[object Function]",$6="[object GeneratorFunction]",H6="[object Proxy]";function Na(t){if(!hr(t))return!1;var e=ui(t);return e==q6||e==$6||e==P6||e==H6}var V6=Pr["__core-js_shared__"];const ul=V6;var u1=function(){var t=/[^.]+$/.exec(ul&&ul.keys&&ul.keys.IE_PROTO||"");return t?"Symbol(src)_1."+t:""}();function W6(t){return!!u1&&u1 in t}var U6=Function.prototype,G6=U6.toString;function ci(t){if(t!=null){try{return G6.call(t)}catch{}try{return t+""}catch{}}return""}var j6=/[\\^$.*+?()[\]{}|]/g,Y6=/^\[object .+?Constructor\]$/,X6=Function.prototype,K6=Object.prototype,Z6=X6.toString,Q6=K6.hasOwnProperty,J6=RegExp("^"+Z6.call(Q6).replace(j6,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function t7(t){if(!hr(t)||W6(t))return!1;var e=Na(t)?J6:Y6;return e.test(ci(t))}function e7(t,e){return t==null?void 0:t[e]}function hi(t,e){var r=e7(t,e);return t7(r)?r:void 0}var r7=hi(Object,"create");const Ra=r7;function n7(){this.__data__=Ra?Ra(null):{},this.size=0}function i7(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}var a7="__lodash_hash_undefined__",s7=Object.prototype,o7=s7.hasOwnProperty;function l7(t){var e=this.__data__;if(Ra){var r=e[t];return r===a7?void 0:r}return o7.call(e,t)?e[t]:void 0}var u7=Object.prototype,c7=u7.hasOwnProperty;function h7(t){var e=this.__data__;return Ra?e[t]!==void 0:c7.call(e,t)}var f7="__lodash_hash_undefined__";function d7(t,e){var r=this.__data__;return this.size+=this.has(t)?0:1,r[t]=Ra&&e===void 0?f7:e,this}function fi(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}fi.prototype.clear=n7,fi.prototype.delete=i7,fi.prototype.get=l7,fi.prototype.has=h7,fi.prototype.set=d7;function p7(){this.__data__=[],this.size=0}function $i(t,e){return t===e||t!==t&&e!==e}function t0(t,e){for(var r=t.length;r--;)if($i(t[r][0],e))return r;return-1}var m7=Array.prototype,g7=m7.splice;function y7(t){var e=this.__data__,r=t0(e,t);if(r<0)return!1;var n=e.length-1;return r==n?e.pop():g7.call(e,r,1),--this.size,!0}function b7(t){var e=this.__data__,r=t0(e,t);return r<0?void 0:e[r][1]}function x7(t){return t0(this.__data__,t)>-1}function v7(t,e){var r=this.__data__,n=t0(r,t);return n<0?(++this.size,r.push([t,e])):r[n][1]=e,this}function bn(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}bn.prototype.clear=p7,bn.prototype.delete=y7,bn.prototype.get=b7,bn.prototype.has=x7,bn.prototype.set=v7;var w7=hi(Pr,"Map");const Pa=w7;function C7(){this.size=0,this.__data__={hash:new fi,map:new(Pa||bn),string:new fi}}function k7(t){var e=typeof t;return e=="string"||e=="number"||e=="symbol"||e=="boolean"?t!=="__proto__":t===null}function e0(t,e){var r=t.__data__;return k7(e)?r[typeof e=="string"?"string":"hash"]:r.map}function _7(t){var e=e0(this,t).delete(t);return this.size-=e?1:0,e}function S7(t){return e0(this,t).get(t)}function T7(t){return e0(this,t).has(t)}function A7(t,e){var r=e0(this,t),n=r.size;return r.set(t,e),this.size+=r.size==n?0:1,this}function xn(t){var e=-1,r=t==null?0:t.length;for(this.clear();++e<r;){var n=t[e];this.set(n[0],n[1])}}xn.prototype.clear=C7,xn.prototype.delete=_7,xn.prototype.get=S7,xn.p Line truncated
`,"ig");t=t.trim().replace(r,"").replace(/'/gm,'"'),E.debug(`Detecting diagram directive${e!==null?" type:"+e:""} based on the text:${t}`);let n;const i=[];for(;(n=za.exec(t))!==null;)if(n.index===za.lastIndex&&za.lastIndex++,n&&!e||e&&n[1]&&n[1].match(e)||e&&n[2]&&n[2].match(e)){const a=n[1]?n[1]:n[2],s=n[3]?n[3].trim():n[4]?JSON.parse(n[4].trim()):null;i.push({type:a,args:s})}return i.length===0?{type:t,args:null}:i.length===1?i[0]:i}catch(r){return E.error(`ERROR: ${r.message} - Unable to parse directive type: '${e}' based on the text: '${t}'`),{type:void 0,args:null}}},oy=function(t){return t.replace(za,"")},ly=function(t,e){for(const[r,n]of e.entries())if(n.match(t))return r;return-1};function f0(t,e){if(!t)return e;const r=`curve${t.charAt(0).toUpperCase()+t.slice(1)}`;return iy[r]??e}function uy(t,e){const r=t.trim();if(r)return e.securityLevel!=="loose"?_c.sanitizeUrl(r):r}const cy=(t,...e)=>{const r=t.split("."),n=r.length-1,i=r[n];let a=window;for(let s=0;s<n;s++)if(a=a[r[s]],!a){E.error(`Function name: ${t} not found in window`);return}a[i](...e)};function z1(t,e){return!t||!e?0:Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function hy(t){let e,r=0;t.forEach(i=>{r+=z1(i,e),e=i});const n=r/2;return yl(t,n)}function fy(t){return t.length===1?t[0]:hy(t)}const O1=(t,e=2)=>{const r=Math.pow(10,e);return Math.round(t*r)/r},yl=(t,e)=>{let r,n=e;for(const i of t){if(r){const a=z1(i,r);if(a<n)n-=a;else{const s=n/a;if(s<=0)return r;if(s>=1)return{x:i.x,y:i.y};if(s>0&&s<1)return{x:O1((1-s)*r.x+s*i.x,5),y:O1((1-s)*r.y+s*i.y,5)}}}r=i}throw new Error("Could not find a suitable point for the given distance")},dy=(t,e,r)=>{E.info(`our points ${JSON.stringify(e)}`),e[0]!==r&&(e=e.reverse());const i=yl(e,25),a=t?10:5,s=Math.atan2(e[0].y-i.y,e[0].x-i.x),o={x:0,y:0};return o.x=Math.sin(s)*a+(e[0].x+i.x)/2,o.y=-Math.cos(s)*a+(e[0].y+i.y)/2,o};function py(t,e,r){const n=structuredClone(r);E.info("our points",n),e!=="start_left"&&e!=="start_right"&&n.reverse();const i=25+t,a=yl(n,i),s=10+t*.5,o=Math.atan2(n[0].y-a.y,n[0].x-a.x),l={x:0,y:0};return e==="start_left"?(l.x=Math.sin(o+Math.PI)*s+(n[0].x+a.x)/2,l.y=-Math.cos(o+Math.PI)*s+(n[0].y+a.y)/2):e==="end_right"?(l.x=Math.sin(o-Math.PI)*s+(n[0].x+a.x)/2-5,l.y=-Math.cos(o-Math.PI)*s+(n[0].y+a.y)/2-5):e==="end_left"?(l.x=Math.sin(o)*s+(n[0].x+a.x)/2-5,l.y=-Math.cos(o)*s+(n[0].y+a.y)/2-5):(l.x=Math.sin(o)*s+(n[0].x+a.x)/2,l.y=-Math.cos(o)*s+(n[0].y+a.y)/2),l}function d0(t){let e="",r="";for(const n of t)n!==void 0&&(n.startsWith("color:")||n.startsWith("text-align:")?r=r+n+";":e=e+n+";");return{style:e,labelStyle:r}}let N1=0;const my=()=>(N1++,"id-"+Math.random().toString(36).substr(2,12)+"-"+N1);function gy(t){let e="";const r="0123456789abcdef",n=r.length;for(let i=0;i<t;i++)e+=r.charAt(Math.floor(Math.random()*n));return e}const yy=t=>gy(t.length),by=function(){return{x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0,text:""}},xy=function(t,e){const r=e.text.replace(Ri.lineBreakRegex," "),[,n]=xl(e.fontSize),i=t.append("text");i.attr("x",e.x),i.attr("y",e.y),i.style("text-anchor",e.anchor),i.style("font-family",e.fontFamily),i.style("font-size",n),i.style("font-weight",e.fontWeight),i.attr("fill",e.fill),e.class!==void 0&&i.attr("class",e.class);const a=i.append("tspan");return a.attr("x",e.x+e.textMargin*2),a.attr("fill",e.fill),a.text(r),i},vy=Hi((t,e,r)=>{if(!t||(r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"<br/>"},r),Ri.lineBreakRegex.test(t)))return t;const n=t.split(" "),i=[];let a="";return n.forEach((s,o)=>{const l=p0(`${s} `,r),u=p0(a,r);if(l>e){const{hyphenatedStrings:f,remainingWord:p}=wy(s,e,"-",r);i.push(a,...f),a=p}else u+l>=e?(i.push(a),a=s):a=[a,s].filter(Boolean).join(" ");o+1===n.length&&i.push(a)}),i.filter(s=>s!=="").join(r.joinWith)},(t,e,r)=>`${t}${e}${r.fontSize}${r.fontWeight}${r.fontFamily}${r.joinWith}`),wy=Hi((t,e,r="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);const i=[...t],a=[];let s="";return i.forEach((o,l)=>{const u=`${s}${o}`;if(p0(u,n)>=e){const h=l+1,f=i.length===h,p=`${u}${r}`;a.push(f?u:p),s=""}else s=u}),{hyphenatedStrings:a,remainingWord:s}},(t,e,r="-",n)=>`${t}${e}${r}${n.fontSize}${n.fontWeight}${n.fontFamily}`);function Cy(t,e){return bl(t,e).height}function p0(t,e){return bl(t,e).width}const bl=Hi((t,e)=>{const{fontSize:r=12,fontFamily:n="Arial",fontWeight:i=400}=e;if(!t)return{width:0,height:0};const[,a]=xl(r),s=["sans-serif",n],o=t.split(Ri.lineBreakRegex),l=[],u=Dt("body");if(!u.remove)return{width:0,height:0,lineHeight:0};const c=u.append("svg");for(const f of s){let p=0;const y={width:0,height:0,lineHeight:0};for(const b of o){const A=by();A.text=b||ny;const _=xy(c,A).style("font-size",a).style("font-weight",i).style("font-family",f),M=(_._groups||_)[0][0].getBBox();if(M.width===0&&M.height===0)throw new Error("svg element not in render tree");y.width=Math.round(Math.max(y.width,M.width)),p=Mat Line truncated
`+Me.showPosition()+`
Expecting `+Eo.join(", ")+", got '"+(this.terminals_[lr]||lr)+"'":Cc="Parse error on line "+(Mr+1)+": Unexpected "+(lr==Nm?"end of input":"'"+(this.terminals_[lr]||lr)+"'"),this.parseError(Cc,{text:Me.match,token:this.terminals_[lr]||lr,line:Me.yylineno,loc:vc,expected:Eo})}if(Dr[0]instanceof Array&&Dr.length>1)throw new Error("Parse Error: multiple actions possible at state: "+Li+", token: "+lr);switch(Dr[0]){case 1:At.push(lr),de.push(Me.yytext),C.push(Me.yylloc),At.push(Dr[1]),lr=null,Om=Me.yyleng,T=Me.yytext,Mr=Me.yylineno,vc=Me.yylloc;break;case 2:if(Nn=this.productions_[Dr[1]][1],ga.$=de[de.length-Nn],ga._$={first_line:C[C.length-(Nn||1)].first_line,last_line:C[C.length-1].last_line,first_column:C[C.length-(Nn||1)].first_column,last_column:C[C.length-1].last_column},dL&&(ga._$.range=[C[C.length-(Nn||1)].range[0],C[C.length-1].range[1]]),wc=this.performAction.apply(ga,[T,Om,Mr,Fi.yy,Dr[1],de,C].concat(fL)),typeof wc<"u")return wc;Nn&&(At=At.slice(0,-1*Nn*2),de=de.slice(0,-1*Nn),C=C.slice(0,-1*Nn)),At.push(this.productions_[Dr[1]][0]),de.push(ga.$),C.push(ga._$),Rm=xs[At[At.length-2]][At[At.length-1]],At.push(Rm);break;case 3:return!0}}return!0}},Pe=function(){var or={EOF:1,parseError:function(yt,At){if(this.yy.parser)this.yy.parser.parseError(yt,At);else throw new Error(yt)},setInput:function(lt,yt){return this.yy=yt||this.yy||{},this._input=lt,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var lt=this._input[0];this.yytext+=lt,this.yyleng++,this.offset++,this.match+=lt,this.matched+=lt;var yt=lt.match(/(?:\r\n?|\n).*/g);return yt?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),lt},unput:function(lt){var yt=lt.length,At=lt.split(/(?:\r\n?|\n)/g);this._input=lt+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-yt),this.offset-=yt;var it=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),At.length-1&&(this.yylineno-=At.length-1);var de=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:At?(At.length===it.length?this.yylloc.first_column:0)+it[it.length-At.length].length-At[0].length:this.yylloc.first_column-yt},this.options.ranges&&(this.yylloc.range=[de[0],de[0]+this.yyleng-yt]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},less:function(lt){this.unput(this.match.slice(lt))},pastInput:function(){var lt=this.matched.substr(0,this.matched.length-this.match.length);return(lt.length>20?"...":"")+lt.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var lt=this.match;return lt.length<20&&(lt+=this._input.substr(0,20-lt.length)),(lt.substr(0,20)+(lt.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var lt=this.pastInput(),yt=new Array(lt.length+1).join("-");return lt+this.upcomingInput()+`
`+yt+"^"},test_match:function(lt,yt){var At,it,de;if(this.options.backtrack_lexer&&(de={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(de.yylloc.range=this.yylloc.range.slice(0))),it=lt[0].match(/(?:\r\n?|\n).*/g),it&&(this.yylineno+=it.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:it?it[it.length-1].length-it[it.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+lt[0].length},this.yytext+=lt[0],this.match+=lt[0],this.matches=lt,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(lt[0].length),this.matched+=lt[0],At=this.performAction.call(this,this.yy,this,yt,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),At)return At;if(this._backtrack){for(var C in de)this[C]=de[C];return!1}return!1},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var lt,yt,At,it;this._more||(this.yytext="",this.match="");for(var de=this._currentRules(),C=0;C<de.length;C++)if(At=this._input.match(this.rules[de[C]]),At&&(!yt||At[0].length>yt[0].length)){if(yt=At,it=C,this.options.backtrack_lexer){if(lt=this.test_match(At,de[C]),lt!==!1)return lt;if(this._backtrack){yt=!1;continue}else return!1}else if(!this.options.flex)break}return yt?(lt=this.test_match(yt,de[it]),lt!==!1?lt:!1):this._input===""?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+`. Unrecognized text.
`+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var yt=this.next();return yt||this.lex()},begin:function(yt){this.conditionStack.push(yt)},popState:function(){var yt=this.conditionStack.length-1;return yt>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(yt){return yt=this.conditionStack.length-1-Math.abs(yt||0),yt>=0?this.conditionStack[yt]:"INITIAL"},pushState:function(yt){this.begin(yt)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(yt,At,it,de){switch(it){case 0:return this.begin("acc_title"),34;case 1:return this.popState(),"acc_title_value";case 2:return this.begin("acc_descr"),36;case 3:return this.popState(),"acc_descr_value";case 4:this.begin("acc_descr_multiline");break;case 5:this.popState();break;case 6:return"acc_descr_multiline_value";case 7:this.begin("callbackname");break;case 8:this.popState();break;case 9:this.popState(),this.begin("callbackargs");break;case 10:return 92;case 11:this.popState();break;case 12:return 93;case 13:return"MD_STR";case 14:this.popState();break;case 15:this.begin("md_string");break;case 16:return"STR";case 17:this.popState();break;case 18:this.pushState("string");break;case 19:return 81;case 20:return 99;case 21:return 82;case 22:return 101;case 23:return 83;case 24:return 84;case 25:return 94;case 26:this.begin("click");break;case 27:this.popState();break;case 28:return 85;case 29:return yt.lex.firstGraph()&&this.begin("dir"),12;case 30:return yt.lex.firstGraph()&&this.begin("dir"),12;case 31:return yt.lex.firstGraph()&&this.begin("dir"),12;case 32:return 27;case 33:return 32;case 34:return 95;case 35:return 95;case 36:return 95;case 37:return 95;case 38:return this.popState(),13;case 39:return this.popState(),14;case 40:return this.popState(),14;case 41:return this.popState(),14;case 42:return this.popState(),14;case 43:return this.popState(),14;case 44:return this.popState(),14;case 45:return this.popState(),14;case 46:return this.popState(),14;case 47:return this.popState(),14;case 48:return this.popState(),14;case 49:return 118;case 50:return 119;case 51:return 120;case 52:return 121;case 53:return 102;case 54:return 108;case 55:return 44;case 56:return 58;case 57:return 42;case 58:return 8;case 59:return 103;case 60:return 112;case 61:return this.popState(),75;case 62:return this.pushState("edgeText"),73;case 63:return 116;case 64:return this.popState(),75;case 65:return this.pushState("thickEdgeText"),73;case 66:return 116;case 67:return this.popState(),75;case 68:return this.pushState("dottedEdgeText"),73;case 69:return 116;case 70:return 75;case 71:return this.popState(),51;case 72:return"TEXT";case 73:return this.pushState("ellipseText"),50;case 74:return this.popState(),53;case 75:return this.pushState("text"),52;case 76:return this.popState(),55;case 77:return this.pushState("text"),54;case 78:return 56;case 79:return this.pushState("text"),65;case 80:return this.popState(),62;case 81:return this.pushState("text"),61;case 82:return this.popState(),47;case 83:return this.pushState("text"),46;case 84:return this.popState(),67;case 85:return this.popState(),69;case 86:return 114;case 87:return this.pushState("trapText"),66;case 88:return this.pushState("trapText"),68;case 89:return 115;case 90:return 65;case 91:return 87;case 92:return"SEP";case 93:return 86;case 94:return 112;case 95:return 108;case 96:return 42;case 97:return 106;case 98:return 111;case 99:return 113;case 100:return this.popState(),60;case 101:return this.pushState("text"),60;case 102:return this.popState(),49;case 103:return this.pushState("text"),48;case 104:return this.popState(),31;case 105:return this.pushState("text"),29;case 106:return this.popState(),64;case 107:return this.pushState("text"),63;case 108:return"TEXT";case 109:return"QUOTE";case 110:return 9;case 111:return 10;case 112:return 11}},rules:[/^(?:accTitle\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*:\s*)/,/^(?:(?!\n||)*[^\n]*)/,/^(?:accDescr\s*\{\s*)/,/^(?:[\}])/,/^(?:[^\}]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:[^`"]+)/,/^(?:[`]["])/,/^(?:["][`])/,/^(?:[^"]+)/,/^(?:["])/,/^(?:["])/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s])/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:flowchart-elk\b)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:.*direction\s+TB[^\n]*)/,/^(?:.*direction\s+BT[^\n]*)/,/^(?:.*direction\s+RL[^\n]*)/,/^(?:.*direLine truncated
font-family: ${r.fontFamily};
font-size: ${r.fontSize};
fill: ${r.textColor}
}
/* Classes common for multiple diagrams */
& .error-icon {
fill: ${r.errorBkgColor};
}
& .error-text {
fill: ${r.errorTextColor};
stroke: ${r.errorTextColor};
}
& .edge-thickness-normal {
stroke-width: 2px;
}
& .edge-thickness-thick {
stroke-width: 3.5px
}
& .edge-pattern-solid {
stroke-dasharray: 0;
}
& .edge-pattern-dashed{
stroke-dasharray: 3;
}
.edge-pattern-dotted {
stroke-dasharray: 2;
}
& .marker {
fill: ${r.lineColor};
stroke: ${r.lineColor};
}
& .marker.cross {
stroke: ${r.lineColor};
}
& svg {
font-family: ${r.fontFamily};
font-size: ${r.fontSize};
}
${n}
${e}
`},Jy=(t,e)=>{e!==void 0&&(_0[t]=e)},tb=Qy;let Al="",Bl="",El="";const Fl=t=>li(t,tn()),nf=()=>{Al="",El="",Bl=""},af=t=>{Al=Fl(t).replace(/^\s+/g,"")},sf=()=>Al,of=t=>{El=Fl(t).replace(/\n\s+/g,`
`)},lf=()=>El,uf=t=>{Bl=Fl(t)},cf=()=>Bl,eb=Object.freeze(Object.defineProperty({__proto__:null,clear:nf,getAccDescription:lf,getAccTitle:sf,getDiagramTitle:cf,setAccDescription:of,setAccTitle:af,setDiagramTitle:uf},Symbol.toStringTag,{value:"Module"})),rb=E,nb=Fo,Et=tn,ib=Z1,ab=Xi,sb=t=>li(t,Et()),ob=rf,lb=()=>eb,S0={},Ll=(t,e,r)=>{var n;if(S0[t])throw new Error(`Diagram ${t} already registered.`);S0[t]=e,r&&a1(t,r),Jy(t,e.styles),(n=e.injectUtils)==null||n.call(e,rb,nb,Et,sb,ob,lb(),()=>{})},Ml=t=>{if(t in S0)return S0[t];throw new ub(t)};class ub extends Error{constructor(e){super(`Diagram ${e} not found.`)}}const cb="flowchart-";let hf=0,Zi=Et(),se={},Hr=[],Qi={},wn=[],T0={},A0={},B0=0,Dl=!0,wr,E0,F0=[];const L0=t=>Ri.sanitizeText(t,Zi),M0=function(t){const e=Object.keys(se);for(const r of e)if(se[r].id===t)return se[r].domId;return t},hb=function(t,e,r,n,i,a,s={}){let o,l=t;l!==void 0&&l.trim().length!==0&&(se[l]===void 0&&(se[l]={id:l,labelType:"text",domId:cb+l+"-"+hf,styles:[],classes:[]}),hf++,e!==void 0?(Zi=Et(),o=L0(e.text.trim()),se[l].labelType=e.type,o[0]==='"'&&o[o.length-1]==='"'&&(o=o.substring(1,o.length-1)),se[l].text=o):se[l].text===void 0&&(se[l].text=t),r!==void 0&&(se[l].type=r),n!=null&&n.forEach(function(u){se[l].styles.push(u)}),i!=null&&i.forEach(function(u){se[l].classes.push(u)}),a!==void 0&&(se[l].dir=a),se[l].props===void 0?se[l].props=s:s!==void 0&&Object.assign(se[l].props,s))},fb=function(t,e,r){const a={start:t,end:e,type:void 0,text:"",labelType:"text"};E.info("abc78 Got edge...",a);const s=r.text;if(s!==void 0&&(a.text=L0(s.text.trim()),a.text[0]==='"'&&a.text[a.text.length-1]==='"'&&(a.text=a.text.substring(1,a.text.length-1)),a.labelType=s.type),r!==void 0&&(a.type=r.type,a.stroke=r.stroke,a.length=r.length),(a==null?void 0:a.length)>10&&(a.length=10),Hr.length<(Zi.maxEdges??500))E.info("abc78 pushing edge..."),Hr.push(a);else throw new Error(`Edge limit exceeded. ${Hr.length} edges found, but the limit is ${Zi.maxEdges}.
Initialize mermaid with maxEdges set to a higher number to allow more edges.
You cannot set this config via configuration inside the diagram as it is a secure config.
You have to call mermaid.initialize.`)},db=function(t,e,r){E.info("addLink (abc78)",t,e,r);let n,i;for(n=0;n<t.length;n++)for(i=0;i<e.length;i++)fb(t[n],e[i],r)},pb=function(t,e){t.forEach(function(r){r==="default"?Hr.defaultInterpolate=e:Hr[r].interpolate=e})},mb=function(t,e){t.forEach(function(r){if(r>=Hr.length)throw new Error(`The index ${r} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${Hr.length-1}. (Help: Ensure that the index is within the range of existing edges.)`);r==="default"?Hr.defaultStyle=e:(Ke.isSubstringInArray("fill",e)===-1&&e.push("fill:none"),Hr[r].style=e)})},gb=function(t,e){t.split(",").forEach(function(r){Qi[r]===void 0&&(Qi[r]={id:r,styles:[],textStyles:[]}),e!=null&&e.forEach(function(n){if(n.match("color")){const i=n.replace("fill","bgFill").replace("color","fill");Qi[r].textStyles.push(i)}Qi[r].styles.push(n)})})},yb=function(t){wr=t,wr.match(/.*</)&&(wr="RL"),wr.match(/.*\^/)&&(wr="BT"),wr.match(/.*>/)&&(wr="LR"),wr.match(/.*v/)&&(wr="TB"),wr==="TD"&&(wr="TB")},Il=function(t,e){t.split(",").forEach(function(r){let n=r;se[n]!==void 0&&se[n].classes.push(e),T0[n]!==void 0&&T0[n].classes.push(e)})},bb=function(t,e){t.split(",").forEach(function(r){e!==void 0&&(A0[E0==="gen-1"?M0(r):r]=L0(e))})},xb=function(t,e,r){let n=M0(t);if(Et().securityLevel!=="loose"||e===void 0)return;let i=[];if(typeof r=="string"){i=r.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let a=0;a<i.length;a++){let s=i[a].trim();s.charAt(0)==='"'&&s.charAt(s.length-1)==='"'&&(s=s.substr(1,s.length-2)),i[a]=s}}i.length===0&&i.push(t),se[t]!==void 0&&(se[t].haveCallback=!0,F0.push(function(){const a=document.querySelector(`[id="${n}"]`);a!==null&&a.addEventListener("click",function(){Ke.runFunc(e,...i)},!1)}))},vb=function(t,e,r){t.split(",").forEach(function(n){se[n]!==void 0&&(se[n].link=Ke.formatUrl(e,Zi),se[n].linkTarget=r)}),Il(t,"clickable")},wb=function(t){if(A0.hasOwnProperty(t))return A0[t]},Cb=function(t,e,r){t.split(",").forEach(function(n){xb(n,e,r)}),Il(t,"clickable")},kb=function(t){F0.forEach(function(e){e(t)})},_b=function(){return wr.trim()},Sb=function(){return se},Tb=function(){return Hr},Ab=function(){return Qi},ff=function(t){let e=Dt(".mermaidTooltip");(e._groups||e)[0][0]===null&&(e=Dt("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),Dt(t).select("svg").selectAll("g.node").on("mouseover",function(){const i=Dt(this);if(i.attr("title")===null)return;const s=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.text(i.attr("title")).style("left",window.scrollX+s.left+(s.right-s.left)/2+"px").style("top",window.scrollY+s.bottom+"px"),e.html(e.html().replace(/&lt;br\/&gt;/g,"<br/>")),i.classed("hover",!0)}).on("mouseout",function(){e.transition().duration(500).style("opacity",0),Dt(this).classed("hover",!1)})};F0.push(ff);const Bb=function(t="gen-1"){se={},Qi={},Hr=[],F0=[ff],wn=[],T0={},B0=0,A0={},Dl=!0,E0=t,Zi=Et(),nf()},Eb=t=>{E0=t||"gen-2"},Fb=function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},Lb=function(t,e,r){let n=t.text.trim(),i=r.text;t===r&&r.text.match(/\s/)&&(n=void 0);function a(c){const h={boolean:{},number:{},string:{}},f=[];let p;return{nodeList:c.filter(function(b){const A=typeof b;return b.stmt&&b.stmt==="dir"?(p=b.value,!1):b.trim()===""?!1:A in h?h[A].hasOwnProperty(b)?!1:h[A][b]=!0:f.includes(b)?!1:f.push(b)}),dir:p}}let s=[];const{nodeList:o,dir:l}=a(s.concat.apply(s,e));if(s=o,E0==="gen-1")for(let c=0;c<s.length;c++)s[c]=M0(s[c]);n=n||"subGraph"+B0,i=i||"",i=L0(i),B0=B0+1;const u={id:n,nodes:s,title:i.trim(),classes:[],dir:l,labelType:r.type};return E.info("Adding",u.id,u.nodes,u.dir),u.nodes=gf(u,wn).nodes,wn.push(u),T0[n]=u,n},Mb=function(t){for(const[e,r]of wn.entries())if(r.id===t)return e;return-1};let ja=-1;const df=[],pf=function(t,e){const r=wn[e].nodes;if(ja=ja+1,ja>2e3)return;if(df[ja]=e,wn[e].id===t)return{result:!0,count:0};let n=0,i=1;for(;n<r.length;){const a=Mb(r[n]);if(a>=0){const s=pf(t,a);if(s.result)return{result:!0,count:i+s.count};i=i+s.count}n=n+1}return{result:!1,count:i}},Db=function(t){return df[t]},Ib=function(){ja=-1,wn.length>0&&pf("none",wn.length-1)},zb=function(){return wn},Ob=()=>Dl?(Dl=!1,!0):!1,Nb=t=>{let e=t.trim(),r="arrow_open";switch(e[0]){case"<":r="arrow_point",e=e.slice(1);break;case"x":r="arrow_cross",e=e.slice(1);break;case"o":r="arrow_circle",e=e.slice(1);break}let n="normal";return e.includes("=")&&(n="thick"),e.includes(".")&&(n="dotted"),{type:r,stroke:n}},Rb=(t,e)=>{const r=e.length;let n=0;for(let i=0;i<r;++i)e[i]===t&&++n;return n},Pb=t=>{const e=t.trim();let r=e.slice(0,-1),n="arrow_open";switch(e.slice(-1)){case"x":n="arrow_cross",e[0]==="x"&&(n="double_"+n,r=r.slice(1));break;case">":n="arrow_point",e[0]==="<"&&(n="double_"+n,r=r.slice(1));break;case"o":n="arrow_circle",e[0]==="o"&&(n="double_"+n,r=r.slice(1));break}let i="normal",a=r.length-1;r[0]==="="&&(i="thick"),r[0]==="~"&&( Line truncated
`;break}case-3:{s=`\r
`;break}case-2:{s=e?" ":" ";break}case-1:{if(!e&&i)continue;s=" ";break}default:s=String.fromCharCode(a)}i=a===-2,n.push(s)}return n.join("")}const bS=Object.freeze(Object.defineProperty({__proto__:null,attentionMarkers:{null:[42,95]},contentInitial:{91:v_},disable:{null:[]},document:{42:tr,43:tr,45:tr,48:tr,49:tr,50:tr,51:tr,52:tr,53:tr,54:tr,55:tr,56:tr,57:tr,62:Nd},flow:{35:T_,42:W0,45:[Yd,W0],60:F_,61:Yd,95:W0,96:Hd,126:Hd},flowInitial:{[-2]:du,[-1]:du,32:du},insideSpan:{null:[hu,hS]},string:{38:qd,92:Rd},text:{[-5]:mu,[-4]:mu,[-3]:mu,33:Y_,38:qd,42:hu,60:[Zk,N_],91:K_,92:[__,Rd],93:pu,95:hu,96:c_}},Symbol.toStringTag,{value:"Module"}));function xS(t){const r=zk([bS,...(t||{}).extensions||[]]),n={defined:[],lazy:{},constructs:r,content:i(Wk),document:i(Gk),flow:i(uS),string:i(fS),text:i(dS)};return n;function i(a){return s;function s(o){return mS(n,a,o)}}}const Zd=/[\0\t\n\r]/g;function vS(){let t=1,e="",r=!0,n;return i;function i(a,s,o){const l=[];let u,c,h,f,p;for(a=e+a.toString(s),h=0,e="",r&&(a.charCodeAt(0)===65279&&h++,r=void 0);h<a.length;){if(Zd.lastIndex=h,u=Zd.exec(a),f=u&&u.index!==void 0?u.index:a.length,p=a.charCodeAt(f),!u){e=a.slice(h);break}if(p===10&&h===f&&n)l.push(-3),n=void 0;else switch(n&&(l.push(-5),n=void 0),h<f&&(l.push(a.slice(h,f)),t+=f-h),p){case 0:{l.push(65533),t++;break}case 9:{for(c=Math.ceil(t/4)*4,l.push(-2);t++<c;)l.push(-1);break}case 10:{l.push(-4),t=1;break}default:n=!0,t=1}h=f+1}return o&&(n&&l.push(-5),e&&l.push(e),l.push(null)),l}}function wS(t){for(;!Vd(t););return t}function Qd(t,e){const r=Number.parseInt(t,e);return r<9||r===11||r>13&&r<32||r>126&&r<160||r>55295&&r<57344||r>64975&&r<65008||(r&65535)===65535||(r&65535)===65534||r>1114111?"�":String.fromCharCode(r)}const CS=/\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;function kS(t){return t.replace(CS,_S)}function _S(t,e,r){if(e)return e;if(r.charCodeAt(0)===35){const i=r.charCodeAt(1),a=i===120||i===88;return Qd(r.slice(a?2:1),a?16:10)}return fu(r)||t}function U0(t){return!t||typeof t!="object"?"":"position"in t||"type"in t?Jd(t.position):"start"in t||"end"in t?Jd(t):"line"in t||"column"in t?gu(t):""}function gu(t){return t2(t&&t.line)+":"+t2(t&&t.column)}function Jd(t){return gu(t&&t.start)+"-"+gu(t&&t.end)}function t2(t){return t&&typeof t=="number"?t:1}const e2={}.hasOwnProperty,r2=function(t,e,r){return typeof e!="string"&&(r=e,e=void 0),SS(r)(wS(xS(r).document().write(vS()(t,e,!0))))};function SS(t){const e={transforms:[],canContainEols:["emphasis","fragment","heading","paragraph","strong"],enter:{autolink:o(In),autolinkProtocol:O,autolinkEmail:O,atxHeading:o(Kt),blockQuote:o(Yt),characterEscape:O,characterReference:O,codeFenced:o(ye),codeFencedFenceInfo:l,codeFencedFenceMeta:l,codeIndented:o(ye,l),codeText:o(Te,l),codeTextData:O,data:O,codeFlowValue:O,definition:o(Ae),definitionDestinationString:l,definitionLabelString:l,definitionTitleString:l,emphasis:o(ir),hardBreakEscape:o(fe),hardBreakTrailing:o(fe),htmlFlow:o(yr,l),htmlFlowData:O,htmlText:o(yr,l),htmlTextData:O,image:o(ar),label:l,link:o(In),listItem:o(jr),listItemValue:y,listOrdered:o(Gr,p),listUnordered:o(Gr),paragraph:o(Yr),reference:zt,referenceString:l,resourceDestinationString:l,resourceTitleString:l,setextHeading:o(Kt),strong:o(Ti),thematicBreak:o(Bi)},exit:{atxHeading:c(),atxHeadingSequence:q,autolink:c(),autolinkEmail:Ft,autolinkProtocol:jt,blockQuote:c(),characterEscapeValue:P,characterReferenceMarkerHexadecimal:Ht,characterReferenceMarkerNumeric:Ht,characterReferenceValue:Wt,codeFenced:c(M),codeFencedFence:_,codeFencedFenceInfo:b,codeFencedFenceMeta:A,codeFlowValue:P,codeIndented:c(I),codeText:c(et),codeTextData:P,data:P,definition:c(),definitionDestinationString:L,definitionLabelString:V,definitionTitleString:N,emphasis:c(),hardBreakEscape:c(X),hardBreakTrailing:c(X),htmlFlow:c($),htmlFlowData:P,htmlText:c(U),htmlTextData:P,image:c(W),label:st,labelText:v,lineEnding:ft,link:c(K),listItem:c(),listOrdered:c(),listUnordered:c(),paragraph:c(),referenceString:Ot,resourceDestinationString:dt,resourceTitleString:w,resource:St,setextHeading:c(J),setextHeadingLineSequence:Y,setextHeadingText:G,strong:c(),thematicBreak:c()}};n2(e,(t||{}).mdastExtensions||[]);const r={};return n;function n(R){let rt={type:"root",children:[]};const gt={stack:[rt],tokenStack:[],config:e,enter:u,exit:h,buffer:l,resume:f,setData:a,getData:s},Nt=[];let Lt=-1;for(;++Lt<R.length;)if(R[Lt][1].type==="listOrdered"||R[Lt][1].type==="listUnordered")if(R[Lt][0]==="enter")Nt.push(Lt);else{const be=Nt.pop();Lt=i(R,be,Lt)}for(Lt=-1;++Lt<R.length;){const be=e[R[Lt][0]];e2.call(be,R[Lt][1].type)&&be[R[Lt][1].type].call(Object.assign({sliceSerialize:R[Lt][2].sliceSerialize},gt),R[Lt][1])}if(gt.tokenStack.length>0){const be=gt.tokenStack[gt.tokenStack.length-1];(be[1]||i2).call(gt,void 0,be[0])}for(rt.position={start:Un(R.length>0?R[0][1].start:{line:1,column:1,offset:0}),end:Un(R.length>0?R[R.length-2][1].end:{line:1,column:1,offset:0})},Lt=-1;++Lt<e.tranLine truncated
`);return Mi(e)}function BS(t){const e=AS(t),{children:r}=r2(e),n=[[]];let i=0;function a(s,o="normal"){s.type==="text"?s.value.split(`
`).forEach((u,c)=>{c!==0&&(i++,n.push([])),u.split(" ").forEach(h=>{h&&n[i].push({content:h,type:o})})}):(s.type==="strong"||s.type==="emphasis")&&s.children.forEach(l=>{a(l,s.type)})}return r.forEach(s=>{s.type==="paragraph"&&s.children.forEach(o=>{a(o)})}),n}function ES(t){const{children:e}=r2(t);function r(n){return n.type==="text"?n.value.replace(/\n/g,"<br/>"):n.type==="strong"?`<strong>${n.children.map(r).join("")}</strong>`:n.type==="emphasis"?`<em>${n.children.map(r).join("")}</em>`:n.type==="paragraph"?`<p>${n.children.map(r).join("")}</p>`:`Unsupported markdown: ${n.type}`}return e.map(r).join("")}function FS(t){return Intl.Segmenter?[...new Intl.Segmenter().segment(t)].map(e=>e.segment):[...t]}function LS(t,e){const r=FS(e.content);return a2(t,[],r,e.type)}function a2(t,e,r,n){if(r.length===0)return[{content:e.join(""),type:n},{content:"",type:n}];const[i,...a]=r,s=[...e,i];return t([{content:s.join(""),type:n}])?a2(t,s,a,n):(e.length===0&&i&&(e.push(i),r.shift()),[{content:e.join(""),type:n},{content:r.join(""),type:n}])}function MS(t,e){if(t.some(({content:r})=>r.includes(`
`)))throw new Error("splitLineToFitWidth does not support newlines in the line");return yu(t,e)}function yu(t,e,r=[],n=[]){if(t.length===0)return n.length>0&&r.push(n),r.length>0?r:[];let i="";t[0].content===" "&&(i=" ",t.shift());const a=t.shift()??{content:" ",type:"normal"},s=[...n];if(i!==""&&s.push({content:i,type:"normal"}),s.push(a),e(s))return yu(t,e,r,s);if(n.length>0)r.push(n),t.unshift(a);else if(a.content){const[o,l]=LS(e,a);r.push([o]),l.content&&t.unshift(l)}return yu(t,e,r)}function DS(t,e){e&&t.attr("style",e)}function IS(t,e,r,n,i=!1){const a=t.append("foreignObject"),s=a.append("xhtml:div"),o=e.label,l=e.isNode?"nodeLabel":"edgeLabel";s.html(`
<span class="${l} ${n}" `+(e.labelStyle?'style="'+e.labelStyle+'"':"")+">"+o+"</span>"),DS(s,e.labelStyle),s.style("display","table-cell"),s.style("white-space","nowrap"),s.style("max-width",r+"px"),s.attr("xmlns","http://www.w3.org/1999/xhtml"),i&&s.attr("class","labelBkg");let u=s.node().getBoundingClientRect();return u.width===r&&(s.style("display","table"),s.style("white-space","break-spaces"),s.style("width",r+"px"),u=s.node().getBoundingClientRect()),a.style("width",u.width),a.style("height",u.height),a.node()}function s2(t,e,r){return t.append("tspan").attr("class","text-outer-tspan").attr("x",0).attr("y",e*r-.1+"em").attr("dy",r+"em")}function zS(t,e,r){const n=t.append("text"),i=s2(n,1,e);o2(i,r);const a=i.node().getComputedTextLength();return n.remove(),a}function OS(t,e,r,n=!1){const a=e.append("g"),s=a.insert("rect").attr("class","background"),o=a.append("text").attr("y","-10.1");let l=0;for(const u of r){const c=f=>zS(a,1.1,f)<=t,h=c(u)?[u]:MS(u,c);for(const f of h){const p=s2(o,l,1.1);o2(p,f),l++}}if(n){const u=o.node().getBBox(),c=2;return s.attr("x",-c).attr("y",-c).attr("width",u.width+2*c).attr("height",u.height+2*c),a.node()}else return o.node()}function o2(t,e){t.text(""),e.forEach((r,n)=>{const i=t.append("tspan").attr("font-style",r.type==="emphasis"?"italic":"normal").attr("class","text-inner-tspan").attr("font-weight",r.type==="strong"?"bold":"normal");n===0?i.text(r.content):i.text(" "+r.content)})}const bu=(t,e="",{style:r="",isTitle:n=!1,classes:i="",useHtmlLabels:a=!0,isNode:s=!0,width:o=200,addSvgBackground:l=!1}={})=>{if(E.info("createText",e,r,n,i,a,s,l),a){const u=ES(e),c={isNode:s,label:Va(u).replace(/fa[blrs]?:fa-[\w-]+/g,f=>`<i class='${f.replace(":"," ")}'></i>`),labelStyle:r.replace("fill:","color:")};return IS(t,c,o,i,l)}else{const u=BS(e);return OS(o,t,u,l)}},Ee=async(t,e,r,n)=>{let i;const a=e.useHtmlLabels||De(Et().flowchart.htmlLabels);r?i=r:i="node default";const s=t.insert("g").attr("class",i).attr("id",e.domId||e.id),o=s.insert("g").attr("class","label").attr("style",e.labelStyle);let l;e.labelText===void 0?l="":l=typeof e.labelText=="string"?e.labelText:e.labelText[0];const u=o.node();let c;e.labelType==="markdown"?c=bu(o,li(Va(l),Et()),{useHtmlLabels:a,width:e.width||Et().flowchart.wrappingWidth,classes:"markdown-node-label"}):c=u.appendChild(Qe(li(Va(l),Et()),e.labelStyle,!1,n));let h=c.getBBox();const f=e.padding/2;if(De(Et().flowchart.htmlLabels)){const p=c.children[0],y=Dt(c),b=p.getElementsByTagName("img");if(b){const A=l.replace(/<img[^>]*>/g,"").trim()==="";await Promise.all([...b].map(_=>new Promise(M=>{function I(){if(_.style.display="flex",_.style.flexDirection="column",A){const V=Et().fontSize?Et().fontSize:window.getComputedStyle(document.body).fontSize,N=5,L=parseInt(V,10)*N+"px";_.style.minWidth=L,_.style.maxWidth=L}else _.style.width="100%";M(_)}setTimeout(()=>{_.complete&&I()}),_.addEventListener("error",I),_.addEventListener("load",I)})))}h=p.getBoundingClientRect(),y.attr("width",h.width),y.attr("height",h.height)}return a?o.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"):o.attr("transform","translate(0, "+-h.height/2+")"),e.centerLabel&&o.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"),o.insert("rect",":first-child"),{shapeSvg:s,bbox:h,halfPadding:f,label:o}},ue=(t,e)=>{const r=e.node().getBBox();t.width=r.width,t.height=r.height};function nn(t,e,r,n){return t.insert("polygon",":first-child").attr("points",n.map(function(i){return i.x+","+i.y}).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+r/2+")")}let Tt={},Wr={},l2={};const NS=()=>{Wr={},l2={},Tt={}},G0=(t,e)=>(E.trace("In isDescendant",e," ",t," = ",Wr[e].includes(t)),!!Wr[e].includes(t)),RS=(t,e)=>(E.info("Descendants of ",e," is ",Wr[e]),E.info("Edge is ",t),t.v===e||t.w===e?!1:Wr[e]?Wr[e].includes(t.v)||G0(t.v,e)||G0(t.w,e)||Wr[e].includes(t.w):(E.debug("Tilt, ",e,",not in descendants"),!1)),u2=(t,e,r,n)=>{E.warn("Copying children of ",t,"root",n,"data",e.node(t),n);const i=e.children(t)||[];t!==n&&i.push(t),E.warn("Copying (nodes) clusterId",t,"nodes",i),i.forEach(a=>{if(e.children(a).length>0)u2(a,e,r,n);else{const s=e.node(a);E.info("cp ",a," to ",n," with parent ",t),r.setNode(a,s),n!==e.parent(a)&&(E.warn("Setting parent",a,e.parent(a)),r.setParent(a,e.parent(a))),t!==n&&a!==t?(E.debug("Setting parent",a,t),r.setParent(a,t)):(E.info("In copy ",t,"root",n,"data",e.node(t),n),E.debug("Not Setting parent for node=",a,"cluster!==rootId",t!==n,"node!==clusterId",a!==t));const o=e.edges(a);E.debug("Copying Edges",o),o.forEach(l=>{E.info("Edge",l);const u=e.edge(l.v,l.w,l.name);E.info("Edge data",u,n);try{RS(l,n)?(E.info("Copying as ",l.v,l.w,u,l.name),r.setEdge(l.v,l.w,u,l.name),E.info("newGraph edges ",r.edges(),r.edge(r.edges()[0]))):E.info("Skipping copy of edge ",l.v,"-->",l.w," rootId: ",n," clusterId:",t)}catch(c){E.error(c)}})}E.debug("Removing node",a),e.removeNode(a)})},c2=(t,e)=>{const r=e.children(t);let n=Line truncated
outsidePoint: ${JSON.stringify(e)}
insidePoint : ${JSON.stringify(r)}
node : x:${t.x} y:${t.y} w:${t.width} h:${t.height}`);const n=t.x,i=t.y,a=Math.abs(n-r.x),s=t.width/2;let o=r.x<e.x?s-a:s+a;const l=t.height/2,u=Math.abs(e.y-r.y),c=Math.abs(e.x-r.x);if(Math.abs(i-e.y)*s>Math.abs(n-e.x)*l){let h=r.y<e.y?e.y-l-i:i-l-e.y;o=c*h/u;const f={x:r.x<e.x?r.x+o:r.x-c+o,y:r.y<e.y?r.y+u-h:r.y-u+h};return o===0&&(f.x=e.x,f.y=e.y),c===0&&(f.x=e.x),u===0&&(f.y=e.y),E.debug(`abc89 topp/bott calc, Q ${u}, q ${h}, R ${c}, r ${o}`,f),f}else{r.x<e.x?o=e.x-s-n:o=n-s-e.x;let h=u*o/c,f=r.x<e.x?r.x+c-o:r.x-c+o,p=r.y<e.y?r.y+h:r.y-h;return E.debug(`sides calc abc89, Q ${u}, q ${h}, R ${c}, r ${o}`,{_x:f,_y:p}),o===0&&(f=e.x,p=e.y),c===0&&(f=e.x),u===0&&(p=e.y),{x:f,y:p}}},C2=(t,e)=>{E.debug("abc88 cutPathAtIntersect",t,e);let r=[],n=t[0],i=!1;return t.forEach(a=>{if(!ST(e,a)&&!i){const s=TT(e,n,a);let o=!1;r.forEach(l=>{o=o||l.x===s.x&&l.y===s.y}),r.some(l=>l.x===s.x&&l.y===s.y)||r.push(s),i=!0}else n=a,i||r.push(a)}),r},AT=function(t,e,r,n,i,a,s){let o=r.points;E.debug("abc88 InsertEdge: edge=",r,"e=",e);let l=!1;const u=a.node(e.v);var c=a.node(e.w);c!=null&&c.intersect&&(u!=null&&u.intersect)&&(o=o.slice(1,r.points.length-1),o.unshift(u.intersect(o[0])),o.push(c.intersect(o[o.length-1]))),r.toCluster&&(E.debug("to cluster abc88",n[r.toCluster]),o=C2(r.points,n[r.toCluster].node),l=!0),r.fromCluster&&(E.debug("from cluster abc88",n[r.fromCluster]),o=C2(o.reverse(),n[r.fromCluster].node).reverse(),l=!0);const h=o.filter(V=>!Number.isNaN(V.y));let f=yh;r.curve&&(i==="graph"||i==="flowchart")&&(f=r.curve);const{x:p,y}=xT(r),b=cg().x(p).y(y).curve(f);let A;switch(r.thickness){case"normal":A="edge-thickness-normal";break;case"thick":A="edge-thickness-thick";break;case"invisible":A="edge-thickness-thick";break;default:A=""}switch(r.pattern){case"solid":A+=" edge-pattern-solid";break;case"dotted":A+=" edge-pattern-dotted";break;case"dashed":A+=" edge-pattern-dashed";break}const _=t.append("path").attr("d",b(h)).attr("id",r.id).attr("class"," "+A+(r.classes?" "+r.classes:"")).attr("style",r.style);let M="";(Et().flowchart.arrowMarkerAbsolute||Et().state.arrowMarkerAbsolute)&&(M=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search,M=M.replace(/\(/g,"\\("),M=M.replace(/\)/g,"\\)")),vT(_,r,M,s,i);let I={};return l&&(I.updatedPath=o),I.originalPath=r.points,I},k2=async(t,e,r,n,i,a)=>{E.info("Graph in recursive render: XXX",_n(e),i);const s=e.graph().rankdir;E.trace("Dir in recursive render - dir:",s);const o=t.insert("g").attr("class","root");e.nodes()?E.info("Recursive render XXX",e.nodes()):E.info("No nodes found for",e),e.edges().length>0&&E.trace("Recursive edges",e.edge(e.edges()[0]));const l=o.insert("g").attr("class","clusters"),u=o.insert("g").attr("class","edgePaths"),c=o.insert("g").attr("class","edgeLabels"),h=o.insert("g").attr("class","nodes");await Promise.all(e.nodes().map(async function(y){const b=e.node(y);if(i!==void 0){const A=JSON.parse(JSON.stringify(i.clusterData));E.info("Setting data for cluster XXX (",y,") ",A,i),e.setNode(i.id,A),e.parent(y)||(E.trace("Setting parent",y,i.id),e.setParent(y,i.id,A))}if(E.info("(Insert) Node XXX"+y+": "+JSON.stringify(e.node(y))),b&&b.clusterNode){E.info("Cluster identified",y,b.width,e.node(y));const A=await k2(h,b.graph,r,n,e.node(y),a),_=A.elem;ue(b,_),b.diff=A.diff||0,E.info("Node bounds (abc123)",y,b,b.width,b.x,b.y),pT(_,b),E.warn("Recursive render complete ",_,b)}else e.children(y).length>0?(E.info("Cluster - the non recursive path XXX",y,b.id,b,e),E.info(is(b.id,e)),Tt[b.id]={id:is(b.id,e),node:b}):(E.info("Node - the non recursive path",y,b.id,b),await dT(h,e.node(y),s))})),e.edges().forEach(function(y){const b=e.edge(y.v,y.w,y.name);E.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(y)),E.info("Edge "+y.v+" -> "+y.w+": ",y," ",JSON.stringify(e.edge(y))),E.info("Fix",Tt,"ids:",y.v,y.w,"Translating: ",Tt[y.v],Tt[y.w]),kT(c,b)}),e.edges().forEach(function(y){E.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(y))}),E.info("#############################################"),E.info("### Layout ###"),E.info("#############################################"),E.info(e),ek(e),E.info("Graph after layout:",_n(e));let f=0;const{subGraphTitleTotalMargin:p}=Y0(a);return qS(e).forEach(function(y){const b=e.node(y);E.info("Position "+y+": "+JSON.stringify(e.node(y))),E.info("Position "+y+": ("+b.x,","+b.y,") width: ",b.width," height: ",b.height),b&&b.clusterNode?(b.y+=p,x2(b)):e.children(y).length>0?(b.height+=p,yT(l,b),Tt[b.id].node=b):(b.y+=p/2,x2(b))}),e.edges().forEach(function(y){const b=e.edge(y);E.info("Edge "+y.v+" -> "+y.w+": "+JSON.stringify(b),b),b.points.forEach(_=>_.y+=p/2);const A=AT(u,y,b,Tt,r,e,n);_T(b,A)}),e.nodes().forEach(function(y){const b=e.node(y);E.info(y,b.type,b.diff),b.type==="group"&&(f=b.diff)}),{elem:o,diff:f}},BT=async(t,e,r,n,i)=>{Ek(t,r,n,i),mT(),CT(),bT(),NS(),E.warn("Graph at first:",JSON.stringify(_n(e))),PS(e),E.warn("Graph after:",JSON.strin Line truncated
`),Et()),o.style===void 0&&(f.style=f.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),f.labelStyle=f.labelStyle.replace("color:","fill:"),f.id=u,f.classes="flowchart-link "+c+" "+h,e.setEdge(o.start,o.end,f,n)}},A2={setConf:LT,addVertices:S2,addEdges:T2,getClasses:function(t,e){return e.db.getClasses()},draw:async function(t,e,r,n){E.info("Drawing flowchart");let i=n.db.getDirection();i===void 0&&(i="TD");const{securityLevel:a,flowchart:s}=Et(),o=s.nodeSpacing||50,l=s.rankSpacing||50;let u;a==="sandbox"&&(u=Dt("#i"+e));const c=Dt(a==="sandbox"?u.nodes()[0].contentDocument.body:"body"),h=a==="sandbox"?u.nodes()[0].contentDocument:document,f=new Cr({multigraph:!0,compound:!0}).setGraph({rankdir:i,nodesep:o,ranksep:l,marginx:0,marginy:0}).setDefaultEdgeLabel(function(){return{}});let p;const y=n.db.getSubGraphs();E.info("Subgraphs - ",y);for(let N=y.length-1;N>=0;N--)p=y[N],E.info("Subgraph - ",p),n.db.addVertex(p.id,{text:p.title,type:p.labelType},"group",void 0,p.classes,p.dir);const b=n.db.getVertices(),A=n.db.getEdges();E.info("Edges",A);let _=0;for(_=y.length-1;_>=0;_--){p=y[_],C3("cluster").append("text");for(let N=0;N<p.nodes.length;N++)E.info("Setting up subgraphs",p.nodes[N],p.id),f.setParent(p.nodes[N],p.id)}await S2(b,f,e,c,h,n),await T2(A,f);const M=c.select(`[id="${e}"]`),I=c.select("#"+e+" g");if(await BT(I,f,["point","circle","cross"],"flowchart",e),Ke.insertTitle(M,"flowchartTitleText",s.titleTopMargin,n.db.getDiagramTitle()),rf(f,M,s.diagramPadding,s.useMaxWidth),n.db.indexNodes("subGraph"+_),!s.htmlLabels){const N=h.querySelectorAll('[id="'+e+'"] .edgeLabel .label');for(const L of N){const q=L.getBBox(),G=h.createElementNS("http://www.w3.org/2000/svg","rect");G.setAttribute("rx",0),G.setAttribute("ry",0),G.setAttribute("width",q.width),G.setAttribute("height",q.height),L.insertBefore(G,L.firstChild)}}Object.keys(b).forEach(function(N){const L=b[N];if(L.link){const q=Dt("#"+e+' [id="'+N+'"]');if(q){const G=h.createElementNS("http://www.w3.org/2000/svg","a");G.setAttributeNS("http://www.w3.org/2000/svg","class",L.classes.join(" ")),G.setAttributeNS("http://www.w3.org/2000/svg","href",L.link),G.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),a==="sandbox"?G.setAttributeNS("http://www.w3.org/2000/svg","target","_top"):L.linkTarget&&G.setAttributeNS("http://www.w3.org/2000/svg","target",L.linkTarget);const Y=q.insert(function(){return G},":first-child"),J=q.select(".label-container");J&&Y.append(function(){return J.node()});const O=q.select(".label");O&&Y.append(function(){return O.node()})}}})}},MT=(t,e)=>{const r=l6,n=r(t,"r"),i=r(t,"g"),a=r(t,"b");return Pi(n,i,a,e)},DT={parser:Xy,db:zl,renderer:A2,styles:t=>`.label {
font-family: ${t.fontFamily};
color: ${t.nodeTextColor||t.textColor};
}
.cluster-label text {
fill: ${t.titleColor};
}
.cluster-label span,p {
color: ${t.titleColor};
}
.label text,span,p {
fill: ${t.nodeTextColor||t.textColor};
color: ${t.nodeTextColor||t.textColor};
}
.node rect,
.node circle,
.node ellipse,
.node polygon,
.node path {
fill: ${t.mainBkg};
stroke: ${t.nodeBorder};
stroke-width: 1px;
}
.flowchart-label text {
text-anchor: middle;
}
// .flowchart-label .text-outer-tspan {
// text-anchor: middle;
// }
// .flowchart-label .text-inner-tspan {
// text-anchor: start;
// }
.node .katex path {
fill: #000;
stroke: #000;
stroke-width: 1px;
}
.node .label {
text-align: center;
}
.node.clickable {
cursor: pointer;
}
.arrowheadPath {
fill: ${t.arrowheadColor};
}
.edgePath .path {
stroke: ${t.lineColor};
stroke-width: 2.0px;
}
.flowchart-link {
stroke: ${t.lineColor};
fill: none;
}
.edgeLabel {
background-color: ${t.edgeLabelBackground};
rect {
opacity: 0.5;
background-color: ${t.edgeLabelBackground};
fill: ${t.edgeLabelBackground};
}
text-align: center;
}
/* For html labels only */
.labelBkg {
background-color: ${MT(t.edgeLabelBackground,.5)};
// background-color:
}
.cluster rect {
fill: ${t.clusterBkg};
stroke: ${t.clusterBorder};
stroke-width: 1px;
}
.cluster text {
fill: ${t.titleColor};
}
.cluster span,p {
color: ${t.titleColor};
}
/* .cluster div {
color: ${t.titleColor};
} */
div.mermaidTooltip {
position: absolute;
text-align: center;
max-width: 200px;
padding: 2px;
font-family: ${t.fontFamily};
font-size: 12px;
background: ${t.tertiaryColor};
border: 1px solid ${t.border2};
border-radius: 2px;
pointer-events: none;
z-index: 100;
}
.flowchartTitleText {
text-anchor: middle;
font-size: 18px;
fill: ${t.textColor};
}
`,init:t=>{t.flowchart||(t.flowchart={}),t.flowchart.arrowMarkerAbsolute=t.arrowMarkerAbsolute,ib({flowchart:{arrowMarkerAbsolute:t.arrowMarkerAbsolute}}),A2.setConf(t.flowchart),zl.clear(),zl.setGen("gen-2")}};let B2=!1;const vu=()=>{B2||(B2=!0,Ll("flowchart-v2",DT,()=>!0))};class E2{constructor(e,r={}){this.text=e,this.metadata=r,this.type="graph",this.text=Ty(e),this.text+=`
`;const n=tn();try{this.type=Js(e,n)}catch(a){this.type="error",this.detectError=a}const i=Ml(this.type);E.debug("Type "+this.type),this.db=i.db,this.renderer=i.renderer,this.parser=i.parser,this.parser.parser.yy=this.db,this.init=i.init,this.parse()}parse(){var r,n,i,a,s;if(this.detectError)throw this.detectError;(n=(r=this.db).clear)==null||n.call(r);const e=tn();(i=this.init)==null||i.call(this,e),this.metadata.title&&((s=(a=this.db).setDiagramTitle)==null||s.call(a,this.metadata.title)),this.parser.parse(this.text)}async render(e,r){await this.renderer.draw(this.text,e,r,this)}getParser(){return this.parser}getType(){return this.type}}const IT=async(t,e={})=>{const r=Js(t,tn());try{Ml(r)}catch{const i=T6(r);if(!i)throw new i1(`Diagram ${r} not found.`);const{id:a,diagram:s}=await i();Ll(a,s)}return new E2(t,e)},zT=t=>{var i;const{securityLevel:e}=Et();let r=Dt("body");if(e==="sandbox"){const s=((i=Dt(`#i${t}`).node())==null?void 0:i.contentDocument)??document;r=Dt(s.body)}return r.select(`#${t}`)},OT={draw:(t,e,r)=>{E.debug(`rendering svg for syntax error
`);const n=zT(e),i=n.append("g");n.attr("viewBox","0 0 2412 512"),ef(n,100,512,!0),i.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),i.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),i.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),i.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),i.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),i.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),i.append("text").attr("class","error-text").attr("x",1440).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in text"),i.append("text").attr("class","error-text").attr("x",1250).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text(`mermaid version ${r}`)}};let F2=[];const NT=()=>{F2.forEach(t=>{t()}),F2=[]},RT="graphics-document document";function PT(t,e){t.attr("role",RT),e!==""&&t.attr("aria-roledescription",e)}function qT(t,e,r,n){if(t.insert!==void 0){if(r){const i=`chart-desc-${n}`;t.attr("aria-describedby",i),t.insert("desc",":first-child").attr("id",i).text(r)}if(e){const i=`chart-title-${n}`;t.attr("aria-labelledby",i),t.insert("title",":first-child").attr("id",i).text(e)}}}const $T=t=>t.replace(/^\s*%%(?!{)[^\n]+\n?/gm,"").trimStart();/*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */function L2(t){return typeof t>"u"||t===null}function HT(t){return typeof t=="object"&&t!==null}function VT(t){return Array.isArray(t)?t:L2(t)?[]:[t]}function WT(t,e){var r,n,i,a;if(e)for(a=Object.keys(e),r=0,n=a.length;r<n;r+=1)i=a[r],t[i]=e[i];return t}function UT(t,e){var r="",n;for(n=0;n<e;n+=1)r+=t;return r}function GT(t){return t===0&&Number.NEGATIVE_INFINITY===1/t}var jT=L2,YT=HT,XT=VT,KT=UT,ZT=GT,QT=WT,Ve={isNothing:jT,isObject:YT,toArray:XT,repeat:KT,isNegativeZero:ZT,extend:QT};function M2(t,e){var r="",n=t.reason||"(unknown reason)";return t.mark?(t.mark.name&&(r+='in "'+t.mark.name+'" '),r+="("+(t.mark.line+1)+":"+(t.mark.column+1)+")",!e&&t.mark.snippet&&(r+=`
`+t.mark.snippet),n+" "+r):n}function ss(t,e){Error.call(this),this.name="YAMLException",this.reason=t,this.mark=e,this.message=M2(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}ss.prototype=Object.create(Error.prototype),ss.prototype.constructor=ss,ss.prototype.toString=function(e){return this.name+": "+M2(this,e)};var Sn=ss;function wu(t,e,r,n,i){var a="",s="",o=Math.floor(i/2)-1;return n-e>o&&(a=" ... ",e=n-o+a.length),r-n>o&&(s=" ...",r=n+o-s.length),{str:a+t.slice(e,r).replace(/\t/g,"→")+s,pos:n-e+a.length}}function Cu(t,e){return Ve.repeat(" ",e-t.length)+t}function JT(t,e){if(e=Object.create(e||null),!t.buffer)return null;e.maxLength||(e.maxLength=79),typeof e.indent!="number"&&(e.indent=1),typeof e.linesBefore!="number"&&(e.linesBefore=3),typeof e.linesAfter!="number"&&(e.linesAfter=2);for(var r=/\r?\n|\r|\0/g,n=[0],i=[],a,s=-1;a=r.exec(t.buffer);)i.push(a.index),n.push(a.index+a[0].length),t.position<=a.index&&s<0&&(s=n.length-2);s<0&&(s=n.length-1);var o="",l,u,c=Math.min(t.line+e.linesAfter,i.length).toString().length,h=e.maxLength-(e.indent+c+3);for(l=1;l<=e.linesBefore&&!(s-l<0);l++)u=wu(t.buffer,n[s-l],i[s-l],t.position-(n[s]-n[s-l]),h),o=Ve.repeat(" ",e.indent)+Cu((t.line-l+1).toString(),c)+" | "+u.str+`
`+o;for(u=wu(t.buffer,n[s],i[s],t.position,h),o+=Ve.repeat(" ",e.indent)+Cu((t.line+1).toString(),c)+" | "+u.str+`
`,o+=Ve.repeat("-",e.indent+c+3+u.pos)+`^
`,l=1;l<=e.linesAfter&&!(s+l>=i.length);l++)u=wu(t.buffer,n[s+l],i[s+l],t.position-(n[s]-n[s+l]),h),o+=Ve.repeat(" ",e.indent)+Cu((t.line+l+1).toString(),c)+" | "+u.str+`
`;return o.replace(/\n$/,"")}var tA=JT,eA=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],rA=["scalar","sequence","mapping"];function nA(t){var e={};return t!==null&&Object.keys(t).forEach(function(r){t[r].forEach(function(n){e[String(n)]=r})}),e}function iA(t,e){if(e=e||{},Object.keys(e).forEach(function(r){if(eA.indexOf(r)===-1)throw new Sn('Unknown option "'+r+'" is met in definition of "'+t+'" YAML type.')}),this.options=e,this.tag=t,this.kind=e.kind||null,this.resolve=e.resolve||function(){return!0},this.construct=e.construct||function(r){return r},this.instanceOf=e.instanceOf||null,this.predicate=e.predicate||null,this.represent=e.represent||null,this.representName=e.representName||null,this.defaultStyle=e.defaultStyle||null,this.multi=e.multi||!1,this.styleAliases=nA(e.styleAliases||null),rA.indexOf(this.kind)===-1)throw new Sn('Unknown kind "'+this.kind+'" is specified for "'+t+'" YAML type.')}var Ne=iA;function D2(t,e){var r=[];return t[e].forEach(function(n){var i=r.length;r.forEach(function(a,s){a.tag===n.tag&&a.kind===n.kind&&a.multi===n.multi&&(i=s)}),r[i]=n}),r}function aA(){var t={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}},e,r;function n(i){i.multi?(t.multi[i.kind].push(i),t.multi.fallback.push(i)):t[i.kind][i.tag]=t.fallback[i.tag]=i}for(e=0,r=arguments.length;e<r;e+=1)arguments[e].forEach(n);return t}function ku(t){return this.extend(t)}ku.prototype.extend=function(e){var r=[],n=[];if(e instanceof Ne)n.push(e);else if(Array.isArray(e))n=n.concat(e);else if(e&&(Array.isArray(e.implicit)||Array.isArray(e.explicit)))e.implicit&&(r=r.concat(e.implicit)),e.explicit&&(n=n.concat(e.explicit));else throw new Sn("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");r.forEach(function(a){if(!(a instanceof Ne))throw new Sn("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(a.loadKind&&a.loadKind!=="scalar")throw new Sn("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(a.multi)throw new Sn("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")}),n.forEach(function(a){if(!(a instanceof Ne))throw new Sn("Specified list of YAML types (or a single Type object) contains a non-Type object.")});var i=Object.create(ku.prototype);return i.implicit=(this.implicit||[]).concat(r),i.explicit=(this.explicit||[]).concat(n),i.compiledImplicit=D2(i,"implicit"),i.compiledExplicit=D2(i,"explicit"),i.compiledTypeMap=aA(i.compiledImplicit,i.compiledExplicit),i};var sA=ku,oA=new Ne("tag:yaml.org,2002:str",{kind:"scalar",construct:function(t){return t!==null?t:""}}),lA=new Ne("tag:yaml.org,2002:seq",{kind:"sequence",construct:function(t){return t!==null?t:[]}}),uA=new Ne("tag:yaml.org,2002:map",{kind:"mapping",construct:function(t){return t!==null?t:{}}}),cA=new sA({explicit:[oA,lA,uA]});function hA(t){if(t===null)return!0;var e=t.length;return e===1&&t==="~"||e===4&&(t==="null"||t==="Null"||t==="NULL")}function fA(){return null}function dA(t){return t===null}var pA=new Ne("tag:yaml.org,2002:null",{kind:"scalar",resolve:hA,construct:fA,predicate:dA,represent:{canonical:function(){return"~"},lowercase:function(){return"null"},uppercase:function(){return"NULL"},camelcase:function(){return"Null"},empty:function(){return""}},defaultStyle:"lowercase"});function mA(t){if(t===null)return!1;var e=t.length;return e===4&&(t==="true"||t==="True"||t==="TRUE")||e===5&&(t==="false"||t==="False"||t==="FALSE")}function gA(t){return t==="true"||t==="True"||t==="TRUE"}function yA(t){return Object.prototype.toString.call(t)==="[object Boolean]"}var bA=new Ne("tag:yaml.org,2002:bool",{kind:"scalar",resolve:mA,construct:gA,predicate:yA,represent:{lowercase:function(t){return t?"true":"false"},uppercase:function(t){return t?"TRUE":"FALSE"},camelcase:function(t){return t?"True":"False"}},defaultStyle:"lowercase"});function xA(t){return 48<=t&&t<=57||65<=t&&t<=70||97<=t&&t<=102}function vA(t){return 48<=t&&t<=55}function wA(t){return 48<=t&&t<=57}function CA(t){if(t===null)return!1;var e=t.length,r=0,n=!1,i;if(!e)return!1;if(i=t[r],(i==="-"||i==="+")&&(i=t[++r]),i==="0"){if(r+1===e)return!0;if(i=t[++r],i==="b"){for(r++;r<e;r++)if(i=t[r],i!=="_"){if(i!=="0"&&i!=="1")return!1;n=!0}return n&&i!=="_"}if(i==="x"){for(r++;r<e;r++)if(i=t[r],i!=="_"){if(!xA(t.charCodeAt(r)))return!1;n=!0}return n&&i!=="_"}if(i==="o"){for(r++;r<e;r++)if(i=t[r],i!=="_"){if(!vA(t.charCodeAt(r)))return!1;n=!0}return n&&i!=="_"}}if(i==="_")return!1;for(;r<e;r++)if(i=t[r],i!=="_"){if(!wA(t.charCodeAt(r)))return!1;n=!0}return!(!n||i==="_")}function kA(t){var e=t,r=1,n;if(e.indexOf("_")!==-1&&(e=e.replace(/_/g,"")),n=e[0],(n==="-"||n==="+")&&(n==="-"&&(r=-1),e=e.slice(1),n=e[0]),e==="0")return 0;if(n==="0"){if(e[1]==="b")return r*parseInt(e. Line truncated
\r`;function qA(t){if(t===null)return!1;var e,r,n=0,i=t.length,a=_u;for(r=0;r<i;r++)if(e=a.indexOf(t.charAt(r)),!(e>64)){if(e<0)return!1;n+=6}return n%8===0}function $A(t){var e,r,n=t.replace(/[\r\n=]/g,""),i=n.length,a=_u,s=0,o=[];for(e=0;e<i;e++)e%4===0&&e&&(o.push(s>>16&255),o.push(s>>8&255),o.push(s&255)),s=s<<6|a.indexOf(n.charAt(e));return r=i%4*6,r===0?(o.push(s>>16&255),o.push(s>>8&255),o.push(s&255)):r===18?(o.push(s>>10&255),o.push(s>>2&255)):r===12&&o.push(s>>4&255),new Uint8Array(o)}function HA(t){var e="",r=0,n,i,a=t.length,s=_u;for(n=0;n<a;n++)n%3===0&&n&&(e+=s[r>>18&63],e+=s[r>>12&63],e+=s[r>>6&63],e+=s[r&63]),r=(r<<8)+t[n];return i=a%3,i===0?(e+=s[r>>18&63],e+=s[r>>12&63],e+=s[r>>6&63],e+=s[r&63]):i===2?(e+=s[r>>10&63],e+=s[r>>4&63],e+=s[r<<2&63],e+=s[64]):i===1&&(e+=s[r>>2&63],e+=s[r<<4&63],e+=s[64],e+=s[64]),e}function VA(t){return Object.prototype.toString.call(t)==="[object Uint8Array]"}var WA=new Ne("tag:yaml.org,2002:binary",{kind:"scalar",resolve:qA,construct:$A,predicate:VA,represent:HA}),UA=Object.prototype.hasOwnProperty,GA=Object.prototype.toString;function jA(t){if(t===null)return!0;var e=[],r,n,i,a,s,o=t;for(r=0,n=o.length;r<n;r+=1){if(i=o[r],s=!1,GA.call(i)!=="[object Object]")return!1;for(a in i)if(UA.call(i,a))if(!s)s=!0;else return!1;if(!s)return!1;if(e.indexOf(a)===-1)e.push(a);else return!1}return!0}function YA(t){return t!==null?t:[]}var XA=new Ne("tag:yaml.org,2002:omap",{kind:"sequence",resolve:jA,construct:YA}),KA=Object.prototype.toString;function ZA(t){if(t===null)return!0;var e,r,n,i,a,s=t;for(a=new Array(s.length),e=0,r=s.length;e<r;e+=1){if(n=s[e],KA.call(n)!=="[object Object]"||(i=Object.keys(n),i.length!==1))return!1;a[e]=[i[0],n[i[0]]]}return!0}function QA(t){if(t===null)return[];var e,r,n,i,a,s=t;for(a=new Array(s.length),e=0,r=s.length;e<r;e+=1)n=s[e],i=Object.keys(n),a[e]=[i[0],n[i[0]]];return a}var JA=new Ne("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:ZA,construct:QA}),tB=Object.prototype.hasOwnProperty;function eB(t){if(t===null)return!0;var e,r=t;for(e in r)if(tB.call(r,e)&&r[e]!==null)return!1;return!0}function rB(t){return t!==null?t:{}}var nB=new Ne("tag:yaml.org,2002:set",{kind:"mapping",resolve:eB,construct:rB}),iB=DA.extend({implicit:[NA,PA],explicit:[WA,XA,JA,nB]}),jn=Object.prototype.hasOwnProperty,J0=1,N2=2,R2=3,to=4,Su=1,aB=2,P2=3,sB=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,oB=/[\x85\u2028\u2029]/,lB=/[,\[\]\{\}]/,q2=/^(?:!|!!|![a-z\-]+!)$/i,$2=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;function H2(t){return Object.prototype.toString.call(t)}function an(t){return t===10||t===13}function wi(t){return t===9||t===32}function er(t){return t===9||t===32||t===10||t===13}function oa(t){return t===44||t===91||t===93||t===123||t===125}function uB(t){var e;return 48<=t&&t<=57?t-48:(e=t|32,97<=e&&e<=102?e-97+10:-1)}function cB(t){return t===120?2:t===117?4:t===85?8:0}function hB(t){return 48<=t&&t<=57?t-48:-1}function V2(t){return t===48?"\0":t===97?"\x07":t===98?"\b":t===116||t===9?" ":t===110?`
`:t===118?"\v":t===102?"\f":t===114?"\r":t===101?"\x1B":t===32?" ":t===34?'"':t===47?"/":t===92?"\\":t===78?"…":t===95?" ":t===76?"\u2028":t===80?"\u2029":""}function fB(t){return t<=65535?String.fromCharCode(t):String.fromCharCode((t-65536>>10)+55296,(t-65536&1023)+56320)}for(var W2=new Array(256),U2=new Array(256),la=0;la<256;la++)W2[la]=V2(la)?1:0,U2[la]=V2(la);function dB(t,e){this.input=t,this.filename=e.filename||null,this.schema=e.schema||iB,this.onWarning=e.onWarning||null,this.legacy=e.legacy||!1,this.json=e.json||!1,this.listener=e.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=t.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function G2(t,e){var r={name:t.filename,buffer:t.input.slice(0,-1),position:t.position,line:t.line,column:t.position-t.lineStart};return r.snippet=tA(r),new Sn(e,r)}function mt(t,e){throw G2(t,e)}function eo(t,e){t.onWarning&&t.onWarning.call(null,G2(t,e))}var j2={YAML:function(e,r,n){var i,a,s;e.version!==null&&mt(e,"duplication of %YAML directive"),n.length!==1&&mt(e,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),i===null&&mt(e,"ill-formed argument of the YAML directive"),a=parseInt(i[1],10),s=parseInt(i[2],10),a!==1&&mt(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=s<2,s!==1&&s!==2&&eo(e,"unsupported YAML version of the document")},TAG:function(e,r,n){var i,a;n.length!==2&&mt(e,"TAG directive accepts exactly two arguments"),i=n[0],a=n[1],q2.test(i)||mt(e,"ill-formed tag handle (first argument) of the TAG directive"),jn.call(e.tagMap,i)&&mt(e,'there is a previously declared suffix for "'+i+'" tag handle'),$2.test(a)||mt(e,"ill-formed tag prefix (second argument) of the TAG directive");try{a=decodeURIComponent(a)}catch{mt(e,"tag prefix is malformed: "+a)}e.tagMap[i]=a}};function Yn(t,e,r,n){var i,a,s,o;if(e<r){if(o=t.input.slice(e,r),n)for(i=0,a=o.length;i<a;i+=1)s=o.charCodeAt(i),s===9||32<=s&&s<=1114111||mt(t,"expected valid JSON character");else sB.test(o)&&mt(t,"the stream contains non-printable characters");t.result+=o}}function Y2(t,e,r,n){var i,a,s,o;for(Ve.isObject(r)||mt(t,"cannot merge mappings; the provided source object is unacceptable"),i=Object.keys(r),s=0,o=i.length;s<o;s+=1)a=i[s],jn.call(e,a)||(e[a]=r[a],n[a]=!0)}function ua(t,e,r,n,i,a,s,o,l){var u,c;if(Array.isArray(i))for(i=Array.prototype.slice.call(i),u=0,c=i.length;u<c;u+=1)Array.isArray(i[u])&&mt(t,"nested arrays are not supported inside keys"),typeof i=="object"&&H2(i[u])==="[object Object]"&&(i[u]="[object Object]");if(typeof i=="object"&&H2(i)==="[object Object]"&&(i="[object Object]"),i=String(i),e===null&&(e={}),n==="tag:yaml.org,2002:merge")if(Array.isArray(a))for(u=0,c=a.length;u<c;u+=1)Y2(t,e,a[u],r);else Y2(t,e,a,r);else!t.json&&!jn.call(r,i)&&jn.call(e,i)&&(t.line=s||t.line,t.lineStart=o||t.lineStart,t.position=l||t.position,mt(t,"duplicated mapping key")),i==="__proto__"?Object.defineProperty(e,i,{configurable:!0,enumerable:!0,writable:!0,value:a}):e[i]=a,delete r[i];return e}function Tu(t){var e;e=t.input.charCodeAt(t.position),e===10?t.position++:e===13?(t.position++,t.input.charCodeAt(t.position)===10&&t.position++):mt(t,"a line break is expected"),t.line+=1,t.lineStart=t.position,t.firstTabInLine=-1}function Ce(t,e,r){for(var n=0,i=t.input.charCodeAt(t.position);i!==0;){for(;wi(i);)i===9&&t.firstTabInLine===-1&&(t.firstTabInLine=t.position),i=t.input.charCodeAt(++t.position);if(e&&i===35)do i=t.input.charCodeAt(++t.position);while(i!==10&&i!==13&&i!==0);if(an(i))for(Tu(t),i=t.input.charCodeAt(t.position),n++,t.lineIndent=0;i===32;)t.lineIndent++,i=t.input.charCodeAt(++t.position);else break}return r!==-1&&n!==0&&t.lineIndent<r&&eo(t,"deficient indentation"),n}function ro(t){var e=t.position,r;return r=t.input.charCodeAt(e),!!((r===45||r===46)&&r===t.input.charCodeAt(e+1)&&r===t.input.charCodeAt(e+2)&&(e+=3,r=t.input.charCodeAt(e),r===0||er(r)))}function Au(t,e){e===1?t.result+=" ":e>1&&(t.result+=Ve.repeat(`
`,e-1))}function pB(t,e,r){var n,i,a,s,o,l,u,c,h=t.kind,f=t.result,p;if(p=t.input.charCodeAt(t.position),er(p)||oa(p)||p===35||p===38||p===42||p===33||p===124||p===62||p===39||p===34||p===37||p===64||p===96||(p===63||p===45)&&(i=t.input.charCodeAt(t.position+1),er(i)||r&&oa(i)))return!1;for(t.kind="scalar",t.result="",a=s=t.position,o=!1;p!==0;){if(p===58){if(i=t.input.charCodeAt(t.position+1),er(i)||r&&oa(i))break}else if(p===35){if(n=t.input.charCodeAt(t.position-1),er(n))break}else{if(t.position===t.lineStart&&ro(t)||r&&oa(p))break;if(an(p))if(l=t.line,u=t.lineStart,c=t.lineIndent,Ce(t,!1,-1),t.lineIndent>=e){o=!0,p=t.input.charCodeAt(t.position);continue}else{t.position=s,t.line=l,t.lineStart=u,t.lineIndent=c;break}}o&&(Yn(t,a,s,!1),Au(t,t.line-l),a=s=t.position,o=!1),wi(p)||(s=t.position+1),p=t.input.charCodeAt(++t.position)}return Yn(t,a,s,!1),t.result?!0:(t.kind=h,t.result=f,!1)}function mB(t,e){var r,n,i;if(r=t.input.charCodeAt(t.position),r!==39)return!1;for(t.kind="scalar",t.result="",t.position++,n=i=t.position;(r=t.input.charCodeAt(t.position))!==0;)if(r===39)if(Yn(t,n,t.position,!0),r=t.input.charCodeAt(++t.position),r===39)n=t.position,t.position++,i=t.position;else return!0;else an(r)?(Yn(t,n,i,!0),Au(t,Ce(t,!1,e)),n=i=t.position):t.position===t.lineStart&&ro(t)?mt(t,"unexpected end of the document within a single quoted scalar"):(t.position++,i=t.position);mt(t,"unexpected end of the stream within a single quoted scalar")}function gB(t,e){var r,n,i,a,s,o;if(o=t.input.charCodeAt(t.position),o!==34)return!1;for(t.kind="scalar",t.result="",t.position++,r=n=t.position;(o=t.input.charCodeAt(t.position))!==0;){if(o===34)return Yn(t,r,t.position,!0),t.position++,!0;if(o===92){if(Yn(t,r,t.position,!0),o=t.input.charCodeAt(++t.position),an(o))Ce(t,!1,e);else if(o<256&&W2[o])t.result+=U2[o],t.position++;else if((s=cB(o))>0){for(i=s,a=0;i>0;i--)o=t.input.charCodeAt(++t.position),(s=uB(o))>=0?a=(a<<4)+s:mt(t,"expected hexadecimal character");t.result+=fB(a),t.position++}else mt(t,"unknown escape sequence");r=n=t.position}else an(o)?(Yn(t,r,n,!0),Au(t,Ce(t,!1,e)),r=n=t.position):t.position===t.lineStart&&ro(t)?mt(t,"unexpected end of the document within a double quoted scalar"):(t.position++,n=t.position)}mt(t,"unexpected end of the stream within a double quoted scalar")}function yB(t,e){var r=!0,n,i,a,s=t.tag,o,l=t.anchor,u,c,h,f,p,y=Object.create(null),b,A,_,M;if(M=t.input.charCodeAt(t.position),M===91)c=93,p=!1,o=[];else if(M===123)c=125,p=!0,o={};else return!1;for(t.anchor!==null&&(t.anchorMap[t.anchor]=o),M=t.input.charCodeAt(++t.position);M!==0;){if(Ce(t,!0,e),M=t.input.charCodeAt(t.position),M===c)return t.position++,t.tag=s,t.anchor=l,t.kind=p?"mapping":"sequence",t.result=o,!0;r?M===44&&mt(t,"expected the node content, but found ','"):mt(t,"missed comma between flow collection entries"),A=b=_=null,h=f=!1,M===63&&(u=t.input.charCodeAt(t.position+1),er(u)&&(h=f=!0,t.position++,Ce(t,!0,e))),n=t.line,i=t.lineStart,a=t.position,ca(t,e,J0,!1,!0),A=t.tag,b=t.result,Ce(t,!0,e),M=t.input.charCodeAt(t.position),(f||t.line===n)&&M===58&&(h=!0,M=t.input.charCodeAt(++t.position),Ce(t,!0,e),ca(t,e,J0,!1,!0),_=t.result),p?ua(t,o,y,A,b,_,n,i,a):h?o.push(ua(t,null,y,A,b,_,n,i,a)):o.push(b),Ce(t,!0,e),M=t.input.charCodeAt(t.position),M===44?(r=!0,M=t.input.charCodeAt(++t.position)):r=!1}mt(t,"unexpected end of the stream within a flow collection")}function bB(t,e){var r,n,i=Su,a=!1,s=!1,o=e,l=0,u=!1,c,h;if(h=t.input.charCodeAt(t.position),h===124)n=!1;else if(h===62)n=!0;else return!1;for(t.kind="scalar",t.result="";h!==0;)if(h=t.input.charCodeAt(++t.position),h===43||h===45)Su===i?i=h===43?P2:aB:mt(t,"repeat of a chomping mode identifier");else if((c=hB(h))>=0)c===0?mt(t,"bad explicit indentation width of a block scalar; it cannot be less than one"):s?mt(t,"repeat of an indentation width identifier"):(o=e+c-1,s=!0);else break;if(wi(h)){do h=t.input.charCodeAt(++t.position);while(wi(h));if(h===35)do h=t.input.charCodeAt(++t.position);while(!an(h)&&h!==0)}for(;h!==0;){for(Tu(t),t.lineIndent=0,h=t.input.charCodeAt(t.position);(!s||t.lineIndent<o)&&h===32;)t.lineIndent++,h=t.input.charCodeAt(++t.position);if(!s&&t.lineIndent>o&&(o=t.lineIndent),an(h)){l++;continue}if(t.lineIndent<o){i===P2?t.result+=Ve.repeat(`
`,a?1+l:l):i===Su&&a&&(t.result+=`
`);break}for(n?wi(h)?(u=!0,t.result+=Ve.repeat(`
`,a?1+l:l)):u?(u=!1,t.result+=Ve.repeat(`
`,l+1)):l===0?a&&(t.result+=" "):t.result+=Ve.repeat(`
`,l):t.result+=Ve.repeat(`
`,a?1+l:l),a=!0,s=!0,l=0,r=t.position;!an(h)&&h!==0;)h=t.input.charCodeAt(++t.position);Yn(t,r,t.position,!1)}return!0}function X2(t,e){var r,n=t.tag,i=t.anchor,a=[],s,o=!1,l;if(t.firstTabInLine!==-1)return!1;for(t.anchor!==null&&(t.anchorMap[t.anchor]=a),l=t.input.charCodeAt(t.position);l!==0&&(t.firstTabInLine!==-1&&(t.position=t.firstTabInLine,mt(t,"tab characters must not be used in indentation")),!(l!==45||(s=t.input.charCodeAt(t.position+1),!er(s))));){if(o=!0,t.position++,Ce(t,!0,-1)&&t.lineIndent<=e){a.push(null),l=t.input.charCodeAt(t.position);continue}if(r=t.line,ca(t,e,R2,!1,!0),a.push(t.result),Ce(t,!0,-1),l=t.input.charCodeAt(t.position),(t.line===r||t.lineIndent>e)&&l!==0)mt(t,"bad indentation of a sequence entry");else if(t.lineIndent<e)break}return o?(t.tag=n,t.anchor=i,t.kind="sequence",t.result=a,!0):!1}function xB(t,e,r){var n,i,a,s,o,l,u=t.tag,c=t.anchor,h={},f=Object.create(null),p=null,y=null,b=null,A=!1,_=!1,M;if(t.firstTabInLine!==-1)return!1;for(t.anchor!==null&&(t.anchorMap[t.anchor]=h),M=t.input.charCodeAt(t.position);M!==0;){if(!A&&t.firstTabInLine!==-1&&(t.position=t.firstTabInLine,mt(t,"tab characters must not be used in indentation")),n=t.input.charCodeAt(t.position+1),a=t.line,(M===63||M===58)&&er(n))M===63?(A&&(ua(t,h,f,p,y,null,s,o,l),p=y=b=null),_=!0,A=!0,i=!0):A?(A=!1,i=!0):mt(t,"incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line"),t.position+=1,M=n;else{if(s=t.line,o=t.lineStart,l=t.position,!ca(t,r,N2,!1,!0))break;if(t.line===a){for(M=t.input.charCodeAt(t.position);wi(M);)M=t.input.charCodeAt(++t.position);if(M===58)M=t.input.charCodeAt(++t.position),er(M)||mt(t,"a whitespace character is expected after the key-value separator within a block mapping"),A&&(ua(t,h,f,p,y,null,s,o,l),p=y=b=null),_=!0,A=!1,i=!1,p=t.tag,y=t.result;else if(_)mt(t,"can not read an implicit mapping pair; a colon is missed");else return t.tag=u,t.anchor=c,!0}else if(_)mt(t,"can not read a block mapping entry; a multiline key may not be an implicit key");else return t.tag=u,t.anchor=c,!0}if((t.line===a||t.lineIndent>e)&&(A&&(s=t.line,o=t.lineStart,l=t.position),ca(t,e,to,!0,i)&&(A?y=t.result:b=t.result),A||(ua(t,h,f,p,y,b,s,o,l),p=y=b=null),Ce(t,!0,-1),M=t.input.charCodeAt(t.position)),(t.line===a||t.lineIndent>e)&&M!==0)mt(t,"bad indentation of a mapping entry");else if(t.lineIndent<e)break}return A&&ua(t,h,f,p,y,null,s,o,l),_&&(t.tag=u,t.anchor=c,t.kind="mapping",t.result=h),_}function vB(t){var e,r=!1,n=!1,i,a,s;if(s=t.input.charCodeAt(t.position),s!==33)return!1;if(t.tag!==null&&mt(t,"duplication of a tag property"),s=t.input.charCodeAt(++t.position),s===60?(r=!0,s=t.input.charCodeAt(++t.position)):s===33?(n=!0,i="!!",s=t.input.charCodeAt(++t.position)):i="!",e=t.position,r){do s=t.input.charCodeAt(++t.position);while(s!==0&&s!==62);t.position<t.length?(a=t.input.slice(e,t.position),s=t.input.charCodeAt(++t.position)):mt(t,"unexpected end of the stream within a verbatim tag")}else{for(;s!==0&&!er(s);)s===33&&(n?mt(t,"tag suffix cannot contain exclamation marks"):(i=t.input.slice(e-1,t.position+1),q2.test(i)||mt(t,"named tag handle cannot contain such characters"),n=!0,e=t.position+1)),s=t.input.charCodeAt(++t.position);a=t.input.slice(e,t.position),lB.test(a)&&mt(t,"tag suffix cannot contain flow indicator characters")}a&&!$2.test(a)&&mt(t,"tag name cannot contain such characters: "+a);try{a=decodeURIComponent(a)}catch{mt(t,"tag name is malformed: "+a)}return r?t.tag=a:jn.call(t.tagMap,i)?t.tag=t.tagMap[i]+a:i==="!"?t.tag="!"+a:i==="!!"?t.tag="tag:yaml.org,2002:"+a:mt(t,'undeclared tag handle "'+i+'"'),!0}function wB(t){var e,r;if(r=t.input.charCodeAt(t.position),r!==38)return!1;for(t.anchor!==null&&mt(t,"duplication of an anchor property"),r=t.input.charCodeAt(++t.position),e=t.position;r!==0&&!er(r)&&!oa(r);)r=t.input.charCodeAt(++t.position);return t.position===e&&mt(t,"name of an anchor node must contain at least one character"),t.anchor=t.input.slice(e,t.position),!0}function CB(t){var e,r,n;if(n=t.input.charCodeAt(t.position),n!==42)return!1;for(n=t.input.charCodeAt(++t.position),e=t.position;n!==0&&!er(n)&&!oa(n);)n=t.input.charCodeAt(++t.position);return t.position===e&&mt(t,"name of an alias node must contain at least one character"),r=t.input.slice(e,t.position),jn.call(t.anchorMap,r)||mt(t,'unidentified alias "'+r+'"'),t.result=t.anchorMap[r],Ce(t,!0,-1),!0}function ca(t,e,r,n,i){var a,s,o,l=1,u=!1,c=!1,h,f,p,y,b,A;if(t.listener!==null&&t.listener("open",t),t.tag=null,t.anchor=null,t.kind=null,t.result=null,a=s=o=to===r||R2===r,n&&Ce(t,!0,-1)&&(u=!0,t.lineIndent>e?l=1:t.lineIndent===e?l=0:t.lineIndent<e&&(l=-1)),l===1)for(;vB(t)||wB(t);)Ce(t,!0,-1)?(u=!0,o=a,t.lineIndent>e?l=1:t.lineIndent===e?l=0:t.lineIndent<e&&(l=-1)):o=!1;if(o&&(o=u||i),(l===1||to===r)&&(J0===r||N2===r?b=e:b=e+1,A=t.position-t.lineStart,l===1?o&&(X2(t,A)||xB(t,A,b))||yB(t,b)?c=!0:(s&&bB(t,b)||mB(t,b)||gB(t,b)?c=!0:CB(t)?(c=!0,(t.tag!==null||t.anchor!==null)&&mt(t, Line truncated
`),t.charCodeAt(0)===65279&&(t=t.slice(1)));var r=new dB(t,e),n=t.indexOf("\0");for(n!==-1&&(r.position=n,mt(r,"null byte is not allowed in input")),r.input+="\0";r.input.charCodeAt(r.position)===32;)r.lineIndent+=1,r.position+=1;for(;r.position<r.length-1;)kB(r);return r.documents}function _B(t,e,r){e!==null&&typeof e=="object"&&typeof r>"u"&&(r=e,e=null);var n=K2(t,r);if(typeof e!="function")return n;for(var i=0,a=n.length;i<a;i+=1)e(n[i])}function SB(t,e){var r=K2(t,e);if(r.length!==0){if(r.length===1)return r[0];throw new Sn("expected a single document in the stream, but found more")}}var TB=_B,AB=SB,BB={loadAll:TB,load:AB},EB=I2,FB=BB.load;function LB(t){const e=t.match(n1);if(!e)return{text:t,metadata:{}};let r=FB(e[1],{schema:EB})??{};r=typeof r=="object"&&!Array.isArray(r)?r:{};const n={};return r.displayMode&&(n.displayMode=r.displayMode.toString()),r.title&&(n.title=r.title.toString()),r.config&&(n.config=r.config),{text:t.slice(e[0].length),metadata:n}}const MB=t=>t.replace(/\r\n?/g,`
`).replace(/<(\w+)([^>]*)>/g,(e,r,n)=>"<"+r+n.replace(/="([^"]*)"/g,"='$1'")+">"),DB=t=>{const{text:e,metadata:r}=LB(t),{displayMode:n,title:i,config:a={}}=r;return n&&(a.gantt||(a.gantt={}),a.gantt.displayMode=n),{title:i,config:a,text:e}},IB=t=>{const e=Ke.detectInit(t)??{},r=Ke.detectDirective(t,"wrap");return Array.isArray(r)?e.wrap=r.some(({type:n})=>{}):(r==null?void 0:r.type)==="wrap"&&(e.wrap=!0),{text:oy(t),directive:e}};function Z2(t){const e=MB(t),r=DB(e),n=IB(r.text),i=P1(r.config,n.directive);return t=$T(n.text),{code:t,title:r.title,config:i}}const zB=5e4,OB="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa",NB="sandbox",RB="loose",PB="http://www.w3.org/2000/svg",qB="http://www.w3.org/1999/xlink",$B="http://www.w3.org/1999/xhtml",HB="100%",VB="100%",WB="border:0;margin:0;",UB="margin:0",GB="allow-top-navigation-by-user-activation allow-popups",jB='The "iframe" tag is not supported by your browser.',YB=["foreignobject"],XB=["dominant-baseline"];function Q2(t){const e=Z2(t);return k0(),Gy(e.config??{}),e}async function KB(t,e){vu(),t=Q2(t).code;try{await Bu(t)}catch(r){if(e!=null&&e.suppressErrors)return!1;throw r}return!0}const J2=(t,e,r=[])=>`
.${t} ${e} { ${r.join(" !important; ")} !important; }`,ZB=(t,e={})=>{var n;let r="";if(t.themeCSS!==void 0&&(r+=`
${t.themeCSS}`),t.fontFamily!==void 0&&(r+=`
:root { --mermaid-font-family: ${t.fontFamily}}`),t.altFontFamily!==void 0&&(r+=`
:root { --mermaid-alt-font-family: ${t.altFontFamily}}`),!Za(e)){const o=t.htmlLabels||((n=t.flowchart)==null?void 0:n.htmlLabels)?["> *","span"]:["rect","polygon","ellipse","circle","path"];for(const l in e){const u=e[l];Za(u.styles)||o.forEach(c=>{r+=J2(u.id,c,u.styles)}),Za(u.textStyles)||(r+=J2(u.id,"tspan",u.textStyles))}}return r},QB=(t,e,r,n)=>{const i=ZB(t,r),a=tb(e,i,t.themeVariables);return Sl(qy(`${n}{${a}}`),Hy)},JB=(t="",e,r)=>{let n=t;return!r&&!e&&(n=n.replace(/marker-end="url\([\d+./:=?A-Za-z-]*?#/g,'marker-end="url(#')),n=Va(n),n=n.replace(/<br>/g,"<br/>"),n},tE=(t="",e)=>{var i,a;const r=(a=(i=e==null?void 0:e.viewBox)==null?void 0:i.baseVal)!=null&&a.height?e.viewBox.baseVal.height+"px":VB,n=btoa('<body style="'+UB+'">'+t+"</body>");return`<iframe style="width:${HB};height:${r};${WB}" src="data:text/html;base64,${n}" sandbox="${GB}">
${jB}
</iframe>`},tp=(t,e,r,n,i)=>{const a=t.append("div");a.attr("id",r),n&&a.attr("style",n);const s=a.append("svg").attr("id",e).attr("width","100%").attr("xmlns",PB);return i&&s.attr("xmlns:xlink",i),s.append("g"),t};function ep(t,e){return t.append("iframe").attr("id",e).attr("style","width: 100%; height: 100%;").attr("sandbox","")}const eE=(t,e,r,n)=>{var i,a,s;(i=t.getElementById(e))==null||i.remove(),(a=t.getElementById(r))==null||a.remove(),(s=t.getElementById(n))==null||s.remove()},rE=async function(t,e,r){var ft,X,$,U,et,K;vu();const n=Q2(e);e=n.code;const i=tn();E.debug(i),e.length>((i==null?void 0:i.maxTextSize)??zB)&&(e=OB);const a="#"+t,s="i"+t,o="#"+s,l="d"+t,u="#"+l;let c=Dt("body");const h=i.securityLevel===NB,f=i.securityLevel===RB,p=i.fontFamily;if(r!==void 0){if(r&&(r.innerHTML=""),h){const W=ep(Dt(r),s);c=Dt(W.nodes()[0].contentDocument.body),c.node().style.margin=0}else c=Dt(r);tp(c,t,l,`font-family: ${p}`,qB)}else{if(eE(document,t,l,s),h){const W=ep(Dt("body"),s);c=Dt(W.nodes()[0].contentDocument.body),c.node().style.margin=0}else c=Dt("body");tp(c,t,l)}let y,b;try{y=await Bu(e,{title:n.title})}catch(W){y=new E2("error"),b=W}const A=c.select(u).node(),_=y.type,M=A.firstChild,I=M.firstChild,V=(X=(ft=y.renderer).getClasses)==null?void 0:X.call(ft,e,y),N=QB(i,_,V,a),L=document.createElement("style");L.innerHTML=N,M.insertBefore(L,I);try{await y.renderer.draw(e,t,Y1,y)}catch(W){throw OT.draw(e,t,Y1),W}const q=c.select(`${u} svg`),G=(U=($=y.db).getAccTitle)==null?void 0:U.call($),Y=(K=(et=y.db).getAccDescription)==null?void 0:K.call(et);iE(_,q,G,Y),c.select(`[id="${t}"]`).selectAll("foreignobject > *").attr("xmlns",$B);let J=c.select(u).node().innerHTML;if(E.debug("config.arrowMarkerAbsolute",i.arrowMarkerAbsolute),J=JB(J,h,De(i.arrowMarkerAbsolute)),h){const W=c.select(u+" svg").node();J=tE(J,W)}else f||(J=Ni.sanitize(J,{ADD_TAGS:YB,ADD_ATTR:XB}));if(NT(),b)throw b;const P=Dt(h?o:u).node();return P&&"remove"in P&&P.remove(),{svg:J,bindFunctions:y.db.bindFunctions}};function nE(t={}){var r;t!=null&&t.fontFamily&&!((r=t.themeVariables)!=null&&r.fontFamily)&&(t.themeVariables||(t.themeVariables={}),t.themeVariables.fontFamily=t.fontFamily),Wy(t),t!=null&&t.theme&&t.theme in gn?t.themeVariables=gn[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=gn.default.getThemeVariables(t.themeVariables));const e=typeof t=="object"?Vy(t):K1();Fo(e.logLevel),vu()}const Bu=(t,e={})=>{const{code:r}=Z2(t);return IT(r,e)};function iE(t,e,r,n){PT(e,t),qT(e,r,n,e.attr("id"))}const Ci=Object.freeze({render:rE,parse:KB,getDiagramFromText:Bu,initialize:nE,getConfig:tn,setConfig:Z1,getSiteConfig:K1,updateSiteConfig:Uy,reset:()=>{k0()},globalReset:()=>{k0(Xi)},defaultConfig:Xi});Fo(tn().logLevel),k0(tn());const aE=async()=>{E.debug("Loading registered diagrams");const e=(await Promise.allSettled(Object.entries(qi).map(async([r,{detector:n,loader:i}])=>{if(i)try{Ml(r)}catch{try{const{diagram:s,id:o}=await i();Ll(o,s,n)}catch(s){throw E.error(`Failed to load external diagram with key ${r}. Removing from detectors.`),delete qi[r],s}}}))).filter(r=>r.status==="rejected");if(e.length>0){E.error(`Failed to load ${e.length} external diagrams`);for(const r of e)E.error(r);throw new Error(`Failed to load ${e.length} external diagrams`)}},sE=(t,e,r)=>{E.warn(t),R1(t)?(r&&r(t.str,t.hash),e.push({...t,message:t.str,error:t})):(r&&r(t),t instanceof Error&&e.push({str:t.message,message:t.message,hash:t.name,error:t}))},rp=async function(t={querySelector:".mermaid"}){try{await oE(t)}catch(e){if(R1(e)&&E.error(e.str),pr.parseError&&pr.parseError(e),!t.suppressErrors)throw E.error("Use the suppressErrors option to suppress these errors"),e}},oE=async function({postRenderCallback:t,querySelector:e,nodes:r}={querySelector:".mermaid"}){const n=Ci.getConfig();E.debug(`${t?"":"No "}Callback function found`);let i;if(r)i=r;else if(e)i=document.querySelectorAll(e);else throw new Error("Nodes and querySelector are both undefined");E.debug(`Found ${i.length} diagrams`),(n==null?void 0:n.startOnLoad)!==void 0&&(E.debug("Start On Load: "+(n==null?void 0:n.startOnLoad)),Ci.updateSiteConfig({startOnLoad:n==null?void 0:n.startOnLoad}));const a=new Ke.InitIDGenerator(n.deterministicIds,n.deterministicIDSeed);let s;const o=[];for(const l of Array.from(i)){E.info("Rendering diagram: "+l.id);/*! Check if previously processed */if(l.getAttribute("data-processed"))continue;l.setAttribute("data-processed","true");const u=`mermaid-${a.next()}`;s=l.innerHTML,s=Mi(Ke.entityDecode(s)).trim().replace(/<br\s*\/?>/gi,"<br/>");const c=Ke.detectInit(s);c&&E.debug("Detected early reinit: ",c);try{const{svg:h,bindFunctions:f}=await sp(u,s,l);l.innerHTML=h,t&&await t(u),f&&f(l)}catch(h){sE(h,o,pr.parseError)}}if(o.length>0)throw o[0]},np=function(t){Ci.initialize(t)},lE=async function(t,e,r){E.warn("mermaid.init is deprecated. Please use run instead."),t&&np(t);const n={postRenderCallback:r,querySelector:".mermaid"};typeof e=="string"?n.querySelector=e:Line truncated
* Wait for document loaded before starting the execution
*/window.addEventListener("load",ip,!1)}const cE=function(t){pr.parseError=t},no=[];let Eu=!1;const ap=async()=>{if(!Eu){for(Eu=!0;no.length>0;){const t=no.shift();if(t)try{await t()}catch(e){E.error("Error executing queue",e)}}Eu=!1}},hE=async(t,e)=>new Promise((r,n)=>{const i=()=>new Promise((a,s)=>{Ci.parse(t,e).then(o=>{a(o),r(o)},o=>{var l;E.error("Error parsing",o),(l=pr.parseError)==null||l.call(pr,o),s(o),n(o)})});no.push(i),ap().catch(n)}),sp=(t,e,r)=>new Promise((n,i)=>{const a=()=>new Promise((s,o)=>{Ci.render(t,e,r).then(l=>{s(l),n(l)},l=>{var u;E.error("Error parsing",l),(u=pr.parseError)==null||u.call(pr,l),o(l),i(l)})});no.push(a),ap().catch(i)}),pr={startOnLoad:!0,mermaidAPI:Ci,parse:hE,render:sp,init:lE,run:rp,registerExternalDiagrams:uE,initialize:np,parseError:void 0,contentLoaded:ip,setParseErrorHandler:cE,detectType:Js};class mr{constructor(e,r,n){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=e,this.start=r,this.end=n}static range(e,r){return r?!e||!e.loc||!r.loc||e.loc.lexer!==r.loc.lexer?null:new mr(e.loc.lexer,e.loc.start,r.loc.end):e&&e.loc}}class sn{constructor(e,r){this.text=void 0,this.loc=void 0,this.noexpand=void 0,this.treatAsRelax=void 0,this.text=e,this.loc=r}range(e,r){return new sn(r,mr.range(this,e))}}class tt{constructor(e,r){this.name=void 0,this.position=void 0,this.length=void 0,this.rawMessage=void 0;var n="KaTeX parse error: "+e,i,a,s=r&&r.loc;if(s&&s.start<=s.end){var o=s.lexer.input;i=s.start,a=s.end,i===o.length?n+=" at end of input: ":n+=" at position "+(i+1)+": ";var l=o.slice(i,a).replace(/[^]/g,"$&̲"),u;i>15?u="…"+o.slice(i-15,i):u=o.slice(0,i);var c;a+15<o.length?c=o.slice(a,a+15)+"…":c=o.slice(a),n+=u+l+c}var h=new Error(n);return h.name="ParseError",h.__proto__=tt.prototype,h.position=i,i!=null&&a!=null&&(h.length=a-i),h.rawMessage=e,h}}tt.prototype.__proto__=Error.prototype;var fE=function(e,r){return e.indexOf(r)!==-1},dE=function(e,r){return e===void 0?r:e},pE=/([A-Z])/g,mE=function(e){return e.replace(pE,"-$1").toLowerCase()},gE={"&":"&amp;",">":"&gt;","<":"&lt;",'"':"&quot;","'":"&#x27;"},yE=/[&><"']/g;function bE(t){return String(t).replace(yE,e=>gE[e])}var op=function t(e){return e.type==="ordgroup"||e.type==="color"?e.body.length===1?t(e.body[0]):e:e.type==="font"?t(e.body):e},xE=function(e){var r=op(e);return r.type==="mathord"||r.type==="textord"||r.type==="atom"},vE=function(e){if(!e)throw new Error("Expected non-null, but got "+String(e));return e},wE=function(e){var r=/^\s*([^\\/#]*?)(?::|&#0*58|&#x0*3a)/i.exec(e);return r!=null?r[1]:"_relative"},Ct={contains:fE,deflt:dE,escape:bE,hyphenate:mE,getBaseElem:op,isCharacterBox:xE,protocolFromUrl:wE},io={displayMode:{type:"boolean",description:"Render math in display mode, which puts the math in display style (so \\int and \\sum are large, for example), and centers the math on the page on its own line.",cli:"-d, --display-mode"},output:{type:{enum:["htmlAndMathml","html","mathml"]},description:"Determines the markup language of the output.",cli:"-F, --format <type>"},leqno:{type:"boolean",description:"Render display math in leqno style (left-justified tags)."},fleqn:{type:"boolean",description:"Render display math flush left."},throwOnError:{type:"boolean",default:!0,cli:"-t, --no-throw-on-error",cliDescription:"Render errors (in the color given by --error-color) instead of throwing a ParseError exception when encountering an error."},errorColor:{type:"string",default:"#cc0000",cli:"-c, --error-color <color>",cliDescription:"A color string given in the format 'rgb' or 'rrggbb' (no #). This option determines the color of errors rendered by the -t option.",cliProcessor:t=>"#"+t},macros:{type:"object",cli:"-m, --macro <def>",cliDescription:"Define custom macro of the form '\\foo:expansion' (use multiple -m arguments for multiple macros).",cliDefault:[],cliProcessor:(t,e)=>(e.push(t),e)},minRuleThickness:{type:"number",description:"Specifies a minimum thickness, in ems, for fraction lines, `\\sqrt` top lines, `{array}` vertical lines, `\\hline`, `\\hdashline`, `\\underline`, `\\overline`, and the borders of `\\fbox`, `\\boxed`, and `\\fcolorbox`.",processor:t=>Math.max(0,t),cli:"--min-rule-thickness <size>",cliProcessor:parseFloat},colorIsTextColor:{type:"boolean",description:"Makes \\color behave like LaTeX's 2-argument \\textcolor, instead of LaTeX's one-argument \\color mode change.",cli:"-b, --color-is-text-color"},strict:{type:[{enum:["warn","ignore","error"]},"boolean","function"],description:"Turn on strict / LaTeX faithfulness mode, which throws an error if the input uses features that are not supported by LaTeX.",cli:"-S, --strict",cliDefault:!1},trust:{type:["boolean","function"],description:"Trust the input, enabling all HTML features such as \\url.",cli:"-T, --trust"},maxSize:{type:"number",default:1/0,description:"If non-zero, all user-specified sizes, e.g. in \\rule{500em}{500em}, will be capped to maxSize ems. Otherwise, elements and spaces can be arbitrarily largLine truncated
c-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14
c0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54
c44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10
s173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429
c69,-144,104.5,-217.7,106.5,-221
l`+e/2.075+" -"+e+`
c5.3,-9.3,12,-14,20,-14
H400000v`+(40+e)+`H845.2724
s-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7
c-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z
M`+(834+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},LE=function(e,r){return"M263,"+(601+e+r)+`c0.7,0,18,39.7,52,119
c34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120
c340,-704.7,510.7,-1060.3,512,-1067
l`+e/2.084+" -"+e+`
c4.7,-7.3,11,-11,19,-11
H40000v`+(40+e)+`H1012.3
s-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232
c-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1
s-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26
c-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z
M`+(1001+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},ME=function(e,r){return"M983 "+(10+e+r)+`
l`+e/3.13+" -"+e+`
c4,-6.7,10,-10,18,-10 H400000v`+(40+e)+`
H1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7
s-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744
c-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30
c26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722
c56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5
c53.7,-170.3,84.5,-266.8,92.5,-289.5z
M`+(1001+e)+" "+r+"h400000v"+(40+e)+"h-400000z"},DE=function(e,r){return"M424,"+(2398+e+r)+`
c-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514
c0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20
s-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121
s209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081
l`+e/4.223+" -"+e+`c4,-6.7,10,-10,18,-10 H400000
v`+(40+e)+`H1014.6
s-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185
c-2,6,-10,9,-24,9
c-8,0,-12,-0.7,-12,-2z M`+(1001+e)+" "+r+`
h400000v`+(40+e)+"h-400000z"},IE=function(e,r){return"M473,"+(2713+e+r)+`
c339.3,-1799.3,509.3,-2700,510,-2702 l`+e/5.298+" -"+e+`
c3.3,-7.3,9.3,-11,18,-11 H400000v`+(40+e)+`H1017.7
s-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9
c-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200
c0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26
s76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,
606zM`+(1001+e)+" "+r+"h400000v"+(40+e)+"H1017.7z"},zE=function(e){var r=e/2;return"M400000 "+e+" H0 L"+r+" 0 l65 45 L145 "+(e-80)+" H400000z"},OE=function(e,r,n){var i=n-54-r-e;return"M702 "+(e+r)+"H400000"+(40+e)+`
H742v`+i+`l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1
h-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170
c-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667
219 661 l218 661zM702 `+r+"H400000v"+(40+e)+"H742z"},NE=function(e,r,n){r=1e3*r;var i="";switch(e){case"sqrtMain":i=FE(r,da);break;case"sqrtSize1":i=LE(r,da);break;case"sqrtSize2":i=ME(r,da);break;case"sqrtSize3":i=DE(r,da);break;case"sqrtSize4":i=IE(r,da);break;case"sqrtTall":i=OE(r,da,n)}return i},RE=function(e,r){switch(e){case"⎜":return"M291 0 H417 V"+r+" H291z M291 0 H417 V"+r+" H291z";case"∣":return"M145 0 H188 V"+r+" H145z M145 0 H188 V"+r+" H145z";case"∥":return"M145 0 H188 V"+r+" H145z M145 0 H188 V"+r+" H145z"+("M367 0 H410 V"+r+" H367z M367 0 H410 V"+r+" H367z");case"⎟":return"M457 0 H583 V"+r+" H457z M457 0 H583 V"+r+" H457z";case"⎢":return"M319 0 H403 V"+r+" H319z M319 0 H403 V"+r+" H319z";case"⎥":return"M263 0 H347 V"+r+" H263z M263 0 H347 V"+r+" H263z";case"⎪":return"M384 0 H504 V"+r+" H384z M384 0 H504 V"+r+" H384z";case"⏐":return"M312 0 H355 V"+r+" H312z M312 0 H355 V"+r+" H312z";case"‖":return"M257 0 H300 V"+r+" H257z M257 0 H300 V"+r+" H257z"+("M478 0 H521 V"+r+" H478z M478 0 H521 V"+r+" H478z");default:return""}},up={doubleleftarrow:`M262 157
l10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3
0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28
14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5
c2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5
157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87
-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7
-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z
m8 0v40h399730v-40zm0 194v40h399730v-40z`,doublerightarrow:`M399738 392l
-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5
14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88
-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68
-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18
-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782
c-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3
-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z`,leftarrow:`M400000 241H110l3-3c68.7-52.7 113.7-120
135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8
-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247
c-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208
490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3
1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202
l-3-3h399890zM100 241v40h399900v-40z`,leftbrace:`M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117
-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7
5-6 9-10 13-.7 1-7.3 1-20 1H6z`,leftbraceunder:`M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13
35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688
0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7
-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z`,leftgroup:`M400000 80
H435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0
435 0h399565z`,leftgroupunder:`M400000 262
H435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219
435 219h399565z`,leftharpoon:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3
-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5
-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7
-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z`,leftharpoonplus:`M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5
20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3
-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7
-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z
m0 0v40h400000v-40z`,leftharpoondown:`M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333
5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5
1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667
-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z`,leftharpoondownplus:`M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12
10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7
-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0
v40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z`,lefthook:`M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5
-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3
-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21
71.5 23h399859zM103 281v-40h399897v40z`,leftlinesegment:`M40 281 V428 H0 V94 H40 V241 H400000 v40z
M40 281 V428 H0 V94 H40 V241 H400000 v40z`,leftmapsto:`M40 281 V448H0V74H40V241H400000v40z
M40 281 V448H0V74H40V241H400000v40z`,leftToFrom:`M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23
-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8
c28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3
68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z`,longequal:`M0 50 h400000 v40H0z m0 194h40000v40H0z
M0 50 h400000 v40H0z m0 194h40000v40H0z`,midbrace:`M200428 334
c-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14
-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7
311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11
12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z`,midbraceunder:`M199572 214
c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14
53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3
11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0
-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z`,oiintSize1:`M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6
-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z
m368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8
60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z`,oiintSize2:`M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8
-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z
m502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2
c0 110 84 276 504 276s502.4-166 502.4-276z`,oiiintSize1:`M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6
-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z
m525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0
85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z`,oiiintSize2:`M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8
-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z
m770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1
c0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z`,rightarrow:`M0 241v40h399891c-47.3 35.3-84 78-110 128
-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20
11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7
39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85
-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5
-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67
151.7 139 205zm0 0v40h399900v-40z`,rightbrace:`M400000 542l
-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5
s-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1
c124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z`,rightbraceunder:`M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3
28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237
-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z`,rightgroup:`M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0
3-1 3-3v-38c-76-158-257-219-435-219H0z`,rightgroupunder:`M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18
0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z`,rightharpoon:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3
-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2
-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58
69.2 92 94.5zm0 0v40h399900v-40z`,rightharpoonplus:`M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11
-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7
2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z
m0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z`,rightharpoondown:`M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8
8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5
-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95
-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z`,rightharpoondownplus:`M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8
15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3
8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3
-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z
m0-194v40h400000v-40zm0 0v40h400000v-40z`,righthook:`M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3
15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0
-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21
66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z`,rightlinesegment:`M399960 241 V94 h40 V428 h-40 V281 H0 v-40z
M399960 241 V94 h40 V428 h-40 V281 H0 v-40z`,rightToFrom:`M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23
1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32
-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142
-167z M100 147v40h399900v-40zM0 341v40h399900v-40z`,twoheadleftarrow:`M0 167c68 40
115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69
-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3
-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19
-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101
10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z`,twoheadrightarrow:`M400000 167
c-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3
41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42
18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333
-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70
101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z`,tilde1:`M200 55.538c-77 0-168 73.953-177 73.953-3 0-7
-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0
114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0
4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128
-68.267.847-113-73.952-191-73.952z`,tilde2:`M344 55.266c-142 0-300.638 81.316-311.5 86.418
-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9
31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114
c1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751
181.476 676 181.476c-149 0-189-126.21-332-126.21z`,tilde3:`M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457
-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0
411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697
16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696
-338 0-409-156.573-744-156.573z`,tilde4:`M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345
-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409
177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9
14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409
-175.236-744-175.236z`,vec:`M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5
3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11
10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63
-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1
-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59
H213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359
c-16-25.333-24-45-24-59z`,widehat1:`M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22
c-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z`,widehat2:`M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10
-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat3:`M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10
-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widehat4:`M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10
-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z`,widecheck1:`M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,
-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z`,widecheck2:`M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,
-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck3:`M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,
-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,widecheck4:`M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,
-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z`,baraboveleftarrow:`M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202
c4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5
c-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130
s-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47
121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6
s2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11
c0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z
M100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z`,rightarrowabovebar:`M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32
-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0
13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39
-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5
-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5
-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67
151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z`,baraboveshortleftharpoon:`M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11
c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17
c2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21
c-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40
c-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z
M0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z`,rightharpoonaboveshortbar:`M0,241 l0,40c399126,0,399993,0,399993,0
c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,
-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6
c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z
M0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z`,shortbaraboveleftharpoon:`M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11
c1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,
1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,
-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z
M93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z`,shortrightharpoonabovebar:`M53,241l0,40c398570,0,399437,0,399437,0
c4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,
-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6
c-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z
M500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z`},PE=function(e,r){switch(e){case"lbrack":return"M403 1759 V84 H666 V0 H319 V1759 v"+r+` v1759 h347 v-84
H403z M403 1759 V0 H319 V1759 v`+r+" v1759 h84z";case"rbrack":return"M347 1759 V0 H0 V84 H263 V1759 v"+r+` v1759 H0 v84 H347z
M347 1759 V0 H263 V1759 v`+r+" v1759 h84z";case"vert":return"M145 15 v585 v"+r+` v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+r+" v585 h43z";case"doublevert":return"M145 15 v585 v"+r+` v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M188 15 H145 v585 v`+r+` v585 h43z
M367 15 v585 v`+r+` v585 c2.667,10,9.667,15,21,15
c10,0,16.667,-5,20,-15 v-585 v`+-r+` v-585 c-2.667,-10,-9.667,-15,-21,-15
c-10,0,-16.667,5,-20,15z M410 15 H367 v585 v`+r+" v585 h43z";case"lfloor":return"M319 602 V0 H403 V602 v"+r+` v1715 h263 v84 H319z
MM319 602 V0 H403 V602 v`+r+" v1715 H319z";case"rfloor":return"M319 602 V0 H403 V602 v"+r+` v1799 H0 v-84 H319z
MM319 602 V0 H403 V602 v`+r+" v1715 H319z";case"lceil":return"M403 1759 V84 H666 V0 H319 V1759 v"+r+` v602 h84z
M403 1759 V0 H319 V1759 v`+r+" v602 h84z";case"rceil":return"M347 1759 V0 H0 V84 H263 V1759 v"+r+` v602 h84z
M347 1759 V0 h-84 V1759 v`+r+" v602 h84z";case"lparen":return`M863,9c0,-2,-2,-5,-6,-9c0,0,-17,0,-17,0c-12.7,0,-19.3,0.3,-20,1
c-5.3,5.3,-10.3,11,-15,17c-242.7,294.7,-395.3,682,-458,1162c-21.3,163.3,-33.3,349,
-36,557 l0,`+(r+84)+`c0.2,6,0,26,0,60c2,159.3,10,310.7,24,454c53.3,528,210,
949.7,470,1265c4.7,6,9.7,11.7,15,17c0.7,0.7,7,1,19,1c0,0,18,0,18,0c4,-4,6,-7,6,-9
c0,-2.7,-3.3,-8.7,-10,-18c-135.3,-192.7,-235.5,-414.3,-300.5,-665c-65,-250.7,-102.5,
-544.7,-112.5,-882c-2,-104,-3,-167,-3,-189
l0,-`+(r+92)+`c0,-162.7,5.7,-314,17,-454c20.7,-272,63.7,-513,129,-723c65.3,
-210,155.3,-396.3,270,-559c6.7,-9.3,10,-15.3,10,-18z`;case"rparen":return`M76,0c-16.7,0,-25,3,-25,9c0,2,2,6.3,6,13c21.3,28.7,42.3,60.3,
63,95c96.7,156.7,172.8,332.5,228.5,527.5c55.7,195,92.8,416.5,111.5,664.5
c11.3,139.3,17,290.7,17,454c0,28,1.7,43,3.3,45l0,`+(r+9)+`
c-3,4,-3.3,16.7,-3.3,38c0,162,-5.7,313.7,-17,455c-18.7,248,-55.8,469.3,-111.5,664
c-55.7,194.7,-131.8,370.3,-228.5,527c-20.7,34.7,-41.7,66.3,-63,95c-2,3.3,-4,7,-6,11
c0,7.3,5.7,11,17,11c0,0,11,0,11,0c9.3,0,14.3,-0.3,15,-1c5.3,-5.3,10.3,-11,15,-17
c242.7,-294.7,395.3,-681.7,458,-1161c21.3,-164.7,33.3,-350.7,36,-558
l0,-`+(r+144)+`c-2,-159.3,-10,-310.7,-24,-454c-53.3,-528,-210,-949.7,
-470,-1265c-4.7,-6,-9.7,-11.7,-15,-17c-0.7,-0.7,-6.7,-1,-18,-1z`;default:throw new Error("Unknown stretchy delimiter.")}};class ls{constructor(e){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=e,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}hasClass(e){return Ct.contains(this.classes,e)}toNode(){for(var e=document.createDocumentFragment(),r=0;r<this.children.length;r++)e.appendChild(this.children[r].toNode());return e}toMarkup(){for(var e="",r=0;r<this.children.length;r++)e+=this.children[r].toMarkup();return e}toText(){var e=r=>r.toText();return this.children.map(e).join("")}}var ln={"AMS-Regular":{32:[0,0,0,0,.25],65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],160:[0,0,0,0,.25],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,. Line truncated
`+q+" children.");for(var O=0;O<q;O++){var P=new Zn(I[O]),ft=new Bn([P],{width:"400em",height:nt(L),viewBox:"0 0 "+o+" "+N,preserveAspectRatio:Y[O]+" slice"}),X=z.makeSvgSpan([G[O]],[ft],r);if(q===1)return{span:X,minWidth:V,height:L};X.style.height=nt(L),_.push(X)}return{span:z.makeSpan(["stretchy"],_,r),minWidth:V,height:L}}}var{span:i,minWidth:a,height:s}=n();return i.height=s,i.style.height=nt(s),a>0&&(i.style.minWidth=nt(a)),i},kF=function(e,r,n,i,a){var s,o=e.height+e.depth+n+i;if(/fbox|color|angl/.test(r)){if(s=z.makeSpan(["stretchy",r],[],a),r==="fbox"){var l=a.color&&a.getColor();l&&(s.style.borderColor=l)}}else{var u=[];/^[bx]cancel$/.test(r)&&u.push(new Nu({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(r)&&u.push(new Nu({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var c=new Bn(u,{width:"100%",height:nt(o)});s=z.makeSvgSpan([],[c],a)}return s.height=o,s.style.height=nt(o),s},Mn={encloseSpan:kF,mathMLnode:xF,svgSpan:CF};function Bt(t,e){if(!t||t.type!==e)throw new Error("Expected node of type "+e+", but got "+(t?"node of type "+t.type:String(t)));return t}function Yu(t){var e=vo(t);if(!e)throw new Error("Expected node of symbol group type, but got "+(t?"node of type "+t.type:String(t)));return e}function vo(t){return t&&(t.type==="atom"||YE.hasOwnProperty(t.type))?t:null}var Xu=(t,e)=>{var r,n,i;t&&t.type==="supsub"?(n=Bt(t.base,"accent"),r=n.base,t.base=r,i=GE($t(t,e)),t.base=n):(n=Bt(t,"accent"),r=n.base);var a=$t(r,e.havingCrampedStyle()),s=n.isShifty&&Ct.isCharacterBox(r),o=0;if(s){var l=Ct.getBaseElem(r),u=$t(l,e.havingCrampedStyle());o=yp(u).skew}var c=n.label==="\\c",h=c?a.height+a.depth:Math.min(a.height,e.fontMetrics().xHeight),f;if(n.isStretchy)f=Mn.svgSpan(n,e),f=z.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:a},{type:"elem",elem:f,wrapperClasses:["svg-align"],wrapperStyle:o>0?{width:"calc(100% - "+nt(2*o)+")",marginLeft:nt(2*o)}:void 0}]},e);else{var p,y;n.label==="\\vec"?(p=z.staticSvg("vec",e),y=z.svgData.vec[1]):(p=z.makeOrd({mode:n.mode,text:n.label},e,"textord"),p=yp(p),p.italic=0,y=p.width,c&&(h+=p.depth)),f=z.makeSpan(["accent-body"],[p]);var b=n.label==="\\textcircled";b&&(f.classes.push("accent-full"),h=a.height);var A=o;b||(A-=y/2),f.style.left=nt(A),n.label==="\\textcircled"&&(f.style.top=".2em"),f=z.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:a},{type:"kern",size:-h},{type:"elem",elem:f}]},e)}var _=z.makeSpan(["mord","accent"],[f],e);return i?(i.children[0]=_,i.height=Math.max(_.height,i.height),i.classes[0]="mord",i):_},zp=(t,e)=>{var r=t.isStretchy?Mn.mathMLnode(t.label):new Z.MathNode("mo",[Ar(t.label,t.mode)]),n=new Z.MathNode("mover",[Qt(t.base,e),r]);return n.setAttribute("accent","true"),n},_F=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(t=>"\\"+t).join("|"));ot({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:(t,e)=>{var r=bo(e[0]),n=!_F.test(t.funcName),i=!n||t.funcName==="\\widehat"||t.funcName==="\\widetilde"||t.funcName==="\\widecheck";return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:n,isShifty:i,base:r}},htmlBuilder:Xu,mathmlBuilder:zp}),ot({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\c","\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!0,argTypes:["primitive"]},handler:(t,e)=>{var r=e[0],n=t.parser.mode;return n==="math"&&(t.parser.settings.reportNonstrict("mathVsTextAccents","LaTeX's accent "+t.funcName+" works only in text mode"),n="text"),{type:"accent",mode:n,label:t.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Xu,mathmlBuilder:zp}),ot({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:(t,e)=>{var{parser:r,funcName:n}=t,i=e[0];return{type:"accentUnder",mode:r.mode,label:n,base:i}},htmlBuilder:(t,e)=>{var r=$t(t.base,e),n=Mn.svgSpan(t,e),i=t.label==="\\utilde"?.12:0,a=z.makeVList({positionType:"top",positionData:r.height,children:[{type:"elem",elem:n,wrapperClasses:["svg-align"]},{type:"kern",size:i},{type:"elem",elem:r}]},e);return z.makeSpan(["mord","accentunder"],[a],e)},mathmlBuilder:(t,e)=>{var r=Mn.mathMLnode(t.label),n=new Z.MathNode("munder",[Qt(t.base,e),r]);return n.setAttribute("accentunder","true"),n}});var wo=t=>{var e=new Z.MathNode("mpadded",t?[t]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};ot({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookLine truncated
]`,ZF="\\\\[a-zA-Z@]+",QF="\\\\[^\uD800-\uDFFF]",JF="("+ZF+")"+Sm+"*",tL=`\\\\(
|[ \r ]+
?)[ \r ]*`,dc="[̀-ͯ]",eL=new RegExp(dc+"+$"),rL="("+Sm+"+)|"+(tL+"|")+"([!-\\[\\]-‧‪-퟿豈-￿]"+(dc+"*")+"|[\uD800-\uDBFF][\uDC00-\uDFFF]"+(dc+"*")+"|\\\\verb\\*([^]).*?\\4|\\\\verb([^*a-zA-Z]).*?\\5"+("|"+JF)+("|"+QF+")");class Tm{constructor(e,r){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=e,this.settings=r,this.tokenRegex=new RegExp(rL,"g"),this.catcodes={"%":14,"~":13}}setCatcode(e,r){this.catcodes[e]=r}lex(){var e=this.input,r=this.tokenRegex.lastIndex;if(r===e.length)return new sn("EOF",new mr(this,r,r));var n=this.tokenRegex.exec(e);if(n===null||n.index!==r)throw new tt("Unexpected character: '"+e[r]+"'",new sn(e[r],new mr(this,r,r+1)));var i=n[6]||n[3]||(n[2]?"\\ ":" ");if(this.catcodes[i]===14){var a=e.indexOf(`
`,this.tokenRegex.lastIndex);return a===-1?(this.tokenRegex.lastIndex=e.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=a+1,this.lex()}return new sn(i,new mr(this,r,this.tokenRegex.lastIndex))}}class nL{constructor(e,r){e===void 0&&(e={}),r===void 0&&(r={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=r,this.builtins=e,this.undefStack=[]}beginGroup(){this.undefStack.push({})}endGroup(){if(this.undefStack.length===0)throw new tt("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var e=this.undefStack.pop();for(var r in e)e.hasOwnProperty(r)&&(e[r]==null?delete this.current[r]:this.current[r]=e[r])}endGroups(){for(;this.undefStack.length>0;)this.endGroup()}has(e){return this.current.hasOwnProperty(e)||this.builtins.hasOwnProperty(e)}get(e){return this.current.hasOwnProperty(e)?this.current[e]:this.builtins[e]}set(e,r,n){if(n===void 0&&(n=!1),n){for(var i=0;i<this.undefStack.length;i++)delete this.undefStack[i][e];this.undefStack.length>0&&(this.undefStack[this.undefStack.length-1][e]=r)}else{var a=this.undefStack[this.undefStack.length-1];a&&!a.hasOwnProperty(e)&&(a[e]=this.current[e])}r==null?delete this.current[e]:this.current[e]=r}}var iL=em;x("\\noexpand",function(t){var e=t.popToken();return t.isExpandable(e.text)&&(e.noexpand=!0,e.treatAsRelax=!0),{tokens:[e],numArgs:0}}),x("\\expandafter",function(t){var e=t.popToken();return t.expandOnce(!0),{tokens:[e],numArgs:0}}),x("\\@firstoftwo",function(t){var e=t.consumeArgs(2);return{tokens:e[0],numArgs:0}}),x("\\@secondoftwo",function(t){var e=t.consumeArgs(2);return{tokens:e[1],numArgs:0}}),x("\\@ifnextchar",function(t){var e=t.consumeArgs(3);t.consumeSpaces();var r=t.future();return e[0].length===1&&e[0][0].text===r.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}}),x("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),x("\\TextOrMath",function(t){var e=t.consumeArgs(2);return t.mode==="text"?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});var Am={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};x("\\char",function(t){var e=t.popToken(),r,n="";if(e.text==="'")r=8,e=t.popToken();else if(e.text==='"')r=16,e=t.popToken();else if(e.text==="`")if(e=t.popToken(),e.text[0]==="\\")n=e.text.charCodeAt(1);else{if(e.text==="EOF")throw new tt("\\char` missing argument");n=e.text.charCodeAt(0)}else r=10;if(r){if(n=Am[e.text],n==null||n>=r)throw new tt("Invalid base-"+r+" digit "+e.text);for(var i;(i=Am[t.future().text])!=null&&i<r;)n*=r,n+=i,t.popToken()}return"\\@char{"+n+"}"});var pc=(t,e,r)=>{var n=t.consumeArg().tokens;if(n.length!==1)throw new tt("\\newcommand's first argument must be a macro name");var i=n[0].text,a=t.isDefined(i);if(a&&!e)throw new tt("\\newcommand{"+i+"} attempting to redefine "+(i+"; use \\renewcommand"));if(!a&&!r)throw new tt("\\renewcommand{"+i+"} when command "+i+" does not yet exist; use \\newcommand");var s=0;if(n=t.consumeArg().tokens,n.length===1&&n[0].text==="["){for(var o="",l=t.expandNextToken();l.text!=="]"&&l.text!=="EOF";)o+=l.text,l=t.expandNextToken();if(!o.match(/^\s*[0-9]+\s*$/))throw new tt("Invalid number of arguments: "+o);s=parseInt(o),n=t.consumeArg().tokens}return t.macros.set(i,{tokens:n,numArgs:s}),""};x("\\newcommand",t=>pc(t,!1,!0)),x("\\renewcommand",t=>pc(t,!0,!1)),x("\\providecommand",t=>pc(t,!0,!0)),x("\\message",t=>{var e=t.consumeArgs(1)[0];return console.log(e.reverse().map(r=>r.text).join("")),""}),x("\\errmessage",t=>{var e=t.consumeArgs(1)[0];return console.error(e.reverse().map(r=>r.text).join("")),""}),x("\\show",t=>{var e=t.popToken(),r=e.text;return console.log(e,t.macros.get(r),ei[r],re.math[r],re.text[r]),""}),x("\\bgroup","{"),x("\\egroup","}"),x("~","\\nobreakspace"),x("\\lq","`"),x("\\rq","'"),x("\\aa","\\r a"),x("\\AA","\\r A"),x("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`©}"),x("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),x("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`®}"),x("ℬ","\\mathscr{B}"),x("ℰ","\\mathscr{E}"),x("ℱ","\\mathscr{F}"),x("ℋ","\\mathscr{H}"),x("ℐ","\\mathscr{I}"),x("ℒ","\\mathscr{L}"),x("ℳ","\\mathscr{M}"),x("ℛ","\\mathscr{R}"),x("ℭ","\\mathfrak{C}"),x("ℌ","\\mathfrak{H}"),x("ℨ","\\mathfrak{Z}"),x("\\Bbbk","\\Bbb{k}"),x("·","\\cdotp"),x("\\llap","\\mathllap{\\textrm{#1}}"),x("\\rlap","\\mathrlap{\\textrm{#1}}"),x("\\clap","\\mathclap{\\textrm{#1}}"),x("\\mathstrut","\\vphantom{(}"),x("\\underbar","\\underline{\\text{#1}}"),x("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),x("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"),x("\\ne","\\neq"),x("≠","\\neq"),x("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`∉}}"),x("∉","\\notin"),x("≘","\\hLine truncated
please report what input caused this bug`);return n=n.slice(1,-1),{type:"verb",mode:"text",body:n,star:i}}Dm.hasOwnProperty(r[0])&&!re[this.mode][r[0]]&&(this.settings.strict&&this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Accented Unicode text character "'+r[0]+'" used in math mode',e),r=Dm[r[0]]+r.slice(1));var a=eL.exec(r);a&&(r=r.substring(0,a.index),r==="i"?r="ı":r==="j"&&(r="ȷ"));var s;if(re[this.mode][r]){this.settings.strict&&this.mode==="math"&&$u.indexOf(r)>=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+r[0]+'" used in math mode',e);var o=re[this.mode][r].group,l=mr.range(e),u;if(jE.hasOwnProperty(o)){var c=o;u={type:"atom",mode:this.mode,family:c,loc:l,text:r}}else u={type:o,mode:this.mode,loc:l,text:r};s=u}else if(r.charCodeAt(0)>=128)this.settings.strict&&(lp(r.charCodeAt(0))?this.mode==="math"&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+r[0]+'" used in math mode',e):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+r[0]+'"'+(" ("+r.charCodeAt(0)+")"),e)),s={type:"textord",mode:"text",loc:mr.range(e),text:r};else return null;if(this.consume(),a)for(var h=0;h<a[0].length;h++){var f=a[0][h];if(!gc[f])throw new tt("Unknown accent ' "+f+"'",e);var p=gc[f][this.mode]||gc[f].text;if(!p)throw new tt("Accent "+f+" unsupported in "+this.mode+" mode",e);s={type:"accent",mode:this.mode,loc:mr.range(e),label:p,isStretchy:!1,isShifty:!0,base:s}}return s}}ms.endOfExpression=["}","\\endgroup","\\end","\\right","&"];var yc=function(e,r){if(!(typeof e=="string"||e instanceof String))throw new TypeError("KaTeX can only parse string typed expression");var n=new ms(e,r);delete n.gullet.macros.current["\\df@tag"];var i=n.parse();if(delete n.gullet.macros.current["\\current@color"],delete n.gullet.macros.current["\\color"],n.gullet.macros.get("\\df@tag")){if(!r.displayMode)throw new tt("\\tag works only in display equations");i=[{type:"tag",mode:"text",body:i,tag:n.subparse([new sn("\\df@tag")])}]}return i},Im=function(e,r,n){r.textContent="";var i=bc(e,n).toNode();r.appendChild(i)};typeof document<"u"&&document.compatMode!=="CSS1Compat"&&(typeof console<"u"&&console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your website has a suitable doctype."),Im=function(){throw new tt("KaTeX doesn't work in quirks mode.")});var sL=function(e,r){var n=bc(e,r).toMarkup();return n},oL=function(e,r){var n=new Fu(r);return yc(e,n)},zm=function(e,r,n){if(n.throwOnError||!(e instanceof tt))throw e;var i=z.makeSpan(["katex-error"],[new Sr(r)]);return i.setAttribute("title",e.toString()),i.setAttribute("style","color:"+n.errorColor),i},bc=function(e,r){var n=new Fu(r);try{var i=yc(e,n);return gF(i,e,n)}catch(a){return zm(a,e,n)}},lL=function(e,r){var n=new Fu(r);try{var i=yc(e,n);return yF(i,e,n)}catch(a){return zm(a,e,n)}},uL={version:"0.16.9",render:Im,renderToString:sL,ParseError:tt,SETTINGS_SCHEMA:io,__parse:oL,__renderToDomTree:bc,__renderToHTMLTree:lL,__setFontMetrics:qE,__defineSymbol:d,__defineFunction:ot,__defineMacro:x,__domTree:{Span:us,Anchor:Ou,SymbolNode:Sr,SvgNode:Bn,PathNode:Zn,LineNode:Nu}};const cL=Object.freeze(Object.defineProperty({__proto__:null,default:uL},Symbol.toStringTag,{value:"Module"}));return pr});