\s*\|\s*]*>Handleiding<\/a><\/span>/', '', $footerContent);
+ }
+
+ // Determine content type and load appropriate template
+ $contentType = $this->getContentType($page);
+ $contentTemplateFile = $this->config['templates_dir'] . '/' . $contentType . '_content.mustache';
+ $contentTemplate = file_exists($contentTemplateFile) ? file_get_contents($contentTemplateFile) : '{{{content}}}
';
+
+ // Determine content type and load appropriate template
+ $pagePath = $_GET['page'] ?? $this->config['default_page'];
+ $pagePath = preg_replace('/\.[^.]+$/', '', $pagePath);
+ $filePath = $this->config['content_dir'] . '/' . $pagePath;
+
+ $contentType = 'markdown'; // default
+ if (file_exists($filePath . '.md')) {
+ $contentType = 'markdown';
+ } elseif (file_exists($filePath . '.php')) {
+ $contentType = 'php';
+ } elseif (file_exists($filePath . '.html')) {
+ $contentType = 'html';
+ }
+
+ $contentTemplateFile = $this->config['templates_dir'] . '/' . $contentType . '_content.mustache';
+ $contentTemplate = file_exists($contentTemplateFile) ? file_get_contents($contentTemplateFile) : '{{{content}}}
';
+
+ $partials = [
+ 'header' => file_get_contents($this->config['templates_dir'] . '/assets/header.mustache'),
+ 'navigation' => file_get_contents($this->config['templates_dir'] . '/assets/navigation.mustache'),
+ 'footer' => file_get_contents($this->config['templates_dir'] . '/assets/footer.mustache'),
+ 'content_template' => $contentTemplate
+ ];
+
+ // Replace partials in template
+ $template = file_get_contents($this->config['templates_dir'] . '/layout.mustache');
+ $template = str_replace('{{>header}}', $partials['header'], $template);
+ $template = str_replace('{{>navigation}}', $partials['navigation'], $template);
+ $template = str_replace('{{>footer}}', $partials['footer'], $template);
+ $template = str_replace('{{>content_template}}', $partials['content_template'], $template);
+
+ // Render template with data
+ $renderedTemplate = SimpleTemplate::render($template, $templateData);
+ echo $renderedTemplate;
+ }
+
+ /**
+ * Generate breadcrumb navigation HTML
+ *
+ * @return string Breadcrumb HTML
+ */
+ private function getBreadcrumb() {
+ if (isset($_GET['search'])) {
+ return 'Home Search ';
+ }
+
+ $page = $_GET['page'] ?? $this->config['default_page'];
+ $page = preg_replace('/\.[^.]+$/', '', $page);
+
+ if ($page === $this->config['default_page']) {
+ return 'Home ';
+ }
+
+ $parts = explode('/', $page);
+ $breadcrumb = 'Home ';
+
+ $path = '';
+ foreach ($parts as $i => $part) {
+ $path .= ($path ? '/' : '') . $part;
+ $title = ucfirst($part);
+
+ if ($i === count($parts) - 1) {
+ $breadcrumb .= '' . $title . ' ';
+ } else {
+ $breadcrumb .= '' . $title . ' ';
+ }
+ }
+
+ $breadcrumb .= ' ';
+ return $breadcrumb;
+ }
+
+ /**
+ * Render menu HTML with dropdown support
+ *
+ * @param array $items Menu items to render
+ * @param int $level Current nesting level
+ * @return string Rendered menu HTML
+ */
+ private function renderMenu($items, $level = 0) {
+ $html = '';
+ foreach ($items as $item) {
+ if ($item['type'] === 'folder') {
+ $hasChildren = !empty($item['children']);
+
+ if ($hasChildren) {
+ $folderId = 'folder-' . str_replace('/', '-', $item['path']);
+ $isExpanded = $this->folderContainsActivePage($item['children']);
+
+ if ($level === 0) {
+ // Root level folders
+ $html .= '';
+ $html .= '';
+ $html .= htmlspecialchars($item['title']);
+ $html .= ' ';
+ $html .= '';
+ $html .= ' ';
+ } else {
+ // Nested folders in dropdown
+ $html .= '';
+ }
+ } else {
+ if ($level === 0) {
+ $html .= '';
+ $html .= '' . htmlspecialchars($item['title']) . ' ';
+ $html .= ' ';
+ } else {
+ $html .= '' . htmlspecialchars($item['title']) . ' ';
+ }
+ }
+ } else {
+ // Show files in root as tabs, files in folders as dropdown items
+ if ($level === 0) {
+ // Don't show the homepage file as a separate tab since it's already the Home button
+ if ($item['path'] === $this->config['default_page']) {
+ continue;
+ }
+
+ $active = (isset($_GET['page']) && $_GET['page'] === $item['path']) ? 'active' : '';
+ $html .= '';
+ $html .= '';
+ $html .= htmlspecialchars($item['title']);
+ $html .= ' ';
+ $html .= ' ';
+ } else {
+ // Show files in dropdown menus
+ $active = (isset($_GET['page']) && $_GET['page'] === $item['path']) ? 'active' : '';
+ $html .= '';
+ $html .= htmlspecialchars($item['title']);
+ $html .= ' ';
+ }
+ }
+ }
+ return $html;
+ }
+
+ /**
+ * Determine content type for current page
+ *
+ * @param array $page Page data
+ * @return string Content type (markdown, php, html)
+ */
+ private function getContentType($page) {
+ // Try to determine content type from page request
+ $pagePath = $_GET['page'] ?? $this->config['default_page'];
+ $pagePath = preg_replace('/\.[^.]+$/', '', $pagePath);
+
+ $filePath = $this->config['content_dir'] . '/' . $pagePath;
+
+ // Check for different file extensions
+ if (file_exists($filePath . '.md')) {
+ return 'markdown';
+ } elseif (file_exists($filePath . '.php')) {
+ return 'php';
+ } elseif (file_exists($filePath . '.html')) {
+ return 'html';
+ } elseif (file_exists($filePath)) {
+ $extension = pathinfo($filePath, PATHINFO_EXTENSION);
+ return in_array($extension, ['md', 'php', 'html']) ? $extension : 'markdown';
+ }
+
+ // Default to markdown
+ return 'markdown';
+ }
+
+ /**
+ * Get homepage title
+ *
+ * @return string Homepage title
+ */
+ private function getHomepageTitle() {
+ // Use formatted filename for homepage title in navigation
+ return $this->formatDisplayName($this->config['default_page']);
+ }
+
+ /**
+ * Check if folder contains the currently active page
+ *
+ * @param array $children Array of child items
+ * @return bool True if active page is found in children
+ */
+ private function folderContainsActivePage($children) {
+ foreach ($children as $child) {
+ if ($child['type'] === 'folder') {
+ if (!empty($child['children']) && $this->folderContainsActivePage($child['children'])) {
+ return true;
+ }
+ } else {
+ if (isset($_GET['page']) && $_GET['page'] === $child['path']) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/engine/core/class/SimpleTemplate.php b/engine/core/class/SimpleTemplate.php
new file mode 100644
index 0000000..b477d0a
--- /dev/null
+++ b/engine/core/class/SimpleTemplate.php
@@ -0,0 +1,92 @@
+partial}}
+ * - Simple string-based rendering (no external dependencies)
+ */
+class SimpleTemplate {
+ private $data;
+
+ /**
+ * Render template with data
+ *
+ * @param string $template Template content with placeholders
+ * @param array $data Data to populate template
+ * @return string Rendered template
+ */
+ public static function render($template, $data) {
+ $instance = new self();
+ $instance->data = $data;
+ return $instance->renderTemplate($template);
+ }
+
+ /**
+ * Process template and replace placeholders
+ *
+ * @param string $template Template content
+ * @return string Processed template
+ */
+ private function renderTemplate($template) {
+ // Handle partial includes first ({{>partial}})
+ $template = preg_replace_callback('/{{>([^}]+)}}/', [$this, 'replacePartial'], $template);
+
+ // Handle conditional blocks
+ foreach ($this->data as $key => $value) {
+ if (is_array($value) || (is_string($value) && !empty($value))) {
+ // Handle {{#key}}...{{/key}} blocks
+ $pattern = '/{{#' . preg_quote($key, '/') . '}}(.*?){{\/' . preg_quote($key, '/') . '}}/s';
+ if (preg_match($pattern, $template, $matches)) {
+ $replacement = $matches[1];
+ $template = preg_replace($pattern, $replacement, $template);
+ }
+
+ // Handle {{^key}}...{{/key}} blocks (negative condition)
+ $pattern = '/{{\^' . preg_quote($key, '/') . '}}(.*?){{\/' . preg_quote($key, '/') . '}}/s';
+ $template = preg_replace($pattern, '', $template);
+ } else {
+ // Handle empty blocks
+ $pattern = '/{{#' . preg_quote($key, '/') . '}}.*?{{\/' . preg_quote($key, '/') . '}}/s';
+ $template = preg_replace($pattern, '', $template);
+
+ // Handle {{^key}}...{{/key}} blocks (show when empty)
+ $pattern = '/{{\^' . preg_quote($key, '/') . '}}(.*?){{\/' . preg_quote($key, '/') . '}}/s';
+ if (preg_match_all($pattern, $template, $matches)) {
+ foreach ($matches[1] as $match) {
+ $template = preg_replace('/{{\^' . preg_quote($key, '/') . '}}.*?{{\/' . preg_quote($key, '/') . '}}/s', $match, $template, 1);
+ }
+ }
+ }
+ }
+
+ // Handle variable replacements
+ foreach ($this->data as $key => $value) {
+ // Handle triple braces for unescaped HTML content
+ if (strpos($template, '{{{' . $key . '}}}') !== false) {
+ $template = str_replace('{{{' . $key . '}}}', $value, $template);
+ }
+ // Handle double braces for escaped content
+ elseif (strpos($template, '{{' . $key . '}}') !== false) {
+ $template = str_replace('{{' . $key . '}}', htmlspecialchars($value, ENT_QUOTES, 'UTF-8'), $template);
+ }
+ }
+ return $template;
+ }
+
+ /**
+ * Replace partial includes with data values
+ *
+ * @param array $matches Regex matches from preg_replace_callback
+ * @return string Replacement content
+ */
+ private function replacePartial($matches) {
+ $partialName = $matches[1];
+ return isset($this->data[$partialName]) ? $this->data[$partialName] : $matches[0];
+ }
+}
\ No newline at end of file
diff --git a/engine/core/config.php b/engine/core/config.php
index 76139ed..ea13b23 100644
--- a/engine/core/config.php
+++ b/engine/core/config.php
@@ -1,16 +1,41 @@
'CodePress',
- 'site_description' => 'A simple PHP CMS',
- 'base_url' => '/',
'content_dir' => __DIR__ . '/../../content',
'templates_dir' => __DIR__ . '/../templates',
- 'cache_dir' => __DIR__ . '/../../cache',
- 'default_page' => 'home',
- 'error_404' => '404',
- 'markdown_enabled' => true,
- 'php_enabled' => true,
- 'bootstrap_version' => '5.3.0',
- 'jquery_version' => '3.7.1'
-];
\ No newline at end of file
+ 'default_page' => 'auto',
+ 'homepage' => 'auto'
+];
+
+// Check for config.json in project root
+$projectRoot = __DIR__ . '/../../';
+$configJsonPath = $projectRoot . 'config.json';
+
+if (file_exists($configJsonPath)) {
+ $jsonContent = file_get_contents($configJsonPath);
+ $jsonConfig = json_decode($jsonContent, true);
+
+ if (json_last_error() === JSON_ERROR_NONE && is_array($jsonConfig)) {
+ // Merge JSON config with defaults, converting relative paths to absolute
+ $mergedConfig = array_merge($defaultConfig, $jsonConfig);
+
+ // Convert relative paths to absolute paths (inline function to avoid redeclaration)
+ $isAbsolutePath = function($path) {
+ return (strpos($path, '/') === 0) || (preg_match('/^[A-Za-z]:/', $path));
+ };
+
+ if (isset($mergedConfig['content_dir']) && !$isAbsolutePath($mergedConfig['content_dir'])) {
+ $mergedConfig['content_dir'] = $projectRoot . $mergedConfig['content_dir'];
+ }
+ if (isset($mergedConfig['templates_dir']) && !$isAbsolutePath($mergedConfig['templates_dir'])) {
+ $mergedConfig['templates_dir'] = $projectRoot . $mergedConfig['templates_dir'];
+ }
+
+ return $mergedConfig;
+ }
+}
+
+// Fallback to default config
+return $defaultConfig;
\ No newline at end of file
diff --git a/engine/core/index.php b/engine/core/index.php
index 4f6a101..0b828f2 100644
--- a/engine/core/index.php
+++ b/engine/core/index.php
@@ -1,476 +1,33 @@
config = $config;
- $this->buildMenu();
-
- if (isset($_GET['search'])) {
- $this->performSearch($_GET['search']);
- }
- }
-
- private function buildMenu() {
- $this->menu = $this->scanDirectory($this->config['content_dir'], '');
- }
-
- private function scanDirectory($dir, $prefix) {
- if (!is_dir($dir)) return [];
-
- $items = scandir($dir);
- sort($items);
- $result = [];
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $result[] = [
- 'type' => 'folder',
- 'title' => ucfirst($item),
- 'path' => $relativePath,
- 'children' => $this->scanDirectory($path, $relativePath)
- ];
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $result[] = [
- 'type' => 'file',
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath
- ];
- }
- }
-
- return $result;
- }
-
- private function performSearch($query) {
- $this->searchResults = [];
- $this->searchInDirectory($this->config['content_dir'], '', $query);
- }
-
- private function searchInDirectory($dir, $prefix, $query) {
- if (!is_dir($dir)) return;
-
- $items = scandir($dir);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $this->searchInDirectory($path, $relativePath, $query);
-} elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $content = file_get_contents($path);
- if (stripos($content, $query) !== false || stripos($item, $query) !== false) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $this->searchResults[] = [
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath,
- 'snippet' => $this->createSnippet($content, $query)
- ];
- }
- }
- }
- }
-
- private function createSnippet($content, $query) {
- $content = strip_tags($content);
- $pos = stripos($content, $query);
- if ($pos === false) return substr($content, 0, 100) . '...';
-
- $start = max(0, $pos - 50);
- $snippet = substr($content, $start, 150);
- return '...' . $snippet . '...';
- }
-
- public function getPage() {
- if (isset($_GET['search'])) {
- return $this->getSearchResults();
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- $filePath = $this->config['content_dir'] . '/' . $page;
- $actualFilePath = null;
-
- if (file_exists($filePath . '.md')) {
- $actualFilePath = $filePath . '.md';
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath . '.php')) {
- $actualFilePath = $filePath . '.php';
- $result = $this->parsePHP($actualFilePath);
- } elseif (file_exists($filePath . '.html')) {
- $actualFilePath = $filePath . '.html';
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath)) {
- $actualFilePath = $filePath;
- $extension = pathinfo($filePath, PATHINFO_EXTENSION);
- if ($extension === 'md') {
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif ($extension === 'php') {
- $result = $this->parsePHP($actualFilePath);
- } elseif ($extension === 'html') {
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- }
- }
-
- if (isset($result) && $actualFilePath) {
- $result['file_info'] = $this->getFileInfo($actualFilePath);
- return $result;
- }
-
- return $this->getError404();
- }
-
- private function getFileInfo($filePath) {
- if (!file_exists($filePath)) {
- return null;
- }
-
- $stats = stat($filePath);
- $created = date('d-m-Y H:i', $stats['ctime']);
- $modified = date('d-m-Y H:i', $stats['mtime']);
-
- return [
- 'created' => $created,
- 'modified' => $modified,
- 'size' => $this->formatFileSize($stats['size'])
- ];
- }
-
- private function formatFileSize($bytes) {
- $units = ['B', 'KB', 'MB', 'GB'];
- $bytes = max($bytes, 0);
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
- $pow = min($pow, count($units) - 1);
-
- $bytes /= pow(1024, $pow);
-
- return round($bytes, 2) . ' ' . $units[$pow];
- }
-
- private function getSearchResults() {
- $query = $_GET['search'];
- $content = 'Search Results for: "' . htmlspecialchars($query) . '" ';
-
- if (empty($this->searchResults)) {
- $content .= 'No results found.
';
- } else {
- $content .= 'Found ' . count($this->searchResults) . ' results:
';
- foreach ($this->searchResults as $result) {
- $content .= '';
- $content .= '
';
- $content .= '
';
- $content .= '
' . htmlspecialchars($result['path']) . '
';
- $content .= '
' . htmlspecialchars($result['snippet']) . '
';
- $content .= '
';
- }
- }
-
- return [
- 'title' => 'Search Results',
- 'content' => $content
- ];
- }
-
- private function parseMarkdown($content) {
- $lines = explode("\n", $content);
- $title = '';
- $body = '';
- $inBody = false;
-
- foreach ($lines as $line) {
- if (!$inBody && preg_match('/^#\s+(.+)$/', $line, $matches)) {
- $title = $matches[1];
- $inBody = true;
- } elseif ($inBody || trim($line) !== '') {
- $body .= $line . "\n";
- $inBody = true;
- }
- }
-
- $body = preg_replace('/### (.+)/', '$1 ', $body);
- $body = preg_replace('/## (.+)/', '$1 ', $body);
- $body = preg_replace('/# (.+)/', '$1 ', $body);
- $body = preg_replace('/\*\*(.+?)\*\*/', '$1 ', $body);
- $body = preg_replace('/\*(.+?)\*/', '$1 ', $body);
-
- // Auto-link page titles to existing content pages (before markdown link processing)
- $body = $this->autoLinkPageTitles($body);
-
- // Convert Markdown links to HTML links
- $body = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', ' $1 ', $body);
-
- // Convert relative internal links to CMS format
- $body = preg_replace('/href="\/blog\/([^"]+)"/', 'href="?page=blog/$1"', $body);
- $body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
-
- $body = preg_replace('/\n\n/', '', $body);
- $body = '
' . $body . '
';
- $body = preg_replace('/<\/p>/', '', $body);
- $body = preg_replace('/
()/', '$1', $body);
- $body = preg_replace('/(<\/h[1-6]>)<\/p>/', '$1', $body);
-
- return [
- 'title' => $title ?: 'Untitled',
- 'content' => $body
- ];
- }
-
-private function autoLinkPageTitles($content) {
- // Get all available pages with their titles
- $pages = $this->getAllPageTitles();
-
- foreach ($pages as $pagePath => $pageTitle) {
- // Create a pattern that matches the exact page title (case-insensitive)
- // Use word boundaries to avoid partial matches
- $pattern = '/\b' . preg_quote($pageTitle, '/') . '\b/i';
-
- // Replace with link, but avoid linking inside existing links, headings, or markdown
- $replacement = function($matches) use ($pageTitle, $pagePath) {
- $text = $matches[0];
-
- // Check if we're inside an existing link or markdown syntax
- if (preg_match('/\[.*?\]\(.*?\)/', $text) ||
- preg_match('/\[.*?\]:/', $text) ||
- preg_match('/]*>/', $text) ||
- preg_match('/href=/', $text)) {
- return $text; // Don't link existing links
- }
-
- return ' ' . $text . ' ';
- };
-
- $content = preg_replace_callback($pattern, $replacement, $content);
- }
-
- return $content;
- }
-
- private function getAllPageTitles() {
- $pages = [];
- $this->scanForPageTitles($this->config['content_dir'], '', $pages);
- return $pages;
- }
-
- private function scanForPageTitles($dir, $prefix, &$pages) {
- if (!is_dir($dir)) return;
-
- $items = scandir($dir);
- sort($items);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $this->scanForPageTitles($path, $relativePath, $pages);
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $title = $this->extractPageTitle($path);
- if ($title && !empty(trim($title))) {
- $pagePath = preg_replace('/\.[^.]+$/', '', $relativePath);
- $pages[$pagePath] = $title;
- }
- }
- }
- }
-
- private function extractPageTitle($filePath) {
- $content = file_get_contents($filePath);
- $extension = pathinfo($filePath, PATHINFO_EXTENSION);
-
- if ($extension === 'md') {
- // Extract first H1 from Markdown
- if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
- return trim($matches[1]);
- }
- } elseif ($extension === 'php') {
- // Extract title from PHP file
- if (preg_match('/\$title\s*=\s*["\']([^"\']+)["\']/', $content, $matches)) {
- return trim($matches[1]);
- }
- } elseif ($extension === 'html') {
- // Extract title from HTML file
- if (preg_match('/(.*?)<\/title>/i', $content, $matches)) {
- return trim(strip_tags($matches[1]));
- }
- if (preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) {
- return trim(strip_tags($matches[1]));
- }
- }
-
- return null;
- }
-
- private function parsePHP($filePath) {
- ob_start();
- $title = 'Untitled';
- include $filePath;
- $content = ob_get_clean();
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function parseHTML($content) {
- $title = 'Untitled';
-
- if (preg_match('/(.*?)<\/title>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- } elseif (preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- }
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function getError404() {
- return [
- 'title' => 'Page Not Found',
- 'content' => '404 - Page Not Found The page you are looking for does not exist.
'
- ];
- }
-
- public function getMenu() {
- return $this->menu;
- }
-
- public function render() {
- $page = $this->getPage();
- $menu = $this->getMenu();
- $breadcrumb = $this->getBreadcrumb();
-
- $template = file_get_contents($this->config['templates_dir'] . '/layout.html');
-
- $template = str_replace('{{site_title}}', $this->config['site_title'], $template);
- $template = str_replace('{{page_title}}', $page['title'], $template);
- $template = str_replace('{{content}}', $page['content'], $template);
- $template = str_replace('{{search_query}}', isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '', $template);
- $template = str_replace('{{breadcrumb}}', $breadcrumb, $template);
-
- // File info for footer
- $fileInfo = '';
- if (isset($page['file_info'])) {
- $fileInfo = ' Created: ' . htmlspecialchars($page['file_info']['created']) .
- ' | Modified: ' . htmlspecialchars($page['file_info']['modified']);
- }
- $template = str_replace('{{file_info}}', $fileInfo, $template);
-
- $menuHtml = $this->renderMenu($menu);
-
- $template = str_replace('{{menu}}', $menuHtml, $template);
-
- echo $template;
- }
-
- private function getBreadcrumb() {
- if (isset($_GET['search'])) {
- return 'Home Search ';
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- if ($page === $this->config['default_page']) {
- return 'Home ';
- }
-
- $parts = explode('/', $page);
- $breadcrumb = 'Home ';
-
- $path = '';
- foreach ($parts as $i => $part) {
- $path .= ($path ? '/' : '') . $part;
- $title = ucfirst($part);
-
- if ($i === count($parts) - 1) {
- $breadcrumb .= '' . $title . ' ';
- } else {
- $breadcrumb .= '' . $title . ' ';
- }
- }
-
- $breadcrumb .= ' ';
- return $breadcrumb;
- }
-
- private function renderMenu($items, $level = 0) {
- $html = '';
- foreach ($items as $item) {
- if ($item['type'] === 'folder') {
- $hasChildren = !empty($item['children']);
- $html .= '';
-
- if ($hasChildren) {
- $folderId = 'folder-' . str_replace('/', '-', $item['path']);
-
- // Check if this folder contains the active page
- $containsActive = $this->folderContainsActivePage($item['children']);
- $ariaExpanded = $containsActive ? 'true' : 'false';
- $collapseClass = $containsActive ? 'collapse show' : 'collapse';
-
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- $html .= '';
- $html .= $this->renderMenu($item['children'], $level + 1);
- $html .= ' ';
- } else {
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- }
-
- $html .= ' ';
- } else {
- $active = (isset($_GET['page']) && $_GET['page'] === $item['path']) ? 'active' : '';
- $html .= '';
- $html .= '' . htmlspecialchars($item['title']) . ' ';
- $html .= ' ';
- }
- }
- return $html;
- }
-
- private function folderContainsActivePage($children) {
- foreach ($children as $child) {
- if ($child['type'] === 'folder') {
- if (!empty($child['children']) && $this->folderContainsActivePage($child['children'])) {
- return true;
- }
- } else {
- if (isset($_GET['page']) && $_GET['page'] === $child['path']) {
- return true;
- }
- }
- }
- return false;
- }
-}
-
-$cms = new CodePressCMS($config);
-$cms->render();
\ No newline at end of file
+// Load main CMS class - handles content parsing, navigation, search, and page rendering
+require_once 'class/CodePressCMS.php';
\ No newline at end of file
diff --git a/public/router.php b/engine/router.php
similarity index 100%
rename from public/router.php
rename to engine/router.php
diff --git a/engine/templates/assets/footer.mustache b/engine/templates/assets/footer.mustache
new file mode 100644
index 0000000..44a7eac
--- /dev/null
+++ b/engine/templates/assets/footer.mustache
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ {{page_title}}
+ {{{file_info_block}}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/templates/assets/header.mustache b/engine/templates/assets/header.mustache
new file mode 100644
index 0000000..bc2a2f1
--- /dev/null
+++ b/engine/templates/assets/header.mustache
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/engine/templates/assets/navigation.mustache b/engine/templates/assets/navigation.mustache
new file mode 100644
index 0000000..f429f01
--- /dev/null
+++ b/engine/templates/assets/navigation.mustache
@@ -0,0 +1,16 @@
+
+
+
\ No newline at end of file
diff --git a/engine/templates/html_content.mustache b/engine/templates/html_content.mustache
new file mode 100644
index 0000000..6d6b9c0
--- /dev/null
+++ b/engine/templates/html_content.mustache
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/engine/templates/layout.html b/engine/templates/layout.html
deleted file mode 100644
index 80162ec..0000000
--- a/engine/templates/layout.html
+++ /dev/null
@@ -1,440 +0,0 @@
-
-
-
-
-
- {{page_title}} - {{site_title}}
-
-
-
-
-
-
-
-
-
-
-
-
-
{{site_title}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{breadcrumb}}
-
-
-
{{page_title}}
-
-
- {{content}}
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/engine/templates/layout.mustache b/engine/templates/layout.mustache
new file mode 100644
index 0000000..f6d9c4a
--- /dev/null
+++ b/engine/templates/layout.mustache
@@ -0,0 +1,59 @@
+
+
+
+
+
+ {{page_title}} - {{site_title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{>header}}
+
+ {{>navigation}}
+
+
+
+
+
+
+
+ {{>content_template}}
+
+
+
+
+
+ {{>footer}}
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/templates/markdown_content.mustache b/engine/templates/markdown_content.mustache
new file mode 100644
index 0000000..b37969a
--- /dev/null
+++ b/engine/templates/markdown_content.mustache
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/engine/templates/php_content.mustache b/engine/templates/php_content.mustache
new file mode 100644
index 0000000..613c083
--- /dev/null
+++ b/engine/templates/php_content.mustache
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/guide/en.md b/guide/en.md
new file mode 100644
index 0000000..9935362
--- /dev/null
+++ b/guide/en.md
@@ -0,0 +1,171 @@
+# CodePress CMS Guide
+
+## Welcome to CodePress CMS
+
+CodePress is a lightweight, file-based Content Management System built with PHP and Bootstrap.
+
+### Table of Contents
+
+1. [Getting Started](#getting-started)
+2. [Content Management](#content-management)
+3. [Templates](#templates)
+4. [Configuration](#configuration)
+
+---
+
+## Getting Started
+
+### Requirements
+- PHP 8.4+
+- Web server (Apache/Nginx)
+- Modern web browser
+
+### Installation
+1. Clone or download the CodePress files
+2. Upload to your web server
+3. Make sure the `content/` directory is writable
+4. Navigate to your website in the browser
+
+### Basic Configuration
+The most important settings are in `engine/core/config.php`:
+
+```php
+$config = [
+ 'site_title' => 'My Website',
+ 'default_page' => 'home',
+ 'content_dir' => __DIR__ . '/../../content',
+ 'templates_dir' => __DIR__ . '/../templates'
+];
+```
+
+---
+
+## Content Management
+
+### File Structure
+```
+content/
+├── home.md # Home page
+├── blog/
+│ ├── index.md # Blog overview
+│ ├── article-1.md # Blog article
+│ └── category/
+│ └── article.md # Article in category
+└── about-us/
+ └── info.md # About us page
+```
+
+### Content Types
+CodePress supports three content types:
+
+#### Markdown (`.md`)
+```markdown
+# Page Title
+
+This is the page content in **Markdown** format.
+
+## Subsection
+
+- List item 1
+- List item 2
+```
+
+#### PHP (`.php`)
+```php
+
+
+ This is dynamic content with PHP.
+```
+
+#### HTML (`.html`)
+```html
+HTML Page
+This is static HTML content.
+```
+
+### Automatic Linking
+CodePress automatically creates links to other pages when you mention page names in your content.
+
+---
+
+## Templates
+
+### Template Structure
+CodePress uses Mustache-compatible templates:
+
+- `layout.mustache` - Main template
+- `assets/header.mustache` - Header component
+- `assets/sidebar.mustache` - Sidebar navigation
+- `assets/footer.mustache` - Footer component
+
+### Template Variables
+Available variables in templates:
+- `{{site_title}}` - Website title
+- `{{page_title}}` - Current page title
+- `{{content}}` - Page content
+- `{{menu}}` - Navigation menu
+- `{{breadcrumb}}` - Breadcrumb navigation
+
+---
+
+## Configuration
+
+### Basic Settings
+Edit `engine/core/config.php` for your website:
+
+```php
+$config = [
+ 'site_title' => 'Your Website Name',
+ 'default_page' => 'home', // Default start page
+ 'content_dir' => __DIR__ . '/../../content',
+ 'templates_dir' => __DIR__ . '/../templates'
+];
+```
+
+### SEO Friendly URLs
+CodePress automatically generates clean URLs:
+- `home.md` → `/home`
+- `blog/article.md` → `/blog/article`
+
+### Search Functionality
+The built-in search function searches through:
+- File names
+- Content of Markdown/PHP/HTML files
+
+---
+
+## Tips and Tricks
+
+### Page Organization
+- Use subdirectories for categories
+- Give each directory an `index.md` for an overview page
+- Keep file names short and descriptive
+
+### Content Optimization
+- Use clear headings (H1, H2, H3)
+- Add descriptive meta information
+- Use internal links for better navigation
+
+### Security
+- Keep your CodePress installation updated
+- Restrict write permissions on the `content/` directory
+- Use HTTPS when possible
+
+---
+
+## Support
+
+### Troubleshooting
+- **Empty pages**: Check file permissions
+- **Template errors**: Verify template syntax
+- **404 errors**: Check file names and paths
+
+### More Information
+- Documentation: [CodePress GitHub](https://git.noorlander.info/E.Noorlander/CodePress.git)
+- Issues and feature requests: GitHub Issues
+
+---
+
+*This guide is part of CodePress CMS and is automatically displayed when no content is available.*
\ No newline at end of file
diff --git a/guide/nl.md b/guide/nl.md
new file mode 100644
index 0000000..eac5584
--- /dev/null
+++ b/guide/nl.md
@@ -0,0 +1,227 @@
+# CodePress CMS Handleiding
+
+## Overzicht
+
+CodePress CMS is een lichtgewicht, file-based content management systeem gebouwd met PHP. Het is ontworpen om eenvoudig te gebruiken, flexibel te zijn en zonder database te werken.
+
+## Functies
+
+### 🏠 **Navigatie**
+- **Tab-style navigatie** met Bootstrap styling
+- **Dropdown menus** voor mappen en sub-mappen
+- **Home knop** met icoon die linkt naar de ingestelde homepage
+- **Automatische menu generatie** op basis van content structuur
+- **Responsive design** voor mobiele apparaten
+
+### 📄 **Content Types**
+- **Markdown (.md)** - Met auto-linking tussen pagina's
+- **PHP (.php)** - Voor dynamische content en functionaliteit
+- **HTML (.html)** - Voor statische HTML pagina's
+- **Automatische template selectie** op basis van bestandstype
+
+### 🔍 **Zoekfunctionaliteit**
+- **Volledige tekst zoek** door alle content bestanden
+- **Resultaten met snippets** en highlighting
+- **Directe navigatie** naar gevonden pagina's
+- **SEO-vriendelijke** zoekresultaten
+
+### 🧭 **Configuratie**
+- **JSON configuratie** in project root (`config.json`)
+- **Dynamische homepage** instelling of automatische detectie
+- **SEO instellingen** (description, keywords)
+- **Author informatie** met links naar website en Git
+- **Thema configuratie** mogelijkheden
+
+### 🎨 **Layout & Design**
+- **Flexbox layout** voor moderne, responsive structuur
+- **Fixed header** met logo en zoekfunctie
+- **Breadcrumb navigatie** tussen header en content
+- **Fixed footer** met file info en links
+- **Bootstrap 5** styling en componenten
+- **Custom CSS** voor specifieke styling
+
+### 📱 **Responsive Features**
+- **Mobile-first** aanpak
+- **Hamburger menu** voor kleine schermen
+- **Touch-friendly** dropdowns en navigatie
+- **Adaptieve breedtes** voor verschillende schermgroottes
+
+## Installatie
+
+1. **Upload bestanden** naar webserver
+2. **Stel permissies in** voor webserver
+3. **Configureer** `config.json` indien nodig
+4. **Toegang** tot website via browser
+
+## Configuratie
+
+### Basis Configuratie (`config.json`)
+
+```json
+{
+ "site_title": "CodePress",
+ "content_dir": "content",
+ "templates_dir": "engine/templates",
+ "default_page": "auto",
+ "homepage": "welkom",
+ "author": {
+ "name": "Edwin Noorlander",
+ "website": "https://noorlander.info",
+ "git": "https://git.noorlander.info/E.Noorlander/CodePress.git"
+ },
+ "seo": {
+ "description": "CodePress CMS - Lightweight file-based content management system",
+ "keywords": "cms, php, content management, file-based"
+ }
+}
+```
+
+### Configuratie Opties
+
+- **`site_title`** - Naam van de website
+- **`content_dir`** - Map met content bestanden
+- **`templates_dir`** - Map met template bestanden
+- **`default_page`** - Standaard pagina (`"auto"` voor automatische detectie)
+- **`homepage`** - Specifieke homepage (`"auto"` voor automatische detectie)
+- **`author`** - Auteur informatie met links
+- **`seo`** - SEO instellingen
+
+## Content Structuur
+
+### Bestandsstructuur
+
+```
+content/
+├── map1/
+│ ├── submap1/
+│ │ ├── pagina1.md
+│ │ └── pagina2.php
+│ └── pagina3.html
+├── map2/
+│ └── pagina4.md
+├── homepage.md
+└── index.html
+```
+
+### Bestandsnamen
+
+- **Gebruik lowercase** bestandsnamen
+- **Geen spaties** - gebruik `-` of `_` als scheidingsteken
+- **Logische extensies** - `.md`, `.php`, `.html`
+- **Unieke namen** - geen duplicaten binnen dezelfde map
+
+## Templates
+
+### Template Structuur
+
+```
+engine/templates/
+├── layout.mustache - Hoofd layout template
+├── assets/
+│ ├── header.mustache - Header template
+│ ├── navigation.mustache - Navigatie template
+│ └── footer.mustache - Footer template
+├── markdown_content.mustache - Markdown content template
+├── php_content.mustache - PHP content template
+└── html_content.mustache - HTML content template
+```
+
+### Template Variabelen
+
+- **`{{site_title}}`** - Website titel
+- **`{{page_title}}`** - Pagina titel
+- **`{{content}}`** - Content (HTML)
+- **`{{menu}}`** - Navigatie menu
+- **`{{breadcrumb}}`** - Breadcrumb navigatie
+- **`{{search_query}}`** - Zoekopdracht
+- **`{{homepage}}`** - Homepage link
+- **`{{author_*}}`** - Auteur informatie
+- **`{{seo_*}}`** - SEO informatie
+
+## SEO Optimalisatie
+
+### Meta Tags
+
+De CMS voegt automatisch de volgende meta tags toe:
+
+```html
+
+
+
+
+
+
+
+```
+
+### Auto-linking
+
+De CMS linkt automatisch pagina titels naar hun content:
+
+- **Automatische detectie** van pagina titels in tekst
+- **Slimme links** met `title` attributen
+- **Geen dubbele links** voor dezelfde pagina
+- **SEO-vriendelijke** URL structuur
+
+## Veelgestelde Vragen
+
+### Hoe stel ik de homepage in?
+
+1. **Automatisch**: Laat de CMS het eerste bestand kiezen
+2. **Handmatig**: Stel `"homepage": "pagina-naam"` in `config.json`
+3. **Flexibel**: Werkt met elk bestandstype (md, php, html)
+
+### Hoe werkt de navigatie?
+
+- **Mappen** worden dropdown menus
+- **Bestanden** worden directe links
+- **Sub-mappen** worden geneste dropdowns
+- **Home knop** linkt altijd naar de homepage
+
+### Hoe voeg ik nieuwe content toe?
+
+1. **Upload** bestanden naar de `content/` map
+2. **Organiseer** in logische mappen
+3. **Gebruik** juiste bestandsnamen en extensies
+4. **Herlaad** de pagina om de navigatie te vernieuwen
+
+### Kan ik custom CSS gebruiken?
+
+Ja! Voeg custom CSS toe aan:
+- **`/public/assets/css/style.css`** - Voor algemene styling
+- **Template bestanden** - Voor specifieke componenten
+- **Inline styles** - In content bestanden indien nodig
+
+## Troubleshooting
+
+### Pagina niet gevonden (404)
+
+1. **Controleer** bestandsnaam en pad
+2. **Controleer** bestandsextensie (.md, .php, .html)
+3. **Controleer** permissies van bestanden
+4. **Controleer** `config.json` syntax
+
+### Template niet geladen
+
+1. **Controleer** template bestandsnamen
+2. **Controleer** template map permissies
+3. **Controleer** PHP error logs
+4. **Controleer** `templates_dir` configuratie
+
+### Navigatie niet bijgewerkt
+
+1. **Herlaad** de pagina
+2. **Controleer** content map structuur
+3. **Controleer** bestandsnamen (geen spaties)
+4. **Controleer** PHP cache indien aanwezig
+
+## Ondersteuning
+
+Voor technische ondersteuning:
+- **GitHub**: https://git.noorlander.info/E.Noorlander/CodePress.git
+- **Website**: https://noorlander.info
+- **Issues**: Rapporteer problemen via GitHub issues
+
+## Licentie
+
+CodePress CMS is open-source software. Controleer de licentie in de repository voor meer informatie.
\ No newline at end of file
diff --git a/index.php b/index.php
deleted file mode 100644
index 8f26139..0000000
--- a/index.php
+++ /dev/null
@@ -1,464 +0,0 @@
-config = $config;
- $this->buildMenu();
-
- if (isset($_GET['search'])) {
- $this->performSearch($_GET['search']);
- }
- }
-
- private function buildMenu() {
- $this->menu = $this->scanDirectory($this->config['content_dir'], '');
- }
-
- private function scanDirectory($dir, $prefix) {
- if (!is_dir($dir)) return [];
-
- $items = scandir($dir);
- sort($items);
- $result = [];
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $result[] = [
- 'type' => 'folder',
- 'title' => ucfirst($item),
- 'path' => $relativePath,
- 'children' => $this->scanDirectory($path, $relativePath)
- ];
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $result[] = [
- 'type' => 'file',
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath
- ];
- }
- }
-
- return $result;
- }
-
- private function performSearch($query) {
- $this->searchResults = [];
- $this->searchInDirectory($this->config['content_dir'], '', $query);
- }
-
- private function searchInDirectory($dir, $prefix, $query) {
- if (!is_dir($dir)) return;
-
- $items = scandir($dir);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $this->searchInDirectory($path, $relativePath, $query);
-} elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $content = file_get_contents($path);
- if (stripos($content, $query) !== false || stripos($item, $query) !== false) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $this->searchResults[] = [
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath,
- 'snippet' => $this->createSnippet($content, $query)
- ];
- }
- }
- }
- }
-
- private function createSnippet($content, $query) {
- $content = strip_tags($content);
- $pos = stripos($content, $query);
- if ($pos === false) return substr($content, 0, 100) . '...';
-
- $start = max(0, $pos - 50);
- $snippet = substr($content, $start, 150);
- return '...' . $snippet . '...';
- }
-
- public function getPage() {
- if (isset($_GET['search'])) {
- return $this->getSearchResults();
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- $filePath = $this->config['content_dir'] . '/' . $page;
- $actualFilePath = null;
-
- if (file_exists($filePath . '.md')) {
- $actualFilePath = $filePath . '.md';
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath . '.php')) {
- $actualFilePath = $filePath . '.php';
- $result = $this->parsePHP($actualFilePath);
- } elseif (file_exists($filePath . '.html')) {
- $actualFilePath = $filePath . '.html';
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath)) {
- $actualFilePath = $filePath;
- $extension = pathinfo($filePath, PATHINFO_EXTENSION);
- if ($extension === 'md') {
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif ($extension === 'php') {
- $result = $this->parsePHP($actualFilePath);
- } elseif ($extension === 'html') {
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- }
- }
-
- if (isset($result) && $actualFilePath) {
- $result['file_info'] = $this->getFileInfo($actualFilePath);
- return $result;
- }
-
- return $this->getError404();
- }
-
- private function getFileInfo($filePath) {
- if (!file_exists($filePath)) {
- return null;
- }
-
- $stats = stat($filePath);
- $created = date('d-m-Y H:i', $stats['ctime']);
- $modified = date('d-m-Y H:i', $stats['mtime']);
-
- return [
- 'created' => $created,
- 'modified' => $modified,
- 'size' => $this->formatFileSize($stats['size'])
- ];
- }
-
- private function formatFileSize($bytes) {
- $units = ['B', 'KB', 'MB', 'GB'];
- $bytes = max($bytes, 0);
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
- $pow = min($pow, count($units) - 1);
-
- $bytes /= pow(1024, $pow);
-
- return round($bytes, 2) . ' ' . $units[$pow];
- }
-
- private function getSearchResults() {
- $query = $_GET['search'];
- $content = 'Search Results for: "' . htmlspecialchars($query) . '" ';
-
- if (empty($this->searchResults)) {
- $content .= 'No results found.
';
- } else {
- $content .= 'Found ' . count($this->searchResults) . ' results:
';
- foreach ($this->searchResults as $result) {
- $content .= '';
- $content .= '
';
- $content .= '
';
- $content .= '
' . htmlspecialchars($result['path']) . '
';
- $content .= '
' . htmlspecialchars($result['snippet']) . '
';
- $content .= '
';
- }
- }
-
- return [
- 'title' => 'Search Results',
- 'content' => $content
- ];
- }
-
- private function parseMarkdown($content) {
- $lines = explode("\n", $content);
- $title = '';
- $body = '';
- $inBody = false;
-
- foreach ($lines as $line) {
- if (!$inBody && preg_match('/^#\s+(.+)$/', $line, $matches)) {
- $title = $matches[1];
- $inBody = true;
- } elseif ($inBody || trim($line) !== '') {
- $body .= $line . "\n";
- $inBody = true;
- }
- }
-
- $body = preg_replace('/### (.+)/', '$1 ', $body);
- $body = preg_replace('/## (.+)/', '$1 ', $body);
- $body = preg_replace('/# (.+)/', '$1 ', $body);
- $body = preg_replace('/\*\*(.+?)\*\*/', '$1 ', $body);
- $body = preg_replace('/\*(.+?)\*/', '$1 ', $body);
-
- // Auto-link page titles to existing content pages (before markdown link processing)
- $body = $this->autoLinkPageTitles($body);
-
- // Convert Markdown links to HTML links
- $body = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '$1 ', $body);
-
- // Convert relative internal links to CMS format
- $body = preg_replace('/href="\/blog\/([^"]+)"/', 'href="?page=blog/$1"', $body);
- $body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
-
- $body = preg_replace('/\n\n/', '
', $body);
- $body = '
' . $body . '
';
- $body = preg_replace('/<\/p>/', '', $body);
- $body = preg_replace('/
()/', '$1', $body);
- $body = preg_replace('/(<\/h[1-6]>)<\/p>/', '$1', $body);
-
- return [
- 'title' => $title ?: 'Untitled',
- 'content' => $body
- ];
- }
-
-private function autoLinkPageTitles($content) {
- // Get all available pages with their titles
- $pages = $this->getAllPageTitles();
-
- foreach ($pages as $pagePath => $pageTitle) {
- // Create a pattern that matches the exact page title (case-insensitive)
- // Use word boundaries to avoid partial matches
- $pattern = '/\b' . preg_quote($pageTitle, '/') . '\b/i';
-
- // Replace with link, but avoid linking inside existing links, headings, or markdown
- $replacement = function($matches) use ($pageTitle, $pagePath) {
- $text = $matches[0];
-
- // Check if we're inside an existing link or markdown syntax
- if (preg_match('/\[.*?\]\(.*?\)/', $text) ||
- preg_match('/\[.*?\]:/', $text) ||
- preg_match('/]*>/', $text) ||
- preg_match('/href=/', $text)) {
- return $text; // Don't link existing links
- }
-
- return ' ' . $text . ' ';
- };
-
- $content = preg_replace_callback($pattern, $replacement, $content);
- }
-
- return $content;
- }
-
- return '' . $matches[0] . ' ';
- };
-
- $content = preg_replace_callback($pattern, $replacement, $content);
- }
-
- return $content;
- }
-
- private function getAllPageTitles() {
- $pages = [];
- $this->scanForPageTitles($this->config['content_dir'], '', $pages);
- return $pages;
- }
-
- private function scanForPageTitles($dir, $prefix, &$pages) {
- if (!is_dir($dir)) return;
-
- $items = scandir($dir);
- sort($items);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $this->scanForPageTitles($path, $relativePath, $pages);
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $title = $this->extractPageTitle($path);
- if ($title && !empty(trim($title))) {
- $pagePath = preg_replace('/\.[^.]+$/', '', $relativePath);
- $pages[$pagePath] = $title;
- }
- }
- }
- }
-
- private function extractPageTitle($filePath) {
- $content = file_get_contents($filePath);
- $extension = pathinfo($filePath, PATHINFO_EXTENSION);
-
- if ($extension === 'md') {
- // Extract first H1 from Markdown
- if (preg_match('/^#\s+(.+)$/m', $content, $matches)) {
- return trim($matches[1]);
- }
- } elseif ($extension === 'php') {
- // Extract title from PHP file
- if (preg_match('/\$title\s*=\s*["\']([^"\']+)["\']/', $content, $matches)) {
- return trim($matches[1]);
- }
- } elseif ($extension === 'html') {
- // Extract title from HTML file
- if (preg_match('/(.*?)<\/title>/i', $content, $matches)) {
- return trim(strip_tags($matches[1]));
- }
- if (preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) {
- return trim(strip_tags($matches[1]));
- }
- }
-
- return null;
- }
-
- private function parsePHP($filePath) {
- ob_start();
- $title = 'Untitled';
- include $filePath;
- $content = ob_get_clean();
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function parseHTML($content) {
- $title = 'Untitled';
-
- if (preg_match('/(.*?)<\/title>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- } elseif (preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- }
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function getError404() {
- return [
- 'title' => 'Page Not Found',
- 'content' => '404 - Page Not Found The page you are looking for does not exist.
'
- ];
- }
-
- public function getMenu() {
- return $this->menu;
- }
-
- public function render() {
- $page = $this->getPage();
- $menu = $this->getMenu();
- $breadcrumb = $this->getBreadcrumb();
-
- $template = file_get_contents($this->config['templates_dir'] . '/layout.html');
-
- $template = str_replace('{{site_title}}', $this->config['site_title'], $template);
- $template = str_replace('{{page_title}}', $page['title'], $template);
- $template = str_replace('{{content}}', $page['content'], $template);
- $template = str_replace('{{search_query}}', isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '', $template);
- $template = str_replace('{{breadcrumb}}', $breadcrumb, $template);
-
- // File info for footer
- $fileInfo = '';
- if (isset($page['file_info'])) {
- $fileInfo = ' Created: ' . htmlspecialchars($page['file_info']['created']) .
- ' | Modified: ' . htmlspecialchars($page['file_info']['modified']);
- }
- $template = str_replace('{{file_info}}', $fileInfo, $template);
-
- $menuHtml = $this->renderMenu($menu);
-
- $template = str_replace('{{menu}}', $menuHtml, $template);
-
- echo $template;
- }
-
- private function getBreadcrumb() {
- if (isset($_GET['search'])) {
- return 'Home Search ';
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- if ($page === $this->config['default_page']) {
- return 'Home ';
- }
-
- $parts = explode('/', $page);
- $breadcrumb = 'Home ';
-
- $path = '';
- foreach ($parts as $i => $part) {
- $path .= ($path ? '/' : '') . $part;
- $title = ucfirst($part);
-
- if ($i === count($parts) - 1) {
- $breadcrumb .= '' . $title . ' ';
- } else {
- $breadcrumb .= '' . $title . ' ';
- }
- }
-
- $breadcrumb .= ' ';
- return $breadcrumb;
- }
-
- private function renderMenu($items, $level = 0) {
- $html = '';
- foreach ($items as $item) {
- if ($item['type'] === 'folder') {
- $hasChildren = !empty($item['children']);
- $html .= '';
-
- if ($hasChildren) {
- $folderId = 'folder-' . str_replace('/', '-', $item['path']);
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- $html .= '';
- $html .= $this->renderMenu($item['children'], $level + 1);
- $html .= ' ';
- } else {
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- }
-
- $html .= ' ';
- } else {
- $active = (isset($_GET['page']) && $_GET['page'] === $item['path']) ? 'active' : '';
- $html .= '';
- $html .= '' . htmlspecialchars($item['title']) . ' ';
- $html .= ' ';
- }
- }
- return $html;
- }
-}
-
-$cms = new CodePressCMS($config);
-$cms->render();
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2fe4b9e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "codepress",
+ "version": "1.0.0",
+ "description": "A lightweight, file-based Content Management System built with PHP and Bootstrap.",
+ "main": "index.js",
+ "scripts": {
+ "build:css": "npx sass --load-path=node_modules src/scss/main.scss public/assets/css/style.css --style=compressed",
+ "watch:css": "npx sass --load-path=node_modules --watch src/scss/main.scss public/assets/css/style.css",
+ "build": "npm run build:css",
+ "clean": "rm -rf node_modules package-lock.json",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://git.noorlander.info/E.Noorlander/CodePress.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "type": "commonjs",
+ "dependencies": {
+ "bootstrap": "^5.3.8",
+ "sass": "^1.94.2"
+ }
+}
diff --git a/engine/assets/css/bootstrap-icons.css b/public/assets/css/bootstrap-icons.css
similarity index 100%
rename from engine/assets/css/bootstrap-icons.css
rename to public/assets/css/bootstrap-icons.css
diff --git a/engine/assets/css/bootstrap.min.css b/public/assets/css/bootstrap.min.css
similarity index 100%
rename from engine/assets/css/bootstrap.min.css
rename to public/assets/css/bootstrap.min.css
diff --git a/engine/assets/css/bootstrap.min.css.map b/public/assets/css/bootstrap.min.css.map
similarity index 100%
rename from engine/assets/css/bootstrap.min.css.map
rename to public/assets/css/bootstrap.min.css.map
diff --git a/engine/assets/css/fonts/bootstrap-icons.woff b/public/assets/css/fonts/bootstrap-icons.woff
similarity index 100%
rename from engine/assets/css/fonts/bootstrap-icons.woff
rename to public/assets/css/fonts/bootstrap-icons.woff
diff --git a/engine/assets/css/fonts/bootstrap-icons.woff2 b/public/assets/css/fonts/bootstrap-icons.woff2
similarity index 100%
rename from engine/assets/css/fonts/bootstrap-icons.woff2
rename to public/assets/css/fonts/bootstrap-icons.woff2
diff --git a/public/assets/css/style.css b/public/assets/css/style.css
new file mode 100644
index 0000000..f90a228
--- /dev/null
+++ b/public/assets/css/style.css
@@ -0,0 +1,5 @@
+/*!
+ * Bootstrap v5.3.8 (https://getbootstrap.com/)
+ * Copyright 2011-2025 The Bootstrap Authors
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */:root,[data-bs-theme=light]{--bs-blue: #0d6efd;--bs-indigo: #6610f2;--bs-purple: #6f42c1;--bs-pink: #d63384;--bs-red: #dc3545;--bs-orange: #fd7e14;--bs-yellow: #ffc107;--bs-green: #198754;--bs-teal: #20c997;--bs-cyan: #0dcaf0;--bs-black: #000;--bs-white: #fff;--bs-gray: #6c757d;--bs-gray-dark: #343a40;--bs-gray-100: #f8f9fa;--bs-gray-200: #e9ecef;--bs-gray-300: #dee2e6;--bs-gray-400: #ced4da;--bs-gray-500: #adb5bd;--bs-gray-600: #6c757d;--bs-gray-700: #495057;--bs-gray-800: #343a40;--bs-gray-900: #212529;--bs-primary: #0d6efd;--bs-secondary: #6c757d;--bs-success: #198754;--bs-info: #0dcaf0;--bs-warning: #ffc107;--bs-danger: #dc3545;--bs-light: #f8f9fa;--bs-dark: #212529;--bs-primary-rgb: 13, 110, 253;--bs-secondary-rgb: 108, 117, 125;--bs-success-rgb: 25, 135, 84;--bs-info-rgb: 13, 202, 240;--bs-warning-rgb: 255, 193, 7;--bs-danger-rgb: 220, 53, 69;--bs-light-rgb: 248, 249, 250;--bs-dark-rgb: 33, 37, 41;--bs-primary-text-emphasis: rgb(5.2, 44, 101.2);--bs-secondary-text-emphasis: rgb(43.2, 46.8, 50);--bs-success-text-emphasis: rgb(10, 54, 33.6);--bs-info-text-emphasis: rgb(5.2, 80.8, 96);--bs-warning-text-emphasis: rgb(102, 77.2, 2.8);--bs-danger-text-emphasis: rgb(88, 21.2, 27.6);--bs-light-text-emphasis: #495057;--bs-dark-text-emphasis: #495057;--bs-primary-bg-subtle: rgb(206.6, 226, 254.6);--bs-secondary-bg-subtle: rgb(225.6, 227.4, 229);--bs-success-bg-subtle: rgb(209, 231, 220.8);--bs-info-bg-subtle: rgb(206.6, 244.4, 252);--bs-warning-bg-subtle: rgb(255, 242.6, 205.4);--bs-danger-bg-subtle: rgb(248, 214.6, 217.8);--bs-light-bg-subtle: rgb(251.5, 252, 252.5);--bs-dark-bg-subtle: #ced4da;--bs-primary-border-subtle: rgb(158.2, 197, 254.2);--bs-secondary-border-subtle: rgb(196.2, 199.8, 203);--bs-success-border-subtle: rgb(163, 207, 186.6);--bs-info-border-subtle: rgb(158.2, 233.8, 249);--bs-warning-border-subtle: rgb(255, 230.2, 155.8);--bs-danger-border-subtle: rgb(241, 174.2, 180.6);--bs-light-border-subtle: #e9ecef;--bs-dark-border-subtle: #adb5bd;--bs-white-rgb: 255, 255, 255;--bs-black-rgb: 0, 0, 0;--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family: var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight: 400;--bs-body-line-height: 1.5;--bs-body-color: #212529;--bs-body-color-rgb: 33, 37, 41;--bs-body-bg: #fff;--bs-body-bg-rgb: 255, 255, 255;--bs-emphasis-color: #000;--bs-emphasis-color-rgb: 0, 0, 0;--bs-secondary-color: rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb: 33, 37, 41;--bs-secondary-bg: #e9ecef;--bs-secondary-bg-rgb: 233, 236, 239;--bs-tertiary-color: rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb: 33, 37, 41;--bs-tertiary-bg: #f8f9fa;--bs-tertiary-bg-rgb: 248, 249, 250;--bs-heading-color: inherit;--bs-link-color: #0d6efd;--bs-link-color-rgb: 13, 110, 253;--bs-link-decoration: underline;--bs-link-hover-color: rgb(10.4, 88, 202.4);--bs-link-hover-color-rgb: 10, 88, 202;--bs-code-color: #d63384;--bs-highlight-color: #212529;--bs-highlight-bg: rgb(255, 242.6, 205.4);--bs-border-width: 1px;--bs-border-style: solid;--bs-border-color: #dee2e6;--bs-border-color-translucent: rgba(0, 0, 0, 0.175);--bs-border-radius: 0.375rem;--bs-border-radius-sm: 0.25rem;--bs-border-radius-lg: 0.5rem;--bs-border-radius-xl: 1rem;--bs-border-radius-xxl: 2rem;--bs-border-radius-2xl: var(--bs-border-radius-xxl);--bs-border-radius-pill: 50rem;--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width: 0.25rem;--bs-focus-ring-opacity: 0.25;--bs-focus-ring-color: rgba(13, 110, 253, 0.25);--bs-form-valid-color: #198754;--bs-form-valid-border-color: #198754;--bs-form-invalid-color: #dc3545;--bs-form-invalid-border-color: #dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color: #dee2e6;--bs-body-color-rgb: 222, 226, 230;--bs-body-bg: #212529;--bs-body-bg-rgb: 33, 37, 41;--bs-emphasis-color: #fff;--bs-emphasis-color-rgb: 255, 255, 255;--bs-secondary-color: rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb: 222, 226, 230;--bs-secondary-bg: #343a40;--bs-secondary-bg-rgb: 52, 58, 64;--bs-tertiary-color: rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb: 222, 226, 230;--bs-tertiary-bg: rgb(42.5, 47.5, 52.5);--bs-tertiary-bg-rgb: 43, 48, 53;--bs-primary-text-emphasis: rgb(109.8, 168, 253.8);--bs-secondary-text-emphasis: rgb(166.8, 172.2, 177);--bs-success-text-emphasis: rgb(117, 183, 152.4);--bs-info-text-emphasis: rgb(109.8, 223.2, 246);--bs-warning-text-emphasis: rgb(255, 217.8, 106.2);--bs-danger-text-emphasis: rgb(234, 133.8, 143.4);--bs-light-text-emphasis: #f8f9fa;--bs-dark-text-emphasis: #dee2e6;--bs-primary-bg-subtle: rgb(2.6, 22, 50.6);--bs-secondary-bg-subtle: rgb(21.6, 23.4, 25);--bs-success-bg-subtle: rgb(5, 27, 16.8);--bs-info-bg-subtle: rgb(2.6, 40.4, 48);--bs-warning-bg-subtle: rgb(51, 38.6, 1.4);--bs-danger-bg-subtle: rgb(44, 10.6, 13.8);--bs-light-bg-subtle: #343a40;--bs-dark-bg-subtle: #1a1d20;--bs-primary-border-subtle: rgb(7.8, 66, 151.8);--bs-secondary-border-subtle: rgb(64.8, 70.2, 75);--bs-success-border-subtle: rgb(15, 81, 50.4);--bs-info-border-subtle: rgb(7.8, 121.2, 144);--bs-warning-border-subtle: rgb(153, 115.8, 4.2);--bs-danger-border-subtle: rgb(132, 31.8, 41.4);--bs-light-border-subtle: #495057;--bs-dark-border-subtle: #343a40;--bs-heading-color: inherit;--bs-link-color: rgb(109.8, 168, 253.8);--bs-link-hover-color: rgb(138.84, 185.4, 254.04);--bs-link-color-rgb: 110, 168, 254;--bs-link-hover-color-rgb: 139, 185, 254;--bs-code-color: rgb(230.4, 132.6, 181.2);--bs-highlight-color: #dee2e6;--bs-highlight-bg: rgb(102, 77.2, 2.8);--bs-border-color: #495057;--bs-border-color-translucent: rgba(255, 255, 255, 0.15);--bs-form-valid-color: rgb(117, 183, 152.4);--bs-form-valid-border-color: rgb(117, 183, 152.4);--bs-form-invalid-color: rgb(234, 133.8, 143.4);--bs-form-invalid-border-color: rgb(234, 133.8, 143.4)}*,*::before,*::after{box-sizing:border-box}@media(prefers-reduced-motion: no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0)}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}h1,.h1{font-size:calc(1.375rem + 1.5vw)}@media(min-width: 1200px){h1,.h1{font-size:2.5rem}}h2,.h2{font-size:calc(1.325rem + 0.9vw)}@media(min-width: 1200px){h2,.h2{font-size:2rem}}h3,.h3{font-size:calc(1.3rem + 0.6vw)}@media(min-width: 1200px){h3,.h3{font-size:1.75rem}}h4,.h4{font-size:calc(1.275rem + 0.3vw)}@media(min-width: 1200px){h4,.h4{font-size:1.5rem}}h5,.h5{font-size:1.25rem}h6,.h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}ol,ul,dl{margin-top:0;margin-bottom:1rem}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small,.small{font-size:0.875em}mark,.mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:0.75em;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}a{color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));text-decoration:underline}a:hover{--bs-link-color-rgb: var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:0.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:0.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:0.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none !important}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:calc(1.275rem + 0.3vw)}@media(min-width: 1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none !important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-weight:300;line-height:1.2;font-size:calc(1.625rem + 4.5vw)}@media(min-width: 1200px){.display-1{font-size:5rem}}.display-2{font-weight:300;line-height:1.2;font-size:calc(1.575rem + 3.9vw)}@media(min-width: 1200px){.display-2{font-size:4.5rem}}.display-3{font-weight:300;line-height:1.2;font-size:calc(1.525rem + 3.3vw)}@media(min-width: 1200px){.display-3{font-size:4rem}}.display-4{font-weight:300;line-height:1.2;font-size:calc(1.475rem + 2.7vw)}@media(min-width: 1200px){.display-4{font-size:3.5rem}}.display-5{font-weight:300;line-height:1.2;font-size:calc(1.425rem + 2.1vw)}@media(min-width: 1200px){.display-5{font-size:3rem}}.display-6{font-weight:300;line-height:1.2;font-size:calc(1.375rem + 1.5vw)}@media(min-width: 1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:0.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:0.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:0.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-right:auto;margin-left:auto}@media(min-width: 576px){.container-sm,.container{max-width:540px}}@media(min-width: 768px){.container-md,.container-sm,.container{max-width:720px}}@media(min-width: 992px){.container-lg,.container-md,.container-sm,.container{max-width:960px}}@media(min-width: 1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px}}@media(min-width: 1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px}}:root{--bs-breakpoint-xs: 0;--bs-breakpoint-sm: 576px;--bs-breakpoint-md: 768px;--bs-breakpoint-lg: 992px;--bs-breakpoint-xl: 1200px;--bs-breakpoint-xxl: 1400px}.row{--bs-gutter-x: 1.5rem;--bs-gutter-y: 0;display:flex;flex-wrap:wrap;margin-top:calc(-1*var(--bs-gutter-y));margin-right:calc(-0.5*var(--bs-gutter-x));margin-left:calc(-0.5*var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x: 0}.g-0,.gy-0{--bs-gutter-y: 0}.g-1,.gx-1{--bs-gutter-x: 0.25rem}.g-1,.gy-1{--bs-gutter-y: 0.25rem}.g-2,.gx-2{--bs-gutter-x: 0.5rem}.g-2,.gy-2{--bs-gutter-y: 0.5rem}.g-3,.gx-3{--bs-gutter-x: 1rem}.g-3,.gy-3{--bs-gutter-y: 1rem}.g-4,.gx-4{--bs-gutter-x: 1.5rem}.g-4,.gy-4{--bs-gutter-y: 1.5rem}.g-5,.gx-5{--bs-gutter-x: 3rem}.g-5,.gy-5{--bs-gutter-y: 3rem}@media(min-width: 576px){.col-sm{flex:1 0 0}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x: 0}.g-sm-0,.gy-sm-0{--bs-gutter-y: 0}.g-sm-1,.gx-sm-1{--bs-gutter-x: 0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y: 0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x: 0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y: 0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x: 1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y: 1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x: 1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y: 1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x: 3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y: 3rem}}@media(min-width: 768px){.col-md{flex:1 0 0}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x: 0}.g-md-0,.gy-md-0{--bs-gutter-y: 0}.g-md-1,.gx-md-1{--bs-gutter-x: 0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y: 0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x: 0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y: 0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x: 1rem}.g-md-3,.gy-md-3{--bs-gutter-y: 1rem}.g-md-4,.gx-md-4{--bs-gutter-x: 1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y: 1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x: 3rem}.g-md-5,.gy-md-5{--bs-gutter-y: 3rem}}@media(min-width: 992px){.col-lg{flex:1 0 0}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x: 0}.g-lg-0,.gy-lg-0{--bs-gutter-y: 0}.g-lg-1,.gx-lg-1{--bs-gutter-x: 0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y: 0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x: 0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y: 0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x: 1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y: 1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x: 1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y: 1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x: 3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y: 3rem}}@media(min-width: 1200px){.col-xl{flex:1 0 0}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x: 0}.g-xl-0,.gy-xl-0{--bs-gutter-y: 0}.g-xl-1,.gx-xl-1{--bs-gutter-x: 0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y: 0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x: 0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y: 0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x: 1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y: 1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x: 1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y: 1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x: 3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y: 3rem}}@media(min-width: 1400px){.col-xxl{flex:1 0 0}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x: 0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y: 0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x: 0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y: 0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x: 0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y: 0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x: 1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y: 1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x: 1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y: 1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x: 3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y: 3rem}}.table{--bs-table-color-type: initial;--bs-table-bg-type: initial;--bs-table-color-state: initial;--bs-table-bg-state: initial;--bs-table-color: var(--bs-emphasis-color);--bs-table-bg: var(--bs-body-bg);--bs-table-border-color: var(--bs-border-color);--bs-table-accent-bg: transparent;--bs-table-striped-color: var(--bs-emphasis-color);--bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color: var(--bs-emphasis-color);--bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color: var(--bs-emphasis-color);--bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width)*2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-color-type: var(--bs-table-striped-color);--bs-table-bg-type: var(--bs-table-striped-bg)}.table-active{--bs-table-color-state: var(--bs-table-active-color);--bs-table-bg-state: var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state: var(--bs-table-hover-color);--bs-table-bg-state: var(--bs-table-hover-bg)}.table-primary{--bs-table-color: #000;--bs-table-bg: rgb(206.6, 226, 254.6);--bs-table-border-color: rgb(165.28, 180.8, 203.68);--bs-table-striped-bg: rgb(196.27, 214.7, 241.87);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(185.94, 203.4, 229.14);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(191.105, 209.05, 235.505);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color: #000;--bs-table-bg: rgb(225.6, 227.4, 229);--bs-table-border-color: rgb(180.48, 181.92, 183.2);--bs-table-striped-bg: rgb(214.32, 216.03, 217.55);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(203.04, 204.66, 206.1);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(208.68, 210.345, 211.825);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color: #000;--bs-table-bg: rgb(209, 231, 220.8);--bs-table-border-color: rgb(167.2, 184.8, 176.64);--bs-table-striped-bg: rgb(198.55, 219.45, 209.76);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(188.1, 207.9, 198.72);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(193.325, 213.675, 204.24);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color: #000;--bs-table-bg: rgb(206.6, 244.4, 252);--bs-table-border-color: rgb(165.28, 195.52, 201.6);--bs-table-striped-bg: rgb(196.27, 232.18, 239.4);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(185.94, 219.96, 226.8);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(191.105, 226.07, 233.1);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color: #000;--bs-table-bg: rgb(255, 242.6, 205.4);--bs-table-border-color: rgb(204, 194.08, 164.32);--bs-table-striped-bg: rgb(242.25, 230.47, 195.13);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(229.5, 218.34, 184.86);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(235.875, 224.405, 189.995);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color: #000;--bs-table-bg: rgb(248, 214.6, 217.8);--bs-table-border-color: rgb(198.4, 171.68, 174.24);--bs-table-striped-bg: rgb(235.6, 203.87, 206.91);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(223.2, 193.14, 196.02);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(229.4, 198.505, 201.465);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color: #000;--bs-table-bg: #f8f9fa;--bs-table-border-color: rgb(198.4, 199.2, 200);--bs-table-striped-bg: rgb(235.6, 236.55, 237.5);--bs-table-striped-color: #000;--bs-table-active-bg: rgb(223.2, 224.1, 225);--bs-table-active-color: #000;--bs-table-hover-bg: rgb(229.4, 230.325, 231.25);--bs-table-hover-color: #000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color: #fff;--bs-table-bg: #212529;--bs-table-border-color: rgb(77.4, 80.6, 83.8);--bs-table-striped-bg: rgb(44.1, 47.9, 51.7);--bs-table-striped-color: #fff;--bs-table-active-bg: rgb(55.2, 58.8, 62.4);--bs-table-active-color: #fff;--bs-table-hover-bg: rgb(49.65, 53.35, 57.05);--bs-table-hover-color: #fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width: 575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width: 1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(0.375rem + var(--bs-border-width));padding-bottom:calc(0.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(0.5rem + var(--bs-border-width));padding-bottom:calc(0.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(0.25rem + var(--bs-border-width));padding-bottom:calc(0.25rem + var(--bs-border-width));font-size:0.875rem}.form-text{margin-top:.25rem;font-size:0.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:rgb(134,182.5,254);outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-0.375rem -0.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:rgba(0,0,0,0);border:solid rgba(0,0,0,0);border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:0.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-0.25rem -0.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-0.5rem -1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0 !important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0 !important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon, none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-select{transition:none}}.form-select:focus{border-color:rgb(134,182.5,254);outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:rgba(0,0,0,0);text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:0.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg: var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:rgb(134,182.5,254);outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgb%28134, 182.5, 254%29'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;appearance:none;background-color:rgba(0,0,0,0)}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-0.25rem;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-webkit-slider-thumb{transition:none}}.form-range::-webkit-slider-thumb:active{background-color:rgb(182.4,211.5,254.4)}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:var(--bs-secondary-bg);border-color:rgba(0,0,0,0);border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.form-range::-moz-range-thumb{transition:none}}.form-range::-moz-range-thumb:active{background-color:rgb(182.4,211.5,254.4)}.form-range::-moz-range-track{width:100%;height:.5rem;color:rgba(0,0,0,0);cursor:pointer;background-color:var(--bs-secondary-bg);border-color:rgba(0,0,0,0);border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem .75rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb), 0.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid rgba(0,0,0,0);transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion: reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:rgba(0,0,0,0)}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:.75rem}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(0.85) translateY(-0.5rem) translateX(0.15rem)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:var(--bs-secondary-bg)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:0.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1*var(--bs-border-width));border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb), 0.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb), 0.25)}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--bs-form-valid-border-color)}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--bs-form-valid-color)}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb), 0.25)}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:0.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:0.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + 0.75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(0.375em + 0.1875rem) center;background-size:calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb), 0.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 0.75rem);background-position:top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem)}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(0.75em + 0.375rem) calc(0.75em + 0.375rem)}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb), 0.25)}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + 0.75rem))}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--bs-form-invalid-border-color)}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--bs-form-invalid-color)}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb), 0.25)}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4}.btn{--bs-btn-padding-x: 0.75rem;--bs-btn-padding-y: 0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight: 400;--bs-btn-line-height: 1.5;--bs-btn-color: var(--bs-body-color);--bs-btn-bg: transparent;--bs-btn-border-width: var(--bs-border-width);--bs-btn-border-color: transparent;--bs-btn-border-radius: var(--bs-border-radius);--bs-btn-hover-border-color: transparent;--bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity: 0.65;--bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color: #fff;--bs-btn-bg: #0d6efd;--bs-btn-border-color: #0d6efd;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(11.05, 93.5, 215.05);--bs-btn-hover-border-color: rgb(10.4, 88, 202.4);--bs-btn-focus-shadow-rgb: 49, 132, 253;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(10.4, 88, 202.4);--bs-btn-active-border-color: rgb(9.75, 82.5, 189.75);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #0d6efd;--bs-btn-disabled-border-color: #0d6efd}.btn-secondary{--bs-btn-color: #fff;--bs-btn-bg: #6c757d;--bs-btn-border-color: #6c757d;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(91.8, 99.45, 106.25);--bs-btn-hover-border-color: rgb(86.4, 93.6, 100);--bs-btn-focus-shadow-rgb: 130, 138, 145;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(86.4, 93.6, 100);--bs-btn-active-border-color: rgb(81, 87.75, 93.75);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #6c757d;--bs-btn-disabled-border-color: #6c757d}.btn-success{--bs-btn-color: #fff;--bs-btn-bg: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(21.25, 114.75, 71.4);--bs-btn-hover-border-color: rgb(20, 108, 67.2);--bs-btn-focus-shadow-rgb: 60, 153, 110;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(20, 108, 67.2);--bs-btn-active-border-color: rgb(18.75, 101.25, 63);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #198754;--bs-btn-disabled-border-color: #198754}.btn-info{--bs-btn-color: #000;--bs-btn-bg: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(49.3, 209.95, 242.25);--bs-btn-hover-border-color: rgb(37.2, 207.3, 241.5);--bs-btn-focus-shadow-rgb: 11, 172, 204;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(61.4, 212.6, 243);--bs-btn-active-border-color: rgb(37.2, 207.3, 241.5);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #0dcaf0;--bs-btn-disabled-border-color: #0dcaf0}.btn-warning{--bs-btn-color: #000;--bs-btn-bg: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(255, 202.3, 44.2);--bs-btn-hover-border-color: rgb(255, 199.2, 31.8);--bs-btn-focus-shadow-rgb: 217, 164, 6;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(255, 205.4, 56.6);--bs-btn-active-border-color: rgb(255, 199.2, 31.8);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #ffc107;--bs-btn-disabled-border-color: #ffc107}.btn-danger{--bs-btn-color: #fff;--bs-btn-bg: #dc3545;--bs-btn-border-color: #dc3545;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(187, 45.05, 58.65);--bs-btn-hover-border-color: rgb(176, 42.4, 55.2);--bs-btn-focus-shadow-rgb: 225, 83, 97;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(176, 42.4, 55.2);--bs-btn-active-border-color: rgb(165, 39.75, 51.75);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #dc3545;--bs-btn-disabled-border-color: #dc3545}.btn-light{--bs-btn-color: #000;--bs-btn-bg: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: rgb(210.8, 211.65, 212.5);--bs-btn-hover-border-color: rgb(198.4, 199.2, 200);--bs-btn-focus-shadow-rgb: 211, 212, 213;--bs-btn-active-color: #000;--bs-btn-active-bg: rgb(198.4, 199.2, 200);--bs-btn-active-border-color: rgb(186, 186.75, 187.5);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #000;--bs-btn-disabled-bg: #f8f9fa;--bs-btn-disabled-border-color: #f8f9fa}.btn-dark{--bs-btn-color: #fff;--bs-btn-bg: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: rgb(66.3, 69.7, 73.1);--bs-btn-hover-border-color: rgb(55.2, 58.8, 62.4);--bs-btn-focus-shadow-rgb: 66, 70, 73;--bs-btn-active-color: #fff;--bs-btn-active-bg: rgb(77.4, 80.6, 83.8);--bs-btn-active-border-color: rgb(55.2, 58.8, 62.4);--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #fff;--bs-btn-disabled-bg: #212529;--bs-btn-disabled-border-color: #212529}.btn-outline-primary{--bs-btn-color: #0d6efd;--bs-btn-border-color: #0d6efd;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #0d6efd;--bs-btn-hover-border-color: #0d6efd;--bs-btn-focus-shadow-rgb: 13, 110, 253;--bs-btn-active-color: #fff;--bs-btn-active-bg: #0d6efd;--bs-btn-active-border-color: #0d6efd;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #0d6efd;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #0d6efd;--bs-gradient: none}.btn-outline-secondary{--bs-btn-color: #6c757d;--bs-btn-border-color: #6c757d;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #6c757d;--bs-btn-hover-border-color: #6c757d;--bs-btn-focus-shadow-rgb: 108, 117, 125;--bs-btn-active-color: #fff;--bs-btn-active-bg: #6c757d;--bs-btn-active-border-color: #6c757d;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #6c757d;--bs-gradient: none}.btn-outline-success{--bs-btn-color: #198754;--bs-btn-border-color: #198754;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #198754;--bs-btn-hover-border-color: #198754;--bs-btn-focus-shadow-rgb: 25, 135, 84;--bs-btn-active-color: #fff;--bs-btn-active-bg: #198754;--bs-btn-active-border-color: #198754;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #198754;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #198754;--bs-gradient: none}.btn-outline-info{--bs-btn-color: #0dcaf0;--bs-btn-border-color: #0dcaf0;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #0dcaf0;--bs-btn-hover-border-color: #0dcaf0;--bs-btn-focus-shadow-rgb: 13, 202, 240;--bs-btn-active-color: #000;--bs-btn-active-bg: #0dcaf0;--bs-btn-active-border-color: #0dcaf0;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #0dcaf0;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #0dcaf0;--bs-gradient: none}.btn-outline-warning{--bs-btn-color: #ffc107;--bs-btn-border-color: #ffc107;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #ffc107;--bs-btn-hover-border-color: #ffc107;--bs-btn-focus-shadow-rgb: 255, 193, 7;--bs-btn-active-color: #000;--bs-btn-active-bg: #ffc107;--bs-btn-active-border-color: #ffc107;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #ffc107;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #ffc107;--bs-gradient: none}.btn-outline-danger{--bs-btn-color: #dc3545;--bs-btn-border-color: #dc3545;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #dc3545;--bs-btn-hover-border-color: #dc3545;--bs-btn-focus-shadow-rgb: 220, 53, 69;--bs-btn-active-color: #fff;--bs-btn-active-bg: #dc3545;--bs-btn-active-border-color: #dc3545;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #dc3545;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #dc3545;--bs-gradient: none}.btn-outline-light{--bs-btn-color: #f8f9fa;--bs-btn-border-color: #f8f9fa;--bs-btn-hover-color: #000;--bs-btn-hover-bg: #f8f9fa;--bs-btn-hover-border-color: #f8f9fa;--bs-btn-focus-shadow-rgb: 248, 249, 250;--bs-btn-active-color: #000;--bs-btn-active-bg: #f8f9fa;--bs-btn-active-border-color: #f8f9fa;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #f8f9fa;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #f8f9fa;--bs-gradient: none}.btn-outline-dark{--bs-btn-color: #212529;--bs-btn-border-color: #212529;--bs-btn-hover-color: #fff;--bs-btn-hover-bg: #212529;--bs-btn-hover-border-color: #212529;--bs-btn-focus-shadow-rgb: 33, 37, 41;--bs-btn-active-color: #fff;--bs-btn-active-bg: #212529;--bs-btn-active-border-color: #212529;--bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color: #212529;--bs-btn-disabled-bg: transparent;--bs-btn-disabled-border-color: #212529;--bs-gradient: none}.btn-link{--bs-btn-font-weight: 400;--bs-btn-color: var(--bs-link-color);--bs-btn-bg: transparent;--bs-btn-border-color: transparent;--bs-btn-hover-color: var(--bs-link-hover-color);--bs-btn-hover-border-color: transparent;--bs-btn-active-color: var(--bs-link-hover-color);--bs-btn-active-border-color: transparent;--bs-btn-disabled-color: #6c757d;--bs-btn-disabled-border-color: transparent;--bs-btn-box-shadow: 0 0 0 #000;--bs-btn-focus-shadow-rgb: 49, 132, 253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-lg,.btn-group-lg>.btn{--bs-btn-padding-y: 0.5rem;--bs-btn-padding-x: 1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius: var(--bs-border-radius-lg)}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y: 0.25rem;--bs-btn-padding-x: 0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius: var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion: reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion: reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion: reduce){.collapsing.collapse-horizontal{transition:none}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid rgba(0,0,0,0);border-bottom:0;border-left:.3em solid rgba(0,0,0,0)}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex: 1000;--bs-dropdown-min-width: 10rem;--bs-dropdown-padding-x: 0;--bs-dropdown-padding-y: 0.5rem;--bs-dropdown-spacer: 0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color: var(--bs-body-color);--bs-dropdown-bg: var(--bs-body-bg);--bs-dropdown-border-color: var(--bs-border-color-translucent);--bs-dropdown-border-radius: var(--bs-border-radius);--bs-dropdown-border-width: var(--bs-border-width);--bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg: var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y: 0.5rem;--bs-dropdown-box-shadow: var(--bs-box-shadow);--bs-dropdown-link-color: var(--bs-body-color);--bs-dropdown-link-hover-color: var(--bs-body-color);--bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #0d6efd;--bs-dropdown-link-disabled-color: var(--bs-tertiary-color);--bs-dropdown-item-padding-x: 1rem;--bs-dropdown-item-padding-y: 0.25rem;--bs-dropdown-header-color: #6c757d;--bs-dropdown-header-padding-x: 1rem;--bs-dropdown-header-padding-y: 0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position: start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position: end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width: 576px){.dropdown-menu-sm-start{--bs-position: start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position: end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 768px){.dropdown-menu-md-start{--bs-position: start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position: end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 992px){.dropdown-menu-lg-start{--bs-position: start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position: end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 1200px){.dropdown-menu-xl-start{--bs-position: start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position: end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width: 1400px){.dropdown-menu-xxl-start{--bs-position: start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position: end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid rgba(0,0,0,0);border-bottom:.3em solid;border-left:.3em solid rgba(0,0,0,0)}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid rgba(0,0,0,0);border-right:0;border-bottom:.3em solid rgba(0,0,0,0);border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid rgba(0,0,0,0);border-right:.3em solid;border-bottom:.3em solid rgba(0,0,0,0)}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:rgba(0,0,0,0);border:0;border-radius:var(--bs-dropdown-item-border-radius, 0)}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:rgba(0,0,0,0)}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:0.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color: #dee2e6;--bs-dropdown-bg: #343a40;--bs-dropdown-border-color: var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color: #dee2e6;--bs-dropdown-link-hover-color: #fff;--bs-dropdown-divider-bg: var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color: #fff;--bs-dropdown-link-active-bg: #0d6efd;--bs-dropdown-link-disabled-color: #adb5bd;--bs-dropdown-header-color: #adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(-1*var(--bs-border-width))}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(-1*var(--bs-border-width))}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x: 1rem;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-link-color);--bs-nav-link-hover-color: var(--bs-link-hover-color);--bs-nav-link-disabled-color: var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:none;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion: reduce){.nav-link{transition:none}}.nav-link:hover,.nav-link:focus{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width: var(--bs-border-width);--bs-nav-tabs-border-color: var(--bs-border-color);--bs-nav-tabs-border-radius: var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color: var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg: var(--bs-body-bg);--bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1*var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid rgba(0,0,0,0);border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1*var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius: var(--bs-border-radius);--bs-nav-pills-link-active-color: #fff;--bs-nav-pills-link-active-bg: #0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap: 1rem;--bs-nav-underline-border-width: 0.125rem;--bs-nav-underline-link-active-color: var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid rgba(0,0,0,0)}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;text-align:center}.nav-justified>.nav-link,.nav-justified .nav-item{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x: 0;--bs-navbar-padding-y: 0.5rem;--bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y: 0.3125rem;--bs-navbar-brand-margin-end: 1rem;--bs-navbar-brand-font-size: 1.25rem;--bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x: 0.5rem;--bs-navbar-toggler-padding-y: 0.25rem;--bs-navbar-toggler-padding-x: 0.75rem;--bs-navbar-toggler-font-size: 1.25rem;--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius: var(--bs-border-radius);--bs-navbar-toggler-focus-width: 0.25rem;--bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x: 0;--bs-nav-link-padding-y: 0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color: var(--bs-navbar-color);--bs-nav-link-hover-color: var(--bs-navbar-hover-color);--bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:rgba(0,0,0,0);border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media(prefers-reduced-motion: reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height, 75vh);overflow-y:auto}@media(min-width: 576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width: 1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex !important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto !important;height:auto !important;visibility:visible !important;background-color:rgba(0,0,0,0) !important;border:0 !important;transform:none !important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color: rgba(255, 255, 255, 0.55);--bs-navbar-hover-color: rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);--bs-navbar-active-color: #fff;--bs-navbar-brand-color: #fff;--bs-navbar-brand-hover-color: #fff;--bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y: 1rem;--bs-card-spacer-x: 1rem;--bs-card-title-spacer-y: 0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width: var(--bs-border-width);--bs-card-border-color: var(--bs-border-color-translucent);--bs-card-border-radius: var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y: 0.5rem;--bs-card-cap-padding-x: 1rem;--bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg: var(--bs-body-bg);--bs-card-img-overlay-padding: 1rem;--bs-card-group-margin: 0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-0.5*var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-bottom:calc(-1*var(--bs-card-cap-padding-y));margin-left:calc(-0.5*var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-0.5*var(--bs-card-cap-padding-x));margin-left:calc(-0.5*var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-top,.card-img-bottom{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media(min-width: 576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-img-top,.card-group>.card:not(:last-child)>.card-header{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-img-bottom,.card-group>.card:not(:last-child)>.card-footer{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-img-top,.card-group>.card:not(:first-child)>.card-header{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-img-bottom,.card-group>.card:not(:first-child)>.card-footer{border-bottom-left-radius:0}}.accordion{--bs-accordion-color: var(--bs-body-color);--bs-accordion-bg: var(--bs-body-bg);--bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color: var(--bs-border-color);--bs-accordion-border-width: var(--bs-border-width);--bs-accordion-border-radius: var(--bs-border-radius);--bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x: 1.25rem;--bs-accordion-btn-padding-y: 1rem;--bs-accordion-btn-color: var(--bs-body-color);--bs-accordion-btn-bg: var(--bs-accordion-bg);--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width: 1.25rem;--bs-accordion-btn-icon-transform: rotate(-180deg);--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='rgb%285.2, 44, 101.2%29' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x: 1.25rem;--bs-accordion-body-padding-y: 1rem;--bs-accordion-active-color: var(--bs-primary-text-emphasis);--bs-accordion-active-bg: var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion: reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1*var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion: reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='rgb%28109.8, 168, 253.8%29'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='rgb%28109.8, 168, 253.8%29'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x: 0;--bs-breadcrumb-padding-y: 0;--bs-breadcrumb-margin-bottom: 1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color: var(--bs-secondary-color);--bs-breadcrumb-item-padding-x: 0.5rem;--bs-breadcrumb-item-active-color: var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x: 0.75rem;--bs-pagination-padding-y: 0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color: var(--bs-link-color);--bs-pagination-bg: var(--bs-body-bg);--bs-pagination-border-width: var(--bs-border-width);--bs-pagination-border-color: var(--bs-border-color);--bs-pagination-border-radius: var(--bs-border-radius);--bs-pagination-hover-color: var(--bs-link-hover-color);--bs-pagination-hover-bg: var(--bs-tertiary-bg);--bs-pagination-hover-border-color: var(--bs-border-color);--bs-pagination-focus-color: var(--bs-link-hover-color);--bs-pagination-focus-bg: var(--bs-secondary-bg);--bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color: #fff;--bs-pagination-active-bg: #0d6efd;--bs-pagination-active-border-color: #0d6efd;--bs-pagination-disabled-color: var(--bs-secondary-color);--bs-pagination-disabled-bg: var(--bs-secondary-bg);--bs-pagination-disabled-border-color: var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion: reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(-1*var(--bs-border-width))}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x: 1.5rem;--bs-pagination-padding-y: 0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius: var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x: 0.5rem;--bs-pagination-padding-y: 0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius: var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x: 0.65em;--bs-badge-padding-y: 0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight: 700;--bs-badge-color: #fff;--bs-badge-border-radius: var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg: transparent;--bs-alert-padding-x: 1rem;--bs-alert-padding-y: 1rem;--bs-alert-margin-bottom: 1rem;--bs-alert-color: inherit;--bs-alert-border-color: transparent;--bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius: var(--bs-border-radius);--bs-alert-link-color: inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color: var(--bs-primary-text-emphasis);--bs-alert-bg: var(--bs-primary-bg-subtle);--bs-alert-border-color: var(--bs-primary-border-subtle);--bs-alert-link-color: var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color: var(--bs-secondary-text-emphasis);--bs-alert-bg: var(--bs-secondary-bg-subtle);--bs-alert-border-color: var(--bs-secondary-border-subtle);--bs-alert-link-color: var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color: var(--bs-success-text-emphasis);--bs-alert-bg: var(--bs-success-bg-subtle);--bs-alert-border-color: var(--bs-success-border-subtle);--bs-alert-link-color: var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color: var(--bs-info-text-emphasis);--bs-alert-bg: var(--bs-info-bg-subtle);--bs-alert-border-color: var(--bs-info-border-subtle);--bs-alert-link-color: var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color: var(--bs-warning-text-emphasis);--bs-alert-bg: var(--bs-warning-bg-subtle);--bs-alert-border-color: var(--bs-warning-border-subtle);--bs-alert-link-color: var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color: var(--bs-danger-text-emphasis);--bs-alert-bg: var(--bs-danger-bg-subtle);--bs-alert-border-color: var(--bs-danger-border-subtle);--bs-alert-link-color: var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color: var(--bs-light-text-emphasis);--bs-alert-bg: var(--bs-light-bg-subtle);--bs-alert-border-color: var(--bs-light-border-subtle);--bs-alert-link-color: var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color: var(--bs-dark-text-emphasis);--bs-alert-bg: var(--bs-dark-bg-subtle);--bs-alert-border-color: var(--bs-dark-border-subtle);--bs-alert-link-color: var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:var(--bs-progress-height)}}.progress,.progress-stacked{--bs-progress-height: 1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg: var(--bs-secondary-bg);--bs-progress-border-radius: var(--bs-border-radius);--bs-progress-box-shadow: var(--bs-box-shadow-inset);--bs-progress-bar-color: #fff;--bs-progress-bar-bg: #0d6efd;--bs-progress-bar-transition: width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media(prefers-reduced-motion: reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion: reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color: var(--bs-body-color);--bs-list-group-bg: var(--bs-body-bg);--bs-list-group-border-color: var(--bs-border-color);--bs-list-group-border-width: var(--bs-border-width);--bs-list-group-border-radius: var(--bs-border-radius);--bs-list-group-item-padding-x: 1rem;--bs-list-group-item-padding-y: 0.5rem;--bs-list-group-action-color: var(--bs-secondary-color);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-tertiary-bg);--bs-list-group-action-active-color: var(--bs-body-color);--bs-list-group-action-active-bg: var(--bs-secondary-bg);--bs-list-group-disabled-color: var(--bs-secondary-color);--bs-list-group-disabled-bg: var(--bs-body-bg);--bs-list-group-active-color: #fff;--bs-list-group-active-bg: #0d6efd;--bs-list-group-active-border-color: #0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1*var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):hover,.list-group-item-action:not(.active):focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width: 576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width: 1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color: var(--bs-primary-text-emphasis);--bs-list-group-bg: var(--bs-primary-bg-subtle);--bs-list-group-border-color: var(--bs-primary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-primary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-primary-border-subtle);--bs-list-group-active-color: var(--bs-primary-bg-subtle);--bs-list-group-active-bg: var(--bs-primary-text-emphasis);--bs-list-group-active-border-color: var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color: var(--bs-secondary-text-emphasis);--bs-list-group-bg: var(--bs-secondary-bg-subtle);--bs-list-group-border-color: var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-secondary-border-subtle);--bs-list-group-active-color: var(--bs-secondary-bg-subtle);--bs-list-group-active-bg: var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color: var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color: var(--bs-success-text-emphasis);--bs-list-group-bg: var(--bs-success-bg-subtle);--bs-list-group-border-color: var(--bs-success-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-success-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-success-border-subtle);--bs-list-group-active-color: var(--bs-success-bg-subtle);--bs-list-group-active-bg: var(--bs-success-text-emphasis);--bs-list-group-active-border-color: var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color: var(--bs-info-text-emphasis);--bs-list-group-bg: var(--bs-info-bg-subtle);--bs-list-group-border-color: var(--bs-info-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-info-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-info-border-subtle);--bs-list-group-active-color: var(--bs-info-bg-subtle);--bs-list-group-active-bg: var(--bs-info-text-emphasis);--bs-list-group-active-border-color: var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color: var(--bs-warning-text-emphasis);--bs-list-group-bg: var(--bs-warning-bg-subtle);--bs-list-group-border-color: var(--bs-warning-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-warning-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-warning-border-subtle);--bs-list-group-active-color: var(--bs-warning-bg-subtle);--bs-list-group-active-bg: var(--bs-warning-text-emphasis);--bs-list-group-active-border-color: var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color: var(--bs-danger-text-emphasis);--bs-list-group-bg: var(--bs-danger-bg-subtle);--bs-list-group-border-color: var(--bs-danger-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-danger-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-danger-border-subtle);--bs-list-group-active-color: var(--bs-danger-bg-subtle);--bs-list-group-active-bg: var(--bs-danger-text-emphasis);--bs-list-group-active-border-color: var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color: var(--bs-light-text-emphasis);--bs-list-group-bg: var(--bs-light-bg-subtle);--bs-list-group-border-color: var(--bs-light-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-light-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-light-border-subtle);--bs-list-group-active-color: var(--bs-light-bg-subtle);--bs-list-group-active-bg: var(--bs-light-text-emphasis);--bs-list-group-active-border-color: var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color: var(--bs-dark-text-emphasis);--bs-list-group-bg: var(--bs-dark-bg-subtle);--bs-list-group-border-color: var(--bs-dark-border-subtle);--bs-list-group-action-hover-color: var(--bs-emphasis-color);--bs-list-group-action-hover-bg: var(--bs-dark-border-subtle);--bs-list-group-action-active-color: var(--bs-emphasis-color);--bs-list-group-action-active-bg: var(--bs-dark-border-subtle);--bs-list-group-active-color: var(--bs-dark-bg-subtle);--bs-list-group-active-bg: var(--bs-dark-text-emphasis);--bs-list-group-active-border-color: var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color: #000;--bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--bs-btn-close-opacity: 0.5;--bs-btn-close-hover-opacity: 0.75;--bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity: 1;--bs-btn-close-disabled-opacity: 0.25;box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:rgba(0,0,0,0) var(--bs-btn-close-bg) center/1em auto no-repeat;filter:var(--bs-btn-close-filter);border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{--bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%)}:root,[data-bs-theme=light]{--bs-btn-close-filter: }[data-bs-theme=dark]{--bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex: 1090;--bs-toast-padding-x: 0.75rem;--bs-toast-padding-y: 0.5rem;--bs-toast-spacing: 1.5rem;--bs-toast-max-width: 350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width: var(--bs-border-width);--bs-toast-border-color: var(--bs-border-color-translucent);--bs-toast-border-radius: var(--bs-border-radius);--bs-toast-box-shadow: var(--bs-box-shadow);--bs-toast-header-color: var(--bs-secondary-color);--bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color: var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex: 1090;position:absolute;z-index:var(--bs-toast-zindex);width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-0.5*var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex: 1055;--bs-modal-width: 500px;--bs-modal-padding: 1rem;--bs-modal-margin: 0.5rem;--bs-modal-color: var(--bs-body-color);--bs-modal-bg: var(--bs-body-bg);--bs-modal-border-color: var(--bs-border-color-translucent);--bs-modal-border-width: var(--bs-border-width);--bs-modal-border-radius: var(--bs-border-radius-lg);--bs-modal-box-shadow: var(--bs-box-shadow-sm);--bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x: 1rem;--bs-modal-header-padding-y: 1rem;--bs-modal-header-padding: 1rem 1rem;--bs-modal-header-border-color: var(--bs-border-color);--bs-modal-header-border-width: var(--bs-border-width);--bs-modal-title-line-height: 1.5;--bs-modal-footer-gap: 0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color: var(--bs-border-color);--bs-modal-footer-border-width: var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translate(0, -50px);transition:transform .3s ease-out}@media(prefers-reduced-motion: reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin)*2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin)*2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex: 1050;--bs-backdrop-bg: #000;--bs-backdrop-opacity: 0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y)*.5) calc(var(--bs-modal-header-padding-x)*.5);margin-top:calc(-0.5*var(--bs-modal-header-padding-y));margin-right:calc(-0.5*var(--bs-modal-header-padding-x));margin-bottom:calc(-0.5*var(--bs-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap)*.5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap)*.5)}@media(min-width: 576px){.modal{--bs-modal-margin: 1.75rem;--bs-modal-box-shadow: var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width: 300px}}@media(min-width: 992px){.modal-lg,.modal-xl{--bs-modal-width: 800px}}@media(min-width: 1200px){.modal-xl{--bs-modal-width: 1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width: 575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width: 767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width: 991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width: 1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width: 1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex: 1080;--bs-tooltip-max-width: 200px;--bs-tooltip-padding-x: 0.5rem;--bs-tooltip-padding-y: 0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color: var(--bs-body-bg);--bs-tooltip-bg: var(--bs-emphasis-color);--bs-tooltip-border-radius: var(--bs-border-radius);--bs-tooltip-opacity: 0.9;--bs-tooltip-arrow-width: 0.8rem;--bs-tooltip-arrow-height: 0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:rgba(0,0,0,0);border-style:solid}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1*var(--bs-tooltip-arrow-height))}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) 0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex: 1070;--bs-popover-max-width: 276px;--bs-popover-font-size:0.875rem;--bs-popover-bg: var(--bs-body-bg);--bs-popover-border-width: var(--bs-border-width);--bs-popover-border-color: var(--bs-border-color-translucent);--bs-popover-border-radius: var(--bs-border-radius-lg);--bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow: var(--bs-box-shadow);--bs-popover-header-padding-x: 1rem;--bs-popover-header-padding-y: 0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: inherit;--bs-popover-header-bg: var(--bs-secondary-bg);--bs-popover-body-padding-x: 1rem;--bs-popover-body-padding-y: 1rem;--bs-popover-body-color: var(--bs-body-color);--bs-popover-arrow-width: 1rem;--bs-popover-arrow-height: 0.5rem;--bs-popover-arrow-border: var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:rgba(0,0,0,0);border-style:solid;border-width:0}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-0.5*var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) 0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height)}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion: reduce){.carousel-item{transition:none}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%)}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion: reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;filter:var(--bs-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion: reduce){.carousel-control-prev,.carousel-control-next{transition:none}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")*/}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e")*/}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--bs-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid rgba(0,0,0,0);border-bottom:10px solid rgba(0,0,0,0);opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion: reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--bs-carousel-caption-color);text-align:center}.carousel-dark{--bs-carousel-indicator-active-bg: #000;--bs-carousel-caption-color: #000;--bs-carousel-control-icon-filter: invert(1) grayscale(100)}:root,[data-bs-theme=light]{--bs-carousel-indicator-active-bg: #fff;--bs-carousel-caption-color: #fff;--bs-carousel-control-icon-filter: }[data-bs-theme=dark]{--bs-carousel-indicator-active-bg: #000;--bs-carousel-caption-color: #000;--bs-carousel-control-icon-filter: invert(1) grayscale(100)}.spinner-grow,.spinner-border{display:inline-block;flex-shrink:0;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg) /* rtl:ignore */}}.spinner-border{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-border-width: 0.25em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:rgba(0,0,0,0)}.spinner-border-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem;--bs-spinner-border-width: 0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width: 2rem;--bs-spinner-height: 2rem;--bs-spinner-vertical-align: -0.125em;--bs-spinner-animation-speed: 0.75s;--bs-spinner-animation-name: spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width: 1rem;--bs-spinner-height: 1rem}@media(prefers-reduced-motion: reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed: 1.5s}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex: 1045;--bs-offcanvas-width: 400px;--bs-offcanvas-height: 30vh;--bs-offcanvas-padding-x: 1rem;--bs-offcanvas-padding-y: 1rem;--bs-offcanvas-color: var(--bs-body-color);--bs-offcanvas-bg: var(--bs-body-bg);--bs-offcanvas-border-width: var(--bs-border-width);--bs-offcanvas-border-color: var(--bs-border-color-translucent);--bs-offcanvas-box-shadow: var(--bs-box-shadow-sm);--bs-offcanvas-transition: transform 0.3s ease-in-out;--bs-offcanvas-title-line-height: 1.5}@media(max-width: 575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 575.98px)and (prefers-reduced-motion: reduce){.offcanvas-sm{transition:none}}@media(max-width: 575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none}.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible}}@media(min-width: 576px){.offcanvas-sm{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 767.98px)and (prefers-reduced-motion: reduce){.offcanvas-md{transition:none}}@media(max-width: 767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none}.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible}}@media(min-width: 768px){.offcanvas-md{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 991.98px)and (prefers-reduced-motion: reduce){.offcanvas-lg{transition:none}}@media(max-width: 991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none}.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible}}@media(min-width: 992px){.offcanvas-lg{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1199.98px)and (prefers-reduced-motion: reduce){.offcanvas-xl{transition:none}}@media(max-width: 1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none}.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible}}@media(min-width: 1200px){.offcanvas-xl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}@media(max-width: 1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width: 1399.98px)and (prefers-reduced-motion: reduce){.offcanvas-xxl{transition:none}}@media(max-width: 1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none}.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible}}@media(min-width: 1400px){.offcanvas-xxl{--bs-offcanvas-height: auto;--bs-offcanvas-border-width: 0;background-color:rgba(0,0,0,0) !important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:rgba(0,0,0,0) !important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion: reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y)*.5) calc(var(--bs-offcanvas-padding-x)*.5);margin-top:calc(-0.5*var(--bs-offcanvas-padding-y));margin-right:calc(-0.5*var(--bs-offcanvas-padding-x));margin-bottom:calc(-0.5*var(--bs-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{mask-image:linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff !important;background-color:RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-secondary{color:#fff !important;background-color:RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-success{color:#fff !important;background-color:RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-info{color:#000 !important;background-color:RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-warning{color:#000 !important;background-color:RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-danger{color:#fff !important;background-color:RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-light{color:#000 !important;background-color:RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important}.text-bg-dark{color:#fff !important;background-color:RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important}.link-primary{color:RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-primary:hover,.link-primary:focus{color:RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important}.link-secondary{color:RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-secondary:hover,.link-secondary:focus{color:RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important}.link-success{color:RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-success:hover,.link-success:focus{color:RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important}.link-info{color:RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-info:hover,.link-info:focus{color:RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important}.link-warning{color:RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-warning:hover,.link-warning:focus{color:RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important}.link-danger{color:RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-danger:hover,.link-danger:focus{color:RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important}.link-light{color:RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-light:hover,.link-light:focus{color:RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important}.link-dark{color:RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-dark:hover,.link-dark:focus{color:RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5));text-underline-offset:.25em;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion: reduce){.icon-link>.bi{transition:none}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--bs-icon-link-transform, translate3d(0.25em, 0, 0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio: 100%}.ratio-4x3{--bs-aspect-ratio: 75%}.ratio-16x9{--bs-aspect-ratio: 56.25%}.ratio-21x9{--bs-aspect-ratio: 42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:sticky;top:0;z-index:1020}.sticky-bottom{position:sticky;bottom:0;z-index:1020}@media(min-width: 576px){.sticky-sm-top{position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 768px){.sticky-md-top{position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 992px){.sticky-lg-top{position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020}}@media(min-width: 1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px !important;height:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0, 0, 0, 0) !important;white-space:nowrap !important;border:0 !important}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute !important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden !important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline !important}.align-top{vertical-align:top !important}.align-middle{vertical-align:middle !important}.align-bottom{vertical-align:bottom !important}.align-text-bottom{vertical-align:text-bottom !important}.align-text-top{vertical-align:text-top !important}.float-start{float:left !important}.float-end{float:right !important}.float-none{float:none !important}.object-fit-contain{object-fit:contain !important}.object-fit-cover{object-fit:cover !important}.object-fit-fill{object-fit:fill !important}.object-fit-scale{object-fit:scale-down !important}.object-fit-none{object-fit:none !important}.opacity-0{opacity:0 !important}.opacity-25{opacity:.25 !important}.opacity-50{opacity:.5 !important}.opacity-75{opacity:.75 !important}.opacity-100{opacity:1 !important}.overflow-auto{overflow:auto !important}.overflow-hidden{overflow:hidden !important}.overflow-visible{overflow:visible !important}.overflow-scroll{overflow:scroll !important}.overflow-x-auto{overflow-x:auto !important}.overflow-x-hidden{overflow-x:hidden !important}.overflow-x-visible{overflow-x:visible !important}.overflow-x-scroll{overflow-x:scroll !important}.overflow-y-auto{overflow-y:auto !important}.overflow-y-hidden{overflow-y:hidden !important}.overflow-y-visible{overflow-y:visible !important}.overflow-y-scroll{overflow-y:scroll !important}.d-inline{display:inline !important}.d-inline-block{display:inline-block !important}.d-block{display:block !important}.d-grid{display:grid !important}.d-inline-grid{display:inline-grid !important}.d-table{display:table !important}.d-table-row{display:table-row !important}.d-table-cell{display:table-cell !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}.d-none{display:none !important}.shadow{box-shadow:var(--bs-box-shadow) !important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm) !important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg) !important}.shadow-none{box-shadow:none !important}.focus-ring-primary{--bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static !important}.position-relative{position:relative !important}.position-absolute{position:absolute !important}.position-fixed{position:fixed !important}.position-sticky{position:sticky !important}.top-0{top:0 !important}.top-50{top:50% !important}.top-100{top:100% !important}.bottom-0{bottom:0 !important}.bottom-50{bottom:50% !important}.bottom-100{bottom:100% !important}.start-0{left:0 !important}.start-50{left:50% !important}.start-100{left:100% !important}.end-0{right:0 !important}.end-50{right:50% !important}.end-100{right:100% !important}.translate-middle{transform:translate(-50%, -50%) !important}.translate-middle-x{transform:translateX(-50%) !important}.translate-middle-y{transform:translateY(-50%) !important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-0{border:0 !important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-top-0{border-top:0 !important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-end-0{border-right:0 !important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-bottom-0{border-bottom:0 !important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important}.border-start-0{border-left:0 !important}.border-primary{--bs-border-opacity: 1;border-color:rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important}.border-secondary{--bs-border-opacity: 1;border-color:rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important}.border-success{--bs-border-opacity: 1;border-color:rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important}.border-info{--bs-border-opacity: 1;border-color:rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important}.border-warning{--bs-border-opacity: 1;border-color:rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important}.border-danger{--bs-border-opacity: 1;border-color:rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important}.border-light{--bs-border-opacity: 1;border-color:rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important}.border-dark{--bs-border-opacity: 1;border-color:rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important}.border-black{--bs-border-opacity: 1;border-color:rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important}.border-white{--bs-border-opacity: 1;border-color:rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle) !important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle) !important}.border-success-subtle{border-color:var(--bs-success-border-subtle) !important}.border-info-subtle{border-color:var(--bs-info-border-subtle) !important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle) !important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle) !important}.border-light-subtle{border-color:var(--bs-light-border-subtle) !important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle) !important}.border-1{border-width:1px !important}.border-2{border-width:2px !important}.border-3{border-width:3px !important}.border-4{border-width:4px !important}.border-5{border-width:5px !important}.border-opacity-10{--bs-border-opacity: 0.1}.border-opacity-25{--bs-border-opacity: 0.25}.border-opacity-50{--bs-border-opacity: 0.5}.border-opacity-75{--bs-border-opacity: 0.75}.border-opacity-100{--bs-border-opacity: 1}.w-25{width:25% !important}.w-50{width:50% !important}.w-75{width:75% !important}.w-100{width:100% !important}.w-auto{width:auto !important}.mw-100{max-width:100% !important}.vw-100{width:100vw !important}.min-vw-100{min-width:100vw !important}.h-25{height:25% !important}.h-50{height:50% !important}.h-75{height:75% !important}.h-100{height:100% !important}.h-auto{height:auto !important}.mh-100{max-height:100% !important}.vh-100{height:100vh !important}.min-vh-100{min-height:100vh !important}.flex-fill{flex:1 1 auto !important}.flex-row{flex-direction:row !important}.flex-column{flex-direction:column !important}.flex-row-reverse{flex-direction:row-reverse !important}.flex-column-reverse{flex-direction:column-reverse !important}.flex-grow-0{flex-grow:0 !important}.flex-grow-1{flex-grow:1 !important}.flex-shrink-0{flex-shrink:0 !important}.flex-shrink-1{flex-shrink:1 !important}.flex-wrap{flex-wrap:wrap !important}.flex-nowrap{flex-wrap:nowrap !important}.flex-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-start{justify-content:flex-start !important}.justify-content-end{justify-content:flex-end !important}.justify-content-center{justify-content:center !important}.justify-content-between{justify-content:space-between !important}.justify-content-around{justify-content:space-around !important}.justify-content-evenly{justify-content:space-evenly !important}.align-items-start{align-items:flex-start !important}.align-items-end{align-items:flex-end !important}.align-items-center{align-items:center !important}.align-items-baseline{align-items:baseline !important}.align-items-stretch{align-items:stretch !important}.align-content-start{align-content:flex-start !important}.align-content-end{align-content:flex-end !important}.align-content-center{align-content:center !important}.align-content-between{align-content:space-between !important}.align-content-around{align-content:space-around !important}.align-content-stretch{align-content:stretch !important}.align-self-auto{align-self:auto !important}.align-self-start{align-self:flex-start !important}.align-self-end{align-self:flex-end !important}.align-self-center{align-self:center !important}.align-self-baseline{align-self:baseline !important}.align-self-stretch{align-self:stretch !important}.order-first{order:-1 !important}.order-0{order:0 !important}.order-1{order:1 !important}.order-2{order:2 !important}.order-3{order:3 !important}.order-4{order:4 !important}.order-5{order:5 !important}.order-last{order:6 !important}.m-0{margin:0 !important}.m-1{margin:.25rem !important}.m-2{margin:.5rem !important}.m-3{margin:1rem !important}.m-4{margin:1.5rem !important}.m-5{margin:3rem !important}.m-auto{margin:auto !important}.mx-0{margin-right:0 !important;margin-left:0 !important}.mx-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-3{margin-right:1rem !important;margin-left:1rem !important}.mx-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-5{margin-right:3rem !important;margin-left:3rem !important}.mx-auto{margin-right:auto !important;margin-left:auto !important}.my-0{margin-top:0 !important;margin-bottom:0 !important}.my-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-0{margin-top:0 !important}.mt-1{margin-top:.25rem !important}.mt-2{margin-top:.5rem !important}.mt-3{margin-top:1rem !important}.mt-4{margin-top:1.5rem !important}.mt-5{margin-top:3rem !important}.mt-auto{margin-top:auto !important}.me-0{margin-right:0 !important}.me-1{margin-right:.25rem !important}.me-2{margin-right:.5rem !important}.me-3{margin-right:1rem !important}.me-4{margin-right:1.5rem !important}.me-5{margin-right:3rem !important}.me-auto{margin-right:auto !important}.mb-0{margin-bottom:0 !important}.mb-1{margin-bottom:.25rem !important}.mb-2{margin-bottom:.5rem !important}.mb-3{margin-bottom:1rem !important}.mb-4{margin-bottom:1.5rem !important}.mb-5{margin-bottom:3rem !important}.mb-auto{margin-bottom:auto !important}.ms-0{margin-left:0 !important}.ms-1{margin-left:.25rem !important}.ms-2{margin-left:.5rem !important}.ms-3{margin-left:1rem !important}.ms-4{margin-left:1.5rem !important}.ms-5{margin-left:3rem !important}.ms-auto{margin-left:auto !important}.p-0{padding:0 !important}.p-1{padding:.25rem !important}.p-2{padding:.5rem !important}.p-3{padding:1rem !important}.p-4{padding:1.5rem !important}.p-5{padding:3rem !important}.px-0{padding-right:0 !important;padding-left:0 !important}.px-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-3{padding-right:1rem !important;padding-left:1rem !important}.px-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-5{padding-right:3rem !important;padding-left:3rem !important}.py-0{padding-top:0 !important;padding-bottom:0 !important}.py-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-0{padding-top:0 !important}.pt-1{padding-top:.25rem !important}.pt-2{padding-top:.5rem !important}.pt-3{padding-top:1rem !important}.pt-4{padding-top:1.5rem !important}.pt-5{padding-top:3rem !important}.pe-0{padding-right:0 !important}.pe-1{padding-right:.25rem !important}.pe-2{padding-right:.5rem !important}.pe-3{padding-right:1rem !important}.pe-4{padding-right:1.5rem !important}.pe-5{padding-right:3rem !important}.pb-0{padding-bottom:0 !important}.pb-1{padding-bottom:.25rem !important}.pb-2{padding-bottom:.5rem !important}.pb-3{padding-bottom:1rem !important}.pb-4{padding-bottom:1.5rem !important}.pb-5{padding-bottom:3rem !important}.ps-0{padding-left:0 !important}.ps-1{padding-left:.25rem !important}.ps-2{padding-left:.5rem !important}.ps-3{padding-left:1rem !important}.ps-4{padding-left:1.5rem !important}.ps-5{padding-left:3rem !important}.gap-0{gap:0 !important}.gap-1{gap:.25rem !important}.gap-2{gap:.5rem !important}.gap-3{gap:1rem !important}.gap-4{gap:1.5rem !important}.gap-5{gap:3rem !important}.row-gap-0{row-gap:0 !important}.row-gap-1{row-gap:.25rem !important}.row-gap-2{row-gap:.5rem !important}.row-gap-3{row-gap:1rem !important}.row-gap-4{row-gap:1.5rem !important}.row-gap-5{row-gap:3rem !important}.column-gap-0{column-gap:0 !important}.column-gap-1{column-gap:.25rem !important}.column-gap-2{column-gap:.5rem !important}.column-gap-3{column-gap:1rem !important}.column-gap-4{column-gap:1.5rem !important}.column-gap-5{column-gap:3rem !important}.font-monospace{font-family:var(--bs-font-monospace) !important}.fs-1{font-size:calc(1.375rem + 1.5vw) !important}.fs-2{font-size:calc(1.325rem + 0.9vw) !important}.fs-3{font-size:calc(1.3rem + 0.6vw) !important}.fs-4{font-size:calc(1.275rem + 0.3vw) !important}.fs-5{font-size:1.25rem !important}.fs-6{font-size:1rem !important}.fst-italic{font-style:italic !important}.fst-normal{font-style:normal !important}.fw-lighter{font-weight:lighter !important}.fw-light{font-weight:300 !important}.fw-normal{font-weight:400 !important}.fw-medium{font-weight:500 !important}.fw-semibold{font-weight:600 !important}.fw-bold{font-weight:700 !important}.fw-bolder{font-weight:bolder !important}.lh-1{line-height:1 !important}.lh-sm{line-height:1.25 !important}.lh-base{line-height:1.5 !important}.lh-lg{line-height:2 !important}.text-start{text-align:left !important}.text-end{text-align:right !important}.text-center{text-align:center !important}.text-decoration-none{text-decoration:none !important}.text-decoration-underline{text-decoration:underline !important}.text-decoration-line-through{text-decoration:line-through !important}.text-lowercase{text-transform:lowercase !important}.text-uppercase{text-transform:uppercase !important}.text-capitalize{text-transform:capitalize !important}.text-wrap{white-space:normal !important}.text-nowrap{white-space:nowrap !important}.text-break{word-wrap:break-word !important;word-break:break-word !important}.text-primary{--bs-text-opacity: 1;color:rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important}.text-secondary{--bs-text-opacity: 1;color:rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important}.text-success{--bs-text-opacity: 1;color:rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important}.text-info{--bs-text-opacity: 1;color:rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important}.text-warning{--bs-text-opacity: 1;color:rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important}.text-danger{--bs-text-opacity: 1;color:rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important}.text-light{--bs-text-opacity: 1;color:rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important}.text-dark{--bs-text-opacity: 1;color:rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important}.text-black{--bs-text-opacity: 1;color:rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important}.text-white{--bs-text-opacity: 1;color:rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important}.text-body{--bs-text-opacity: 1;color:rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important}.text-muted{--bs-text-opacity: 1;color:var(--bs-secondary-color) !important}.text-black-50{--bs-text-opacity: 1;color:rgba(0,0,0,.5) !important}.text-white-50{--bs-text-opacity: 1;color:hsla(0,0%,100%,.5) !important}.text-body-secondary{--bs-text-opacity: 1;color:var(--bs-secondary-color) !important}.text-body-tertiary{--bs-text-opacity: 1;color:var(--bs-tertiary-color) !important}.text-body-emphasis{--bs-text-opacity: 1;color:var(--bs-emphasis-color) !important}.text-reset{--bs-text-opacity: 1;color:inherit !important}.text-opacity-25{--bs-text-opacity: 0.25}.text-opacity-50{--bs-text-opacity: 0.5}.text-opacity-75{--bs-text-opacity: 0.75}.text-opacity-100{--bs-text-opacity: 1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis) !important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis) !important}.text-success-emphasis{color:var(--bs-success-text-emphasis) !important}.text-info-emphasis{color:var(--bs-info-text-emphasis) !important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis) !important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis) !important}.text-light-emphasis{color:var(--bs-light-text-emphasis) !important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis) !important}.link-opacity-10{--bs-link-opacity: 0.1}.link-opacity-10-hover:hover{--bs-link-opacity: 0.1}.link-opacity-25{--bs-link-opacity: 0.25}.link-opacity-25-hover:hover{--bs-link-opacity: 0.25}.link-opacity-50{--bs-link-opacity: 0.5}.link-opacity-50-hover:hover{--bs-link-opacity: 0.5}.link-opacity-75{--bs-link-opacity: 0.75}.link-opacity-75-hover:hover{--bs-link-opacity: 0.75}.link-opacity-100{--bs-link-opacity: 1}.link-opacity-100-hover:hover{--bs-link-opacity: 1}.link-offset-1{text-underline-offset:.125em !important}.link-offset-1-hover:hover{text-underline-offset:.125em !important}.link-offset-2{text-underline-offset:.25em !important}.link-offset-2-hover:hover{text-underline-offset:.25em !important}.link-offset-3{text-underline-offset:.375em !important}.link-offset-3-hover:hover{text-underline-offset:.375em !important}.link-underline-primary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-secondary{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-success{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-info{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-warning{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-danger{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-light{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important}.link-underline-dark{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important}.link-underline{--bs-link-underline-opacity: 1;text-decoration-color:rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important}.link-underline-opacity-0{--bs-link-underline-opacity: 0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity: 0}.link-underline-opacity-10{--bs-link-underline-opacity: 0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity: 0.1}.link-underline-opacity-25{--bs-link-underline-opacity: 0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity: 0.25}.link-underline-opacity-50{--bs-link-underline-opacity: 0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity: 0.5}.link-underline-opacity-75{--bs-link-underline-opacity: 0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity: 0.75}.link-underline-opacity-100{--bs-link-underline-opacity: 1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity: 1}.bg-primary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important}.bg-secondary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important}.bg-success{--bs-bg-opacity: 1;background-color:rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important}.bg-info{--bs-bg-opacity: 1;background-color:rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important}.bg-warning{--bs-bg-opacity: 1;background-color:rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important}.bg-danger{--bs-bg-opacity: 1;background-color:rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important}.bg-light{--bs-bg-opacity: 1;background-color:rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important}.bg-dark{--bs-bg-opacity: 1;background-color:rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important}.bg-black{--bs-bg-opacity: 1;background-color:rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important}.bg-white{--bs-bg-opacity: 1;background-color:rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important}.bg-body{--bs-bg-opacity: 1;background-color:rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important}.bg-transparent{--bs-bg-opacity: 1;background-color:rgba(0,0,0,0) !important}.bg-body-secondary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important}.bg-body-tertiary{--bs-bg-opacity: 1;background-color:rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important}.bg-opacity-10{--bs-bg-opacity: 0.1}.bg-opacity-25{--bs-bg-opacity: 0.25}.bg-opacity-50{--bs-bg-opacity: 0.5}.bg-opacity-75{--bs-bg-opacity: 0.75}.bg-opacity-100{--bs-bg-opacity: 1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle) !important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle) !important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle) !important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle) !important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle) !important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle) !important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle) !important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle) !important}.bg-gradient{background-image:var(--bs-gradient) !important}.user-select-all{user-select:all !important}.user-select-auto{user-select:auto !important}.user-select-none{user-select:none !important}.pe-none{pointer-events:none !important}.pe-auto{pointer-events:auto !important}.rounded{border-radius:var(--bs-border-radius) !important}.rounded-0{border-radius:0 !important}.rounded-1{border-radius:var(--bs-border-radius-sm) !important}.rounded-2{border-radius:var(--bs-border-radius) !important}.rounded-3{border-radius:var(--bs-border-radius-lg) !important}.rounded-4{border-radius:var(--bs-border-radius-xl) !important}.rounded-5{border-radius:var(--bs-border-radius-xxl) !important}.rounded-circle{border-radius:50% !important}.rounded-pill{border-radius:var(--bs-border-radius-pill) !important}.rounded-top{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-0{border-top-left-radius:0 !important;border-top-right-radius:0 !important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm) !important;border-top-right-radius:var(--bs-border-radius-sm) !important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius) !important;border-top-right-radius:var(--bs-border-radius) !important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg) !important;border-top-right-radius:var(--bs-border-radius-lg) !important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl) !important;border-top-right-radius:var(--bs-border-radius-xl) !important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl) !important;border-top-right-radius:var(--bs-border-radius-xxl) !important}.rounded-top-circle{border-top-left-radius:50% !important;border-top-right-radius:50% !important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill) !important;border-top-right-radius:var(--bs-border-radius-pill) !important}.rounded-end{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-0{border-top-right-radius:0 !important;border-bottom-right-radius:0 !important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm) !important;border-bottom-right-radius:var(--bs-border-radius-sm) !important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius) !important;border-bottom-right-radius:var(--bs-border-radius) !important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg) !important;border-bottom-right-radius:var(--bs-border-radius-lg) !important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl) !important;border-bottom-right-radius:var(--bs-border-radius-xl) !important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-right-radius:var(--bs-border-radius-xxl) !important}.rounded-end-circle{border-top-right-radius:50% !important;border-bottom-right-radius:50% !important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill) !important;border-bottom-right-radius:var(--bs-border-radius-pill) !important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-0{border-bottom-right-radius:0 !important;border-bottom-left-radius:0 !important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm) !important;border-bottom-left-radius:var(--bs-border-radius-sm) !important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius) !important;border-bottom-left-radius:var(--bs-border-radius) !important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg) !important;border-bottom-left-radius:var(--bs-border-radius-lg) !important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl) !important;border-bottom-left-radius:var(--bs-border-radius-xl) !important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl) !important;border-bottom-left-radius:var(--bs-border-radius-xxl) !important}.rounded-bottom-circle{border-bottom-right-radius:50% !important;border-bottom-left-radius:50% !important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill) !important;border-bottom-left-radius:var(--bs-border-radius-pill) !important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-0{border-bottom-left-radius:0 !important;border-top-left-radius:0 !important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm) !important;border-top-left-radius:var(--bs-border-radius-sm) !important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius) !important;border-top-left-radius:var(--bs-border-radius) !important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg) !important;border-top-left-radius:var(--bs-border-radius-lg) !important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl) !important;border-top-left-radius:var(--bs-border-radius-xl) !important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl) !important;border-top-left-radius:var(--bs-border-radius-xxl) !important}.rounded-start-circle{border-bottom-left-radius:50% !important;border-top-left-radius:50% !important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill) !important;border-top-left-radius:var(--bs-border-radius-pill) !important}.visible{visibility:visible !important}.invisible{visibility:hidden !important}.z-n1{z-index:-1 !important}.z-0{z-index:0 !important}.z-1{z-index:1 !important}.z-2{z-index:2 !important}.z-3{z-index:3 !important}@media(min-width: 576px){.float-sm-start{float:left !important}.float-sm-end{float:right !important}.float-sm-none{float:none !important}.object-fit-sm-contain{object-fit:contain !important}.object-fit-sm-cover{object-fit:cover !important}.object-fit-sm-fill{object-fit:fill !important}.object-fit-sm-scale{object-fit:scale-down !important}.object-fit-sm-none{object-fit:none !important}.d-sm-inline{display:inline !important}.d-sm-inline-block{display:inline-block !important}.d-sm-block{display:block !important}.d-sm-grid{display:grid !important}.d-sm-inline-grid{display:inline-grid !important}.d-sm-table{display:table !important}.d-sm-table-row{display:table-row !important}.d-sm-table-cell{display:table-cell !important}.d-sm-flex{display:flex !important}.d-sm-inline-flex{display:inline-flex !important}.d-sm-none{display:none !important}.flex-sm-fill{flex:1 1 auto !important}.flex-sm-row{flex-direction:row !important}.flex-sm-column{flex-direction:column !important}.flex-sm-row-reverse{flex-direction:row-reverse !important}.flex-sm-column-reverse{flex-direction:column-reverse !important}.flex-sm-grow-0{flex-grow:0 !important}.flex-sm-grow-1{flex-grow:1 !important}.flex-sm-shrink-0{flex-shrink:0 !important}.flex-sm-shrink-1{flex-shrink:1 !important}.flex-sm-wrap{flex-wrap:wrap !important}.flex-sm-nowrap{flex-wrap:nowrap !important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-sm-start{justify-content:flex-start !important}.justify-content-sm-end{justify-content:flex-end !important}.justify-content-sm-center{justify-content:center !important}.justify-content-sm-between{justify-content:space-between !important}.justify-content-sm-around{justify-content:space-around !important}.justify-content-sm-evenly{justify-content:space-evenly !important}.align-items-sm-start{align-items:flex-start !important}.align-items-sm-end{align-items:flex-end !important}.align-items-sm-center{align-items:center !important}.align-items-sm-baseline{align-items:baseline !important}.align-items-sm-stretch{align-items:stretch !important}.align-content-sm-start{align-content:flex-start !important}.align-content-sm-end{align-content:flex-end !important}.align-content-sm-center{align-content:center !important}.align-content-sm-between{align-content:space-between !important}.align-content-sm-around{align-content:space-around !important}.align-content-sm-stretch{align-content:stretch !important}.align-self-sm-auto{align-self:auto !important}.align-self-sm-start{align-self:flex-start !important}.align-self-sm-end{align-self:flex-end !important}.align-self-sm-center{align-self:center !important}.align-self-sm-baseline{align-self:baseline !important}.align-self-sm-stretch{align-self:stretch !important}.order-sm-first{order:-1 !important}.order-sm-0{order:0 !important}.order-sm-1{order:1 !important}.order-sm-2{order:2 !important}.order-sm-3{order:3 !important}.order-sm-4{order:4 !important}.order-sm-5{order:5 !important}.order-sm-last{order:6 !important}.m-sm-0{margin:0 !important}.m-sm-1{margin:.25rem !important}.m-sm-2{margin:.5rem !important}.m-sm-3{margin:1rem !important}.m-sm-4{margin:1.5rem !important}.m-sm-5{margin:3rem !important}.m-sm-auto{margin:auto !important}.mx-sm-0{margin-right:0 !important;margin-left:0 !important}.mx-sm-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-sm-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-sm-3{margin-right:1rem !important;margin-left:1rem !important}.mx-sm-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-sm-5{margin-right:3rem !important;margin-left:3rem !important}.mx-sm-auto{margin-right:auto !important;margin-left:auto !important}.my-sm-0{margin-top:0 !important;margin-bottom:0 !important}.my-sm-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-sm-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-sm-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-sm-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-sm-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-sm-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-sm-0{margin-top:0 !important}.mt-sm-1{margin-top:.25rem !important}.mt-sm-2{margin-top:.5rem !important}.mt-sm-3{margin-top:1rem !important}.mt-sm-4{margin-top:1.5rem !important}.mt-sm-5{margin-top:3rem !important}.mt-sm-auto{margin-top:auto !important}.me-sm-0{margin-right:0 !important}.me-sm-1{margin-right:.25rem !important}.me-sm-2{margin-right:.5rem !important}.me-sm-3{margin-right:1rem !important}.me-sm-4{margin-right:1.5rem !important}.me-sm-5{margin-right:3rem !important}.me-sm-auto{margin-right:auto !important}.mb-sm-0{margin-bottom:0 !important}.mb-sm-1{margin-bottom:.25rem !important}.mb-sm-2{margin-bottom:.5rem !important}.mb-sm-3{margin-bottom:1rem !important}.mb-sm-4{margin-bottom:1.5rem !important}.mb-sm-5{margin-bottom:3rem !important}.mb-sm-auto{margin-bottom:auto !important}.ms-sm-0{margin-left:0 !important}.ms-sm-1{margin-left:.25rem !important}.ms-sm-2{margin-left:.5rem !important}.ms-sm-3{margin-left:1rem !important}.ms-sm-4{margin-left:1.5rem !important}.ms-sm-5{margin-left:3rem !important}.ms-sm-auto{margin-left:auto !important}.p-sm-0{padding:0 !important}.p-sm-1{padding:.25rem !important}.p-sm-2{padding:.5rem !important}.p-sm-3{padding:1rem !important}.p-sm-4{padding:1.5rem !important}.p-sm-5{padding:3rem !important}.px-sm-0{padding-right:0 !important;padding-left:0 !important}.px-sm-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-sm-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-sm-3{padding-right:1rem !important;padding-left:1rem !important}.px-sm-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-sm-5{padding-right:3rem !important;padding-left:3rem !important}.py-sm-0{padding-top:0 !important;padding-bottom:0 !important}.py-sm-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-sm-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-sm-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-sm-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-sm-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-sm-0{padding-top:0 !important}.pt-sm-1{padding-top:.25rem !important}.pt-sm-2{padding-top:.5rem !important}.pt-sm-3{padding-top:1rem !important}.pt-sm-4{padding-top:1.5rem !important}.pt-sm-5{padding-top:3rem !important}.pe-sm-0{padding-right:0 !important}.pe-sm-1{padding-right:.25rem !important}.pe-sm-2{padding-right:.5rem !important}.pe-sm-3{padding-right:1rem !important}.pe-sm-4{padding-right:1.5rem !important}.pe-sm-5{padding-right:3rem !important}.pb-sm-0{padding-bottom:0 !important}.pb-sm-1{padding-bottom:.25rem !important}.pb-sm-2{padding-bottom:.5rem !important}.pb-sm-3{padding-bottom:1rem !important}.pb-sm-4{padding-bottom:1.5rem !important}.pb-sm-5{padding-bottom:3rem !important}.ps-sm-0{padding-left:0 !important}.ps-sm-1{padding-left:.25rem !important}.ps-sm-2{padding-left:.5rem !important}.ps-sm-3{padding-left:1rem !important}.ps-sm-4{padding-left:1.5rem !important}.ps-sm-5{padding-left:3rem !important}.gap-sm-0{gap:0 !important}.gap-sm-1{gap:.25rem !important}.gap-sm-2{gap:.5rem !important}.gap-sm-3{gap:1rem !important}.gap-sm-4{gap:1.5rem !important}.gap-sm-5{gap:3rem !important}.row-gap-sm-0{row-gap:0 !important}.row-gap-sm-1{row-gap:.25rem !important}.row-gap-sm-2{row-gap:.5rem !important}.row-gap-sm-3{row-gap:1rem !important}.row-gap-sm-4{row-gap:1.5rem !important}.row-gap-sm-5{row-gap:3rem !important}.column-gap-sm-0{column-gap:0 !important}.column-gap-sm-1{column-gap:.25rem !important}.column-gap-sm-2{column-gap:.5rem !important}.column-gap-sm-3{column-gap:1rem !important}.column-gap-sm-4{column-gap:1.5rem !important}.column-gap-sm-5{column-gap:3rem !important}.text-sm-start{text-align:left !important}.text-sm-end{text-align:right !important}.text-sm-center{text-align:center !important}}@media(min-width: 768px){.float-md-start{float:left !important}.float-md-end{float:right !important}.float-md-none{float:none !important}.object-fit-md-contain{object-fit:contain !important}.object-fit-md-cover{object-fit:cover !important}.object-fit-md-fill{object-fit:fill !important}.object-fit-md-scale{object-fit:scale-down !important}.object-fit-md-none{object-fit:none !important}.d-md-inline{display:inline !important}.d-md-inline-block{display:inline-block !important}.d-md-block{display:block !important}.d-md-grid{display:grid !important}.d-md-inline-grid{display:inline-grid !important}.d-md-table{display:table !important}.d-md-table-row{display:table-row !important}.d-md-table-cell{display:table-cell !important}.d-md-flex{display:flex !important}.d-md-inline-flex{display:inline-flex !important}.d-md-none{display:none !important}.flex-md-fill{flex:1 1 auto !important}.flex-md-row{flex-direction:row !important}.flex-md-column{flex-direction:column !important}.flex-md-row-reverse{flex-direction:row-reverse !important}.flex-md-column-reverse{flex-direction:column-reverse !important}.flex-md-grow-0{flex-grow:0 !important}.flex-md-grow-1{flex-grow:1 !important}.flex-md-shrink-0{flex-shrink:0 !important}.flex-md-shrink-1{flex-shrink:1 !important}.flex-md-wrap{flex-wrap:wrap !important}.flex-md-nowrap{flex-wrap:nowrap !important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-md-start{justify-content:flex-start !important}.justify-content-md-end{justify-content:flex-end !important}.justify-content-md-center{justify-content:center !important}.justify-content-md-between{justify-content:space-between !important}.justify-content-md-around{justify-content:space-around !important}.justify-content-md-evenly{justify-content:space-evenly !important}.align-items-md-start{align-items:flex-start !important}.align-items-md-end{align-items:flex-end !important}.align-items-md-center{align-items:center !important}.align-items-md-baseline{align-items:baseline !important}.align-items-md-stretch{align-items:stretch !important}.align-content-md-start{align-content:flex-start !important}.align-content-md-end{align-content:flex-end !important}.align-content-md-center{align-content:center !important}.align-content-md-between{align-content:space-between !important}.align-content-md-around{align-content:space-around !important}.align-content-md-stretch{align-content:stretch !important}.align-self-md-auto{align-self:auto !important}.align-self-md-start{align-self:flex-start !important}.align-self-md-end{align-self:flex-end !important}.align-self-md-center{align-self:center !important}.align-self-md-baseline{align-self:baseline !important}.align-self-md-stretch{align-self:stretch !important}.order-md-first{order:-1 !important}.order-md-0{order:0 !important}.order-md-1{order:1 !important}.order-md-2{order:2 !important}.order-md-3{order:3 !important}.order-md-4{order:4 !important}.order-md-5{order:5 !important}.order-md-last{order:6 !important}.m-md-0{margin:0 !important}.m-md-1{margin:.25rem !important}.m-md-2{margin:.5rem !important}.m-md-3{margin:1rem !important}.m-md-4{margin:1.5rem !important}.m-md-5{margin:3rem !important}.m-md-auto{margin:auto !important}.mx-md-0{margin-right:0 !important;margin-left:0 !important}.mx-md-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-md-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-md-3{margin-right:1rem !important;margin-left:1rem !important}.mx-md-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-md-5{margin-right:3rem !important;margin-left:3rem !important}.mx-md-auto{margin-right:auto !important;margin-left:auto !important}.my-md-0{margin-top:0 !important;margin-bottom:0 !important}.my-md-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-md-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-md-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-md-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-md-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-md-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-md-0{margin-top:0 !important}.mt-md-1{margin-top:.25rem !important}.mt-md-2{margin-top:.5rem !important}.mt-md-3{margin-top:1rem !important}.mt-md-4{margin-top:1.5rem !important}.mt-md-5{margin-top:3rem !important}.mt-md-auto{margin-top:auto !important}.me-md-0{margin-right:0 !important}.me-md-1{margin-right:.25rem !important}.me-md-2{margin-right:.5rem !important}.me-md-3{margin-right:1rem !important}.me-md-4{margin-right:1.5rem !important}.me-md-5{margin-right:3rem !important}.me-md-auto{margin-right:auto !important}.mb-md-0{margin-bottom:0 !important}.mb-md-1{margin-bottom:.25rem !important}.mb-md-2{margin-bottom:.5rem !important}.mb-md-3{margin-bottom:1rem !important}.mb-md-4{margin-bottom:1.5rem !important}.mb-md-5{margin-bottom:3rem !important}.mb-md-auto{margin-bottom:auto !important}.ms-md-0{margin-left:0 !important}.ms-md-1{margin-left:.25rem !important}.ms-md-2{margin-left:.5rem !important}.ms-md-3{margin-left:1rem !important}.ms-md-4{margin-left:1.5rem !important}.ms-md-5{margin-left:3rem !important}.ms-md-auto{margin-left:auto !important}.p-md-0{padding:0 !important}.p-md-1{padding:.25rem !important}.p-md-2{padding:.5rem !important}.p-md-3{padding:1rem !important}.p-md-4{padding:1.5rem !important}.p-md-5{padding:3rem !important}.px-md-0{padding-right:0 !important;padding-left:0 !important}.px-md-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-md-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-md-3{padding-right:1rem !important;padding-left:1rem !important}.px-md-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-md-5{padding-right:3rem !important;padding-left:3rem !important}.py-md-0{padding-top:0 !important;padding-bottom:0 !important}.py-md-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-md-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-md-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-md-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-md-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-md-0{padding-top:0 !important}.pt-md-1{padding-top:.25rem !important}.pt-md-2{padding-top:.5rem !important}.pt-md-3{padding-top:1rem !important}.pt-md-4{padding-top:1.5rem !important}.pt-md-5{padding-top:3rem !important}.pe-md-0{padding-right:0 !important}.pe-md-1{padding-right:.25rem !important}.pe-md-2{padding-right:.5rem !important}.pe-md-3{padding-right:1rem !important}.pe-md-4{padding-right:1.5rem !important}.pe-md-5{padding-right:3rem !important}.pb-md-0{padding-bottom:0 !important}.pb-md-1{padding-bottom:.25rem !important}.pb-md-2{padding-bottom:.5rem !important}.pb-md-3{padding-bottom:1rem !important}.pb-md-4{padding-bottom:1.5rem !important}.pb-md-5{padding-bottom:3rem !important}.ps-md-0{padding-left:0 !important}.ps-md-1{padding-left:.25rem !important}.ps-md-2{padding-left:.5rem !important}.ps-md-3{padding-left:1rem !important}.ps-md-4{padding-left:1.5rem !important}.ps-md-5{padding-left:3rem !important}.gap-md-0{gap:0 !important}.gap-md-1{gap:.25rem !important}.gap-md-2{gap:.5rem !important}.gap-md-3{gap:1rem !important}.gap-md-4{gap:1.5rem !important}.gap-md-5{gap:3rem !important}.row-gap-md-0{row-gap:0 !important}.row-gap-md-1{row-gap:.25rem !important}.row-gap-md-2{row-gap:.5rem !important}.row-gap-md-3{row-gap:1rem !important}.row-gap-md-4{row-gap:1.5rem !important}.row-gap-md-5{row-gap:3rem !important}.column-gap-md-0{column-gap:0 !important}.column-gap-md-1{column-gap:.25rem !important}.column-gap-md-2{column-gap:.5rem !important}.column-gap-md-3{column-gap:1rem !important}.column-gap-md-4{column-gap:1.5rem !important}.column-gap-md-5{column-gap:3rem !important}.text-md-start{text-align:left !important}.text-md-end{text-align:right !important}.text-md-center{text-align:center !important}}@media(min-width: 992px){.float-lg-start{float:left !important}.float-lg-end{float:right !important}.float-lg-none{float:none !important}.object-fit-lg-contain{object-fit:contain !important}.object-fit-lg-cover{object-fit:cover !important}.object-fit-lg-fill{object-fit:fill !important}.object-fit-lg-scale{object-fit:scale-down !important}.object-fit-lg-none{object-fit:none !important}.d-lg-inline{display:inline !important}.d-lg-inline-block{display:inline-block !important}.d-lg-block{display:block !important}.d-lg-grid{display:grid !important}.d-lg-inline-grid{display:inline-grid !important}.d-lg-table{display:table !important}.d-lg-table-row{display:table-row !important}.d-lg-table-cell{display:table-cell !important}.d-lg-flex{display:flex !important}.d-lg-inline-flex{display:inline-flex !important}.d-lg-none{display:none !important}.flex-lg-fill{flex:1 1 auto !important}.flex-lg-row{flex-direction:row !important}.flex-lg-column{flex-direction:column !important}.flex-lg-row-reverse{flex-direction:row-reverse !important}.flex-lg-column-reverse{flex-direction:column-reverse !important}.flex-lg-grow-0{flex-grow:0 !important}.flex-lg-grow-1{flex-grow:1 !important}.flex-lg-shrink-0{flex-shrink:0 !important}.flex-lg-shrink-1{flex-shrink:1 !important}.flex-lg-wrap{flex-wrap:wrap !important}.flex-lg-nowrap{flex-wrap:nowrap !important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-lg-start{justify-content:flex-start !important}.justify-content-lg-end{justify-content:flex-end !important}.justify-content-lg-center{justify-content:center !important}.justify-content-lg-between{justify-content:space-between !important}.justify-content-lg-around{justify-content:space-around !important}.justify-content-lg-evenly{justify-content:space-evenly !important}.align-items-lg-start{align-items:flex-start !important}.align-items-lg-end{align-items:flex-end !important}.align-items-lg-center{align-items:center !important}.align-items-lg-baseline{align-items:baseline !important}.align-items-lg-stretch{align-items:stretch !important}.align-content-lg-start{align-content:flex-start !important}.align-content-lg-end{align-content:flex-end !important}.align-content-lg-center{align-content:center !important}.align-content-lg-between{align-content:space-between !important}.align-content-lg-around{align-content:space-around !important}.align-content-lg-stretch{align-content:stretch !important}.align-self-lg-auto{align-self:auto !important}.align-self-lg-start{align-self:flex-start !important}.align-self-lg-end{align-self:flex-end !important}.align-self-lg-center{align-self:center !important}.align-self-lg-baseline{align-self:baseline !important}.align-self-lg-stretch{align-self:stretch !important}.order-lg-first{order:-1 !important}.order-lg-0{order:0 !important}.order-lg-1{order:1 !important}.order-lg-2{order:2 !important}.order-lg-3{order:3 !important}.order-lg-4{order:4 !important}.order-lg-5{order:5 !important}.order-lg-last{order:6 !important}.m-lg-0{margin:0 !important}.m-lg-1{margin:.25rem !important}.m-lg-2{margin:.5rem !important}.m-lg-3{margin:1rem !important}.m-lg-4{margin:1.5rem !important}.m-lg-5{margin:3rem !important}.m-lg-auto{margin:auto !important}.mx-lg-0{margin-right:0 !important;margin-left:0 !important}.mx-lg-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-lg-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-lg-3{margin-right:1rem !important;margin-left:1rem !important}.mx-lg-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-lg-5{margin-right:3rem !important;margin-left:3rem !important}.mx-lg-auto{margin-right:auto !important;margin-left:auto !important}.my-lg-0{margin-top:0 !important;margin-bottom:0 !important}.my-lg-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-lg-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-lg-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-lg-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-lg-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-lg-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-lg-0{margin-top:0 !important}.mt-lg-1{margin-top:.25rem !important}.mt-lg-2{margin-top:.5rem !important}.mt-lg-3{margin-top:1rem !important}.mt-lg-4{margin-top:1.5rem !important}.mt-lg-5{margin-top:3rem !important}.mt-lg-auto{margin-top:auto !important}.me-lg-0{margin-right:0 !important}.me-lg-1{margin-right:.25rem !important}.me-lg-2{margin-right:.5rem !important}.me-lg-3{margin-right:1rem !important}.me-lg-4{margin-right:1.5rem !important}.me-lg-5{margin-right:3rem !important}.me-lg-auto{margin-right:auto !important}.mb-lg-0{margin-bottom:0 !important}.mb-lg-1{margin-bottom:.25rem !important}.mb-lg-2{margin-bottom:.5rem !important}.mb-lg-3{margin-bottom:1rem !important}.mb-lg-4{margin-bottom:1.5rem !important}.mb-lg-5{margin-bottom:3rem !important}.mb-lg-auto{margin-bottom:auto !important}.ms-lg-0{margin-left:0 !important}.ms-lg-1{margin-left:.25rem !important}.ms-lg-2{margin-left:.5rem !important}.ms-lg-3{margin-left:1rem !important}.ms-lg-4{margin-left:1.5rem !important}.ms-lg-5{margin-left:3rem !important}.ms-lg-auto{margin-left:auto !important}.p-lg-0{padding:0 !important}.p-lg-1{padding:.25rem !important}.p-lg-2{padding:.5rem !important}.p-lg-3{padding:1rem !important}.p-lg-4{padding:1.5rem !important}.p-lg-5{padding:3rem !important}.px-lg-0{padding-right:0 !important;padding-left:0 !important}.px-lg-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-lg-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-lg-3{padding-right:1rem !important;padding-left:1rem !important}.px-lg-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-lg-5{padding-right:3rem !important;padding-left:3rem !important}.py-lg-0{padding-top:0 !important;padding-bottom:0 !important}.py-lg-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-lg-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-lg-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-lg-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-lg-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-lg-0{padding-top:0 !important}.pt-lg-1{padding-top:.25rem !important}.pt-lg-2{padding-top:.5rem !important}.pt-lg-3{padding-top:1rem !important}.pt-lg-4{padding-top:1.5rem !important}.pt-lg-5{padding-top:3rem !important}.pe-lg-0{padding-right:0 !important}.pe-lg-1{padding-right:.25rem !important}.pe-lg-2{padding-right:.5rem !important}.pe-lg-3{padding-right:1rem !important}.pe-lg-4{padding-right:1.5rem !important}.pe-lg-5{padding-right:3rem !important}.pb-lg-0{padding-bottom:0 !important}.pb-lg-1{padding-bottom:.25rem !important}.pb-lg-2{padding-bottom:.5rem !important}.pb-lg-3{padding-bottom:1rem !important}.pb-lg-4{padding-bottom:1.5rem !important}.pb-lg-5{padding-bottom:3rem !important}.ps-lg-0{padding-left:0 !important}.ps-lg-1{padding-left:.25rem !important}.ps-lg-2{padding-left:.5rem !important}.ps-lg-3{padding-left:1rem !important}.ps-lg-4{padding-left:1.5rem !important}.ps-lg-5{padding-left:3rem !important}.gap-lg-0{gap:0 !important}.gap-lg-1{gap:.25rem !important}.gap-lg-2{gap:.5rem !important}.gap-lg-3{gap:1rem !important}.gap-lg-4{gap:1.5rem !important}.gap-lg-5{gap:3rem !important}.row-gap-lg-0{row-gap:0 !important}.row-gap-lg-1{row-gap:.25rem !important}.row-gap-lg-2{row-gap:.5rem !important}.row-gap-lg-3{row-gap:1rem !important}.row-gap-lg-4{row-gap:1.5rem !important}.row-gap-lg-5{row-gap:3rem !important}.column-gap-lg-0{column-gap:0 !important}.column-gap-lg-1{column-gap:.25rem !important}.column-gap-lg-2{column-gap:.5rem !important}.column-gap-lg-3{column-gap:1rem !important}.column-gap-lg-4{column-gap:1.5rem !important}.column-gap-lg-5{column-gap:3rem !important}.text-lg-start{text-align:left !important}.text-lg-end{text-align:right !important}.text-lg-center{text-align:center !important}}@media(min-width: 1200px){.float-xl-start{float:left !important}.float-xl-end{float:right !important}.float-xl-none{float:none !important}.object-fit-xl-contain{object-fit:contain !important}.object-fit-xl-cover{object-fit:cover !important}.object-fit-xl-fill{object-fit:fill !important}.object-fit-xl-scale{object-fit:scale-down !important}.object-fit-xl-none{object-fit:none !important}.d-xl-inline{display:inline !important}.d-xl-inline-block{display:inline-block !important}.d-xl-block{display:block !important}.d-xl-grid{display:grid !important}.d-xl-inline-grid{display:inline-grid !important}.d-xl-table{display:table !important}.d-xl-table-row{display:table-row !important}.d-xl-table-cell{display:table-cell !important}.d-xl-flex{display:flex !important}.d-xl-inline-flex{display:inline-flex !important}.d-xl-none{display:none !important}.flex-xl-fill{flex:1 1 auto !important}.flex-xl-row{flex-direction:row !important}.flex-xl-column{flex-direction:column !important}.flex-xl-row-reverse{flex-direction:row-reverse !important}.flex-xl-column-reverse{flex-direction:column-reverse !important}.flex-xl-grow-0{flex-grow:0 !important}.flex-xl-grow-1{flex-grow:1 !important}.flex-xl-shrink-0{flex-shrink:0 !important}.flex-xl-shrink-1{flex-shrink:1 !important}.flex-xl-wrap{flex-wrap:wrap !important}.flex-xl-nowrap{flex-wrap:nowrap !important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-xl-start{justify-content:flex-start !important}.justify-content-xl-end{justify-content:flex-end !important}.justify-content-xl-center{justify-content:center !important}.justify-content-xl-between{justify-content:space-between !important}.justify-content-xl-around{justify-content:space-around !important}.justify-content-xl-evenly{justify-content:space-evenly !important}.align-items-xl-start{align-items:flex-start !important}.align-items-xl-end{align-items:flex-end !important}.align-items-xl-center{align-items:center !important}.align-items-xl-baseline{align-items:baseline !important}.align-items-xl-stretch{align-items:stretch !important}.align-content-xl-start{align-content:flex-start !important}.align-content-xl-end{align-content:flex-end !important}.align-content-xl-center{align-content:center !important}.align-content-xl-between{align-content:space-between !important}.align-content-xl-around{align-content:space-around !important}.align-content-xl-stretch{align-content:stretch !important}.align-self-xl-auto{align-self:auto !important}.align-self-xl-start{align-self:flex-start !important}.align-self-xl-end{align-self:flex-end !important}.align-self-xl-center{align-self:center !important}.align-self-xl-baseline{align-self:baseline !important}.align-self-xl-stretch{align-self:stretch !important}.order-xl-first{order:-1 !important}.order-xl-0{order:0 !important}.order-xl-1{order:1 !important}.order-xl-2{order:2 !important}.order-xl-3{order:3 !important}.order-xl-4{order:4 !important}.order-xl-5{order:5 !important}.order-xl-last{order:6 !important}.m-xl-0{margin:0 !important}.m-xl-1{margin:.25rem !important}.m-xl-2{margin:.5rem !important}.m-xl-3{margin:1rem !important}.m-xl-4{margin:1.5rem !important}.m-xl-5{margin:3rem !important}.m-xl-auto{margin:auto !important}.mx-xl-0{margin-right:0 !important;margin-left:0 !important}.mx-xl-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-xl-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-xl-3{margin-right:1rem !important;margin-left:1rem !important}.mx-xl-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-xl-5{margin-right:3rem !important;margin-left:3rem !important}.mx-xl-auto{margin-right:auto !important;margin-left:auto !important}.my-xl-0{margin-top:0 !important;margin-bottom:0 !important}.my-xl-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-xl-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-xl-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-xl-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-xl-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-xl-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-xl-0{margin-top:0 !important}.mt-xl-1{margin-top:.25rem !important}.mt-xl-2{margin-top:.5rem !important}.mt-xl-3{margin-top:1rem !important}.mt-xl-4{margin-top:1.5rem !important}.mt-xl-5{margin-top:3rem !important}.mt-xl-auto{margin-top:auto !important}.me-xl-0{margin-right:0 !important}.me-xl-1{margin-right:.25rem !important}.me-xl-2{margin-right:.5rem !important}.me-xl-3{margin-right:1rem !important}.me-xl-4{margin-right:1.5rem !important}.me-xl-5{margin-right:3rem !important}.me-xl-auto{margin-right:auto !important}.mb-xl-0{margin-bottom:0 !important}.mb-xl-1{margin-bottom:.25rem !important}.mb-xl-2{margin-bottom:.5rem !important}.mb-xl-3{margin-bottom:1rem !important}.mb-xl-4{margin-bottom:1.5rem !important}.mb-xl-5{margin-bottom:3rem !important}.mb-xl-auto{margin-bottom:auto !important}.ms-xl-0{margin-left:0 !important}.ms-xl-1{margin-left:.25rem !important}.ms-xl-2{margin-left:.5rem !important}.ms-xl-3{margin-left:1rem !important}.ms-xl-4{margin-left:1.5rem !important}.ms-xl-5{margin-left:3rem !important}.ms-xl-auto{margin-left:auto !important}.p-xl-0{padding:0 !important}.p-xl-1{padding:.25rem !important}.p-xl-2{padding:.5rem !important}.p-xl-3{padding:1rem !important}.p-xl-4{padding:1.5rem !important}.p-xl-5{padding:3rem !important}.px-xl-0{padding-right:0 !important;padding-left:0 !important}.px-xl-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-xl-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-xl-3{padding-right:1rem !important;padding-left:1rem !important}.px-xl-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-xl-5{padding-right:3rem !important;padding-left:3rem !important}.py-xl-0{padding-top:0 !important;padding-bottom:0 !important}.py-xl-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-xl-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-xl-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-xl-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-xl-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-xl-0{padding-top:0 !important}.pt-xl-1{padding-top:.25rem !important}.pt-xl-2{padding-top:.5rem !important}.pt-xl-3{padding-top:1rem !important}.pt-xl-4{padding-top:1.5rem !important}.pt-xl-5{padding-top:3rem !important}.pe-xl-0{padding-right:0 !important}.pe-xl-1{padding-right:.25rem !important}.pe-xl-2{padding-right:.5rem !important}.pe-xl-3{padding-right:1rem !important}.pe-xl-4{padding-right:1.5rem !important}.pe-xl-5{padding-right:3rem !important}.pb-xl-0{padding-bottom:0 !important}.pb-xl-1{padding-bottom:.25rem !important}.pb-xl-2{padding-bottom:.5rem !important}.pb-xl-3{padding-bottom:1rem !important}.pb-xl-4{padding-bottom:1.5rem !important}.pb-xl-5{padding-bottom:3rem !important}.ps-xl-0{padding-left:0 !important}.ps-xl-1{padding-left:.25rem !important}.ps-xl-2{padding-left:.5rem !important}.ps-xl-3{padding-left:1rem !important}.ps-xl-4{padding-left:1.5rem !important}.ps-xl-5{padding-left:3rem !important}.gap-xl-0{gap:0 !important}.gap-xl-1{gap:.25rem !important}.gap-xl-2{gap:.5rem !important}.gap-xl-3{gap:1rem !important}.gap-xl-4{gap:1.5rem !important}.gap-xl-5{gap:3rem !important}.row-gap-xl-0{row-gap:0 !important}.row-gap-xl-1{row-gap:.25rem !important}.row-gap-xl-2{row-gap:.5rem !important}.row-gap-xl-3{row-gap:1rem !important}.row-gap-xl-4{row-gap:1.5rem !important}.row-gap-xl-5{row-gap:3rem !important}.column-gap-xl-0{column-gap:0 !important}.column-gap-xl-1{column-gap:.25rem !important}.column-gap-xl-2{column-gap:.5rem !important}.column-gap-xl-3{column-gap:1rem !important}.column-gap-xl-4{column-gap:1.5rem !important}.column-gap-xl-5{column-gap:3rem !important}.text-xl-start{text-align:left !important}.text-xl-end{text-align:right !important}.text-xl-center{text-align:center !important}}@media(min-width: 1400px){.float-xxl-start{float:left !important}.float-xxl-end{float:right !important}.float-xxl-none{float:none !important}.object-fit-xxl-contain{object-fit:contain !important}.object-fit-xxl-cover{object-fit:cover !important}.object-fit-xxl-fill{object-fit:fill !important}.object-fit-xxl-scale{object-fit:scale-down !important}.object-fit-xxl-none{object-fit:none !important}.d-xxl-inline{display:inline !important}.d-xxl-inline-block{display:inline-block !important}.d-xxl-block{display:block !important}.d-xxl-grid{display:grid !important}.d-xxl-inline-grid{display:inline-grid !important}.d-xxl-table{display:table !important}.d-xxl-table-row{display:table-row !important}.d-xxl-table-cell{display:table-cell !important}.d-xxl-flex{display:flex !important}.d-xxl-inline-flex{display:inline-flex !important}.d-xxl-none{display:none !important}.flex-xxl-fill{flex:1 1 auto !important}.flex-xxl-row{flex-direction:row !important}.flex-xxl-column{flex-direction:column !important}.flex-xxl-row-reverse{flex-direction:row-reverse !important}.flex-xxl-column-reverse{flex-direction:column-reverse !important}.flex-xxl-grow-0{flex-grow:0 !important}.flex-xxl-grow-1{flex-grow:1 !important}.flex-xxl-shrink-0{flex-shrink:0 !important}.flex-xxl-shrink-1{flex-shrink:1 !important}.flex-xxl-wrap{flex-wrap:wrap !important}.flex-xxl-nowrap{flex-wrap:nowrap !important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse !important}.justify-content-xxl-start{justify-content:flex-start !important}.justify-content-xxl-end{justify-content:flex-end !important}.justify-content-xxl-center{justify-content:center !important}.justify-content-xxl-between{justify-content:space-between !important}.justify-content-xxl-around{justify-content:space-around !important}.justify-content-xxl-evenly{justify-content:space-evenly !important}.align-items-xxl-start{align-items:flex-start !important}.align-items-xxl-end{align-items:flex-end !important}.align-items-xxl-center{align-items:center !important}.align-items-xxl-baseline{align-items:baseline !important}.align-items-xxl-stretch{align-items:stretch !important}.align-content-xxl-start{align-content:flex-start !important}.align-content-xxl-end{align-content:flex-end !important}.align-content-xxl-center{align-content:center !important}.align-content-xxl-between{align-content:space-between !important}.align-content-xxl-around{align-content:space-around !important}.align-content-xxl-stretch{align-content:stretch !important}.align-self-xxl-auto{align-self:auto !important}.align-self-xxl-start{align-self:flex-start !important}.align-self-xxl-end{align-self:flex-end !important}.align-self-xxl-center{align-self:center !important}.align-self-xxl-baseline{align-self:baseline !important}.align-self-xxl-stretch{align-self:stretch !important}.order-xxl-first{order:-1 !important}.order-xxl-0{order:0 !important}.order-xxl-1{order:1 !important}.order-xxl-2{order:2 !important}.order-xxl-3{order:3 !important}.order-xxl-4{order:4 !important}.order-xxl-5{order:5 !important}.order-xxl-last{order:6 !important}.m-xxl-0{margin:0 !important}.m-xxl-1{margin:.25rem !important}.m-xxl-2{margin:.5rem !important}.m-xxl-3{margin:1rem !important}.m-xxl-4{margin:1.5rem !important}.m-xxl-5{margin:3rem !important}.m-xxl-auto{margin:auto !important}.mx-xxl-0{margin-right:0 !important;margin-left:0 !important}.mx-xxl-1{margin-right:.25rem !important;margin-left:.25rem !important}.mx-xxl-2{margin-right:.5rem !important;margin-left:.5rem !important}.mx-xxl-3{margin-right:1rem !important;margin-left:1rem !important}.mx-xxl-4{margin-right:1.5rem !important;margin-left:1.5rem !important}.mx-xxl-5{margin-right:3rem !important;margin-left:3rem !important}.mx-xxl-auto{margin-right:auto !important;margin-left:auto !important}.my-xxl-0{margin-top:0 !important;margin-bottom:0 !important}.my-xxl-1{margin-top:.25rem !important;margin-bottom:.25rem !important}.my-xxl-2{margin-top:.5rem !important;margin-bottom:.5rem !important}.my-xxl-3{margin-top:1rem !important;margin-bottom:1rem !important}.my-xxl-4{margin-top:1.5rem !important;margin-bottom:1.5rem !important}.my-xxl-5{margin-top:3rem !important;margin-bottom:3rem !important}.my-xxl-auto{margin-top:auto !important;margin-bottom:auto !important}.mt-xxl-0{margin-top:0 !important}.mt-xxl-1{margin-top:.25rem !important}.mt-xxl-2{margin-top:.5rem !important}.mt-xxl-3{margin-top:1rem !important}.mt-xxl-4{margin-top:1.5rem !important}.mt-xxl-5{margin-top:3rem !important}.mt-xxl-auto{margin-top:auto !important}.me-xxl-0{margin-right:0 !important}.me-xxl-1{margin-right:.25rem !important}.me-xxl-2{margin-right:.5rem !important}.me-xxl-3{margin-right:1rem !important}.me-xxl-4{margin-right:1.5rem !important}.me-xxl-5{margin-right:3rem !important}.me-xxl-auto{margin-right:auto !important}.mb-xxl-0{margin-bottom:0 !important}.mb-xxl-1{margin-bottom:.25rem !important}.mb-xxl-2{margin-bottom:.5rem !important}.mb-xxl-3{margin-bottom:1rem !important}.mb-xxl-4{margin-bottom:1.5rem !important}.mb-xxl-5{margin-bottom:3rem !important}.mb-xxl-auto{margin-bottom:auto !important}.ms-xxl-0{margin-left:0 !important}.ms-xxl-1{margin-left:.25rem !important}.ms-xxl-2{margin-left:.5rem !important}.ms-xxl-3{margin-left:1rem !important}.ms-xxl-4{margin-left:1.5rem !important}.ms-xxl-5{margin-left:3rem !important}.ms-xxl-auto{margin-left:auto !important}.p-xxl-0{padding:0 !important}.p-xxl-1{padding:.25rem !important}.p-xxl-2{padding:.5rem !important}.p-xxl-3{padding:1rem !important}.p-xxl-4{padding:1.5rem !important}.p-xxl-5{padding:3rem !important}.px-xxl-0{padding-right:0 !important;padding-left:0 !important}.px-xxl-1{padding-right:.25rem !important;padding-left:.25rem !important}.px-xxl-2{padding-right:.5rem !important;padding-left:.5rem !important}.px-xxl-3{padding-right:1rem !important;padding-left:1rem !important}.px-xxl-4{padding-right:1.5rem !important;padding-left:1.5rem !important}.px-xxl-5{padding-right:3rem !important;padding-left:3rem !important}.py-xxl-0{padding-top:0 !important;padding-bottom:0 !important}.py-xxl-1{padding-top:.25rem !important;padding-bottom:.25rem !important}.py-xxl-2{padding-top:.5rem !important;padding-bottom:.5rem !important}.py-xxl-3{padding-top:1rem !important;padding-bottom:1rem !important}.py-xxl-4{padding-top:1.5rem !important;padding-bottom:1.5rem !important}.py-xxl-5{padding-top:3rem !important;padding-bottom:3rem !important}.pt-xxl-0{padding-top:0 !important}.pt-xxl-1{padding-top:.25rem !important}.pt-xxl-2{padding-top:.5rem !important}.pt-xxl-3{padding-top:1rem !important}.pt-xxl-4{padding-top:1.5rem !important}.pt-xxl-5{padding-top:3rem !important}.pe-xxl-0{padding-right:0 !important}.pe-xxl-1{padding-right:.25rem !important}.pe-xxl-2{padding-right:.5rem !important}.pe-xxl-3{padding-right:1rem !important}.pe-xxl-4{padding-right:1.5rem !important}.pe-xxl-5{padding-right:3rem !important}.pb-xxl-0{padding-bottom:0 !important}.pb-xxl-1{padding-bottom:.25rem !important}.pb-xxl-2{padding-bottom:.5rem !important}.pb-xxl-3{padding-bottom:1rem !important}.pb-xxl-4{padding-bottom:1.5rem !important}.pb-xxl-5{padding-bottom:3rem !important}.ps-xxl-0{padding-left:0 !important}.ps-xxl-1{padding-left:.25rem !important}.ps-xxl-2{padding-left:.5rem !important}.ps-xxl-3{padding-left:1rem !important}.ps-xxl-4{padding-left:1.5rem !important}.ps-xxl-5{padding-left:3rem !important}.gap-xxl-0{gap:0 !important}.gap-xxl-1{gap:.25rem !important}.gap-xxl-2{gap:.5rem !important}.gap-xxl-3{gap:1rem !important}.gap-xxl-4{gap:1.5rem !important}.gap-xxl-5{gap:3rem !important}.row-gap-xxl-0{row-gap:0 !important}.row-gap-xxl-1{row-gap:.25rem !important}.row-gap-xxl-2{row-gap:.5rem !important}.row-gap-xxl-3{row-gap:1rem !important}.row-gap-xxl-4{row-gap:1.5rem !important}.row-gap-xxl-5{row-gap:3rem !important}.column-gap-xxl-0{column-gap:0 !important}.column-gap-xxl-1{column-gap:.25rem !important}.column-gap-xxl-2{column-gap:.5rem !important}.column-gap-xxl-3{column-gap:1rem !important}.column-gap-xxl-4{column-gap:1.5rem !important}.column-gap-xxl-5{column-gap:3rem !important}.text-xxl-start{text-align:left !important}.text-xxl-end{text-align:right !important}.text-xxl-center{text-align:center !important}}@media(min-width: 1200px){.fs-1{font-size:2.5rem !important}.fs-2{font-size:2rem !important}.fs-3{font-size:1.75rem !important}.fs-4{font-size:1.5rem !important}}@media print{.d-print-inline{display:inline !important}.d-print-inline-block{display:inline-block !important}.d-print-block{display:block !important}.d-print-grid{display:grid !important}.d-print-inline-grid{display:inline-grid !important}.d-print-table{display:table !important}.d-print-table-row{display:table-row !important}.d-print-table-cell{display:table-cell !important}.d-print-flex{display:flex !important}.d-print-inline-flex{display:inline-flex !important}.d-print-none{display:none !important}}.navigation-50-opacity{background-color:rgba(13,110,253,.5) !important}*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;margin:0;padding:0}body{display:flex;flex-direction:column;min-height:100vh}header.navbar{flex-shrink:0;height:66px}.navigation-section{background-color:#fff;border-bottom:1px solid #dee2e6;padding:.5rem 0;flex-shrink:0}.nav-tabs{border-bottom:none}.nav-tabs .nav-link{border:1px solid rgba(0,0,0,0);border-bottom:none;border-radius:0;color:#6c757d;background-color:rgba(0,0,0,0);margin-right:.25rem;transition:all .2s ease}.nav-tabs .nav-link:hover{color:#495057;background-color:rgba(0,0,0,.05);border-color:rgba(0,0,0,0)}.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 rgba(0,0,0,0);font-weight:500}.nav-tabs .nav-item.dropdown .nav-link{background-color:rgba(0,0,0,0);border:1px solid rgba(0,0,0,0);border-radius:0}.nav-tabs .nav-item.dropdown .nav-link:hover{background-color:rgba(0,0,0,.05);border-color:rgba(0,0,0,0)}.nav-tabs .dropdown-menu{margin-top:.25rem;border-radius:.375rem;box-shadow:0 .125rem .25rem rgba(0,0,0,.075);min-width:200px;max-width:300px;width:auto}.nav-tabs .dropdown-menu .dropdown-item{white-space:normal;padding:.5rem 1rem;line-height:1.4}.nav-tabs .dropdown-menu .dropdown-item{overflow:hidden;text-overflow:ellipsis;max-width:280px}.breadcrumb-section{background-color:#f8f9fa;border-bottom:1px solid #dee2e6;flex-shrink:0}.breadcrumb-section .breadcrumb{margin-bottom:0}.breadcrumb-section .breadcrumb-item{color:#6c757d !important}.breadcrumb-section .breadcrumb-item a{color:#0d6efd !important;text-decoration:none}.breadcrumb-section .breadcrumb-item a:hover{color:#0a58ca !important;text-decoration:underline}.breadcrumb-section .breadcrumb-item.active{color:#6c757d !important}.main-content{flex:1;overflow-y:auto;padding:1rem 0}.markdown-content,.php-content,.html-content{margin-bottom:2rem}.markdown-content .content-body,.php-content .content-body,.html-content .content-body{padding:2rem;line-height:1.6}footer{flex-shrink:0}.navigation-section .btn{font-weight:500}.navigation-section .collapse .nav-link{color:#495057;padding:.375rem .75rem;border-radius:.25rem;transition:all .2s ease}.navigation-section .collapse .nav-link:hover{background-color:#e9ecef;color:#212529}.navigation-section .collapse .nav-link.active{background-color:#0d6efd;color:#fff}.navigation-section .collapse .nav-link i{margin-right:.5rem}.file-info{font-size:.9rem;color:#6c757d;flex:1;min-width:0}.file-info i{margin-right:5px}.page-title{color:#6c757d;max-width:250px;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;margin-right:10px}.page-title:hover{color:#495057;cursor:default}.file-details{color:#6c757d;font-size:.85rem}.site-info a{color:#0d6efd;text-decoration:none}.site-info a:hover{text-decoration:underline}.guide-link{color:#6c757d !important;text-decoration:none;font-size:.9rem}.guide-link:hover{color:#0d6efd !important;text-decoration:none}.guide-link i{font-size:1rem;color:#6c757d !important}.guide-link:hover i{color:#0d6efd !important}.auto-link{color:#0d6efd;text-decoration:none;border-bottom:2px dashed #0d6efd;font-weight:500;transition:all .2s ease}.auto-link:hover{color:#0a58ca;text-decoration:none;border-bottom-style:solid;border-bottom-color:#0a58ca}.search-form{max-width:300px}.card-title a{text-decoration:none;color:inherit}.card-title a:hover{text-decoration:underline}.dropdown-menu .dropdown-menu{top:0;left:100%;margin-top:-1px;margin-left:-1px}.dropdown-submenu{position:relative}.dropdown-submenu .dropdown-menu{top:0;left:100%;margin-top:-1px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropdown-submenu>.dropdown-toggle:active::after{transform:rotate(90deg)}@media(max-width: 768px){.main-content{padding-top:120px}.markdown-content .content-body,.php-content .content-body,.html-content .content-body{padding:1.5rem}.nav-tabs .dropdown-menu{min-width:180px;max-width:250px}.nav-tabs .dropdown-menu .dropdown-item{max-width:230px;font-size:.9rem}}@media(max-width: 576px){.markdown-content .content-body,.php-content .content-body,.html-content .content-body{padding:1rem}.nav-tabs .dropdown-menu{min-width:160px;max-width:200px}.nav-tabs .dropdown-menu .dropdown-item{max-width:180px;font-size:.85rem;padding:.4rem .8rem}}/*# sourceMappingURL=style.css.map */
diff --git a/public/assets/css/style.css.map b/public/assets/css/style.css.map
new file mode 100644
index 0000000..6993769
--- /dev/null
+++ b/public/assets/css/style.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../../../node_modules/bootstrap/scss/mixins/_banner.scss","../../../node_modules/bootstrap/scss/_root.scss","../../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../../node_modules/bootstrap/scss/_reboot.scss","../../../node_modules/bootstrap/scss/_variables.scss","../../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../../../node_modules/bootstrap/scss/_type.scss","../../../node_modules/bootstrap/scss/mixins/_lists.scss","../../../node_modules/bootstrap/scss/_images.scss","../../../node_modules/bootstrap/scss/mixins/_image.scss","../../../node_modules/bootstrap/scss/_containers.scss","../../../node_modules/bootstrap/scss/mixins/_container.scss","../../../node_modules/bootstrap/scss/mixins/_breakpoints.scss","../../../node_modules/bootstrap/scss/_grid.scss","../../../node_modules/bootstrap/scss/mixins/_grid.scss","../../../node_modules/bootstrap/scss/_tables.scss","../../../node_modules/bootstrap/scss/mixins/_table-variants.scss","../../../node_modules/bootstrap/scss/forms/_labels.scss","../../../node_modules/bootstrap/scss/forms/_form-text.scss","../../../node_modules/bootstrap/scss/forms/_form-control.scss","../../../node_modules/bootstrap/scss/mixins/_transition.scss","../../../node_modules/bootstrap/scss/mixins/_gradients.scss","../../../node_modules/bootstrap/scss/forms/_form-select.scss","../../../node_modules/bootstrap/scss/forms/_form-check.scss","../../../src/scss/main.scss","../../../node_modules/bootstrap/scss/forms/_form-range.scss","../../../node_modules/bootstrap/scss/forms/_floating-labels.scss","../../../node_modules/bootstrap/scss/forms/_input-group.scss","../../../node_modules/bootstrap/scss/mixins/_forms.scss","../../../node_modules/bootstrap/scss/_buttons.scss","../../../node_modules/bootstrap/scss/mixins/_buttons.scss","../../../node_modules/bootstrap/scss/_transitions.scss","../../../node_modules/bootstrap/scss/_dropdown.scss","../../../node_modules/bootstrap/scss/mixins/_caret.scss","../../../node_modules/bootstrap/scss/_button-group.scss","../../../node_modules/bootstrap/scss/_nav.scss","../../../node_modules/bootstrap/scss/_navbar.scss","../../../node_modules/bootstrap/scss/_card.scss","../../../node_modules/bootstrap/scss/_accordion.scss","../../../node_modules/bootstrap/scss/_breadcrumb.scss","../../../node_modules/bootstrap/scss/_pagination.scss","../../../node_modules/bootstrap/scss/mixins/_pagination.scss","../../../node_modules/bootstrap/scss/_badge.scss","../../../node_modules/bootstrap/scss/_alert.scss","../../../node_modules/bootstrap/scss/_progress.scss","../../../node_modules/bootstrap/scss/_list-group.scss","../../../node_modules/bootstrap/scss/_close.scss","../../../node_modules/bootstrap/scss/_toasts.scss","../../../node_modules/bootstrap/scss/_modal.scss","../../../node_modules/bootstrap/scss/mixins/_backdrop.scss","../../../node_modules/bootstrap/scss/_tooltip.scss","../../../node_modules/bootstrap/scss/mixins/_reset-text.scss","../../../node_modules/bootstrap/scss/_popover.scss","../../../node_modules/bootstrap/scss/_carousel.scss","../../../node_modules/bootstrap/scss/mixins/_clearfix.scss","../../../node_modules/bootstrap/scss/_spinners.scss","../../../node_modules/bootstrap/scss/_offcanvas.scss","../../../node_modules/bootstrap/scss/_placeholders.scss","../../../node_modules/bootstrap/scss/helpers/_color-bg.scss","../../../node_modules/bootstrap/scss/helpers/_colored-links.scss","../../../node_modules/bootstrap/scss/helpers/_focus-ring.scss","../../../node_modules/bootstrap/scss/helpers/_icon-link.scss","../../../node_modules/bootstrap/scss/helpers/_ratio.scss","../../../node_modules/bootstrap/scss/helpers/_position.scss","../../../node_modules/bootstrap/scss/helpers/_stacks.scss","../../../node_modules/bootstrap/scss/helpers/_visually-hidden.scss","../../../node_modules/bootstrap/scss/mixins/_visually-hidden.scss","../../../node_modules/bootstrap/scss/helpers/_stretched-link.scss","../../../node_modules/bootstrap/scss/helpers/_text-truncation.scss","../../../node_modules/bootstrap/scss/mixins/_text-truncate.scss","../../../node_modules/bootstrap/scss/helpers/_vr.scss","../../../node_modules/bootstrap/scss/mixins/_utilities.scss","../../../node_modules/bootstrap/scss/utilities/_api.scss"],"names":[],"mappings":"CACE;AAAA;AAAA;AAAA;AAAA,GCDF,4BASI,mRAIA,+MAIA,yKAIA,8OAIA,8VAIA,gWAIA,iXAGF,8BACA,wBAMA,sNACA,0GACA,0FAOA,iDC2OI,oBALI,KDpOR,2BACA,2BAKA,yBACA,gCACA,mBACA,gCAEA,0BACA,iCAEA,6CACA,qCACA,2BACA,qCAEA,2CACA,oCACA,0BACA,oCAGA,4BAEA,yBACA,kCACA,gCAEA,4CACA,uCAMA,yBACA,8BACA,0CAGA,uBACA,yBACA,2BACA,oDAEA,6BACA,+BACA,8BACA,4BACA,6BACA,oDACA,+BAGA,mDACA,4DACA,qDACA,4DAIA,+BACA,8BACA,gDAIA,+BACA,sCACA,iCACA,wCEhHE,qBFsHA,kBAGA,yBACA,mCACA,sBACA,6BAEA,0BACA,uCAEA,gDACA,wCACA,2BACA,kCAEA,8CACA,uCACA,wCACA,iCAGE,iXAIA,2TAIA,kWAGF,4BAEA,wCACA,kDACA,mCACA,yCAEA,0CACA,8BACA,uCAEA,2BACA,yDAEA,4CACA,mDACA,gDACA,uDGxKJ,qBAGE,sBAeE,8CANJ,MAOM,wBAcN,KACE,SACA,uCF6OI,UALI,yBEtOR,uCACA,uCACA,2BACA,qCACA,mCACA,8BACA,0CASF,GACE,cACA,MCmnB4B,QDlnB5B,SACA,wCACA,QCynB4B,ID/mB9B,0CACE,aACA,cCwjB4B,MDrjB5B,YCwjB4B,IDvjB5B,YCwjB4B,IDvjB5B,8BAGF,OFuMQ,iCA5JJ,0BE3CJ,OF8MQ,kBEzMR,OFkMQ,iCA5JJ,0BEtCJ,OFyMQ,gBEpMR,OF6LQ,+BA5JJ,0BEjCJ,OFoMQ,mBE/LR,OFwLQ,iCA5JJ,0BE5BJ,OF+LQ,kBE1LR,OF+KM,UALI,QErKV,OF0KM,UALI,KE1JV,EACE,aACA,cCwV0B,KD9U5B,YACE,iCACA,YACA,8BAMF,QACE,mBACA,kBACA,oBAMF,MAEE,kBAGF,SAGE,aACA,mBAGF,wBAIE,gBAGF,GACE,YC6b4B,IDxb9B,GACE,oBACA,cAMF,WACE,gBAQF,SAEE,YCsa4B,OD9Z9B,aF6EM,UALI,QEjEV,WACE,QCqf4B,QDpf5B,gCACA,wCASF,QAEE,kBFwDI,UALI,OEjDR,cACA,wBAGF,mBACA,eAKA,EACE,gEACA,gBCgNwC,UD9MxC,QACE,oDAWF,4DAEE,cACA,qBAOJ,kBAIE,YCgV4B,yBHlUxB,UALI,IEDV,IACE,cACA,aACA,mBACA,cFEI,UALI,QEQR,SFHI,UALI,QEUN,cACA,kBAIJ,KFVM,UALI,QEiBR,2BACA,qBAGA,OACE,cAIJ,IACE,yBFtBI,UALI,QE6BR,MC25CkC,kBD15ClC,iBC25CkC,qBChsDhC,qBFwSF,QACE,UF7BE,UALI,IE6CV,OACE,gBAMF,QAEE,sBAQF,MACE,oBACA,yBAGF,QACE,YC4X4B,MD3X5B,eC2X4B,MD1X5B,MC4Z4B,0BD3Z5B,gBAOF,GAEE,mBACA,gCAGF,2BAME,qBACA,mBACA,eAQF,MACE,qBAMF,OAEE,gBAQF,iCACE,UAKF,sCAKE,SACA,oBF5HI,UALI,QEmIR,oBAIF,cAEE,oBAKF,cACE,eAGF,OAGE,iBAGA,gBACE,UAOJ,0IACE,wBAQF,gDAIE,0BAGE,4GACE,eAON,mBACE,UACA,kBAKF,SACE,gBAUF,SACE,YACA,UACA,SACA,SAQF,OACE,WACA,WACA,UACA,cCmN4B,MDjN5B,oBFnNM,iCA5JJ,0BEyWJ,OFtMQ,kBE+MN,SACE,WAOJ,+OAOE,UAGF,4BACE,YASF,cACE,6BACA,oBAGA,4CACE,eACA,oBAoBJ,4BACE,wBAKF,+BACE,UAOF,uBACE,aACA,0BAKF,OACE,qBAKF,OACE,SAOF,QACE,kBACA,eAQF,SACE,wBAQF,SACE,wBG3kBF,MLmQM,UALI,QK5PR,YFwoB4B,IEnoB5B,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,gBKvQN,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,kBKvQN,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,gBKvQN,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,kBKvQN,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,gBKvQN,WAGE,YF0nBkB,IEznBlB,YFymB0B,IH7WtB,iCA5JJ,0BKpGF,WLuQM,kBK/OR,eCvDE,eACA,gBD2DF,aC5DE,eACA,gBD8DF,kBACE,qBAEA,mCACE,aFsoB0B,ME5nB9B,YL8MM,UALI,QKvMR,yBAIF,YACE,cFiUO,KH1HH,UALI,QK/LR,wBACE,gBAIJ,mBACE,iBACA,cFuTO,KH1HH,UALI,QKtLR,MFtFS,QEwFT,2BACE,aEhGJ,WCIE,eAGA,YDDF,eACE,QJ+jDkC,OI9jDlC,iBJ+jDkC,kBI9jDlC,2DHGE,sCIRF,eAGA,YDcF,QAEE,qBAGF,YACE,oBACA,cAGF,gBPyPM,UALI,QOlPR,MJkjDkC,0BMplDlC,mGCHA,sBACA,iBACA,WACA,0CACA,yCACA,kBACA,iBCsDE,yBF5CE,yBACE,UNkee,OQvbnB,yBF5CE,uCACE,UNkee,OQvbnB,yBF5CE,qDACE,UNkee,OQvbnB,0BF5CE,mEACE,UNkee,QQvbnB,0BF5CE,kFACE,UNkee,QSlfvB,MAEI,2JAKF,KCNA,sBACA,iBACA,aACA,eAEA,uCACA,2CACA,0CDEE,OCOF,cACA,WACA,eACA,0CACA,yCACA,8BA+CI,KACE,WAGF,iBApCJ,cACA,WAcA,cACE,cACA,WAFF,cACE,cACA,UAFF,cACE,cACA,mBAFF,cACE,cACA,UAFF,cACE,cACA,UAFF,cACE,cACA,mBA+BE,UAhDJ,cACA,WAqDQ,OAhEN,cACA,kBA+DM,OAhEN,cACA,mBA+DM,OAhEN,cACA,UA+DM,OAhEN,cACA,mBA+DM,OAhEN,cACA,mBA+DM,OAhEN,cACA,UA+DM,OAhEN,cACA,mBA+DM,OAhEN,cACA,mBA+DM,OAhEN,cACA,UA+DM,QAhEN,cACA,mBA+DM,QAhEN,cACA,mBA+DM,QAhEN,cACA,WAuEQ,UAxDV,wBAwDU,UAxDV,yBAwDU,UAxDV,gBAwDU,UAxDV,yBAwDU,UAxDV,yBAwDU,UAxDV,gBAwDU,UAxDV,yBAwDU,UAxDV,yBAwDU,UAxDV,gBAwDU,WAxDV,yBAwDU,WAxDV,yBAmEM,WAEE,iBAGF,WAEE,iBAPF,WAEE,uBAGF,WAEE,uBAPF,WAEE,sBAGF,WAEE,sBAPF,WAEE,oBAGF,WAEE,oBAPF,WAEE,sBAGF,WAEE,sBAPF,WAEE,oBAGF,WAEE,oBF1DN,yBEUE,QACE,WAGF,oBApCJ,cACA,WAcA,iBACE,cACA,WAFF,iBACE,cACA,UAFF,iBACE,cACA,mBAFF,iBACE,cACA,UAFF,iBACE,cACA,UAFF,iBACE,cACA,mBA+BE,aAhDJ,cACA,WAqDQ,UAhEN,cACA,kBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,WAuEQ,aAxDV,cAwDU,aAxDV,wBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAmEM,iBAEE,iBAGF,iBAEE,iBAPF,iBAEE,uBAGF,iBAEE,uBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,oBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,qBF1DN,yBEUE,QACE,WAGF,oBApCJ,cACA,WAcA,iBACE,cACA,WAFF,iBACE,cACA,UAFF,iBACE,cACA,mBAFF,iBACE,cACA,UAFF,iBACE,cACA,UAFF,iBACE,cACA,mBA+BE,aAhDJ,cACA,WAqDQ,UAhEN,cACA,kBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,WAuEQ,aAxDV,cAwDU,aAxDV,wBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAmEM,iBAEE,iBAGF,iBAEE,iBAPF,iBAEE,uBAGF,iBAEE,uBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,oBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,qBF1DN,yBEUE,QACE,WAGF,oBApCJ,cACA,WAcA,iBACE,cACA,WAFF,iBACE,cACA,UAFF,iBACE,cACA,mBAFF,iBACE,cACA,UAFF,iBACE,cACA,UAFF,iBACE,cACA,mBA+BE,aAhDJ,cACA,WAqDQ,UAhEN,cACA,kBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,WAuEQ,aAxDV,cAwDU,aAxDV,wBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAmEM,iBAEE,iBAGF,iBAEE,iBAPF,iBAEE,uBAGF,iBAEE,uBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,oBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,qBF1DN,0BEUE,QACE,WAGF,oBApCJ,cACA,WAcA,iBACE,cACA,WAFF,iBACE,cACA,UAFF,iBACE,cACA,mBAFF,iBACE,cACA,UAFF,iBACE,cACA,UAFF,iBACE,cACA,mBA+BE,aAhDJ,cACA,WAqDQ,UAhEN,cACA,kBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,mBA+DM,UAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,WAuEQ,aAxDV,cAwDU,aAxDV,wBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,aAxDV,yBAwDU,aAxDV,yBAwDU,aAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAmEM,iBAEE,iBAGF,iBAEE,iBAPF,iBAEE,uBAGF,iBAEE,uBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,oBAPF,iBAEE,sBAGF,iBAEE,sBAPF,iBAEE,oBAGF,iBAEE,qBF1DN,0BEUE,SACE,WAGF,qBApCJ,cACA,WAcA,kBACE,cACA,WAFF,kBACE,cACA,UAFF,kBACE,cACA,mBAFF,kBACE,cACA,UAFF,kBACE,cACA,UAFF,kBACE,cACA,mBA+BE,cAhDJ,cACA,WAqDQ,WAhEN,cACA,kBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,UA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,mBA+DM,WAhEN,cACA,UA+DM,YAhEN,cACA,mBA+DM,YAhEN,cACA,mBA+DM,YAhEN,cACA,WAuEQ,cAxDV,cAwDU,cAxDV,wBAwDU,cAxDV,yBAwDU,cAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAwDU,cAxDV,gBAwDU,cAxDV,yBAwDU,cAxDV,yBAwDU,cAxDV,gBAwDU,eAxDV,yBAwDU,eAxDV,yBAmEM,mBAEE,iBAGF,mBAEE,iBAPF,mBAEE,uBAGF,mBAEE,uBAPF,mBAEE,sBAGF,mBAEE,sBAPF,mBAEE,oBAGF,mBAEE,oBAPF,mBAEE,sBAGF,mBAEE,sBAPF,mBAEE,oBAGF,mBAEE,qBCrHV,OAEE,+BACA,4BACA,gCACA,6BAEA,2CACA,iCACA,gDACA,kCACA,mDACA,gEACA,kDACA,8DACA,iDACA,+DAEA,WACA,cXkYO,KWjYP,eXusB4B,IWtsB5B,0CAOA,yBACE,oBAEA,qFACA,oCACA,oBX+sB0B,uBW9sB1B,2GAGF,aACE,uBAGF,aACE,sBAIJ,qBACE,6DAOF,aACE,iBAUA,4BACE,sBAeF,gCACE,sCAGA,kCACE,sCAOJ,oCACE,sBAGF,qCACE,mBAUF,2CACE,qDACA,+CAMF,yDACE,qDACA,+CAQJ,cACE,qDACA,+CAQA,8BACE,oDACA,8CC5IF,eAOE,uBACA,sCACA,oDACA,kDACA,+BACA,iDACA,8BACA,mDACA,6BAEA,4BACA,0CAlBF,iBAOE,uBACA,sCACA,oDACA,mDACA,+BACA,iDACA,8BACA,mDACA,6BAEA,4BACA,0CAlBF,eAOE,uBACA,oCACA,mDACA,mDACA,+BACA,gDACA,8BACA,mDACA,6BAEA,4BACA,0CAlBF,YAOE,uBACA,sCACA,oDACA,kDACA,+BACA,iDACA,8BACA,iDACA,6BAEA,4BACA,0CAlBF,eAOE,uBACA,sCACA,kDACA,mDACA,+BACA,iDACA,8BACA,oDACA,6BAEA,4BACA,0CAlBF,cAOE,uBACA,sCACA,oDACA,kDACA,+BACA,iDACA,8BACA,kDACA,6BAEA,4BACA,0CAlBF,aAOE,uBACA,uBACA,gDACA,iDACA,+BACA,6CACA,8BACA,iDACA,6BAEA,4BACA,0CAlBF,YAOE,uBACA,uBACA,+CACA,6CACA,+BACA,4CACA,8BACA,8CACA,6BAEA,4BACA,0CDiJA,kBACE,gBACA,iCH3FF,4BGyFA,qBACE,gBACA,kCH3FF,4BGyFA,qBACE,gBACA,kCH3FF,4BGyFA,qBACE,gBACA,kCH3FF,6BGyFA,qBACE,gBACA,kCH3FF,6BGyFA,sBACE,gBACA,kCEnKN,YACE,cbu2BsC,Ma91BxC,gBACE,oDACA,uDACA,gBhB8QI,UALI,QgBrQR,Yb+lB4B,Ia3lB9B,mBACE,kDACA,qDhBoQI,UALI,QgB3PV,mBACE,mDACA,sDhB8PI,UALI,SiBtRV,WACE,Wd+1BsC,OHrkBlC,UALI,QiBjRR,Md+1BsC,0Bep2BxC,cACE,cACA,WACA,uBlBwRI,UALI,KkBhRR,YfkmB4B,IejmB5B,YfymB4B,IexmB5B,Mf43BsC,qBe33BtC,gBACA,iBfq3BsC,kBep3BtC,4BACA,2DdGE,sCeHE,WDMJ,0DCFI,uCDhBN,cCiBQ,iBDGN,yBACE,gBAEA,wDACE,eAKJ,oBACE,Mfs2BoC,qBer2BpC,iBfg2BoC,kBe/1BpC,af82BoC,mBe72BpC,UAKE,WfkhBkB,kCe9gBtB,2CAME,eAMA,aAKA,SAKF,qCACE,cACA,UAIF,2BACE,Mf40BoC,0Be10BpC,UAQF,uBAEE,iBf8yBoC,uBe3yBpC,UAIF,oCACE,uBACA,0BACA,kBforB0B,OenrB1B,MfsyBoC,qBiBp4BtC,iBjBqiCgC,sBer8B9B,oBACA,qBACA,mBACA,eACA,wBfgsB0B,uBe/rB1B,gBCzFE,WD0FF,mHCtFE,uCD0EJ,oCCzEM,iBDwFN,yEACE,iBf47B8B,uBen7BlC,wBACE,cACA,WACA,kBACA,gBACA,Yfwf4B,Ievf5B,Mf2xBsC,qBe1xBtC,+BACA,2BACA,sCAEA,8BACE,UAGF,gFAEE,gBACA,eAWJ,iBACE,Wf4wBsC,wDe3wBtC,qBlByII,UALI,SIvQN,yCcuIF,uCACE,qBACA,wBACA,kBfooB0B,MehoB9B,iBACE,WfgwBsC,sDe/vBtC,mBlB4HI,UALI,QIvQN,yCcoJF,uCACE,mBACA,qBACA,kBf2nB0B,KennB5B,sBACE,Wf6uBoC,yDe1uBtC,yBACE,Wf0uBoC,wDevuBtC,yBACE,WfuuBoC,sDeluBxC,oBACE,MfquBsC,KepuBtC,Of8tBsC,yDe7tBtC,QfilB4B,Qe/kB5B,mDACE,eAGF,uCACE,oBdvLA,sCc2LF,0CACE,oBd5LA,sCcgMF,2Cf8sBsC,wDe7sBtC,2Cf8sBsC,sDkB75BxC,aACE,yPAEA,cACA,WACA,uCrBqRI,UALI,KqB7QR,YlB+lB4B,IkB9lB5B,YlBsmB4B,IkBrmB5B,MlBy3BsC,qBkBx3BtC,gBACA,iBlBk3BsC,kBkBj3BtC,kFACA,4BACA,oBlB+9BkC,oBkB99BlC,gBlB+9BkC,UkB99BlC,2DjBHE,sCeHE,WESJ,0DFLI,uCEfN,aFgBQ,iBEMN,mBACE,alBs3BoC,mBkBr3BpC,UAKE,WlBi+B4B,kCkB79BhC,0DAEE,clB6uB0B,OkB5uB1B,sBAGF,sBAEE,iBlBu1BoC,uBkBl1BtC,4BACE,oBACA,uCAIJ,gBACE,YlBsuB4B,OkBruB5B,elBquB4B,OkBpuB5B,alBquB4B,MHlgBxB,UALI,SIvQN,yCiB8CJ,gBACE,YlBkuB4B,MkBjuB5B,elBiuB4B,MkBhuB5B,alBiuB4B,KHtgBxB,UALI,QIvQN,yCiBwDA,kCACE,yPCxEN,YACE,cACA,WnBq6BwC,OmBp6BxC,anBq6BwC,MmBp6BxC,cnBq6BwC,QmBn6BxC,8BACE,WACA,mBAIJ,oBACE,cnB25BwC,MmB15BxC,eACA,iBAEA,sCACE,YACA,oBACA,cAIJ,kBACE,sCAEA,cACA,MnB04BwC,ImBz4BxC,OnBy4BwC,ImBx4BxC,iBACA,mBACA,gBACA,yCACA,+CACA,4BACA,2BACA,wBACA,OnB24BwC,oDmB14BxC,yBAGA,iClB3BE,oBkB+BF,8BAEE,cnBm4BsC,ImBh4BxC,yBACE,OnB03BsC,gBmBv3BxC,wBACE,anBs1BoC,mBmBr1BpC,UACA,WnB8foB,kCmB3ftB,0BACE,iBCjEM,QDkEN,aClEM,QDoEN,yCAII,wPAIJ,sCAII,gKAKN,+CACE,iBCtFM,QDuFN,aCvFM,QD4FJ,kPAIJ,2BACE,oBACA,YACA,QnBk2BuC,GmB31BvC,2FACE,eACA,QnBy1BqC,GmB30B3C,aACE,anBo1BgC,MmBl1BhC,+BACE,4KAEA,MnB80B8B,ImB70B9B,mBACA,0CACA,gClBjHA,kBeHE,WGsHF,qCHlHE,uCG0GJ,+BHzGM,iBGmHJ,qCACE,6KAGF,uCACE,oBnB60B4B,amBx0B1B,2JAKN,gCACE,cnBwzB8B,MmBvzB9B,eAEA,kDACE,oBACA,cAKN,mBACE,qBACA,anBsyBgC,KmBnyBlC,WACE,kBACA,sBACA,oBAIE,mDACE,oBACA,YACA,QnBspBwB,ImB/oB1B,8EACE,kLEnLN,YACE,WACA,cACA,UACA,gBACA,+BAEA,kBACE,UAIA,mDrB8gCuC,iDqB7gCvC,+CrB6gCuC,iDqB1gCzC,8BACE,SAGF,kCACE,MrB+/BuC,KqB9/BvC,OrB8/BuC,KqB7/BvC,oBACA,gBJ1BF,iBGHQ,QC+BN,OrB6/BuC,EC1gCvC,mBeHE,WKmBF,4FLfE,uCKMJ,kCLLM,iBKgBJ,yCJjCF,iBjB8hCyC,uBqBx/BzC,2CACE,MrBw+B8B,KqBv+B9B,OrBw+B8B,MqBv+B9B,oBACA,OrBu+B8B,QqBt+B9B,iBrBu+B8B,uBqBt+B9B,2BpB7BA,mBoBkCF,8BACE,MrBo+BuC,KqBn+BvC,OrBm+BuC,KqBl+BvC,gBJpDF,iBGHQ,QCyDN,OrBm+BuC,EC1gCvC,mBeHE,WK6CF,4FLzCE,uCKiCJ,8BLhCM,iBK0CJ,qCJ3DF,iBjB8hCyC,uBqB99BzC,8BACE,MrB88B8B,KqB78B9B,OrB88B8B,MqB78B9B,oBACA,OrB68B8B,QqB58B9B,iBrB68B8B,uBqB58B9B,2BpBvDA,mBoB4DF,qBACE,oBAEA,2CACE,iBrBg9BqC,0BqB78BvC,uCACE,iBrB48BqC,0BsBniC3C,eACE,kBAEA,gGAGE,OtBwiCoC,gDsBviCpC,WtBuiCoC,gDsBtiCpC,YtBuiCoC,KsBpiCtC,qBACE,kBACA,MACA,OACA,UACA,eACA,YACA,oBACA,gBACA,2CACA,iBACA,uBACA,mBACA,oBACA,kDACA,qBNVE,WMWF,kDNPE,uCMTJ,qBNUM,iBMSN,oEAEE,oBAEA,8FACE,oBAGF,oMAEE,YtB0gCkC,SsBzgClC,etB0gCkC,QsBvgCpC,sGACE,YtBqgCkC,SsBpgClC,etBqgCkC,QsBjgCtC,4BACE,YtB+/BoC,SsB9/BpC,etB+/BoC,QsB9/BpC,atBguB0B,OsBztB1B,mLACE,UtBy/BkC,oDsBp/BpC,oDACE,UtBm/BkC,oDsB9+BpC,wGACE,kBACA,mBACA,WACA,OtBw+BkC,MsBv+BlC,WACA,iBtBqzBkC,kBCh3BpC,sCqB+DF,8CACE,iBtBkzBoC,uBsB9yBpC,6CACE,sCAIJ,2EAEE,MtBhFO,QuBVX,aACE,kBACA,aACA,eACA,oBACA,WAEA,iFAGE,kBACA,cACA,SACA,YAIF,0GAGE,UAMF,kBACE,kBACA,UAEA,wBACE,UAWN,kBACE,aACA,mBACA,uB1B8OI,UALI,K0BvOR,YvByjB4B,IuBxjB5B,YvBgkB4B,IuB/jB5B,MvBm1BsC,qBuBl1BtC,kBACA,mBACA,iBvB06BsC,sBuBz6BtC,2DtBtCE,sCsBgDJ,kHAIE,mB1BwNI,UALI,QIvQN,yCsByDJ,kHAIE,qB1B+MI,UALI,SIvQN,yCsBkEJ,0DAEE,mBAaE,wVtBjEA,0BACA,6BsByEA,yUtB1EA,0BACA,6BsBsFF,0IACE,4CtB1EA,yBACA,4BsB6EF,uHtB9EE,yBACA,4BuBxBF,gBACE,aACA,WACA,WxBu0BoC,OHrkBlC,UALI,Q2B1PN,MxBkjCqB,2BwB/iCvB,eACE,kBACA,SACA,UACA,aACA,eACA,qBACA,iB3BqPE,UALI,S2B7ON,MxBqiCqB,KwBpiCrB,iBxBoiCqB,kBC/jCrB,sCuBgCA,8HAEE,cA/CF,0DAqDE,axBuhCmB,kCwBphCjB,cxB81BgC,sBwB71BhC,0PACA,4BACA,2DACA,gEAGF,sEACE,axB4gCiB,kCwBvgCf,WxBugCe,+CwB5kCrB,0EA+EI,cxBu0BgC,sBwBt0BhC,kFAhFJ,wDAuFE,axBq/BmB,kCwBl/BjB,4NAEE,mQACA,cxBq5B8B,SwBp5B9B,6DACA,0EAIJ,oEACE,axBw+BiB,kCwBn+Bf,WxBm+Be,+CwB5kCrB,sEAkHI,yCAlHJ,kEAyHE,axBm9BmB,kCwBj9BnB,kFACE,iBxBg9BiB,2BwB78BnB,8EACE,WxB48BiB,+CwBz8BnB,sGACE,MxBw8BiB,2BwBn8BrB,qDACE,iBA1IF,kVAoJM,UAhIR,kBACE,aACA,WACA,WxBu0BoC,OHrkBlC,UALI,Q2B1PN,MxBkjCqB,6BwB/iCvB,iBACE,kBACA,SACA,UACA,aACA,eACA,qBACA,iB3BqPE,UALI,S2B7ON,MxBqiCqB,KwBpiCrB,iBxBoiCqB,iBC/jCrB,sCuBgCA,8IAEE,cA/CF,8DAqDE,axBuhCmB,oCwBphCjB,cxB81BgC,sBwB71BhC,4UACA,4BACA,2DACA,gEAGF,0EACE,axB4gCiB,oCwBvgCf,WxBugCe,8CwB5kCrB,8EA+EI,cxBu0BgC,sBwBt0BhC,kFAhFJ,4DAuFE,axBq/BmB,oCwBl/BjB,oOAEE,qVACA,cxBq5B8B,SwBp5B9B,6DACA,0EAIJ,wEACE,axBw+BiB,oCwBn+Bf,WxBm+Be,8CwB5kCrB,0EAkHI,yCAlHJ,sEAyHE,axBm9BmB,oCwBj9BnB,sFACE,iBxBg9BiB,6BwB78BnB,kFACE,WxB48BiB,8CwBz8BnB,0GACE,MxBw8BiB,6BwBn8BrB,uDACE,iBA1IF,8VAsJM,UCxJV,KAEE,4BACA,6BACA,uB5BuRI,mBALI,K4BhRR,0BACA,0BACA,qCACA,yBACA,8CACA,mCACA,gDACA,yCACA,6FACA,gCACA,kFAGA,qBACA,wDACA,sC5BsQI,UALI,wB4B/PR,sCACA,sCACA,0BACA,kBACA,qBAEA,sBACA,eACA,iBACA,mExBjBE,0CgBfF,iBQkCqB,iBTtBjB,WSwBJ,mHTpBI,uCShBN,KTiBQ,iBSqBN,WACE,gCAEA,wCACA,8CAGF,sBAEE,0BACA,kCACA,wCAGF,mBACE,gCRrDF,iBQsDuB,uBACrB,8CACA,UAKE,0CAIJ,8BACE,8CACA,UAKE,0CAIJ,mGAKE,iCACA,yCAGA,+CAGA,yKAKI,0CAKN,sCAKI,0CAIJ,mDAGE,mCACA,oBACA,2CAEA,iDACA,uCAYF,aC/GA,qBACA,qBACA,+BACA,2BACA,4CACA,kDACA,wCACA,4BACA,yCACA,sDACA,6DACA,8BACA,8BACA,wCDkGA,eC/GA,qBACA,qBACA,+BACA,2BACA,4CACA,kDACA,yCACA,4BACA,yCACA,oDACA,6DACA,8BACA,8BACA,wCDkGA,aC/GA,qBACA,qBACA,+BACA,2BACA,4CACA,gDACA,wCACA,4BACA,uCACA,qDACA,6DACA,8BACA,8BACA,wCDkGA,UC/GA,qBACA,qBACA,+BACA,2BACA,6CACA,qDACA,wCACA,4BACA,0CACA,sDACA,6DACA,8BACA,8BACA,wCDkGA,aC/GA,qBACA,qBACA,+BACA,2BACA,yCACA,mDACA,uCACA,4BACA,0CACA,oDACA,6DACA,8BACA,8BACA,wCDkGA,YC/GA,qBACA,qBACA,+BACA,2BACA,0CACA,kDACA,uCACA,4BACA,yCACA,qDACA,6DACA,8BACA,8BACA,wCDkGA,WC/GA,qBACA,qBACA,+BACA,2BACA,6CACA,oDACA,yCACA,4BACA,2CACA,sDACA,6DACA,8BACA,8BACA,wCDkGA,UC/GA,qBACA,qBACA,+BACA,2BACA,yCACA,mDACA,sCACA,4BACA,0CACA,oDACA,6DACA,8BACA,8BACA,wCD4HA,qBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,wCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,uBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,yCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,qBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,uCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,kBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,wCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,qBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,uCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,oBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,uCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,mBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,yCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBDmGA,kBChHA,wBACA,+BACA,2BACA,2BACA,qCACA,sCACA,4BACA,4BACA,sCACA,6DACA,iCACA,kCACA,wCACA,oBD+GF,UACE,0BACA,qCACA,yBACA,mCACA,iDACA,yCACA,kDACA,0CACA,iCACA,4CACA,gCACA,wCAEA,gBzB8QwC,UyBpQxC,wBACE,0BAGF,gBACE,gCAWJ,2BCjJE,2BACA,yB7B8NI,mBALI,Q6BvNR,mDDkJF,2BCrJE,4BACA,2B7B8NI,mBALI,S6BvNR,mDCnEF,MXgBM,WWfJ,oBXmBI,uCWpBN,MXqBQ,iBWlBN,iBACE,UAMF,qBACE,aAIJ,YACE,SACA,gBXDI,WWEJ,iBXEI,uCWLN,YXMQ,iBWDN,gCACE,QACA,YXNE,WWOF,gBXHE,uEACE,iBYpBR,sEAME,kBAGF,iBACE,mBCwBE,wBACE,qBACA,Y7B6hBwB,O6B5hBxB,e7B2hBwB,O6B1hBxB,WArCJ,sBACA,sCACA,gBACA,qCA0DE,8BACE,cD9CN,eAEE,2BACA,+BACA,2BACA,gCACA,+B/BuQI,wBALI,K+BhQR,0CACA,oCACA,+DACA,qDACA,mDACA,0FACA,6DACA,uCACA,+CACA,+CACA,qDACA,mDACA,sCACA,sCACA,4DACA,mCACA,sCACA,oCACA,qCACA,uCAGA,kBACA,kCACA,aACA,uCACA,kEACA,S/B0OI,UALI,6B+BnOR,+BACA,gBACA,gBACA,uCACA,4BACA,6E3BzCE,+C2B6CF,+BACE,SACA,OACA,qCAwBA,qBACE,qBAEA,qCACE,WACA,OAIJ,mBACE,mBAEA,mCACE,QACA,UpB1CJ,yBoB4BA,wBACE,qBAEA,wCACE,WACA,OAIJ,sBACE,mBAEA,sCACE,QACA,WpB1CJ,yBoB4BA,wBACE,qBAEA,wCACE,WACA,OAIJ,sBACE,mBAEA,sCACE,QACA,WpB1CJ,yBoB4BA,wBACE,qBAEA,wCACE,WACA,OAIJ,sBACE,mBAEA,sCACE,QACA,WpB1CJ,0BoB4BA,wBACE,qBAEA,wCACE,WACA,OAIJ,sBACE,mBAEA,sCACE,QACA,WpB1CJ,0BoB4BA,yBACE,qBAEA,yCACE,WACA,OAIJ,uBACE,mBAEA,uCACE,QACA,WAUN,uCACE,SACA,YACA,aACA,wCCpFA,gCACE,qBACA,Y7B6hBwB,O6B5hBxB,e7B2hBwB,O6B1hBxB,WA9BJ,aACA,sCACA,yBACA,qCAmDE,sCACE,cDgEJ,wCACE,MACA,WACA,UACA,aACA,sCClGA,iCACE,qBACA,Y7B6hBwB,O6B5hBxB,e7B2hBwB,O6B1hBxB,WAvBJ,oCACA,eACA,uCACA,uBA4CE,uCACE,cD0EF,iCACE,iBAMJ,0CACE,MACA,WACA,UACA,aACA,uCCnHA,mCACE,qBACA,Y7B6hBwB,O6B5hBxB,e7B2hBwB,O6B1hBxB,WAWA,mCACE,aAGF,oCACE,qBACA,a7B0gBsB,O6BzgBtB,e7BwgBsB,O6BvgBtB,WAnCN,oCACA,wBACA,uCAsCE,yCACE,cD2FF,oCACE,iBAON,kBACE,SACA,6CACA,gBACA,mDACA,UAMF,eACE,cACA,WACA,4EACA,WACA,Y5Byb4B,I4Bxb5B,oCACA,mBACA,qBACA,mBACA,+BACA,S3BtKE,uD2ByKF,0CAEE,0CX1LF,iBW4LuB,iCAGvB,4CAEE,2CACA,qBXlMF,iBWmMuB,kCAGvB,gDAEE,6CACA,oBACA,+BAMJ,oBACE,cAIF,iBACE,cACA,gFACA,gB/BmEI,UALI,S+B5DR,sCACA,mBAIF,oBACE,cACA,4EACA,oCAIF,oBAEE,6BACA,0BACA,+DACA,2BACA,kCACA,qCACA,6DACA,uDACA,sCACA,sCACA,2CACA,oCEtPF,+BAEE,kBACA,oBACA,sBAEA,yCACE,kBACA,cAKF,kXAME,UAKJ,aACE,aACA,eACA,2BAEA,0BACE,WAIJ,W7BhBI,sC6BoBF,qFAEE,4CAIF,qJ7BVE,0BACA,6B6BmBF,6G7BNE,yBACA,4B6BwBJ,uBACE,uBACA,sBAEA,2GAGE,cAGF,0CACE,eAIJ,yEACE,sBACA,qBAGF,yEACE,qBACA,oBAoBF,oBACE,sBACA,uBACA,uBAEA,wDAEE,WAGF,4FAEE,2CAIF,qH7B1FE,6BACA,4B6BkGF,wI7BjHE,yBACA,0B8BxBJ,KAEE,8BACA,gCAEA,4BACA,0CACA,sDACA,wDAGA,aACA,eACA,eACA,gBACA,gBAGF,UACE,cACA,kElCsQI,UALI,6BkC/PR,2CACA,+BACA,qBACA,gBACA,SffI,WegBJ,uFfZI,uCeGN,UfFQ,iBeaN,gCAEE,qCAIF,wBACE,UACA,W/BkhBoB,kC+B9gBtB,sCAEE,wCACA,oBACA,eAQJ,UAEE,mDACA,mDACA,qDACA,4GACA,0DACA,gDACA,wGAGA,oFAEA,oBACE,uDACA,2D9B7CA,wDACA,yD8B+CA,oDAGE,kBACA,wDAIJ,8DAEE,2CACA,mDACA,yDAGF,yBAEE,oD9BjEA,yBACA,0B8B2EJ,WAEE,sDACA,uCACA,uCAGA,qB9B5FE,gD8BgGF,uDAEE,4CdjHF,iBckHuB,mCASzB,eAEE,6BACA,0CACA,+DAGA,gCAEA,yBACE,gBACA,eACA,uEAEA,8DAEE,iCAIJ,+DAEE,Y/B0d0B,I+Bzd1B,gDACA,iCAUF,wCAEE,cACA,kBAKF,kDAEE,YACA,aACA,kBAMF,iEACE,WAUF,uBACE,aAEF,qBACE,cC7LJ,QAEE,yBACA,8BACA,4DACA,iEACA,oEACA,gEACA,uCACA,mCACA,qCACA,+DACA,qEACA,uCACA,uCACA,uCACA,uCACA,4QACA,2EACA,2DACA,yCACA,6DAGA,kBACA,aACA,eACA,mBACA,8BACA,8DAMA,2JACE,aACA,kBACA,mBACA,8BAoBJ,cACE,6CACA,gDACA,+CnC4NI,UALI,iCmCrNR,mCACA,qBACA,mBAEA,wCAEE,yCAUJ,YAEE,2BACA,gCAEA,4BACA,4CACA,wDACA,8DAGA,aACA,sBACA,eACA,gBACA,gBAGE,wDAEE,oCAIJ,2BACE,gBASJ,aACE,YhC8gCkC,MgC7gClC,ehC6gCkC,MgC5gClC,6BAEA,yDAGE,oCAaJ,iBACE,YACA,gBAGA,mBAIF,gBACE,8EnCyII,UALI,mCmClIR,cACA,6BACA,+BACA,0E/BxIE,qDeHE,WgB6IJ,oChBzII,uCgBiIN,gBhBhIQ,iBgB0IN,sBACE,qBAGF,sBACE,qBACA,UACA,sDAMJ,qBACE,qBACA,YACA,aACA,sBACA,kDACA,4BACA,2BACA,qBAGF,mBACE,yCACA,gBxB1HE,yBwBsIA,kBAEI,iBACA,2BAEA,8BACE,mBAEA,6CACE,kBAGF,wCACE,kDACA,iDAIJ,qCACE,iBAGF,mCACE,wBACA,gBAGF,kCACE,aAGF,6BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,+CACE,aAGF,6CACE,aACA,YACA,UACA,oBxB5LR,yBwBsIA,kBAEI,iBACA,2BAEA,8BACE,mBAEA,6CACE,kBAGF,wCACE,kDACA,iDAIJ,qCACE,iBAGF,mCACE,wBACA,gBAGF,kCACE,aAGF,6BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,+CACE,aAGF,6CACE,aACA,YACA,UACA,oBxB5LR,yBwBsIA,kBAEI,iBACA,2BAEA,8BACE,mBAEA,6CACE,kBAGF,wCACE,kDACA,iDAIJ,qCACE,iBAGF,mCACE,wBACA,gBAGF,kCACE,aAGF,6BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,+CACE,aAGF,6CACE,aACA,YACA,UACA,oBxB5LR,0BwBsIA,kBAEI,iBACA,2BAEA,8BACE,mBAEA,6CACE,kBAGF,wCACE,kDACA,iDAIJ,qCACE,iBAGF,mCACE,wBACA,gBAGF,kCACE,aAGF,6BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,+CACE,aAGF,6CACE,aACA,YACA,UACA,oBxB5LR,0BwBsIA,mBAEI,iBACA,2BAEA,+BACE,mBAEA,8CACE,kBAGF,yCACE,kDACA,iDAIJ,sCACE,iBAGF,oCACE,wBACA,gBAGF,mCACE,aAGF,8BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,gDACE,aAGF,8CACE,aACA,YACA,UACA,oBAtDR,eAEI,iBACA,2BAEA,2BACE,mBAEA,0CACE,kBAGF,qCACE,kDACA,iDAIJ,kCACE,iBAGF,gCACE,wBACA,gBAGF,+BACE,aAGF,0BAEE,gBACA,aACA,YACA,sBACA,uBACA,8BACA,0CACA,oBACA,0BhB9NJ,WgBgOI,KAGA,4CACE,aAGF,0CACE,aACA,YACA,UACA,mBAiBZ,yCAGE,6CACA,mDACA,sDACA,+BACA,8BACA,oCACA,2DACA,+QAME,0CACE,+QCzRN,MAEE,yBACA,yBACA,iCACA,wBACA,2BACA,+CACA,2DACA,iDACA,uBACA,wFACA,gCACA,8BACA,uDACA,sBACA,mBACA,kBACA,gCACA,oCACA,gCAGA,kBACA,aACA,sBACA,YACA,6BACA,2BACA,qBACA,mCACA,2BACA,qEhCjBE,2CgCqBF,SACE,eACA,cAGF,kBACE,mBACA,sBAEA,8BACE,mBhCtBF,0DACA,2DgCyBA,6BACE,sBhCbF,8DACA,6DgCmBF,8DAEE,aAIJ,WAGE,cACA,wDACA,2BAGF,YACE,4CACA,iCAGF,eACE,oDACA,gBACA,oCAGF,sBACE,gBAQA,sBACE,oCAQJ,aACE,kEACA,gBACA,+BACA,uCACA,4EAEA,yBhC7FE,wFgCkGJ,aACE,kEACA,+BACA,uCACA,yEAEA,wBhCxGE,wFgCkHJ,kBACE,qDACA,oDACA,oDACA,gBAEA,mCACE,mCACA,sCAIJ,mBACE,qDACA,oDAIF,kBACE,kBACA,MACA,QACA,SACA,OACA,2ChC1IE,iDgC8IJ,yCAGE,WAGF,wBhC3II,0DACA,2DgC+IJ,2BhClII,8DACA,6DgC8IF,kBACE,0CzB3HA,yByBuHJ,YAQI,aACA,mBAGA,kBACE,WACA,gBAEA,wBACE,cACA,cAKA,mChC1KJ,0BACA,6BgC4KM,iGAGE,0BAEF,oGAGE,6BAIJ,oChC3KJ,yBACA,4BgC6KM,mGAGE,yBAEF,sGAGE,6BCnOZ,WAEE,2CACA,qCACA,+KACA,oDACA,oDACA,sDACA,6FACA,sCACA,mCACA,+CACA,8CACA,wOACA,uCACA,mDACA,+DACA,6PACA,4EACA,uCACA,oCACA,6DACA,sDAIF,kBACE,kBACA,aACA,mBACA,WACA,4ErC4PI,UALI,KqCrPR,oCACA,gBACA,4CACA,SjCrBE,gBiCuBF,qBlB1BI,WkB2BJ,+BlBvBI,uCkBUN,kBlBTQ,iBkBwBN,kCACE,uCACA,+CACA,gGAEA,yCACE,qDACA,iDAKJ,yBACE,cACA,yCACA,0CACA,iBACA,WACA,8CACA,4BACA,mDlBjDE,WkBkDF,wClB9CE,uCkBqCJ,yBlBpCM,iBkBgDN,wBACE,UAGF,wBACE,UACA,UACA,oDAIJ,kBACE,gBAGF,gBACE,gCACA,wCACA,+EAEA,8BjC7DE,yDACA,0DiC+DA,kEjChEA,+DACA,gEiCoEF,oCACE,aAIF,6BjC5DE,6DACA,4DiC+DE,2EjChEF,mEACA,kEiCoEA,iDjCrEA,6DACA,4DiC0EJ,gBACE,8EASA,iCACE,eACA,cjC9GA,gBiCiHA,0DACA,4DAGA,yMjCrHA,gBiCgIA,8CACE,yTACA,gUCrJN,YAEE,6BACA,6BACA,oCAEA,qBACA,gCACA,yDACA,uCACA,6DAGA,aACA,eACA,sEACA,iDtC+QI,UALI,+BsCxQR,gBACA,0FAMA,kCACE,iDAEA,0CACE,WACA,kDACA,yCACA,uFAIJ,wBACE,6CCrCJ,YAEE,mCACA,oCvC4RI,0BALI,KuCrRR,4CACA,sCACA,qDACA,qDACA,uDACA,wDACA,gDACA,2DACA,wDACA,iDACA,yEACA,mCACA,mCACA,6CACA,0DACA,oDACA,8DAGA,ajCpBA,eACA,gBiCuBF,WACE,kBACA,cACA,sEvCgQI,UALI,+BuCzPR,iCACA,qBACA,yCACA,iFpBpBI,WoBqBJ,mHpBjBI,uCoBQN,WpBPQ,iBoBkBN,iBACE,UACA,uCAEA,+CACA,qDAGF,iBACE,UACA,uCACA,+CACA,QpC2uCgC,EoC1uChC,iDAGF,qCAEE,UACA,wCnBtDF,iBmBuDuB,+BACrB,sDAGF,yCAEE,0CACA,oBACA,kDACA,wDAKF,wCACE,YpC8sCgC,gCoCzsC9B,kCnC9BF,0DACA,6DmCmCE,iCnClDF,2DACA,8DmCkEJ,eClGE,kCACA,mCxC0RI,0BALI,QwCnRR,0DDmGF,eCtGE,kCACA,mCxC0RI,0BALI,SwCnRR,0DCFF,OAEE,6BACA,6BzCuRI,qBALI,OyChRR,4BACA,uBACA,kDAGA,qBACA,4DzC+QI,UALI,0ByCxQR,wCACA,cACA,4BACA,kBACA,mBACA,wBrCJE,4CqCSF,aACE,aAKJ,YACE,kBACA,SChCF,OAEE,2BACA,2BACA,2BACA,+BACA,0BACA,qCACA,6EACA,kDACA,+BAGA,kBACA,4DACA,4CACA,4BACA,oCACA,8BtCHE,4CsCQJ,eAEE,cAIF,YACE,YvC6kB4B,IuC5kB5B,iCAQF,mBACE,cvCs+C8B,KuCn+C9B,8BACE,kBACA,MACA,QACA,UACA,qBAQF,eACE,kDACA,2CACA,yDACA,uDAJF,iBACE,oDACA,6CACA,2DACA,yDAJF,eACE,kDACA,2CACA,yDACA,uDAJF,YACE,+CACA,wCACA,sDACA,oDAJF,eACE,kDACA,2CACA,yDACA,uDAJF,cACE,iDACA,0CACA,wDACA,sDAJF,aACE,gDACA,yCACA,uDACA,qDAJF,YACE,+CACA,wCACA,sDACA,oDC5DF,gCACE,oDAKJ,4BAGE,2B3CkRI,wBALI,Q2C3QR,yCACA,qDACA,qDACA,8BACA,8BACA,8CAGA,aACA,iCACA,gB3CsQI,UALI,6B2C/PR,uCvCRE,+CuCaJ,cACE,aACA,sBACA,uBACA,gBACA,mCACA,kBACA,mBACA,2CxBxBI,WwByBJ,kCxBrBI,uCwBYN,cxBXQ,iBwBuBR,2NAEE,oEAGF,4BACE,iBAGF,0CACE,WAIA,uBACE,kDAGE,uCAJJ,uBAKM,gBC3DR,YAEE,4CACA,sCACA,qDACA,qDACA,uDACA,qCACA,uCACA,wDACA,6DACA,uDACA,0DACA,yDACA,0DACA,+CACA,mCACA,mCACA,6CAGA,aACA,sBAGA,eACA,gBxCXE,iDwCeJ,qBACE,qBACA,sBAEA,8CAEE,oCACA,0BAQJ,iBACE,kBACA,cACA,gFACA,iCACA,qBACA,yCACA,iFAEA,6BxC9BE,+BACA,gCwCiCF,4BxCpBE,mCACA,kCwCuBF,oDAEE,0CACA,oBACA,kDAIF,wBACE,UACA,wCACA,gDACA,sDAIF,kCACE,mBAEA,yCACE,sDACA,mDAUN,wBACE,WACA,wCACA,mBAIE,sFAEE,UACA,8CACA,qBACA,sDAGF,4CACE,+CACA,uDAaF,uBACE,mBAGE,qExCzDJ,6DAZA,0BwC0EI,qExC1EJ,2DAYA,4BwCmEI,+CACE,aAGF,yDACE,mDACA,oBAEA,gEACE,uDACA,oDjCxFR,yBiCgEA,0BACE,mBAGE,wExCzDJ,6DAZA,0BwC0EI,wExC1EJ,2DAYA,4BwCmEI,kDACE,aAGF,4DACE,mDACA,oBAEA,mEACE,uDACA,qDjCxFR,yBiCgEA,0BACE,mBAGE,wExCzDJ,6DAZA,0BwC0EI,wExC1EJ,2DAYA,4BwCmEI,kDACE,aAGF,4DACE,mDACA,oBAEA,mEACE,uDACA,qDjCxFR,yBiCgEA,0BACE,mBAGE,wExCzDJ,6DAZA,0BwC0EI,wExC1EJ,2DAYA,4BwCmEI,kDACE,aAGF,4DACE,mDACA,oBAEA,mEACE,uDACA,qDjCxFR,0BiCgEA,0BACE,mBAGE,wExCzDJ,6DAZA,0BwC0EI,wExC1EJ,2DAYA,4BwCmEI,kDACE,aAGF,4DACE,mDACA,oBAEA,mEACE,uDACA,qDjCxFR,0BiCgEA,2BACE,mBAGE,yExCzDJ,6DAZA,0BwC0EI,yExC1EJ,2DAYA,4BwCmEI,mDACE,aAGF,6DACE,mDACA,oBAEA,oEACE,uDACA,qDAcZ,kBxClJI,gBwCqJF,mCACE,mDAEA,8CACE,sBAaJ,yBACE,uDACA,gDACA,8DACA,6DACA,iEACA,8DACA,kEACA,0DACA,2DACA,qEAVF,2BACE,yDACA,kDACA,gEACA,6DACA,mEACA,8DACA,oEACA,4DACA,6DACA,uEAVF,yBACE,uDACA,gDACA,8DACA,6DACA,iEACA,8DACA,kEACA,0DACA,2DACA,qEAVF,sBACE,oDACA,6CACA,2DACA,6DACA,8DACA,8DACA,+DACA,uDACA,wDACA,kEAVF,yBACE,uDACA,gDACA,8DACA,6DACA,iEACA,8DACA,kEACA,0DACA,2DACA,qEAVF,wBACE,sDACA,+CACA,6DACA,6DACA,gEACA,8DACA,iEACA,yDACA,0DACA,oEAVF,uBACE,qDACA,8CACA,4DACA,6DACA,+DACA,8DACA,gEACA,wDACA,yDACA,mEAVF,sBACE,oDACA,6CACA,2DACA,6DACA,8DACA,8DACA,+DACA,uDACA,wDACA,kEC9LJ,WAEE,2BACA,oVACA,4BACA,mCACA,oEACA,gCACA,sCAGA,uBACA,M1CupD2B,I0CtpD3B,O1CspD2B,I0CrpD3B,oBACA,gCACA,0EACA,kCACA,SzCJE,sByCMF,oCAGA,iBACE,gCACA,qBACA,0CAGF,iBACE,UACA,4CACA,0CAGF,wCAEE,oBACA,iBACA,6CAQJ,iBAHE,kEAOF,4BAEE,wB5C3CE,qB4CkCF,kECjDF,OAEE,wBACA,8BACA,6BACA,2BACA,4B9CyRI,qBALI,S8ClRR,mBACA,iDACA,gDACA,4DACA,kDACA,4CACA,mDACA,wDACA,mEAGA,gCACA,e9C2QI,UALI,0B8CpQR,4BACA,oBACA,oCACA,4BACA,uEACA,sC1CRE,4C0CWF,eACE,UAGF,kBACE,aAIJ,iBACE,wBAEA,kBACA,+BACA,kBACA,eACA,oBAEA,mCACE,sCAIJ,cACE,aACA,mBACA,4DACA,mCACA,2CACA,4BACA,qF1ChCE,0FACA,2F0CkCF,yBACE,kDACA,sCAIJ,YACE,kCACA,qBC9DF,OAEE,wBACA,wBACA,yBACA,0BACA,uCACA,iCACA,4DACA,gDACA,qDACA,+CACA,4FACA,kCACA,kCACA,qCACA,uDACA,uDACA,kCACA,8BACA,uBACA,uDACA,uDAGA,eACA,MACA,OACA,+BACA,aACA,WACA,YACA,kBACA,gBAGA,UAOF,cACE,kBACA,WACA,8BAEA,oBAGA,0BACE,U5Cm8CgC,oBgBh/C9B,W4B8CF,uB5B1CE,uC4BwCJ,0B5BvCM,iB4B2CN,0BACE,U5Cg8CgC,K4C57ClC,kCACE,U5C67CgC,Y4Cz7CpC,yBACE,6CAEA,wCACE,gBACA,gBAGF,qCACE,gBAIJ,uBACE,aACA,mBACA,iDAIF,eACE,kBACA,aACA,sBACA,WAEA,4BACA,oBACA,oCACA,4BACA,uE3CrFE,4C2CyFF,UAIF,gBAEE,2BACA,uBACA,2BClHA,eACA,MACA,OACA,QDkH0B,0BCjH1B,YACA,aACA,iBD+G4D,sBC5G5D,+BACA,6BD2G0F,2BAK5F,cACE,aACA,cACA,mBACA,uCACA,4F3CrGE,2DACA,4D2CuGF,yBACE,4FAEA,uDACA,yDACA,0DACA,iBAKJ,aACE,gBACA,8CAKF,YACE,kBAGA,cACA,gCAIF,cACE,aACA,cACA,eACA,mBACA,yBACA,sEACA,2CACA,yF3C7HE,+DACA,8D2CkIF,gBACE,2CpC/GA,yBoCqHF,OACE,2BACA,4CAIF,cACE,gCACA,kBACA,iBAGF,UACE,yBpClIA,yBoCuIF,oBAEE,yBpCzIA,0BoC8IF,UACE,0BAUA,kBACE,YACA,eACA,YACA,SAEA,iCACE,YACA,S3C7MJ,gB2CiNE,gE3CjNF,gB2CsNE,8BACE,gBpC9JJ,4BoC4IA,0BACE,YACA,eACA,YACA,SAEA,yCACE,YACA,S3C7MJ,gB2CiNE,gF3CjNF,gB2CsNE,sCACE,iBpC9JJ,4BoC4IA,0BACE,YACA,eACA,YACA,SAEA,yCACE,YACA,S3C7MJ,gB2CiNE,gF3CjNF,gB2CsNE,sCACE,iBpC9JJ,4BoC4IA,0BACE,YACA,eACA,YACA,SAEA,yCACE,YACA,S3C7MJ,gB2CiNE,gF3CjNF,gB2CsNE,sCACE,iBpC9JJ,6BoC4IA,0BACE,YACA,eACA,YACA,SAEA,yCACE,YACA,S3C7MJ,gB2CiNE,gF3CjNF,gB2CsNE,sCACE,iBpC9JJ,6BoC4IA,2BACE,YACA,eACA,YACA,SAEA,0CACE,YACA,S3C7MJ,gB2CiNE,kF3CjNF,gB2CsNE,uCACE,iBEzOR,SAEE,0BACA,8BACA,+BACA,gCACA,sBjDwRI,uBALI,SiDjRR,sCACA,0CACA,oDACA,0BACA,iCACA,kCAGA,iCACA,cACA,gCClBA,Y/C+lB4B,0B+C7lB5B,kBACA,Y/CwmB4B,I+CvmB5B,Y/C+mB4B,I+C9mB5B,gBACA,iBACA,qBACA,iBACA,oBACA,sBACA,kBACA,mBACA,oBACA,gBlDgRI,UALI,4BiDhQR,qBACA,UAEA,gDAEA,wBACE,cACA,oCACA,sCAEA,gCACE,kBACA,WACA,2BACA,mBAKN,2FACE,+CAEA,2GACE,SACA,qFACA,sCAKJ,6FACE,6CACA,qCACA,qCAEA,6GACE,WACA,4HACA,wCAMJ,iGACE,4CAEA,iHACE,YACA,qFACA,yCAKJ,8FACE,8CACA,qCACA,qCAEA,8GACE,UACA,4HACA,uCAsBJ,eACE,sCACA,gEACA,8BACA,kBACA,sC7CjGE,8C+CnBJ,SAEE,0BACA,8BnD4RI,uBALI,SmDrRR,mCACA,kDACA,8DACA,uDACA,4FACA,8CACA,oCACA,sCnDmRI,8BALI,KmD5QR,mCACA,+CACA,kCACA,kCACA,8CACA,+BACA,kCACA,0DAGA,iCACA,cACA,sCDzBA,Y/C+lB4B,0B+C7lB5B,kBACA,Y/CwmB4B,I+CvmB5B,Y/C+mB4B,I+C9mB5B,gBACA,iBACA,qBACA,iBACA,oBACA,sBACA,kBACA,mBACA,oBACA,gBlDgRI,UALI,4BmD1PR,qBACA,sCACA,4BACA,2E/ChBE,8C+CoBF,wBACE,cACA,oCACA,sCAEA,+DAEE,kBACA,cACA,WACA,2BACA,mBACA,eAMJ,2FACE,kFAEA,oNAEE,qFAGF,2GACE,SACA,gDAGF,yGACE,sCACA,sCAOJ,6FACE,gFACA,qCACA,qCAEA,wNAEE,4HAGF,6GACE,OACA,kDAGF,2GACE,oCACA,wCAQJ,iGACE,+EAEA,gOAEE,qFAGF,iHACE,MACA,mDAGF,+GACE,mCACA,yCAKJ,mHACE,kBACA,MACA,SACA,cACA,oCACA,qDACA,WACA,+EAMF,8FACE,iFACA,qCACA,qCAEA,0NAEE,4HAGF,8GACE,QACA,iDAGF,4GACE,qCACA,uCAuBN,gBACE,8EACA,gBnD2GI,UALI,mCmDpGR,qCACA,6CACA,kF/C5JE,6DACA,8D+C8JF,sBACE,aAIJ,cACE,0EACA,mCCrLF,UACE,kBAGF,wBACE,mBAGF,gBACE,kBACA,WACA,gBCtBA,uBACE,cACA,WACA,WDuBJ,eACE,kBACA,aACA,WACA,WACA,mBACA,2BjClBI,WiCmBJ,0BjCfI,uCiCQN,ejCPQ,iBiCiBR,8DAGE,cAGF,wEAEE,2BAGF,wEAEE,4BASA,8BACE,UACA,4BACA,eAGF,iJAGE,UACA,UAGF,oFAEE,UACA,UjC5DE,WiC6DF,ejCzDE,uCiCqDJ,oFjCpDM,iBiCiER,8CAEE,kBACA,MACA,SACA,UAEA,aACA,mBACA,uBACA,MjDkhDmC,IiDjhDnC,UACA,MjD1FS,KiD2FT,kBACA,gBACA,8CACA,SACA,QjD4gDmC,GgBnmD/B,WiCwFJ,kBjCpFI,uCiCkEN,8CjCjEQ,iBiCsFN,oHAEE,MjDrGO,KiDsGP,qBACA,UACA,QjDogDiC,GiDjgDrC,uBACE,OAGF,uBACE,QAKF,wDAEE,qBACA,MjDsgDmC,KiDrgDnC,OjDqgDmC,KiDpgDnC,4BACA,wBACA,0BAGF,4BACE,wgBAEF,4BACE,wgBAQF,qBACE,kBACA,QACA,SACA,OACA,UACA,aACA,uBACA,UAEA,ajDq9CmC,IiDp9CnC,mBACA,YjDm9CmC,IiDj9CnC,sCACE,uBACA,cACA,MjDo9CiC,KiDn9CjC,OjDo9CiC,IiDn9CjC,UACA,ajDo9CiC,IiDn9CjC,YjDm9CiC,IiDl9CjC,mBACA,eACA,wDACA,4BACA,SAEA,oCACA,uCACA,QjD28CiC,GgB5mD/B,WiCkKF,iBjC9JE,uCiC6IJ,sCjC5IM,iBiCgKN,6BACE,QjDw8CiC,EiD/7CrC,kBACE,kBACA,UACA,OjDk8CmC,QiDj8CnC,SACA,YjD+7CmC,QiD97CnC,ejD87CmC,QiD77CnC,uCACA,kBAWF,eALE,wCACA,kCACA,4DAOF,4BAEE,wCACA,kCACA,oCnD3ME,qBmD8LF,wCACA,kCACA,4DE3MF,8BAEE,qBACA,cACA,8BACA,gCACA,gDAEA,kBACA,6FAIF,0BACE,8CAIF,gBAEE,yBACA,0BACA,sCACA,kCACA,oCACA,4CAGA,yDACA,iCAGF,mBAEE,yBACA,0BACA,iCASF,wBACE,GACE,mBAEF,IACE,UACA,gBAKJ,cAEE,yBACA,0BACA,sCACA,oCACA,0CAGA,8BACA,UAGF,iBACE,yBACA,0BAIA,uCACE,8BAEE,oCChFN,kFAEE,4BACA,4BACA,4BACA,+BACA,+BACA,2CACA,qCACA,oDACA,gEACA,mDACA,sDACA,sC5C6DE,4B4C5CF,cAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,gCpC1BA,gEoCYJ,cpCXM,iBRuDJ,4B4C5BE,8BACE,MACA,OACA,gCACA,qFACA,4BAGF,4BACE,MACA,QACA,gCACA,oFACA,2BAGF,4BACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,+BACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,sDAEE,eAGF,8DAGE,oB5C5BJ,yB4C/BF,cAiEM,4BACA,+BACA,0CAEA,gCACE,aAGF,8BACE,aACA,YACA,UACA,mBAEA,2C5CnCN,4B4C5CF,cAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,gCpC1BA,gEoCYJ,cpCXM,iBRuDJ,4B4C5BE,8BACE,MACA,OACA,gCACA,qFACA,4BAGF,4BACE,MACA,QACA,gCACA,oFACA,2BAGF,4BACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,+BACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,sDAEE,eAGF,8DAGE,oB5C5BJ,yB4C/BF,cAiEM,4BACA,+BACA,0CAEA,gCACE,aAGF,8BACE,aACA,YACA,UACA,mBAEA,2C5CnCN,4B4C5CF,cAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,gCpC1BA,gEoCYJ,cpCXM,iBRuDJ,4B4C5BE,8BACE,MACA,OACA,gCACA,qFACA,4BAGF,4BACE,MACA,QACA,gCACA,oFACA,2BAGF,4BACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,+BACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,sDAEE,eAGF,8DAGE,oB5C5BJ,yB4C/BF,cAiEM,4BACA,+BACA,0CAEA,gCACE,aAGF,8BACE,aACA,YACA,UACA,mBAEA,2C5CnCN,6B4C5CF,cAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,gCpC1BA,iEoCYJ,cpCXM,iBRuDJ,6B4C5BE,8BACE,MACA,OACA,gCACA,qFACA,4BAGF,4BACE,MACA,QACA,gCACA,oFACA,2BAGF,4BACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,+BACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,sDAEE,eAGF,8DAGE,oB5C5BJ,0B4C/BF,cAiEM,4BACA,+BACA,0CAEA,gCACE,aAGF,8BACE,aACA,YACA,UACA,mBAEA,2C5CnCN,6B4C5CF,eAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,gCpC1BA,iEoCYJ,epCXM,iBRuDJ,6B4C5BE,+BACE,MACA,OACA,gCACA,qFACA,4BAGF,6BACE,MACA,QACA,gCACA,oFACA,2BAGF,6BACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,gCACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,wDAEE,eAGF,iEAGE,oB5C5BJ,0B4C/BF,eAiEM,4BACA,+BACA,0CAEA,iCACE,aAGF,+BACE,aACA,YACA,UACA,mBAEA,2CA/ER,WAEI,eACA,SACA,mCACA,aACA,sBACA,eACA,gCACA,kBACA,wCACA,4BACA,UpC5BA,WoC8BA,+BpC1BA,uCoCYJ,WpCXM,iBoC2BF,2BACE,MACA,OACA,gCACA,qFACA,4BAGF,yBACE,MACA,QACA,gCACA,oFACA,2BAGF,yBACE,MACA,QACA,OACA,kCACA,gBACA,sFACA,4BAGF,4BACE,QACA,OACA,kCACA,gBACA,mFACA,2BAGF,gDAEE,eAGF,qDAGE,mBA2BR,oBPpHE,eACA,MACA,OACA,Q7C0mCkC,K6CzmClC,YACA,aACA,iB7CUS,K6CPT,mCACA,iC7Cm+CkC,GoDr3CpC,kBACE,aACA,mBACA,oEAEA,6BACE,sFAEA,oDACA,sDACA,uDACA,iBAIJ,iBACE,gBACA,kDAGF,gBACE,YACA,oEACA,gBCjJF,aACE,qBACA,eACA,sBACA,YACA,8BACA,QrDgzCkC,GqD9yClC,yBACE,qBACA,WAKJ,gBACE,gBAGF,gBACE,gBAGF,gBACE,iBAKA,+BACE,mDAIJ,4BACE,IACE,QrDmxCgC,IqD/wCpC,kBACE,+EACA,oBACA,8CAGF,4BACE,KACE,wBH9CF,iBACE,cACA,WACA,WIHF,iBACE,sBACA,iFAFF,mBACE,sBACA,mFAFF,iBACE,sBACA,iFAFF,cACE,sBACA,8EAFF,iBACE,sBACA,iFAFF,gBACE,sBACA,gFAFF,eACE,sBACA,+EAFF,cACE,sBACA,8ECFF,cACE,wEACA,kGAGE,wCAGE,8DACA,wFATN,gBACE,0EACA,oGAGE,4CAGE,8DACA,wFATN,cACE,wEACA,kGAGE,wCAGE,8DACA,wFATN,WACE,qEACA,+FAGE,kCAGE,+DACA,yFATN,cACE,wEACA,kGAGE,wCAGE,+DACA,yFATN,aACE,uEACA,iGAGE,sCAGE,8DACA,wFATN,YACE,sEACA,gGAGE,oCAGE,gEACA,0FATN,WACE,qEACA,+FAGE,kCAGE,6DACA,uFAOR,oBACE,+EACA,yGAGE,oDAEE,kFACA,4GC1BN,kBACE,UAEA,kJCHF,WACE,oBACA,IzD6c4B,QyD5c5B,mBACA,kFACA,sBzD2c4B,MyD1c5B,2BAEA,eACE,cACA,MzDuc0B,IyDtc1B,OzDsc0B,IyDrc1B,kBzCIE,WyCHF,0BzCOE,uCyCZJ,ezCaM,iByCDJ,8DACE,mECnBN,OACE,kBACA,WAEA,eACE,cACA,mCACA,WAGF,SACE,kBACA,MACA,OACA,WACA,YAKF,WACE,wBADF,WACE,uBADF,YACE,0BADF,YACE,kCCrBJ,WACE,eACA,MACA,QACA,OACA,Q3DumCkC,K2DpmCpC,cACE,eACA,QACA,SACA,OACA,Q3D+lCkC,K2DvlChC,YACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,eACE,gBACA,SACA,Q3D6kC8B,KQ9iChC,yBmDxCA,eACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,kBACE,gBACA,SACA,Q3D6kC8B,MQ9iChC,yBmDxCA,eACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,kBACE,gBACA,SACA,Q3D6kC8B,MQ9iChC,yBmDxCA,eACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,kBACE,gBACA,SACA,Q3D6kC8B,MQ9iChC,0BmDxCA,eACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,kBACE,gBACA,SACA,Q3D6kC8B,MQ9iChC,0BmDxCA,gBACE,gBACA,MACA,Q3DmlC8B,K2DhlChC,mBACE,gBACA,SACA,Q3D6kC8B,M4D5mCpC,QACE,aACA,mBACA,mBACA,mBAGF,QACE,aACA,cACA,sBACA,mBCRF,2ECIE,qBACA,sBACA,qBACA,uBACA,2BACA,iCACA,8BACA,oBAGA,qGACE,6BAIF,+EACE,2BCnBF,uBACE,kBACA,MACA,QACA,SACA,OACA,Q/DgcsC,E+D/btC,WCRJ,+BCCE,uBACA,mBCNF,IACE,qBACA,mBACA,MlEisB4B,uBkEhsB5B,eACA,8BACA,QlE2rB4B,ImE/nBtB,gBAOI,mCAPJ,WAOI,8BAPJ,cAOI,iCAPJ,cAOI,iCAPJ,mBAOI,sCAPJ,gBAOI,mCAPJ,aAOI,sBAPJ,WAOI,uBAPJ,YAOI,sBAPJ,oBAOI,8BAPJ,kBAOI,4BAPJ,iBAOI,2BAPJ,kBAOI,iCAPJ,iBAOI,2BAPJ,WAOI,qBAPJ,YAOI,uBAPJ,YAOI,sBAPJ,YAOI,uBAPJ,aAOI,qBAPJ,eAOI,yBAPJ,iBAOI,2BAPJ,kBAOI,4BAPJ,iBAOI,2BAPJ,iBAOI,2BAPJ,mBAOI,6BAPJ,oBAOI,8BAPJ,mBAOI,6BAPJ,iBAOI,2BAPJ,mBAOI,6BAPJ,oBAOI,8BAPJ,mBAOI,6BAPJ,UAOI,0BAPJ,gBAOI,gCAPJ,SAOI,yBAPJ,QAOI,wBAPJ,eAOI,+BAPJ,SAOI,yBAPJ,aAOI,6BAPJ,cAOI,8BAPJ,QAOI,wBAPJ,eAOI,+BAPJ,QAOI,wBAPJ,QAOI,2CAPJ,WAOI,8CAPJ,WAOI,8CAPJ,aAOI,2BAjBJ,oBACE,iFADF,sBACE,mFADF,oBACE,iFADF,iBACE,8EADF,oBACE,iFADF,mBACE,gFADF,kBACE,+EADF,iBACE,8EASF,iBAOI,2BAPJ,mBAOI,6BAPJ,mBAOI,6BAPJ,gBAOI,0BAPJ,iBAOI,2BAPJ,OAOI,iBAPJ,QAOI,mBAPJ,SAOI,oBAPJ,UAOI,oBAPJ,WAOI,sBAPJ,YAOI,uBAPJ,SAOI,kBAPJ,UAOI,oBAPJ,WAOI,qBAPJ,OAOI,mBAPJ,QAOI,qBAPJ,SAOI,sBAPJ,kBAOI,2CAPJ,oBAOI,sCAPJ,oBAOI,sCAPJ,QAOI,uFAPJ,UAOI,oBAPJ,YAOI,2FAPJ,cAOI,wBAPJ,YAOI,6FAPJ,cAOI,0BAPJ,eAOI,8FAPJ,iBAOI,2BAPJ,cAOI,4FAPJ,gBAOI,yBAPJ,gBAIQ,uBAGJ,8EAPJ,kBAIQ,uBAGJ,gFAPJ,gBAIQ,uBAGJ,8EAPJ,aAIQ,uBAGJ,2EAPJ,gBAIQ,uBAGJ,8EAPJ,eAIQ,uBAGJ,6EAPJ,cAIQ,uBAGJ,4EAPJ,aAIQ,uBAGJ,2EAPJ,cAIQ,uBAGJ,4EAPJ,cAIQ,uBAGJ,4EAPJ,uBAOI,wDAPJ,yBAOI,0DAPJ,uBAOI,wDAPJ,oBAOI,qDAPJ,uBAOI,wDAPJ,sBAOI,uDAPJ,qBAOI,sDAPJ,oBAOI,qDAPJ,UAOI,4BAPJ,UAOI,4BAPJ,UAOI,4BAPJ,UAOI,4BAPJ,UAOI,4BAjBJ,mBACE,yBADF,mBACE,0BADF,mBACE,yBADF,mBACE,0BADF,oBACE,uBASF,MAOI,qBAPJ,MAOI,qBAPJ,MAOI,qBAPJ,OAOI,sBAPJ,QAOI,sBAPJ,QAOI,0BAPJ,QAOI,uBAPJ,YAOI,2BAPJ,MAOI,sBAPJ,MAOI,sBAPJ,MAOI,sBAPJ,OAOI,uBAPJ,QAOI,uBAPJ,QAOI,2BAPJ,QAOI,wBAPJ,YAOI,4BAPJ,WAOI,yBAPJ,UAOI,8BAPJ,aAOI,iCAPJ,kBAOI,sCAPJ,qBAOI,yCAPJ,aAOI,uBAPJ,aAOI,uBAPJ,eAOI,yBAPJ,eAOI,yBAPJ,WAOI,0BAPJ,aAOI,4BAPJ,mBAOI,kCAPJ,uBAOI,sCAPJ,qBAOI,oCAPJ,wBAOI,kCAPJ,yBAOI,yCAPJ,wBAOI,wCAPJ,wBAOI,wCAPJ,mBAOI,kCAPJ,iBAOI,gCAPJ,oBAOI,8BAPJ,sBAOI,gCAPJ,qBAOI,+BAPJ,qBAOI,oCAPJ,mBAOI,kCAPJ,sBAOI,gCAPJ,uBAOI,uCAPJ,sBAOI,sCAPJ,uBAOI,iCAPJ,iBAOI,2BAPJ,kBAOI,iCAPJ,gBAOI,+BAPJ,mBAOI,6BAPJ,qBAOI,+BAPJ,oBAOI,8BAPJ,aAOI,oBAPJ,SAOI,mBAPJ,SAOI,mBAPJ,SAOI,mBAPJ,SAOI,mBAPJ,SAOI,mBAPJ,SAOI,mBAPJ,YAOI,mBAPJ,KAOI,oBAPJ,KAOI,yBAPJ,KAOI,wBAPJ,KAOI,uBAPJ,KAOI,yBAPJ,KAOI,uBAPJ,QAOI,uBAPJ,MAOI,mDAPJ,MAOI,6DAPJ,MAOI,2DAPJ,MAOI,yDAPJ,MAOI,6DAPJ,MAOI,yDAPJ,SAOI,yDAPJ,MAOI,mDAPJ,MAOI,6DAPJ,MAOI,2DAPJ,MAOI,yDAPJ,MAOI,6DAPJ,MAOI,yDAPJ,SAOI,yDAPJ,MAOI,wBAPJ,MAOI,6BAPJ,MAOI,4BAPJ,MAOI,2BAPJ,MAOI,6BAPJ,MAOI,2BAPJ,SAOI,2BAPJ,MAOI,0BAPJ,MAOI,+BAPJ,MAOI,8BAPJ,MAOI,6BAPJ,MAOI,+BAPJ,MAOI,6BAPJ,SAOI,6BAPJ,MAOI,2BAPJ,MAOI,gCAPJ,MAOI,+BAPJ,MAOI,8BAPJ,MAOI,gCAPJ,MAOI,8BAPJ,SAOI,8BAPJ,MAOI,yBAPJ,MAOI,8BAPJ,MAOI,6BAPJ,MAOI,4BAPJ,MAOI,8BAPJ,MAOI,4BAPJ,SAOI,4BAPJ,KAOI,qBAPJ,KAOI,0BAPJ,KAOI,yBAPJ,KAOI,wBAPJ,KAOI,0BAPJ,KAOI,wBAPJ,MAOI,qDAPJ,MAOI,+DAPJ,MAOI,6DAPJ,MAOI,2DAPJ,MAOI,+DAPJ,MAOI,2DAPJ,MAOI,qDAPJ,MAOI,+DAPJ,MAOI,6DAPJ,MAOI,2DAPJ,MAOI,+DAPJ,MAOI,2DAPJ,MAOI,yBAPJ,MAOI,8BAPJ,MAOI,6BAPJ,MAOI,4BAPJ,MAOI,8BAPJ,MAOI,4BAPJ,MAOI,2BAPJ,MAOI,gCAPJ,MAOI,+BAPJ,MAOI,8BAPJ,MAOI,gCAPJ,MAOI,8BAPJ,MAOI,4BAPJ,MAOI,iCAPJ,MAOI,gCAPJ,MAOI,+BAPJ,MAOI,iCAPJ,MAOI,+BAPJ,MAOI,0BAPJ,MAOI,+BAPJ,MAOI,8BAPJ,MAOI,6BAPJ,MAOI,+BAPJ,MAOI,6BAPJ,OAOI,iBAPJ,OAOI,sBAPJ,OAOI,qBAPJ,OAOI,oBAPJ,OAOI,sBAPJ,OAOI,oBAPJ,WAOI,qBAPJ,WAOI,0BAPJ,WAOI,yBAPJ,WAOI,wBAPJ,WAOI,0BAPJ,WAOI,wBAPJ,cAOI,wBAPJ,cAOI,6BAPJ,cAOI,4BAPJ,cAOI,2BAPJ,cAOI,6BAPJ,cAOI,2BAPJ,gBAOI,gDAPJ,MAOI,4CAPJ,MAOI,4CAPJ,MAOI,0CAPJ,MAOI,4CAPJ,MAOI,6BAPJ,MAOI,0BAPJ,YAOI,6BAPJ,YAOI,6BAPJ,YAOI,+BAPJ,UAOI,2BAPJ,WAOI,2BAPJ,WAOI,2BAPJ,aAOI,2BAPJ,SAOI,2BAPJ,WAOI,8BAPJ,MAOI,yBAPJ,OAOI,4BAPJ,SAOI,2BAPJ,OAOI,yBAPJ,YAOI,2BAPJ,UAOI,4BAPJ,aAOI,6BAPJ,sBAOI,gCAPJ,2BAOI,qCAPJ,8BAOI,wCAPJ,gBAOI,oCAPJ,gBAOI,oCAPJ,iBAOI,qCAPJ,WAOI,8BAPJ,aAOI,8BAPJ,YAOI,iEAPJ,cAIQ,qBAGJ,qEAPJ,gBAIQ,qBAGJ,uEAPJ,cAIQ,qBAGJ,qEAPJ,WAIQ,qBAGJ,kEAPJ,cAIQ,qBAGJ,qEAPJ,aAIQ,qBAGJ,oEAPJ,YAIQ,qBAGJ,mEAPJ,WAIQ,qBAGJ,kEAPJ,YAIQ,qBAGJ,mEAPJ,YAIQ,qBAGJ,mEAPJ,WAIQ,qBAGJ,wEAPJ,YAIQ,qBAGJ,2CAPJ,eAIQ,qBAGJ,gCAPJ,eAIQ,qBAGJ,oCAPJ,qBAIQ,qBAGJ,2CAPJ,oBAIQ,qBAGJ,0CAPJ,oBAIQ,qBAGJ,0CAPJ,YAIQ,qBAGJ,yBAjBJ,iBACE,wBADF,iBACE,uBADF,iBACE,wBADF,kBACE,qBASF,uBAOI,iDAPJ,yBAOI,mDAPJ,uBAOI,iDAPJ,oBAOI,8CAPJ,uBAOI,iDAPJ,sBAOI,gDAPJ,qBAOI,+CAPJ,oBAOI,8CAjBJ,iBACE,uBAIA,6BACE,uBANJ,iBACE,wBAIA,6BACE,wBANJ,iBACE,uBAIA,6BACE,uBANJ,iBACE,wBAIA,6BACE,wBANJ,kBACE,qBAIA,8BACE,qBAIJ,eAOI,wCAKF,2BAOI,wCAnBN,eAOI,uCAKF,2BAOI,uCAnBN,eAOI,wCAKF,2BAOI,wCAnBN,wBAIQ,+BAGJ,+FAPJ,0BAIQ,+BAGJ,iGAPJ,wBAIQ,+BAGJ,+FAPJ,qBAIQ,+BAGJ,4FAPJ,wBAIQ,+BAGJ,+FAPJ,uBAIQ,+BAGJ,8FAPJ,sBAIQ,+BAGJ,6FAPJ,qBAIQ,+BAGJ,4FAPJ,gBAIQ,+BAGJ,qGAjBJ,0BACE,+BAIA,sCACE,+BANJ,2BACE,iCAIA,uCACE,iCANJ,2BACE,kCAIA,uCACE,kCANJ,2BACE,iCAIA,uCACE,iCANJ,2BACE,kCAIA,uCACE,kCANJ,4BACE,+BAIA,wCACE,+BAIJ,YAIQ,mBAGJ,8EAPJ,cAIQ,mBAGJ,gFAPJ,YAIQ,mBAGJ,8EAPJ,SAIQ,mBAGJ,2EAPJ,YAIQ,mBAGJ,8EAPJ,WAIQ,mBAGJ,6EAPJ,UAIQ,mBAGJ,4EAPJ,SAIQ,mBAGJ,2EAPJ,UAIQ,mBAGJ,4EAPJ,UAIQ,mBAGJ,4EAPJ,SAIQ,mBAGJ,8EAPJ,gBAIQ,mBAGJ,0CAPJ,mBAIQ,mBAGJ,mFAPJ,kBAIQ,mBAGJ,kFAjBJ,eACE,qBADF,eACE,sBADF,eACE,qBADF,eACE,sBADF,gBACE,mBASF,mBAOI,wDAPJ,qBAOI,0DAPJ,mBAOI,wDAPJ,gBAOI,qDAPJ,mBAOI,wDAPJ,kBAOI,uDAPJ,iBAOI,sDAPJ,gBAOI,qDAPJ,aAOI,+CAPJ,iBAOI,2BAPJ,kBAOI,4BAPJ,kBAOI,4BAPJ,SAOI,+BAPJ,SAOI,+BAPJ,SAOI,iDAPJ,WAOI,2BAPJ,WAOI,oDAPJ,WAOI,iDAPJ,WAOI,oDAPJ,WAOI,oDAPJ,WAOI,qDAPJ,gBAOI,6BAPJ,cAOI,sDAPJ,aAOI,qHAPJ,eAOI,yEAPJ,eAOI,2HAPJ,eAOI,qHAPJ,eAOI,2HAPJ,eAOI,2HAPJ,eAOI,6HAPJ,oBAOI,6EAPJ,kBAOI,+HAPJ,aAOI,yHAPJ,eAOI,6EAPJ,eAOI,+HAPJ,eAOI,yHAPJ,eAOI,+HAPJ,eAOI,+HAPJ,eAOI,iIAPJ,oBAOI,iFAPJ,kBAOI,mIAPJ,gBAOI,2HAPJ,kBAOI,+EAPJ,kBAOI,iIAPJ,kBAOI,2HAPJ,kBAOI,iIAPJ,kBAOI,iIAPJ,kBAOI,mIAPJ,uBAOI,mFAPJ,qBAOI,qIAPJ,eAOI,uHAPJ,iBAOI,2EAPJ,iBAOI,6HAPJ,iBAOI,uHAPJ,iBAOI,6HAPJ,iBAOI,6HAPJ,iBAOI,+HAPJ,sBAOI,+EAPJ,oBAOI,iIAPJ,SAOI,8BAPJ,WAOI,6BAPJ,MAOI,sBAPJ,KAOI,qBAPJ,KAOI,qBAPJ,KAOI,qBAPJ,KAOI,qB3DVR,yB2DGI,gBAOI,sBAPJ,cAOI,uBAPJ,eAOI,sBAPJ,uBAOI,8BAPJ,qBAOI,4BAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,oBAOI,2BAPJ,aAOI,0BAPJ,mBAOI,gCAPJ,YAOI,yBAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,YAOI,yBAPJ,gBAOI,6BAPJ,iBAOI,8BAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,WAOI,wBAPJ,cAOI,yBAPJ,aAOI,8BAPJ,gBAOI,iCAPJ,qBAOI,sCAPJ,wBAOI,yCAPJ,gBAOI,uBAPJ,gBAOI,uBAPJ,kBAOI,yBAPJ,kBAOI,yBAPJ,cAOI,0BAPJ,gBAOI,4BAPJ,sBAOI,kCAPJ,0BAOI,sCAPJ,wBAOI,oCAPJ,2BAOI,kCAPJ,4BAOI,yCAPJ,2BAOI,wCAPJ,2BAOI,wCAPJ,sBAOI,kCAPJ,oBAOI,gCAPJ,uBAOI,8BAPJ,yBAOI,gCAPJ,wBAOI,+BAPJ,wBAOI,oCAPJ,sBAOI,kCAPJ,yBAOI,gCAPJ,0BAOI,uCAPJ,yBAOI,sCAPJ,0BAOI,iCAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,mBAOI,+BAPJ,sBAOI,6BAPJ,wBAOI,+BAPJ,uBAOI,8BAPJ,gBAOI,oBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,eAOI,mBAPJ,QAOI,oBAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,uBAPJ,QAOI,yBAPJ,QAOI,uBAPJ,WAOI,uBAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,wBAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,6BAPJ,SAOI,2BAPJ,YAOI,2BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,YAOI,6BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,YAOI,8BAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,YAOI,4BAPJ,QAOI,qBAPJ,QAOI,0BAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,0BAPJ,QAOI,wBAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,iCAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,iCAPJ,SAOI,+BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,UAOI,iBAPJ,UAOI,sBAPJ,UAOI,qBAPJ,UAOI,oBAPJ,UAOI,sBAPJ,UAOI,oBAPJ,cAOI,qBAPJ,cAOI,0BAPJ,cAOI,yBAPJ,cAOI,wBAPJ,cAOI,0BAPJ,cAOI,wBAPJ,iBAOI,wBAPJ,iBAOI,6BAPJ,iBAOI,4BAPJ,iBAOI,2BAPJ,iBAOI,6BAPJ,iBAOI,2BAPJ,eAOI,2BAPJ,aAOI,4BAPJ,gBAOI,8B3DVR,yB2DGI,gBAOI,sBAPJ,cAOI,uBAPJ,eAOI,sBAPJ,uBAOI,8BAPJ,qBAOI,4BAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,oBAOI,2BAPJ,aAOI,0BAPJ,mBAOI,gCAPJ,YAOI,yBAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,YAOI,yBAPJ,gBAOI,6BAPJ,iBAOI,8BAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,WAOI,wBAPJ,cAOI,yBAPJ,aAOI,8BAPJ,gBAOI,iCAPJ,qBAOI,sCAPJ,wBAOI,yCAPJ,gBAOI,uBAPJ,gBAOI,uBAPJ,kBAOI,yBAPJ,kBAOI,yBAPJ,cAOI,0BAPJ,gBAOI,4BAPJ,sBAOI,kCAPJ,0BAOI,sCAPJ,wBAOI,oCAPJ,2BAOI,kCAPJ,4BAOI,yCAPJ,2BAOI,wCAPJ,2BAOI,wCAPJ,sBAOI,kCAPJ,oBAOI,gCAPJ,uBAOI,8BAPJ,yBAOI,gCAPJ,wBAOI,+BAPJ,wBAOI,oCAPJ,sBAOI,kCAPJ,yBAOI,gCAPJ,0BAOI,uCAPJ,yBAOI,sCAPJ,0BAOI,iCAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,mBAOI,+BAPJ,sBAOI,6BAPJ,wBAOI,+BAPJ,uBAOI,8BAPJ,gBAOI,oBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,eAOI,mBAPJ,QAOI,oBAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,uBAPJ,QAOI,yBAPJ,QAOI,uBAPJ,WAOI,uBAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,wBAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,6BAPJ,SAOI,2BAPJ,YAOI,2BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,YAOI,6BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,YAOI,8BAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,YAOI,4BAPJ,QAOI,qBAPJ,QAOI,0BAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,0BAPJ,QAOI,wBAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,iCAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,iCAPJ,SAOI,+BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,UAOI,iBAPJ,UAOI,sBAPJ,UAOI,qBAPJ,UAOI,oBAPJ,UAOI,sBAPJ,UAOI,oBAPJ,cAOI,qBAPJ,cAOI,0BAPJ,cAOI,yBAPJ,cAOI,wBAPJ,cAOI,0BAPJ,cAOI,wBAPJ,iBAOI,wBAPJ,iBAOI,6BAPJ,iBAOI,4BAPJ,iBAOI,2BAPJ,iBAOI,6BAPJ,iBAOI,2BAPJ,eAOI,2BAPJ,aAOI,4BAPJ,gBAOI,8B3DVR,yB2DGI,gBAOI,sBAPJ,cAOI,uBAPJ,eAOI,sBAPJ,uBAOI,8BAPJ,qBAOI,4BAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,oBAOI,2BAPJ,aAOI,0BAPJ,mBAOI,gCAPJ,YAOI,yBAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,YAOI,yBAPJ,gBAOI,6BAPJ,iBAOI,8BAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,WAOI,wBAPJ,cAOI,yBAPJ,aAOI,8BAPJ,gBAOI,iCAPJ,qBAOI,sCAPJ,wBAOI,yCAPJ,gBAOI,uBAPJ,gBAOI,uBAPJ,kBAOI,yBAPJ,kBAOI,yBAPJ,cAOI,0BAPJ,gBAOI,4BAPJ,sBAOI,kCAPJ,0BAOI,sCAPJ,wBAOI,oCAPJ,2BAOI,kCAPJ,4BAOI,yCAPJ,2BAOI,wCAPJ,2BAOI,wCAPJ,sBAOI,kCAPJ,oBAOI,gCAPJ,uBAOI,8BAPJ,yBAOI,gCAPJ,wBAOI,+BAPJ,wBAOI,oCAPJ,sBAOI,kCAPJ,yBAOI,gCAPJ,0BAOI,uCAPJ,yBAOI,sCAPJ,0BAOI,iCAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,mBAOI,+BAPJ,sBAOI,6BAPJ,wBAOI,+BAPJ,uBAOI,8BAPJ,gBAOI,oBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,eAOI,mBAPJ,QAOI,oBAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,uBAPJ,QAOI,yBAPJ,QAOI,uBAPJ,WAOI,uBAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,wBAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,6BAPJ,SAOI,2BAPJ,YAOI,2BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,YAOI,6BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,YAOI,8BAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,YAOI,4BAPJ,QAOI,qBAPJ,QAOI,0BAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,0BAPJ,QAOI,wBAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,iCAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,iCAPJ,SAOI,+BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,UAOI,iBAPJ,UAOI,sBAPJ,UAOI,qBAPJ,UAOI,oBAPJ,UAOI,sBAPJ,UAOI,oBAPJ,cAOI,qBAPJ,cAOI,0BAPJ,cAOI,yBAPJ,cAOI,wBAPJ,cAOI,0BAPJ,cAOI,wBAPJ,iBAOI,wBAPJ,iBAOI,6BAPJ,iBAOI,4BAPJ,iBAOI,2BAPJ,iBAOI,6BAPJ,iBAOI,2BAPJ,eAOI,2BAPJ,aAOI,4BAPJ,gBAOI,8B3DVR,0B2DGI,gBAOI,sBAPJ,cAOI,uBAPJ,eAOI,sBAPJ,uBAOI,8BAPJ,qBAOI,4BAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,oBAOI,2BAPJ,aAOI,0BAPJ,mBAOI,gCAPJ,YAOI,yBAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,YAOI,yBAPJ,gBAOI,6BAPJ,iBAOI,8BAPJ,WAOI,wBAPJ,kBAOI,+BAPJ,WAOI,wBAPJ,cAOI,yBAPJ,aAOI,8BAPJ,gBAOI,iCAPJ,qBAOI,sCAPJ,wBAOI,yCAPJ,gBAOI,uBAPJ,gBAOI,uBAPJ,kBAOI,yBAPJ,kBAOI,yBAPJ,cAOI,0BAPJ,gBAOI,4BAPJ,sBAOI,kCAPJ,0BAOI,sCAPJ,wBAOI,oCAPJ,2BAOI,kCAPJ,4BAOI,yCAPJ,2BAOI,wCAPJ,2BAOI,wCAPJ,sBAOI,kCAPJ,oBAOI,gCAPJ,uBAOI,8BAPJ,yBAOI,gCAPJ,wBAOI,+BAPJ,wBAOI,oCAPJ,sBAOI,kCAPJ,yBAOI,gCAPJ,0BAOI,uCAPJ,yBAOI,sCAPJ,0BAOI,iCAPJ,oBAOI,2BAPJ,qBAOI,iCAPJ,mBAOI,+BAPJ,sBAOI,6BAPJ,wBAOI,+BAPJ,uBAOI,8BAPJ,gBAOI,oBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,YAOI,mBAPJ,eAOI,mBAPJ,QAOI,oBAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,uBAPJ,QAOI,yBAPJ,QAOI,uBAPJ,WAOI,uBAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,mDAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,yDAPJ,SAOI,6DAPJ,SAOI,yDAPJ,YAOI,yDAPJ,SAOI,wBAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,6BAPJ,SAOI,2BAPJ,YAOI,2BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,YAOI,6BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,YAOI,8BAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,YAOI,4BAPJ,QAOI,qBAPJ,QAOI,0BAPJ,QAOI,yBAPJ,QAOI,wBAPJ,QAOI,0BAPJ,QAOI,wBAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,qDAPJ,SAOI,+DAPJ,SAOI,6DAPJ,SAOI,2DAPJ,SAOI,+DAPJ,SAOI,2DAPJ,SAOI,yBAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,4BAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,2BAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,gCAPJ,SAOI,8BAPJ,SAOI,4BAPJ,SAOI,iCAPJ,SAOI,gCAPJ,SAOI,+BAPJ,SAOI,iCAPJ,SAOI,+BAPJ,SAOI,0BAPJ,SAOI,+BAPJ,SAOI,8BAPJ,SAOI,6BAPJ,SAOI,+BAPJ,SAOI,6BAPJ,UAOI,iBAPJ,UAOI,sBAPJ,UAOI,qBAPJ,UAOI,oBAPJ,UAOI,sBAPJ,UAOI,oBAPJ,cAOI,qBAPJ,cAOI,0BAPJ,cAOI,yBAPJ,cAOI,wBAPJ,cAOI,0BAPJ,cAOI,wBAPJ,iBAOI,wBAPJ,iBAOI,6BAPJ,iBAOI,4BAPJ,iBAOI,2BAPJ,iBAOI,6BAPJ,iBAOI,2BAPJ,eAOI,2BAPJ,aAOI,4BAPJ,gBAOI,8B3DVR,0B2DGI,iBAOI,sBAPJ,eAOI,uBAPJ,gBAOI,sBAPJ,wBAOI,8BAPJ,sBAOI,4BAPJ,qBAOI,2BAPJ,sBAOI,iCAPJ,qBAOI,2BAPJ,cAOI,0BAPJ,oBAOI,gCAPJ,aAOI,yBAPJ,YAOI,wBAPJ,mBAOI,+BAPJ,aAOI,yBAPJ,iBAOI,6BAPJ,kBAOI,8BAPJ,YAOI,wBAPJ,mBAOI,+BAPJ,YAOI,wBAPJ,eAOI,yBAPJ,cAOI,8BAPJ,iBAOI,iCAPJ,sBAOI,sCAPJ,yBAOI,yCAPJ,iBAOI,uBAPJ,iBAOI,uBAPJ,mBAOI,yBAPJ,mBAOI,yBAPJ,eAOI,0BAPJ,iBAOI,4BAPJ,uBAOI,kCAPJ,2BAOI,sCAPJ,yBAOI,oCAPJ,4BAOI,kCAPJ,6BAOI,yCAPJ,4BAOI,wCAPJ,4BAOI,wCAPJ,uBAOI,kCAPJ,qBAOI,gCAPJ,wBAOI,8BAPJ,0BAOI,gCAPJ,yBAOI,+BAPJ,yBAOI,oCAPJ,uBAOI,kCAPJ,0BAOI,gCAPJ,2BAOI,uCAPJ,0BAOI,sCAPJ,2BAOI,iCAPJ,qBAOI,2BAPJ,sBAOI,iCAPJ,oBAOI,+BAPJ,uBAOI,6BAPJ,yBAOI,+BAPJ,wBAOI,8BAPJ,iBAOI,oBAPJ,aAOI,mBAPJ,aAOI,mBAPJ,aAOI,mBAPJ,aAOI,mBAPJ,aAOI,mBAPJ,aAOI,mBAPJ,gBAOI,mBAPJ,SAOI,oBAPJ,SAOI,yBAPJ,SAOI,wBAPJ,SAOI,uBAPJ,SAOI,yBAPJ,SAOI,uBAPJ,YAOI,uBAPJ,UAOI,mDAPJ,UAOI,6DAPJ,UAOI,2DAPJ,UAOI,yDAPJ,UAOI,6DAPJ,UAOI,yDAPJ,aAOI,yDAPJ,UAOI,mDAPJ,UAOI,6DAPJ,UAOI,2DAPJ,UAOI,yDAPJ,UAOI,6DAPJ,UAOI,yDAPJ,aAOI,yDAPJ,UAOI,wBAPJ,UAOI,6BAPJ,UAOI,4BAPJ,UAOI,2BAPJ,UAOI,6BAPJ,UAOI,2BAPJ,aAOI,2BAPJ,UAOI,0BAPJ,UAOI,+BAPJ,UAOI,8BAPJ,UAOI,6BAPJ,UAOI,+BAPJ,UAOI,6BAPJ,aAOI,6BAPJ,UAOI,2BAPJ,UAOI,gCAPJ,UAOI,+BAPJ,UAOI,8BAPJ,UAOI,gCAPJ,UAOI,8BAPJ,aAOI,8BAPJ,UAOI,yBAPJ,UAOI,8BAPJ,UAOI,6BAPJ,UAOI,4BAPJ,UAOI,8BAPJ,UAOI,4BAPJ,aAOI,4BAPJ,SAOI,qBAPJ,SAOI,0BAPJ,SAOI,yBAPJ,SAOI,wBAPJ,SAOI,0BAPJ,SAOI,wBAPJ,UAOI,qDAPJ,UAOI,+DAPJ,UAOI,6DAPJ,UAOI,2DAPJ,UAOI,+DAPJ,UAOI,2DAPJ,UAOI,qDAPJ,UAOI,+DAPJ,UAOI,6DAPJ,UAOI,2DAPJ,UAOI,+DAPJ,UAOI,2DAPJ,UAOI,yBAPJ,UAOI,8BAPJ,UAOI,6BAPJ,UAOI,4BAPJ,UAOI,8BAPJ,UAOI,4BAPJ,UAOI,2BAPJ,UAOI,gCAPJ,UAOI,+BAPJ,UAOI,8BAPJ,UAOI,gCAPJ,UAOI,8BAPJ,UAOI,4BAPJ,UAOI,iCAPJ,UAOI,gCAPJ,UAOI,+BAPJ,UAOI,iCAPJ,UAOI,+BAPJ,UAOI,0BAPJ,UAOI,+BAPJ,UAOI,8BAPJ,UAOI,6BAPJ,UAOI,+BAPJ,UAOI,6BAPJ,WAOI,iBAPJ,WAOI,sBAPJ,WAOI,qBAPJ,WAOI,oBAPJ,WAOI,sBAPJ,WAOI,oBAPJ,eAOI,qBAPJ,eAOI,0BAPJ,eAOI,yBAPJ,eAOI,wBAPJ,eAOI,0BAPJ,eAOI,wBAPJ,kBAOI,wBAPJ,kBAOI,6BAPJ,kBAOI,4BAPJ,kBAOI,2BAPJ,kBAOI,6BAPJ,kBAOI,2BAPJ,gBAOI,2BAPJ,cAOI,4BAPJ,iBAOI,8BCtDZ,0BD+CQ,MAOI,4BAPJ,MAOI,0BAPJ,MAOI,6BAPJ,MAOI,6BCnCZ,aD4BQ,gBAOI,0BAPJ,sBAOI,gCAPJ,eAOI,yBAPJ,cAOI,wBAPJ,qBAOI,+BAPJ,eAOI,yBAPJ,mBAOI,6BAPJ,oBAOI,8BAPJ,cAOI,wBAPJ,qBAOI,+BAPJ,cAOI,yB/CjEZ,uBACE,gDAIF,EACE,SACA,UACA,sBAGF,UACE,YACA,SACA,UAGF,KACE,aACA,sBACA,iBAIF,cACE,cACA,YAIF,oBACE,sBACA,gCACA,gBACA,cAIF,UACE,mBAGF,oBACE,+BACA,mBACA,gBACA,cACA,+BACA,oBACA,wBAGF,0BACE,cACA,iCACA,2BAGF,2BACE,cACA,sBACA,2CACA,gBAIF,uCACE,+BACA,+BACA,gBAGF,6CACE,iCACA,2BAIF,yBACE,kBACA,sBACA,6CACA,gBACA,gBACA,WAIF,wCACE,mBACA,mBACA,gBAIF,wCACE,gBACA,uBACA,gBAIF,oBACE,yBACA,gCACA,cAIF,gCACE,gBAGF,qCACE,yBAGF,uCACE,yBACA,qBAGF,6CACE,yBACA,0BAGF,4CACE,yBAIF,cACE,OACA,gBACA,eAIF,6CAGE,mBAGF,uFAGE,aACA,gBAIF,OACE,cAIF,yBACE,gBAGF,wCACE,cACA,uBACA,qBACA,wBAGF,8CACE,yBACA,cAGF,+CACE,yBACA,WAGF,0CACE,mBAIF,WACE,gBACA,cACA,OACA,YAGF,aACE,iBAGF,YACE,cACA,gBACA,qBACA,mBACA,gBACA,uBACA,sBACA,kBAGF,kBACE,cACA,eAGF,cACE,cACA,iBAGF,aACE,cACA,qBAGF,mBACE,0BAGF,YACE,yBACA,qBACA,gBAGF,kBACE,yBACA,qBAGF,cACE,eACA,yBAGF,oBACE,yBAGF,WACE,cACA,qBACA,iCACA,gBACA,wBAGF,iBACE,cACA,qBACA,0BACA,4BAIF,aACE,gBAIF,cACE,qBACA,cAGF,oBACE,0BAIF,8BACE,MACA,UACA,gBACA,iBAIF,kBACE,kBAGF,iCACE,MACA,UACA,gBAGF,uCACE,cAIF,iDACE,wBAIF,yBACE,cACE,kBAGF,uFAGE,eAGF,yBACE,gBACA,gBAGF,wCACE,gBACA,iBAIJ,yBACE,uFAGE,aAGF,yBACE,gBACA,gBAGF,wCACE,gBACA,iBACA","file":"style.css"}
\ No newline at end of file
diff --git a/engine/assets/favicon.svg b/public/assets/favicon.svg
similarity index 100%
rename from engine/assets/favicon.svg
rename to public/assets/favicon.svg
diff --git a/engine/assets/icon.svg b/public/assets/icon.svg
similarity index 100%
rename from engine/assets/icon.svg
rename to public/assets/icon.svg
diff --git a/public/assets/js/app.js b/public/assets/js/app.js
new file mode 100644
index 0000000..adce183
--- /dev/null
+++ b/public/assets/js/app.js
@@ -0,0 +1,41 @@
+// Main application JavaScript
+// This file contains general application functionality
+
+// Initialize application when DOM is ready
+document.addEventListener('DOMContentLoaded', function() {
+ console.log('CodePress CMS initialized');
+
+ // Handle nested dropdowns for touch devices
+ const dropdownSubmenus = document.querySelectorAll('.dropdown-submenu');
+
+ dropdownSubmenus.forEach(function(submenu) {
+ const toggle = submenu.querySelector('.dropdown-toggle');
+ const dropdown = submenu.querySelector('.dropdown-menu');
+
+ if (toggle && dropdown) {
+ // Prevent default link behavior
+ toggle.addEventListener('click', function(e) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ // Close other submenus at the same level
+ const parent = submenu.parentElement;
+ parent.querySelectorAll('.dropdown-submenu').forEach(function(sibling) {
+ if (sibling !== submenu) {
+ sibling.querySelector('.dropdown-menu').classList.remove('show');
+ }
+ });
+
+ // Toggle current submenu
+ dropdown.classList.toggle('show');
+ });
+
+ // Close submenu when clicking outside
+ document.addEventListener('click', function(e) {
+ if (!submenu.contains(e.target)) {
+ dropdown.classList.remove('show');
+ }
+ });
+ }
+ });
+});
\ No newline at end of file
diff --git a/engine/assets/js/bootstrap.bundle.min.js b/public/assets/js/bootstrap.bundle.min.js
similarity index 100%
rename from engine/assets/js/bootstrap.bundle.min.js
rename to public/assets/js/bootstrap.bundle.min.js
diff --git a/engine/assets/js/bootstrap.bundle.min.js.map b/public/assets/js/bootstrap.bundle.min.js.map
similarity index 100%
rename from engine/assets/js/bootstrap.bundle.min.js.map
rename to public/assets/js/bootstrap.bundle.min.js.map
diff --git a/content/blog/ict/excel-als-database.md b/public/content/ict/excel-als-database.md
similarity index 100%
rename from content/blog/ict/excel-als-database.md
rename to public/content/ict/excel-als-database.md
diff --git a/content/blog/hardware/de-ware-aard-van-ict.md b/public/content/ict/hardware/de-ware-aard-van-ict.md
similarity index 100%
rename from content/blog/hardware/de-ware-aard-van-ict.md
rename to public/content/ict/hardware/de-ware-aard-van-ict.md
diff --git a/content/blog/micro-electronica/leren-gaat-niet-over-perfectie.md b/public/content/ict/hardware/micro-electronica/leren-gaat-niet-over-perfectie.md
similarity index 100%
rename from content/blog/micro-electronica/leren-gaat-niet-over-perfectie.md
rename to public/content/ict/hardware/micro-electronica/leren-gaat-niet-over-perfectie.md
diff --git a/content/blog/micro-electronica/wat-is-arduino.md b/public/content/ict/hardware/micro-electronica/wat-is-arduino.md
similarity index 100%
rename from content/blog/micro-electronica/wat-is-arduino.md
rename to public/content/ict/hardware/micro-electronica/wat-is-arduino.md
diff --git a/content/blog/linux/open-source-voorbeelden.md b/public/content/ict/linux/open-source-voorbeelden.md
similarity index 100%
rename from content/blog/linux/open-source-voorbeelden.md
rename to public/content/ict/linux/open-source-voorbeelden.md
diff --git a/content/blog/open-source/de-toekomst-van-ict.md b/public/content/ict/open-source/de-toekomst-van-ict.md
similarity index 100%
rename from content/blog/open-source/de-toekomst-van-ict.md
rename to public/content/ict/open-source/de-toekomst-van-ict.md
diff --git a/content/blog/open-source/standaardisatie.md b/public/content/ict/open-source/standaardisatie.md
similarity index 100%
rename from content/blog/open-source/standaardisatie.md
rename to public/content/ict/open-source/standaardisatie.md
diff --git a/public/content/leren/index.md b/public/content/leren/index.md
new file mode 100644
index 0000000..9a11366
--- /dev/null
+++ b/public/content/leren/index.md
@@ -0,0 +1,7 @@
+# Leren
+
+Welkom bij de leersectie. Hier vind je verschillende artikelen over leren en persoonlijke ontwikkeling.
+
+## Beschikbare Artikelen
+
+- [Het Cruciale Belang van Kennis boven Aantallen in de Werkkracht](?page=leren/kennis-boven-aantallen)
\ No newline at end of file
diff --git a/content/blog/leren/kennis-boven-aantallen.md b/public/content/leren/kennis-boven-aantallen.md
similarity index 100%
rename from content/blog/leren/kennis-boven-aantallen.md
rename to public/content/leren/kennis-boven-aantallen.md
diff --git a/public/content/phpinfo.php b/public/content/phpinfo.php
new file mode 100644
index 0000000..8d0740a
--- /dev/null
+++ b/public/content/phpinfo.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/content/blog/retro-gaming/commodore-64.md b/public/content/retro-gaming/commodore-64.md
similarity index 100%
rename from content/blog/retro-gaming/commodore-64.md
rename to public/content/retro-gaming/commodore-64.md
diff --git a/content/blog/over-mij/welkom.md b/public/content/welkom.md
similarity index 94%
rename from content/blog/over-mij/welkom.md
rename to public/content/welkom.md
index 2938c51..f0a7825 100644
--- a/content/blog/over-mij/welkom.md
+++ b/public/content/welkom.md
@@ -26,7 +26,7 @@ Momenteel werk ik als adviseur bij de overheid, waar ik mijn kennis en ervaring
Mijn ultieme doel is om anderen te inspireren die zich in een vergelijkbare positie bevinden, met een handicap zoals dyslexie en ADHD, om zichzelf te blijven ontwikkelen en te leren wat ze willen leren. Ik geloof dat het nooit te laat is om te beginnen met leren en dat er altijd manieren zijn om je doelen te bereiken, zelfs als de weg er naartoe uitdagend lijkt.
-Daarom heb ik deze blog gecreëerd. Hier wil ik graag mijn kennis en ervaringen delen en anderen aanmoedigen om hun passie te volgen en te blijven leren, ongeacht welke obstakels zich voordoen.
+Daarom heb ik deze blog gecreëerd. Hier wil ik graag mijn kennis en ervaringen delen en anderen aanmoedigen om hun passie te volgen en te blijven leren, en open-source ondersteunen en bijdragen waar het kan. Ongeacht welke obstakels zich voordoen.
Dankjewel voor het bezoeken van mijn blog, en ik hoop je snel weer terug te zien!
diff --git a/public/engine b/public/engine
deleted file mode 120000
index a19e810..0000000
--- a/public/engine
+++ /dev/null
@@ -1 +0,0 @@
-../engine
\ No newline at end of file
diff --git a/public/index.php b/public/index.php
index f69d332..484e57d 100644
--- a/public/index.php
+++ b/public/index.php
@@ -1,442 +1,9 @@
config = $config;
- $this->buildMenu();
-
- if (isset($_GET['search'])) {
- $this->performSearch($_GET['search']);
- }
- }
-
- private function buildMenu() {
- $this->menu = $this->scanDirectory($this->config['content_dir'], '');
- }
-
- private function scanDirectory($dir, $prefix) {
- if (!is_dir($dir)) return [];
-
- $items = scandir($dir);
- sort($items);
- $result = [];
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $result[] = [
- 'type' => 'folder',
- 'title' => ucfirst($item),
- 'path' => $relativePath,
- 'children' => $this->scanDirectory($path, $relativePath)
- ];
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $result[] = [
- 'type' => 'file',
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath
- ];
- }
- }
-
- return $result;
- }
-
- private function performSearch($query) {
- $this->searchResults = [];
- $this->searchInDirectory($this->config['content_dir'], '', $query);
- }
-
- private function searchInDirectory($dir, $prefix, $query) {
- if (!is_dir($dir)) return;
-
- $items = scandir($dir);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dir . '/' . $item;
- $relativePath = $prefix ? $prefix . '/' . $item : $item;
-
- if (is_dir($path)) {
- $this->searchInDirectory($path, $relativePath, $query);
-} elseif (preg_match('/\.(md|php|html)$/', $item)) {
- $content = file_get_contents($path);
- if (stripos($content, $query) !== false || stripos($item, $query) !== false) {
- $title = ucfirst(pathinfo($item, PATHINFO_FILENAME));
- $this->searchResults[] = [
- 'title' => $title,
- 'path' => $relativePath,
- 'url' => '?page=' . $relativePath,
- 'snippet' => $this->createSnippet($content, $query)
- ];
- }
- }
- }
- }
-
- private function createSnippet($content, $query) {
- $content = strip_tags($content);
- $pos = stripos($content, $query);
- if ($pos === false) return substr($content, 0, 100) . '...';
-
- $start = max(0, $pos - 50);
- $snippet = substr($content, $start, 150);
- return '...' . $snippet . '...';
- }
-
- public function getPage() {
- if (isset($_GET['search'])) {
- return $this->getSearchResults();
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- $filePath = $this->config['content_dir'] . '/' . $page;
- $actualFilePath = null;
-
- if (file_exists($filePath . '.md')) {
- $actualFilePath = $filePath . '.md';
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath . '.php')) {
- $actualFilePath = $filePath . '.php';
- $result = $this->parsePHP($actualFilePath);
- } elseif (file_exists($filePath . '.html')) {
- $actualFilePath = $filePath . '.html';
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- } elseif (is_dir($filePath)) {
- // Check for index files in directory
- if (file_exists($filePath . '/index.md')) {
- $actualFilePath = $filePath . '/index.md';
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif (file_exists($filePath . '/index.php')) {
- $actualFilePath = $filePath . '/index.php';
- $result = $this->parsePHP($actualFilePath);
- } elseif (file_exists($filePath . '/index.html')) {
- $actualFilePath = $filePath . '/index.html';
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- } else {
- // Generate directory listing
- return $this->generateDirectoryListing($filePath, $page);
- }
- } elseif (file_exists($filePath)) {
- $actualFilePath = $filePath;
- $extension = pathinfo($filePath, PATHINFO_EXTENSION);
- if ($extension === 'md') {
- $result = $this->parseMarkdown(file_get_contents($actualFilePath));
- } elseif ($extension === 'php') {
- $result = $this->parsePHP($actualFilePath);
- } elseif ($extension === 'html') {
- $result = $this->parseHTML(file_get_contents($actualFilePath));
- }
- }
-
- if (isset($result) && $actualFilePath) {
- $result['file_info'] = $this->getFileInfo($actualFilePath);
- return $result;
- }
-
- return $this->getError404();
- }
-
- private function getFileInfo($filePath) {
- if (!file_exists($filePath)) {
- return null;
- }
-
- $stats = stat($filePath);
- $created = date('d-m-Y H:i', $stats['ctime']);
- $modified = date('d-m-Y H:i', $stats['mtime']);
-
- return [
- 'created' => $created,
- 'modified' => $modified,
- 'size' => $this->formatFileSize($stats['size'])
- ];
- }
-
- private function formatFileSize($bytes) {
- $units = ['B', 'KB', 'MB', 'GB'];
- $bytes = max($bytes, 0);
- $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
- $pow = min($pow, count($units) - 1);
-
- $bytes /= pow(1024, $pow);
-
- return round($bytes, 2) . ' ' . $units[$pow];
- }
-
- private function generateDirectoryListing($dirPath, $urlPath) {
- $title = ucfirst(basename($dirPath));
- $content = '';
-
- $items = scandir($dirPath);
- sort($items);
-
- foreach ($items as $item) {
- if ($item[0] === '.') continue;
-
- $path = $dirPath . '/' . $item;
- $relativePath = $urlPath . '/' . $item;
- $itemName = ucfirst(pathinfo($item, PATHINFO_FILENAME));
-
- if (is_dir($path)) {
- $content .= '
';
- $content .= '
';
- $content .= '
';
- $content .= '
';
- $content .= '
';
- } elseif (preg_match('/\.(md|php|html)$/', $item)) {
- // Remove extension from URL for cleaner links
- $cleanPath = preg_replace('/\.[^.]+$/', '', $relativePath);
-
- // Get preview content
- $preview = '';
- $fileContent = file_get_contents($path);
-
- // Extract title if possible
- $fileTitle = $itemName;
- if (preg_match('/^#\s+(.+)$/m', $fileContent, $matches)) {
- $fileTitle = trim($matches[1]);
- }
-
- // Extract preview text (first paragraph)
- $fileContent = strip_tags($this->parseMarkdown($fileContent)['content']);
- $preview = substr($fileContent, 0, 150) . '...';
-
- $content .= '
';
- $content .= '
';
- $content .= '
';
- $content .= '
';
- $content .= '
' . $preview . '
';
- $content .= '
Lees meer ';
- $content .= '
';
- }
- }
-
- $content .= '
';
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function getSearchResults() {
- $query = $_GET['search'];
- $content = 'Search Results for: "' . htmlspecialchars($query) . '" ';
-
- if (empty($this->searchResults)) {
- $content .= 'No results found.
';
- } else {
- $content .= 'Found ' . count($this->searchResults) . ' results:
';
- foreach ($this->searchResults as $result) {
- $content .= '';
- $content .= '
';
- $content .= '
';
- $content .= '
' . htmlspecialchars($result['path']) . '
';
- $content .= '
' . htmlspecialchars($result['snippet']) . '
';
- $content .= '
';
- }
- }
-
- return [
- 'title' => 'Search Results',
- 'content' => $content
- ];
- }
-
- private function parseMarkdown($content) {
- $lines = explode("\n", $content);
- $title = '';
- $body = '';
- $inBody = false;
-
- foreach ($lines as $line) {
- if (!$inBody && preg_match('/^#\s+(.+)$/', $line, $matches)) {
- $title = $matches[1];
- $inBody = true;
- } elseif ($inBody || trim($line) !== '') {
- $body .= $line . "\n";
- $inBody = true;
- }
- }
-
- $body = preg_replace('/### (.+)/', '$1 ', $body);
- $body = preg_replace('/## (.+)/', '$1 ', $body);
- $body = preg_replace('/# (.+)/', '$1 ', $body);
- $body = preg_replace('/\*\*(.+?)\*\*/', '$1 ', $body);
- $body = preg_replace('/\*(.+?)\*/', '$1 ', $body);
-
- // Convert Markdown links to HTML links
- $body = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '$1 ', $body);
-
- // Convert relative internal links to CMS format
- $body = preg_replace('/href="\/blog\/([^"]+)"/', 'href="?page=blog/$1"', $body);
- $body = preg_replace('/href="\/([^"]+)"/', 'href="?page=$1"', $body);
-
- $body = preg_replace('/\n\n/', '
', $body);
- $body = '
' . $body . '
';
- $body = preg_replace('/<\/p>/', '', $body);
- $body = preg_replace('/
()/', '$1', $body);
- $body = preg_replace('/(<\/h[1-6]>)<\/p>/', '$1', $body);
-
- return [
- 'title' => $title ?: 'Untitled',
- 'content' => $body
- ];
- }
-
- private function parsePHP($filePath) {
- ob_start();
- $title = 'Untitled';
- include $filePath;
- $content = ob_get_clean();
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function parseHTML($content) {
- $title = 'Untitled';
-
- if (preg_match('/(.*?)<\/title>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- } elseif (preg_match('/]*>(.*?)<\/h1>/i', $content, $matches)) {
- $title = strip_tags($matches[1]);
- }
-
- return [
- 'title' => $title,
- 'content' => $content
- ];
- }
-
- private function getError404() {
- return [
- 'title' => 'Page Not Found',
- 'content' => '404 - Page Not Found The page you are looking for does not exist.
'
- ];
- }
-
- public function getMenu() {
- return $this->menu;
- }
-
- public function render() {
- $page = $this->getPage();
- $menu = $this->getMenu();
- $breadcrumb = $this->getBreadcrumb();
-
- $template = file_get_contents($this->config['templates_dir'] . '/layout.html');
-
- $template = str_replace('{{site_title}}', $this->config['site_title'], $template);
- $template = str_replace('{{page_title}}', $page['title'], $template);
- $template = str_replace('{{content}}', $page['content'], $template);
- $template = str_replace('{{search_query}}', isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '', $template);
- $template = str_replace('{{breadcrumb}}', $breadcrumb, $template);
-
- // File info for footer
- $fileInfo = '';
- if (isset($page['file_info'])) {
- $fileInfo = ' Created: ' . htmlspecialchars($page['file_info']['created']) .
- ' | Modified: ' . htmlspecialchars($page['file_info']['modified']);
- }
- $template = str_replace('{{file_info}}', $fileInfo, $template);
-
- $menuHtml = $this->renderMenu($menu);
-
- $template = str_replace('{{menu}}', $menuHtml, $template);
-
- echo $template;
- }
-
- private function getBreadcrumb() {
- if (isset($_GET['search'])) {
- return 'Home Search ';
- }
-
- $page = $_GET['page'] ?? $this->config['default_page'];
- $page = preg_replace('/\.[^.]+$/', '', $page);
-
- if ($page === $this->config['default_page']) {
- return 'Home ';
- }
-
- $parts = explode('/', $page);
- $breadcrumb = 'Home ';
-
- $path = '';
- foreach ($parts as $i => $part) {
- $path .= ($path ? '/' : '') . $part;
- $title = ucfirst($part);
-
- if ($i === count($parts) - 1) {
- $breadcrumb .= '' . $title . ' ';
- } else {
- // Check if directory has index file
- $dirPath = $this->config['content_dir'] . '/' . $path;
- $hasIndex = file_exists($dirPath . '/index.md') || file_exists($dirPath . '/index.php') || file_exists($dirPath . '/index.html');
-
- // Always make breadcrumb items clickable, CMS will generate index if missing
- $breadcrumb .= '' . $title . ' ';
- }
- }
-
- $breadcrumb .= ' ';
- return $breadcrumb;
- }
-
- private function renderMenu($items, $level = 0) {
- $html = '';
- foreach ($items as $item) {
- if ($item['type'] === 'folder') {
- $hasChildren = !empty($item['children']);
- $html .= '';
-
- if ($hasChildren) {
- $folderId = 'folder-' . str_replace('/', '-', $item['path']);
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- $html .= '';
- $html .= $this->renderMenu($item['children'], $level + 1);
- $html .= ' ';
- } else {
- $html .= '';
- $html .= ' ' . htmlspecialchars($item['title']);
- $html .= ' ';
- }
-
- $html .= ' ';
- } else {
- $active = (isset($_GET['page']) && $_GET['page'] === $item['path']) ? 'active' : '';
- $html .= '';
- $html .= '' . htmlspecialchars($item['title']) . ' ';
- $html .= ' ';
- }
- }
- return $html;
- }
-}
-
// Block direct access to content files
$requestUri = $_SERVER['REQUEST_URI'] ?? '';
if (strpos($requestUri, '/content/') !== false) {
diff --git a/server.log b/server.log
deleted file mode 100644
index c548f93..0000000
--- a/server.log
+++ /dev/null
@@ -1 +0,0 @@
-[Wed Nov 19 17:58:28 2025] Failed to listen on localhost:8080 (reason: Address already in use)
diff --git a/src/scss/main.scss b/src/scss/main.scss
new file mode 100644
index 0000000..57ebd81
--- /dev/null
+++ b/src/scss/main.scss
@@ -0,0 +1,352 @@
+// Custom variables (must come before Bootstrap import)
+$primary: #0d6efd;
+$navigation-opacity: 0.5; // 50% opacity
+
+// Import Bootstrap
+@import "bootstrap/scss/bootstrap";
+
+// Custom navigation background with 50% opacity of primary color
+.navigation-50-opacity {
+ background-color: rgba($primary, $navigation-opacity) !important;
+}
+
+// Custom CodePress styles
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html, body {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+}
+
+/* Header */
+header.navbar {
+ flex-shrink: 0;
+ height: 66px;
+}
+
+/* Navigation section */
+.navigation-section {
+ background-color: #ffffff;
+ border-bottom: 1px solid #dee2e6;
+ padding: 0.5rem 0;
+ flex-shrink: 0;
+}
+
+/* Tab styling */
+.nav-tabs {
+ border-bottom: none;
+}
+
+.nav-tabs .nav-link {
+ border: 1px solid transparent;
+ border-bottom: none;
+ border-radius: 0; // Geen ronde hoeken
+ color: #6c757d;
+ background-color: transparent; // Transparante achtergrond voor eerste laag
+ margin-right: 0.25rem;
+ transition: all 0.2s ease;
+}
+
+.nav-tabs .nav-link:hover {
+ color: #495057;
+ background-color: rgba(0, 0, 0, 0.05); // Subtiel hover effect
+ border-color: transparent;
+}
+
+.nav-tabs .nav-link.active {
+ color: #495057;
+ background-color: #ffffff; // Alleen active heeft witte achtergrond
+ border-color: #dee2e6 #dee2e6 transparent;
+ font-weight: 500;
+}
+
+/* Dropdown in tabs - eerste laag */
+.nav-tabs .nav-item.dropdown .nav-link {
+ background-color: transparent; // Transparante achtergrond
+ border: 1px solid transparent;
+ border-radius: 0; // Geen ronde hoeken
+}
+
+.nav-tabs .nav-item.dropdown .nav-link:hover {
+ background-color: rgba(0, 0, 0, 0.05); // Subtiel hover effect
+ border-color: transparent;
+}
+
+/* Dropdown menu styling */
+.nav-tabs .dropdown-menu {
+ margin-top: 0.25rem;
+ border-radius: 0.375rem;
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+ min-width: 200px;
+ max-width: 300px;
+ width: auto;
+}
+
+/* Dropdown items styling */
+.nav-tabs .dropdown-menu .dropdown-item {
+ white-space: normal;
+ padding: 0.5rem 1rem;
+ line-height: 1.4;
+}
+
+/* Long text truncation for very long titles */
+.nav-tabs .dropdown-menu .dropdown-item {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 280px;
+}
+
+/* Breadcrumb section */
+.breadcrumb-section {
+ background-color: #f8f9fa;
+ border-bottom: 1px solid #dee2e6;
+ flex-shrink: 0;
+}
+
+/* Breadcrumb styling consistency */
+.breadcrumb-section .breadcrumb {
+ margin-bottom: 0;
+}
+
+.breadcrumb-section .breadcrumb-item {
+ color: #6c757d !important;
+}
+
+.breadcrumb-section .breadcrumb-item a {
+ color: #0d6efd !important;
+ text-decoration: none;
+}
+
+.breadcrumb-section .breadcrumb-item a:hover {
+ color: #0a58ca !important;
+ text-decoration: underline;
+}
+
+.breadcrumb-section .breadcrumb-item.active {
+ color: #6c757d !important;
+}
+
+/* Main content area */
+.main-content {
+ flex: 1;
+ overflow-y: auto;
+ padding: 1rem 0;
+}
+
+/* Content specific styling */
+.markdown-content,
+.php-content,
+.html-content {
+ margin-bottom: 2rem;
+}
+
+.markdown-content .content-body,
+.php-content .content-body,
+.html-content .content-body {
+ padding: 2rem;
+ line-height: 1.6;
+}
+
+/* Footer */
+footer {
+ flex-shrink: 0;
+}
+
+/* Navigation dropdown styling */
+.navigation-section .btn {
+ font-weight: 500;
+}
+
+.navigation-section .collapse .nav-link {
+ color: #495057;
+ padding: 0.375rem 0.75rem;
+ border-radius: 0.25rem;
+ transition: all 0.2s ease;
+}
+
+.navigation-section .collapse .nav-link:hover {
+ background-color: #e9ecef;
+ color: #212529;
+}
+
+.navigation-section .collapse .nav-link.active {
+ background-color: #0d6efd;
+ color: white;
+}
+
+.navigation-section .collapse .nav-link i {
+ margin-right: 0.5rem;
+}
+
+/* File info styling */
+.file-info {
+ font-size: 0.9rem;
+ color: #6c757d;
+ flex: 1;
+ min-width: 0;
+}
+
+.file-info i {
+ margin-right: 5px;
+}
+
+.page-title {
+ color: #6c757d;
+ max-width: 250px;
+ display: inline-block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ vertical-align: middle;
+ margin-right: 10px;
+}
+
+.page-title:hover {
+ color: #495057;
+ cursor: default;
+}
+
+.file-details {
+ color: #6c757d;
+ font-size: 0.85rem;
+}
+
+.site-info a {
+ color: #0d6efd;
+ text-decoration: none;
+}
+
+.site-info a:hover {
+ text-decoration: underline;
+}
+
+.guide-link {
+ color: #6c757d !important;
+ text-decoration: none;
+ font-size: 0.9rem;
+}
+
+.guide-link:hover {
+ color: #0d6efd !important;
+ text-decoration: none;
+}
+
+.guide-link i {
+ font-size: 1rem;
+ color: #6c757d !important;
+}
+
+.guide-link:hover i {
+ color: #0d6efd !important;
+}
+
+.auto-link {
+ color: #0d6efd;
+ text-decoration: none;
+ border-bottom: 2px dashed #0d6efd;
+ font-weight: 500;
+ transition: all 0.2s ease;
+}
+
+.auto-link:hover {
+ color: #0a58ca;
+ text-decoration: none;
+ border-bottom-style: solid;
+ border-bottom-color: #0a58ca;
+}
+
+/* Search form */
+.search-form {
+ max-width: 300px;
+}
+
+/* Card styling */
+.card-title a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.card-title a:hover {
+ text-decoration: underline;
+}
+
+/* Dropdown submenu styling */
+.dropdown-menu .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -1px;
+ margin-left: -1px;
+}
+
+/* Show nested dropdowns on hover */
+.dropdown-submenu {
+ position: relative;
+}
+
+.dropdown-submenu .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -1px;
+}
+
+.dropdown-submenu:hover > .dropdown-menu {
+ display: block;
+}
+
+/* For touch devices - click to open */
+.dropdown-submenu > .dropdown-toggle:active::after {
+ transform: rotate(90deg);
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .main-content {
+ padding-top: 120px; /* More space for mobile */
+ }
+
+ .markdown-content .content-body,
+ .php-content .content-body,
+ .html-content .content-body {
+ padding: 1.5rem;
+ }
+
+ .nav-tabs .dropdown-menu {
+ min-width: 180px;
+ max-width: 250px;
+ }
+
+ .nav-tabs .dropdown-menu .dropdown-item {
+ max-width: 230px;
+ font-size: 0.9rem;
+ }
+}
+
+@media (max-width: 576px) {
+ .markdown-content .content-body,
+ .php-content .content-body,
+ .html-content .content-body {
+ padding: 1rem;
+ }
+
+ .nav-tabs .dropdown-menu {
+ min-width: 160px;
+ max-width: 200px;
+ }
+
+ .nav-tabs .dropdown-menu .dropdown-item {
+ max-width: 180px;
+ font-size: 0.85rem;
+ padding: 0.4rem 0.8rem;
+ }
+}
\ No newline at end of file
diff --git a/templates/layout.html b/templates/layout.html
deleted file mode 100644
index 4539bd4..0000000
--- a/templates/layout.html
+++ /dev/null
@@ -1,381 +0,0 @@
-
-
-
-
-
- {{page_title}} - {{site_title}}
-
-
-
-
-
-
-
-
-
-
-
-
-
{{site_title}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{breadcrumb}}
-
-
-
{{page_title}}
-
-
- {{content}}
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644
index 0000000..504385e
--- /dev/null
+++ b/vendor/autoload.php
@@ -0,0 +1,22 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var \Closure(string):void */
+ private static $includeFile;
+
+ /** @var string|null */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var array
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var array
+ */
+ private $missingClasses = array();
+
+ /** @var string|null */
+ private $apcuPrefix;
+
+ /**
+ * @var array
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param string|null $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ self::initializeIncludeClosure();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return array Array of classname => path
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ $includeFile = self::$includeFile;
+ $includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
+ *
+ * @return array
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return void
+ */
+ private static function initializeIncludeClosure()
+ {
+ if (self::$includeFile !== null) {
+ return;
+ }
+
+ /**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ */
+ self::$includeFile = \Closure::bind(static function($file) {
+ include $file;
+ }, null, null);
+ }
+}
diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..2052022
--- /dev/null
+++ b/vendor/composer/InstalledVersions.php
@@ -0,0 +1,396 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+ /**
+ * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
+ * @internal
+ */
+ private static $selfDir = null;
+
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool
+ */
+ private static $installedIsLocalDir;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints((string) $constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+
+ // when using reload, we disable the duplicate protection to ensure that self::$installed data is
+ // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
+ // so we have to assume it does not, and that may result in duplicate data being returned when listing
+ // all installed packages for example
+ self::$installedIsLocalDir = false;
+ }
+
+ /**
+ * @return string
+ */
+ private static function getSelfDir()
+ {
+ if (self::$selfDir === null) {
+ self::$selfDir = strtr(__DIR__, '\\', '/');
+ }
+
+ return self::$selfDir;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+ $copiedLocalDir = false;
+
+ if (self::$canGetVendors) {
+ $selfDir = self::getSelfDir();
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ $vendorDir = strtr($vendorDir, '\\', '/');
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ self::$installedByVendor[$vendorDir] = $required;
+ $installed[] = $required;
+ if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
+ self::$installed = $required;
+ self::$installedIsLocalDir = true;
+ }
+ }
+ if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
+ $copiedLocalDir = true;
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ if (self::$installed !== array() && !$copiedLocalDir) {
+ $installed[] = self::$installed;
+ }
+
+ return $installed;
+ }
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+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.
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..ef2a038
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,110 @@
+ $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'Mustache_Cache' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_AbstractCache' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_FilesystemCache' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_NoopCache' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Compiler' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Context' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Engine' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_InvalidArgumentException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_LogicException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_RuntimeException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_SyntaxException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownFilterException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownHelperException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownTemplateException' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_HelperCollection' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_LambdaHelper' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_ArrayLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_CascadingLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_FilesystemLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_InlineLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_MutableLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_ProductionFilesystemLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_StringLoader' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger_AbstractLogger' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger_StreamLogger' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Parser' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Source' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Source_FilesystemSource' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Template' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Mustache_Tokenizer' => $vendorDir . '/mustache/mustache/src/compat.php',
+ 'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\HtmlStringable' => $vendorDir . '/nette/utils/src/HtmlStringable.php',
+ 'Nette\\IOException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php',
+ 'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php',
+ 'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/compatibility.php',
+ 'Nette\\Localization\\Translator' => $vendorDir . '/nette/utils/src/Translator.php',
+ 'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\Schema\\Context' => $vendorDir . '/nette/schema/src/Schema/Context.php',
+ 'Nette\\Schema\\DynamicParameter' => $vendorDir . '/nette/schema/src/Schema/DynamicParameter.php',
+ 'Nette\\Schema\\Elements\\AnyOf' => $vendorDir . '/nette/schema/src/Schema/Elements/AnyOf.php',
+ 'Nette\\Schema\\Elements\\Base' => $vendorDir . '/nette/schema/src/Schema/Elements/Base.php',
+ 'Nette\\Schema\\Elements\\Structure' => $vendorDir . '/nette/schema/src/Schema/Elements/Structure.php',
+ 'Nette\\Schema\\Elements\\Type' => $vendorDir . '/nette/schema/src/Schema/Elements/Type.php',
+ 'Nette\\Schema\\Expect' => $vendorDir . '/nette/schema/src/Schema/Expect.php',
+ 'Nette\\Schema\\Helpers' => $vendorDir . '/nette/schema/src/Schema/Helpers.php',
+ 'Nette\\Schema\\Message' => $vendorDir . '/nette/schema/src/Schema/Message.php',
+ 'Nette\\Schema\\Processor' => $vendorDir . '/nette/schema/src/Schema/Processor.php',
+ 'Nette\\Schema\\Schema' => $vendorDir . '/nette/schema/src/Schema/Schema.php',
+ 'Nette\\Schema\\ValidationException' => $vendorDir . '/nette/schema/src/Schema/ValidationException.php',
+ 'Nette\\ShouldNotHappenException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php',
+ 'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php',
+ 'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php',
+ 'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php',
+ 'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php',
+ 'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php',
+ 'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php',
+ 'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php',
+ 'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php',
+ 'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php',
+ 'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php',
+ 'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php',
+ 'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php',
+ 'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php',
+ 'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php',
+ 'Nette\\Utils\\ImageColor' => $vendorDir . '/nette/utils/src/Utils/ImageColor.php',
+ 'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ImageType' => $vendorDir . '/nette/utils/src/Utils/ImageType.php',
+ 'Nette\\Utils\\Iterables' => $vendorDir . '/nette/utils/src/Utils/Iterables.php',
+ 'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php',
+ 'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php',
+ 'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php',
+ 'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php',
+ 'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php',
+ 'Nette\\Utils\\ReflectionMethod' => $vendorDir . '/nette/utils/src/Utils/ReflectionMethod.php',
+ 'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php',
+ 'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php',
+ 'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php',
+ 'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+ 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+ 'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+ 'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..414f45c
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,11 @@
+ $vendorDir . '/symfony/deprecation-contracts/function.php',
+ 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..15a2ff3
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/symfony/polyfill-php80'),
+ 'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
+ 'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'),
+ 'Mustache\\' => array($vendorDir . '/mustache/mustache/src'),
+ 'League\\Config\\' => array($vendorDir . '/league/config/src'),
+ 'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
+ 'Dflydev\\DotAccessData\\' => array($vendorDir . '/dflydev/dot-access-data/src'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..b743160
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,50 @@
+register(true);
+
+ $filesToLoad = \Composer\Autoload\ComposerStaticInit071586d19f5409de22b3235d85d8476c::$files;
+ $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+ require $file;
+ }
+ }, null, null);
+ foreach ($filesToLoad as $fileIdentifier => $file) {
+ $requireFile($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..0333ea2
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,187 @@
+ __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
+ 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'S' =>
+ array (
+ 'Symfony\\Polyfill\\Php80\\' => 23,
+ ),
+ 'P' =>
+ array (
+ 'Psr\\EventDispatcher\\' => 20,
+ ),
+ 'N' =>
+ array (
+ 'Nette\\' => 6,
+ ),
+ 'M' =>
+ array (
+ 'Mustache\\' => 9,
+ ),
+ 'L' =>
+ array (
+ 'League\\Config\\' => 14,
+ 'League\\CommonMark\\' => 18,
+ ),
+ 'D' =>
+ array (
+ 'Dflydev\\DotAccessData\\' => 22,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Symfony\\Polyfill\\Php80\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
+ ),
+ 'Psr\\EventDispatcher\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/event-dispatcher/src',
+ ),
+ 'Nette\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/nette/schema/src',
+ 1 => __DIR__ . '/..' . '/nette/utils/src',
+ ),
+ 'Mustache\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/mustache/mustache/src',
+ ),
+ 'League\\Config\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/league/config/src',
+ ),
+ 'League\\CommonMark\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/league/commonmark/src',
+ ),
+ 'Dflydev\\DotAccessData\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/dflydev/dot-access-data/src',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'Mustache_Cache' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_AbstractCache' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_FilesystemCache' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Cache_NoopCache' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Compiler' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Context' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Engine' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_InvalidArgumentException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_LogicException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_RuntimeException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_SyntaxException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownFilterException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownHelperException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Exception_UnknownTemplateException' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_HelperCollection' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_LambdaHelper' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_ArrayLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_CascadingLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_FilesystemLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_InlineLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_MutableLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_ProductionFilesystemLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Loader_StringLoader' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger_AbstractLogger' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Logger_StreamLogger' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Parser' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Source' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Source_FilesystemSource' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Template' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Mustache_Tokenizer' => __DIR__ . '/..' . '/mustache/mustache/src/compat.php',
+ 'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\HtmlStringable' => __DIR__ . '/..' . '/nette/utils/src/HtmlStringable.php',
+ 'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php',
+ 'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php',
+ 'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php',
+ 'Nette\\Localization\\Translator' => __DIR__ . '/..' . '/nette/utils/src/Translator.php',
+ 'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\Schema\\Context' => __DIR__ . '/..' . '/nette/schema/src/Schema/Context.php',
+ 'Nette\\Schema\\DynamicParameter' => __DIR__ . '/..' . '/nette/schema/src/Schema/DynamicParameter.php',
+ 'Nette\\Schema\\Elements\\AnyOf' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/AnyOf.php',
+ 'Nette\\Schema\\Elements\\Base' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Base.php',
+ 'Nette\\Schema\\Elements\\Structure' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Structure.php',
+ 'Nette\\Schema\\Elements\\Type' => __DIR__ . '/..' . '/nette/schema/src/Schema/Elements/Type.php',
+ 'Nette\\Schema\\Expect' => __DIR__ . '/..' . '/nette/schema/src/Schema/Expect.php',
+ 'Nette\\Schema\\Helpers' => __DIR__ . '/..' . '/nette/schema/src/Schema/Helpers.php',
+ 'Nette\\Schema\\Message' => __DIR__ . '/..' . '/nette/schema/src/Schema/Message.php',
+ 'Nette\\Schema\\Processor' => __DIR__ . '/..' . '/nette/schema/src/Schema/Processor.php',
+ 'Nette\\Schema\\Schema' => __DIR__ . '/..' . '/nette/schema/src/Schema/Schema.php',
+ 'Nette\\Schema\\ValidationException' => __DIR__ . '/..' . '/nette/schema/src/Schema/ValidationException.php',
+ 'Nette\\ShouldNotHappenException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php',
+ 'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php',
+ 'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php',
+ 'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php',
+ 'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php',
+ 'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php',
+ 'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php',
+ 'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php',
+ 'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php',
+ 'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php',
+ 'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php',
+ 'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php',
+ 'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php',
+ 'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php',
+ 'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php',
+ 'Nette\\Utils\\ImageColor' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageColor.php',
+ 'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ImageType' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageType.php',
+ 'Nette\\Utils\\Iterables' => __DIR__ . '/..' . '/nette/utils/src/Utils/Iterables.php',
+ 'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php',
+ 'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php',
+ 'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php',
+ 'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php',
+ 'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php',
+ 'Nette\\Utils\\ReflectionMethod' => __DIR__ . '/..' . '/nette/utils/src/Utils/ReflectionMethod.php',
+ 'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php',
+ 'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php',
+ 'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php',
+ 'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
+ 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
+ 'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
+ 'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit071586d19f5409de22b3235d85d8476c::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit071586d19f5409de22b3235d85d8476c::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInit071586d19f5409de22b3235d85d8476c::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000..358c13d
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,705 @@
+{
+ "packages": [
+ {
+ "name": "dflydev/dot-access-data",
+ "version": "v3.0.3",
+ "version_normalized": "3.0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
+ },
+ "time": "2024-07-08T12:26:09+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com"
+ },
+ {
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com"
+ }
+ ],
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+ "keywords": [
+ "access",
+ "data",
+ "dot",
+ "notation"
+ ],
+ "support": {
+ "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues",
+ "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3"
+ },
+ "install-path": "../dflydev/dot-access-data"
+ },
+ {
+ "name": "league/commonmark",
+ "version": "2.7.1",
+ "version_normalized": "2.7.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/commonmark.git",
+ "reference": "10732241927d3971d28e7ea7b5712721fa2296ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca",
+ "reference": "10732241927d3971d28e7ea7b5712721fa2296ca",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "php": "^7.4 || ^8.0",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "ext-json": "*",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
+ },
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "time": "2025-07-20T12:47:49+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.8-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "homepage": "https://commonmark.thephpleague.com",
+ "keywords": [
+ "commonmark",
+ "flavored",
+ "gfm",
+ "github",
+ "github-flavored",
+ "markdown",
+ "md",
+ "parser"
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/commonmark",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../league/commonmark"
+ },
+ {
+ "name": "league/config",
+ "version": "v1.2.0",
+ "version_normalized": "1.2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/config.git",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3",
+ "shasum": ""
+ },
+ "require": {
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "time": "2022-12-11T20:36:23+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "homepage": "https://config.thephpleague.com",
+ "keywords": [
+ "array",
+ "config",
+ "configuration",
+ "dot",
+ "dot-access",
+ "nested",
+ "schema"
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "funding": [
+ {
+ "url": "https://www.colinodell.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.paypal.me/colinpodell/10.00",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/colinodell",
+ "type": "github"
+ }
+ ],
+ "install-path": "../league/config"
+ },
+ {
+ "name": "mustache/mustache",
+ "version": "v3.0.0",
+ "version_normalized": "3.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bobthecow/mustache.php.git",
+ "reference": "176b6b21d68516dd5107a63ab71b0050e518b7a4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/176b6b21d68516dd5107a63ab71b0050e518b7a4",
+ "reference": "176b6b21d68516dd5107a63ab71b0050e518b7a4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2.19.3",
+ "yoast/phpunit-polyfills": "^2.0"
+ },
+ "time": "2025-06-28T18:28:20+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Mustache\\": "src/"
+ },
+ "classmap": [
+ "src/compat.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com"
+ }
+ ],
+ "description": "A Mustache implementation in PHP.",
+ "homepage": "https://github.com/bobthecow/mustache.php",
+ "keywords": [
+ "mustache",
+ "templating"
+ ],
+ "support": {
+ "issues": "https://github.com/bobthecow/mustache.php/issues",
+ "source": "https://github.com/bobthecow/mustache.php/tree/v3.0.0"
+ },
+ "install-path": "../mustache/mustache"
+ },
+ {
+ "name": "nette/schema",
+ "version": "v1.3.3",
+ "version_normalized": "1.3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/schema.git",
+ "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004",
+ "reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^4.0",
+ "php": "8.1 - 8.5"
+ },
+ "require-dev": {
+ "nette/tester": "^2.5.2",
+ "phpstan/phpstan-nette": "^2.0@stable",
+ "tracy/tracy": "^2.8"
+ },
+ "time": "2025-10-30T22:57:59+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "📐 Nette Schema: validating data structures against a given Schema.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "config",
+ "nette"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/schema/issues",
+ "source": "https://github.com/nette/schema/tree/v1.3.3"
+ },
+ "install-path": "../nette/schema"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.0.8",
+ "version_normalized": "4.0.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
+ "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.0 - 8.5"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "^1.2",
+ "nette/tester": "^2.5",
+ "phpstan/phpstan-nette": "^2.0@stable",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "time": "2025-08-06T21:43:34+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Nette\\": "src"
+ },
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.0.8"
+ },
+ "install-path": "../nette/utils"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "version_normalized": "1.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "install-path": "../psr/event-dispatcher"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "version_normalized": "3.6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "time": "2024-09-25T14:21:43+00:00",
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/deprecation-contracts"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.33.0",
+ "version_normalized": "1.33.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "time": "2025-01-02T08:10:11+00:00",
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "install-path": "../symfony/polyfill-php80"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": []
+}
diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php
new file mode 100644
index 0000000..04d179a
--- /dev/null
+++ b/vendor/composer/installed.php
@@ -0,0 +1,104 @@
+ array(
+ 'name' => '__root__',
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => 'dfe2df141ba6e64e5425699cd553fb9b7e6d6193',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'dev' => true,
+ ),
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => 'dfe2df141ba6e64e5425699cd553fb9b7e6d6193',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'dflydev/dot-access-data' => array(
+ 'pretty_version' => 'v3.0.3',
+ 'version' => '3.0.3.0',
+ 'reference' => 'a23a2bf4f31d3518f3ecb38660c95715dfead60f',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../dflydev/dot-access-data',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'league/commonmark' => array(
+ 'pretty_version' => '2.7.1',
+ 'version' => '2.7.1.0',
+ 'reference' => '10732241927d3971d28e7ea7b5712721fa2296ca',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../league/commonmark',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'league/config' => array(
+ 'pretty_version' => 'v1.2.0',
+ 'version' => '1.2.0.0',
+ 'reference' => '754b3604fb2984c71f4af4a9cbe7b57f346ec1f3',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../league/config',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'mustache/mustache' => array(
+ 'pretty_version' => 'v3.0.0',
+ 'version' => '3.0.0.0',
+ 'reference' => '176b6b21d68516dd5107a63ab71b0050e518b7a4',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../mustache/mustache',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'nette/schema' => array(
+ 'pretty_version' => 'v1.3.3',
+ 'version' => '1.3.3.0',
+ 'reference' => '2befc2f42d7c715fd9d95efc31b1081e5d765004',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../nette/schema',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'nette/utils' => array(
+ 'pretty_version' => 'v4.0.8',
+ 'version' => '4.0.8.0',
+ 'reference' => 'c930ca4e3cf4f17dcfb03037703679d2396d2ede',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../nette/utils',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'psr/event-dispatcher' => array(
+ 'pretty_version' => '1.0.0',
+ 'version' => '1.0.0.0',
+ 'reference' => 'dbefd12671e8a14ec7f180cab83036ed26714bb0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../psr/event-dispatcher',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'symfony/deprecation-contracts' => array(
+ 'pretty_version' => 'v3.6.0',
+ 'version' => '3.6.0.0',
+ 'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ 'symfony/polyfill-php80' => array(
+ 'pretty_version' => 'v1.33.0',
+ 'version' => '1.33.0.0',
+ 'reference' => '0cc9dd0f17f61d8131e7df6b84bd344899fe2608',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../symfony/polyfill-php80',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ ),
+);
diff --git a/vendor/composer/platform_check.php b/vendor/composer/platform_check.php
new file mode 100644
index 0000000..2beb149
--- /dev/null
+++ b/vendor/composer/platform_check.php
@@ -0,0 +1,25 @@
+= 80100)) {
+ $issues[] = 'Your Composer dependencies require a PHP version ">= 8.1.0". You are running ' . PHP_VERSION . '.';
+}
+
+if ($issues) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
+ } elseif (!headers_sent()) {
+ echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
+ }
+ }
+ throw new \RuntimeException(
+ 'Composer detected issues in your platform: ' . implode(' ', $issues)
+ );
+}
diff --git a/vendor/dflydev/dot-access-data/CHANGELOG.md b/vendor/dflydev/dot-access-data/CHANGELOG.md
new file mode 100644
index 0000000..b8b468d
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/CHANGELOG.md
@@ -0,0 +1,74 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [3.0.3] - 2024-07-08
+
+### Fixed
+
+ - Fixed PHP 8.4 deprecation notices (#47)
+
+## [3.0.2] - 2022-10-27
+
+### Fixed
+
+ - Added missing return types to docblocks (#44, #45)
+
+## [3.0.1] - 2021-08-13
+
+### Added
+
+ - Adds ReturnTypeWillChange to suppress PHP 8.1 warnings (#40)
+
+## [3.0.0] - 2021-01-01
+
+### Added
+ - Added support for both `.` and `/`-delimited key paths (#24)
+ - Added parameter and return types to everything; enabled strict type checks (#18)
+ - Added new exception classes to better identify certain types of errors (#20)
+ - `Data` now implements `ArrayAccess` (#17)
+ - Added ability to merge non-associative array values (#31, #32)
+
+### Changed
+ - All thrown exceptions are now instances or subclasses of `DataException` (#20)
+ - Calling `get()` on a missing key path without providing a default will throw a `MissingPathException` instead of returning `null` (#29)
+ - Bumped supported PHP versions to 7.1 - 8.x (#18)
+
+### Fixed
+ - Fixed incorrect merging of array values into string values (#32)
+ - Fixed `get()` method behaving as if keys with `null` values didn't exist
+
+## [2.0.0] - 2017-12-21
+
+### Changed
+ - Bumped supported PHP versions to 7.0 - 7.4 (#12)
+ - Switched to PSR-4 autoloading
+
+## [1.1.0] - 2017-01-20
+
+### Added
+ - Added new `has()` method to check for the existence of the given key (#4, #7)
+
+## [1.0.1] - 2015-08-12
+
+### Added
+ - Added new optional `$default` parameter to the `get()` method (#2)
+
+## [1.0.0] - 2012-07-17
+
+**Initial release!**
+
+[Unreleased]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.3...main
+[3.0.3]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.2...v3.0.3
+[3.0.2]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.1...v3.0.2
+[3.0.1]: https://github.com/dflydev/dflydev-dot-access-data/compare/v3.0.0...v3.0.1
+[3.0.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v2.0.0...v3.0.0
+[2.0.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.1.0...v2.0.0
+[1.1.0]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/dflydev/dflydev-dot-access-data/compare/v1.0.0...v1.0.1
+[1.0.0]: https://github.com/dflydev/dflydev-dot-access-data/releases/tag/v1.0.0
diff --git a/vendor/dflydev/dot-access-data/LICENSE b/vendor/dflydev/dot-access-data/LICENSE
new file mode 100644
index 0000000..b6880d4
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Dragonfly Development Inc.
+
+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.
diff --git a/vendor/dflydev/dot-access-data/README.md b/vendor/dflydev/dot-access-data/README.md
new file mode 100644
index 0000000..775fbdf
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/README.md
@@ -0,0 +1,158 @@
+Dot Access Data
+===============
+
+[](https://packagist.org/packages/dflydev/dot-access-data)
+[](https://packagist.org/packages/dflydev/dot-access-data)
+[](LICENSE)
+[](https://github.com/dflydev/dflydev-dot-access-data/actions?query=workflow%3ATests+branch%3Amain)
+[](https://scrutinizer-ci.com/g/dflydev/dflydev-dot-access-data/code-structure/)
+[](https://scrutinizer-ci.com/g/dflydev/dflydev-dot-access-data)
+
+Given a deep data structure, access data by dot notation.
+
+
+Requirements
+------------
+
+ * PHP (7.1+)
+
+> For PHP (5.3+) please refer to version `1.0`.
+
+
+Usage
+-----
+
+Abstract example:
+
+```php
+use Dflydev\DotAccessData\Data;
+
+$data = new Data;
+
+$data->set('a.b.c', 'C');
+$data->set('a.b.d', 'D1');
+$data->append('a.b.d', 'D2');
+$data->set('a.b.e', ['E0', 'E1', 'E2']);
+
+// C
+$data->get('a.b.c');
+
+// ['D1', 'D2']
+$data->get('a.b.d');
+
+// ['E0', 'E1', 'E2']
+$data->get('a.b.e');
+
+// true
+$data->has('a.b.c');
+
+// false
+$data->has('a.b.d.j');
+
+
+// 'some-default-value'
+$data->get('some.path.that.does.not.exist', 'some-default-value');
+
+// throws a MissingPathException because no default was given
+$data->get('some.path.that.does.not.exist');
+```
+
+A more concrete example:
+
+```php
+use Dflydev\DotAccessData\Data;
+
+$data = new Data([
+ 'hosts' => [
+ 'hewey' => [
+ 'username' => 'hman',
+ 'password' => 'HPASS',
+ 'roles' => ['web'],
+ ],
+ 'dewey' => [
+ 'username' => 'dman',
+ 'password' => 'D---S',
+ 'roles' => ['web', 'db'],
+ 'nick' => 'dewey dman',
+ ],
+ 'lewey' => [
+ 'username' => 'lman',
+ 'password' => 'LP@$$',
+ 'roles' => ['db'],
+ ],
+ ],
+]);
+
+// hman
+$username = $data->get('hosts.hewey.username');
+// HPASS
+$password = $data->get('hosts.hewey.password');
+// ['web']
+$roles = $data->get('hosts.hewey.roles');
+// dewey dman
+$nick = $data->get('hosts.dewey.nick');
+// Unknown
+$nick = $data->get('hosts.lewey.nick', 'Unknown');
+
+// DataInterface instance
+$dewey = $data->getData('hosts.dewey');
+// dman
+$username = $dewey->get('username');
+// D---S
+$password = $dewey->get('password');
+// ['web', 'db']
+$roles = $dewey->get('roles');
+
+// No more lewey
+$data->remove('hosts.lewey');
+
+// Add DB to hewey's roles
+$data->append('hosts.hewey.roles', 'db');
+
+$data->set('hosts.april', [
+ 'username' => 'aman',
+ 'password' => '@---S',
+ 'roles' => ['web'],
+]);
+
+// Check if a key exists (true to this case)
+$hasKey = $data->has('hosts.dewey.username');
+```
+
+`Data` may be used as an array, since it implements `ArrayAccess` interface:
+
+```php
+// Get
+$data->get('name') === $data['name']; // true
+
+$data['name'] = 'Dewey';
+// is equivalent to
+$data->set($name, 'Dewey');
+
+isset($data['name']) === $data->has('name');
+
+// Remove key
+unset($data['name']);
+```
+
+`/` can also be used as a path delimiter:
+
+```php
+$data->set('a/b/c', 'd');
+echo $data->get('a/b/c'); // "d"
+
+$data->get('a/b/c') === $data->get('a.b.c'); // true
+```
+
+License
+-------
+
+This library is licensed under the MIT License - see the LICENSE file
+for details.
+
+
+Community
+---------
+
+If you have questions or want to help out, join us in the
+[#dflydev](irc://irc.freenode.net/#dflydev) channel on irc.freenode.net.
diff --git a/vendor/dflydev/dot-access-data/composer.json b/vendor/dflydev/dot-access-data/composer.json
new file mode 100644
index 0000000..44dc5ed
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/composer.json
@@ -0,0 +1,67 @@
+{
+ "name": "dflydev/dot-access-data",
+ "type": "library",
+ "description": "Given a deep data structure, access data by dot notation.",
+ "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+ "keywords": ["dot", "access", "data", "notation"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Dragonfly Development Inc.",
+ "email": "info@dflydev.com",
+ "homepage": "http://dflydev.com"
+ },
+ {
+ "name": "Beau Simensen",
+ "email": "beau@dflydev.com",
+ "homepage": "http://beausimensen.com"
+ },
+ {
+ "name": "Carlos Frutos",
+ "email": "carlos@kiwing.it",
+ "homepage": "https://github.com/cfrutos"
+ },
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com"
+ }
+ ],
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^0.12.42",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3",
+ "scrutinizer/ocular": "1.6.0",
+ "squizlabs/php_codesniffer": "^3.5",
+ "vimeo/psalm": "^4.0.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Dflydev\\DotAccessData\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "scripts": {
+ "phpcs": "phpcs",
+ "phpstan": "phpstan analyse",
+ "phpunit": "phpunit --no-coverage",
+ "psalm": "psalm",
+ "test": [
+ "@phpcs",
+ "@phpstan",
+ "@psalm",
+ "@phpunit"
+ ]
+ }
+}
diff --git a/vendor/dflydev/dot-access-data/src/Data.php b/vendor/dflydev/dot-access-data/src/Data.php
new file mode 100644
index 0000000..3409b8e
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/src/Data.php
@@ -0,0 +1,286 @@
+
+ */
+class Data implements DataInterface, ArrayAccess
+{
+ private const DELIMITERS = ['.', '/'];
+
+ /**
+ * Internal representation of data data
+ *
+ * @var array
+ */
+ protected $data;
+
+ /**
+ * Constructor
+ *
+ * @param array $data
+ */
+ public function __construct(array $data = [])
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function append(string $key, $value = null): void
+ {
+ $currentValue =& $this->data;
+ $keyPath = self::keyToPathArray($key);
+
+ $endKey = array_pop($keyPath);
+ foreach ($keyPath as $currentKey) {
+ if (! isset($currentValue[$currentKey])) {
+ $currentValue[$currentKey] = [];
+ }
+ $currentValue =& $currentValue[$currentKey];
+ }
+
+ if (!isset($currentValue[$endKey])) {
+ $currentValue[$endKey] = [];
+ }
+
+ if (!is_array($currentValue[$endKey])) {
+ // Promote this key to an array.
+ // TODO: Is this really what we want to do?
+ $currentValue[$endKey] = [$currentValue[$endKey]];
+ }
+
+ $currentValue[$endKey][] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $key, $value = null): void
+ {
+ $currentValue =& $this->data;
+ $keyPath = self::keyToPathArray($key);
+
+ $endKey = array_pop($keyPath);
+ foreach ($keyPath as $currentKey) {
+ if (!isset($currentValue[$currentKey])) {
+ $currentValue[$currentKey] = [];
+ }
+ if (!is_array($currentValue[$currentKey])) {
+ throw new DataException(sprintf('Key path "%s" within "%s" cannot be indexed into (is not an array)', $currentKey, self::formatPath($key)));
+ }
+ $currentValue =& $currentValue[$currentKey];
+ }
+ $currentValue[$endKey] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(string $key): void
+ {
+ $currentValue =& $this->data;
+ $keyPath = self::keyToPathArray($key);
+
+ $endKey = array_pop($keyPath);
+ foreach ($keyPath as $currentKey) {
+ if (!isset($currentValue[$currentKey])) {
+ return;
+ }
+ $currentValue =& $currentValue[$currentKey];
+ }
+ unset($currentValue[$endKey]);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @psalm-mutation-free
+ */
+ public function get(string $key, $default = null)
+ {
+ /** @psalm-suppress ImpureFunctionCall */
+ $hasDefault = \func_num_args() > 1;
+
+ $currentValue = $this->data;
+ $keyPath = self::keyToPathArray($key);
+
+ foreach ($keyPath as $currentKey) {
+ if (!is_array($currentValue) || !array_key_exists($currentKey, $currentValue)) {
+ if ($hasDefault) {
+ return $default;
+ }
+
+ throw new MissingPathException($key, sprintf('No data exists at the given path: "%s"', self::formatPath($keyPath)));
+ }
+
+ $currentValue = $currentValue[$currentKey];
+ }
+
+ return $currentValue === null ? $default : $currentValue;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @psalm-mutation-free
+ */
+ public function has(string $key): bool
+ {
+ $currentValue = $this->data;
+
+ foreach (self::keyToPathArray($key) as $currentKey) {
+ if (
+ !is_array($currentValue) ||
+ !array_key_exists($currentKey, $currentValue)
+ ) {
+ return false;
+ }
+ $currentValue = $currentValue[$currentKey];
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @psalm-mutation-free
+ */
+ public function getData(string $key): DataInterface
+ {
+ $value = $this->get($key);
+ if (is_array($value) && Util::isAssoc($value)) {
+ return new Data($value);
+ }
+
+ throw new DataException(sprintf('Value at "%s" could not be represented as a DataInterface', self::formatPath($key)));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function import(array $data, int $mode = self::REPLACE): void
+ {
+ $this->data = Util::mergeAssocArray($this->data, $data, $mode);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function importData(DataInterface $data, int $mode = self::REPLACE): void
+ {
+ $this->import($data->export(), $mode);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @psalm-mutation-free
+ */
+ public function export(): array
+ {
+ return $this->data;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($key)
+ {
+ return $this->has($key);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return mixed
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->get($key, null);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param string $key
+ * @param mixed $value
+ *
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($key, $value)
+ {
+ $this->set($key, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return void
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($key)
+ {
+ $this->remove($key);
+ }
+
+ /**
+ * @param string $path
+ *
+ * @return string[]
+ *
+ * @psalm-return non-empty-list
+ *
+ * @psalm-pure
+ */
+ protected static function keyToPathArray(string $path): array
+ {
+ if (\strlen($path) === 0) {
+ throw new InvalidPathException('Path cannot be an empty string');
+ }
+
+ $path = \str_replace(self::DELIMITERS, '.', $path);
+
+ return \explode('.', $path);
+ }
+
+ /**
+ * @param string|string[] $path
+ *
+ * @return string
+ *
+ * @psalm-pure
+ */
+ protected static function formatPath($path): string
+ {
+ if (is_string($path)) {
+ $path = self::keyToPathArray($path);
+ }
+
+ return implode(' » ', $path);
+ }
+}
diff --git a/vendor/dflydev/dot-access-data/src/DataInterface.php b/vendor/dflydev/dot-access-data/src/DataInterface.php
new file mode 100644
index 0000000..5909a8c
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/src/DataInterface.php
@@ -0,0 +1,131 @@
+ $data
+ * @param self::PRESERVE|self::REPLACE|self::MERGE $mode
+ */
+ public function import(array $data, int $mode = self::REPLACE): void;
+
+ /**
+ * Import data from an external data into existing data
+ *
+ * @param DataInterface $data
+ * @param self::PRESERVE|self::REPLACE|self::MERGE $mode
+ */
+ public function importData(DataInterface $data, int $mode = self::REPLACE): void;
+
+ /**
+ * Export data as raw data
+ *
+ * @return array
+ *
+ * @psalm-mutation-free
+ */
+ public function export(): array;
+}
diff --git a/vendor/dflydev/dot-access-data/src/Exception/DataException.php b/vendor/dflydev/dot-access-data/src/Exception/DataException.php
new file mode 100644
index 0000000..2faf9f5
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/src/Exception/DataException.php
@@ -0,0 +1,21 @@
+path = $path;
+
+ parent::__construct($message, $code, $previous);
+ }
+
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+}
diff --git a/vendor/dflydev/dot-access-data/src/Util.php b/vendor/dflydev/dot-access-data/src/Util.php
new file mode 100644
index 0000000..5634c51
--- /dev/null
+++ b/vendor/dflydev/dot-access-data/src/Util.php
@@ -0,0 +1,78 @@
+ $arr
+ *
+ * @return bool
+ *
+ * @psalm-pure
+ */
+ public static function isAssoc(array $arr): bool
+ {
+ return !count($arr) || count(array_filter(array_keys($arr), 'is_string')) == count($arr);
+ }
+
+ /**
+ * Merge contents from one associtative array to another
+ *
+ * @param mixed $to
+ * @param mixed $from
+ * @param DataInterface::PRESERVE|DataInterface::REPLACE|DataInterface::MERGE $mode
+ *
+ * @return mixed
+ *
+ * @psalm-pure
+ */
+ public static function mergeAssocArray($to, $from, int $mode = DataInterface::REPLACE)
+ {
+ if ($mode === DataInterface::MERGE && self::isList($to) && self::isList($from)) {
+ return array_merge($to, $from);
+ }
+
+ if (is_array($from) && is_array($to)) {
+ foreach ($from as $k => $v) {
+ if (!isset($to[$k])) {
+ $to[$k] = $v;
+ } else {
+ $to[$k] = self::mergeAssocArray($to[$k], $v, $mode);
+ }
+ }
+
+ return $to;
+ }
+
+ return $mode === DataInterface::PRESERVE ? $to : $from;
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return bool
+ *
+ * @psalm-pure
+ */
+ private static function isList($value): bool
+ {
+ return is_array($value) && array_values($value) === $value;
+ }
+}
diff --git a/vendor/league/commonmark/.phpstorm.meta.php b/vendor/league/commonmark/.phpstorm.meta.php
new file mode 100644
index 0000000..5eb9270
--- /dev/null
+++ b/vendor/league/commonmark/.phpstorm.meta.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace PHPSTORM_META
+{
+ expectedArguments(\League\CommonMark\Util\HtmlElement::__construct(), 0, 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kdb', 'keygen', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'menu', 'menuitem', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress', 'q', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr');
+
+ expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\Heading::__construct(), 0, 1, 2, 3, 4, 5, 6);
+ expectedReturnValues(\League\CommonMark\Extension\CommonMark\Node\Block\Heading::getLevel(), 1, 2, 3, 4, 5, 6);
+
+ registerArgumentsSet('league_commonmark_htmlblock_types', \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_1_CODE_CONTAINER, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_2_COMMENT, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_3, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_4, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_5_CDATA, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_6_BLOCK_ELEMENT, \League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::TYPE_7_MISC_ELEMENT);
+ expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::__construct(), 0, argumentsSet('league_commonmark_htmlblock_types'));
+ expectedArguments(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::setType(), 0, argumentsSet('league_commonmark_htmlblock_types'));
+ expectedReturnValues(\League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock::getType(), argumentsSet('league_commonmark_htmlblock_types'));
+ expectedArguments(\League\CommonMark\Util\RegexHelper::getHtmlBlockOpenRegex(), 0, argumentsSet('league_commonmark_htmlblock_types'));
+ expectedArguments(\League\CommonMark\Util\RegexHelper::getHtmlBlockCloseRegex(), 0, argumentsSet('league_commonmark_htmlblock_types'));
+
+ registerArgumentsSet('league_commonmark_newline_types', \League\CommonMark\Node\Inline\Newline::HARDBREAK, \League\CommonMark\Node\Inline\Newline::SOFTBREAK);
+ expectedArguments(\League\CommonMark\Node\Inline\Newline::__construct(), 0, argumentsSet('league_commonmark_newline_types'));
+ expectedReturnValues(\League\CommonMark\Node\Inline\Newline::getType(), argumentsSet('league_commonmark_newline_types'));
+
+ registerArgumentsSet('league_commonmark_options',
+ 'html_input',
+ 'allow_unsafe_links',
+ 'max_nesting_level',
+ 'max_delimiters_per_line',
+ 'renderer',
+ 'renderer/block_separator',
+ 'renderer/inner_separator',
+ 'renderer/soft_break',
+ 'commonmark',
+ 'commonmark/enable_em',
+ 'commonmark/enable_strong',
+ 'commonmark/use_asterisk',
+ 'commonmark/use_underscore',
+ 'commonmark/unordered_list_markers',
+ 'disallowed_raw_html',
+ 'disallowed_raw_html/disallowed_tags',
+ 'external_link',
+ 'external_link/html_class',
+ 'external_link/internal_hosts',
+ 'external_link/nofollow',
+ 'external_link/noopener',
+ 'external_link/noreferrer',
+ 'external_link/open_in_new_window',
+ 'footnote',
+ 'footnote/backref_class',
+ 'footnote/backref_symbol',
+ 'footnote/container_add_hr',
+ 'footnote/container_class',
+ 'footnote/ref_class',
+ 'footnote/ref_id_prefix',
+ 'footnote/footnote_class',
+ 'footnote/footnote_id_prefix',
+ 'heading_permalink',
+ 'heading_permalink/apply_id_to_heading',
+ 'heading_permalink/heading_class',
+ 'heading_permalink/html_class',
+ 'heading_permalink/fragment_prefix',
+ 'heading_permalink/id_prefix',
+ 'heading_permalink/inner_contents',
+ 'heading_permalink/insert',
+ 'heading_permalink/max_heading_level',
+ 'heading_permalink/min_heading_level',
+ 'heading_permalink/symbol',
+ 'heading_permalink/title',
+ 'mentions',
+ 'smartpunct/double_quote_closer',
+ 'smartpunct/double_quote_opener',
+ 'smartpunct/single_quote_closer',
+ 'smartpunct/single_quote_opener',
+ 'slug_normalizer',
+ 'slug_normalizer/instance',
+ 'slug_normalizer/max_length',
+ 'slug_normalizer/unique',
+ 'table',
+ 'table/wrap',
+ 'table/wrap/attributes',
+ 'table/wrap/enabled',
+ 'table/wrap/tag',
+ 'table/alignment_attributes',
+ 'table/alignment_attributes/left',
+ 'table/alignment_attributes/center',
+ 'table/alignment_attributes/right',
+ 'table/max_autocompleted_cells',
+ 'table_of_contents',
+ 'table_of_contents/html_class',
+ 'table_of_contents/max_heading_level',
+ 'table_of_contents/min_heading_level',
+ 'table_of_contents/normalize',
+ 'table_of_contents/placeholder',
+ 'table_of_contents/position',
+ 'table_of_contents/style',
+ );
+ expectedArguments(\League\Config\ConfigurationInterface::get(), 0, argumentsSet('league_commonmark_options'));
+ expectedArguments(\League\Config\ConfigurationInterface::exists(), 0, argumentsSet('league_commonmark_options'));
+ expectedArguments(\League\Config\MutableConfigurationInterface::set(), 0, argumentsSet('league_commonmark_options'));
+}
diff --git a/vendor/league/commonmark/CHANGELOG.md b/vendor/league/commonmark/CHANGELOG.md
new file mode 100644
index 0000000..1e4ea70
--- /dev/null
+++ b/vendor/league/commonmark/CHANGELOG.md
@@ -0,0 +1,756 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
+
+**Upgrading from 1.x?** See for additional information.
+
+## [Unreleased][unreleased]
+
+## [2.7.1] - 2025-07-20
+
+### Changed
+- Optimized several regular expressions in `RegexHelper` to improve performance (#674, #1086)
+
+### Fixed
+- `EmbedProcessor` no longer calls `updateEmbeds()` when there are no embeds to update (#1081)
+- Fixed missing `benchmark.php` CSV path validation for non-existent files (#1068, #1085)
+
+## [2.7.0] - 2025-05-05
+
+This is a **security release** to address a potential cross-site scripting (XSS) vulnerability when using the `AttributesExtension` with untrusted user input.
+
+### Added
+- Added `attributes/allow` config option to specify which attributes users are allowed to set on elements (default allows virtually all attributes)
+
+### Changed
+- The `AttributesExtension` blocks all attributes starting with `on` unless explicitly allowed via the `attributes/allow` config option
+- The `allow_unsafe_links` option is now respected by the `AttributesExtension` when users specify `href` and `src` attributes
+
+## [2.6.2] - 2025-04-18
+
+### Fixed
+
+- Fixed Attributes extension parsing regression (#1071)
+
+## [2.6.1] - 2024-12-29
+
+### Fixed
+
+- Rendered list items should only add newlines around block-level children (#1059, #1061)
+
+## [2.6.0] - 2024-12-07
+
+This is a **security release** to address potential denial of service attacks when parsing specially crafted,
+malicious input from untrusted sources (like user input).
+
+### Added
+
+- Added `max_delimiters_per_line` config option to prevent denial of service attacks when parsing malicious input
+- Added `table/max_autocompleted_cells` config option to prevent denial of service attacks when parsing large tables
+- The `AttributesExtension` now supports attributes without values (#985, #986)
+- The `AutolinkExtension` exposes two new configuration options to override the default behavior (#969, #987):
+ - `autolink/allowed_protocols` - an array of protocols to allow autolinking for
+ - `autolink/default_protocol` - the default protocol to use when none is specified
+- Added `RegexHelper::isWhitespace()` method to check if a given character is an ASCII whitespace character
+- Added `CacheableDelimiterProcessorInterface` to ensure linear complexity for dynamic delimiter processing
+- Added `Bracket` delimiter type to optimize bracket parsing
+
+### Changed
+
+- `[` and `]` are no longer added as `Delimiter` objects on the stack; a new `Bracket` type with its own stack is used instead
+- `UrlAutolinkParser` no longer parses URLs with more than 127 subdomains
+- Expanded reference links can no longer exceed 100kb, or the size of the input document (whichever is greater)
+- Delimiters should always provide a non-null value via `DelimiterInterface::getIndex()`
+ - We'll attempt to infer the index based on surrounding delimiters where possible
+- The `DelimiterStack` now accepts integer positions for any `$stackBottom` argument
+- Several small performance optimizations
+
+## [2.5.3] - 2024-08-16
+
+### Changed
+
+- Made compatible with CommonMark spec 0.31.1, including:
+ - Remove `source`, add `search` to list of recognized block tags
+
+## [2.5.2] - 2024-08-14
+
+### Changed
+
+- Boolean attributes now require an explicit `true` value (#1040)
+
+### Fixed
+
+- Fixed regression where text could be misinterpreted as an attribute (#1040)
+
+## [2.5.1] - 2024-07-24
+
+### Fixed
+
+- Fixed attribute parsing incorrectly parsing mustache-like syntax (#1035)
+- Fixed incorrect `Table` start line numbers (#1037)
+
+## [2.5.0] - 2024-07-22
+
+### Added
+
+- The `AttributesExtension` now supports attributes without values (#985, #986)
+- The `AutolinkExtension` exposes two new configuration options to override the default behavior (#969, #987):
+ - `autolink/allowed_protocols` - an array of protocols to allow autolinking for
+ - `autolink/default_protocol` - the default protocol to use when none is specified
+
+### Changed
+
+- Made compatible with CommonMark spec 0.31.0, including:
+ - Allow closing fence to be followed by tabs
+ - Remove restrictive limitation on inline comments
+ - Unicode symbols now treated like punctuation (for purposes of flankingness)
+ - Trailing tabs on the last line of indented code blocks will be excluded
+ - Improved HTML comment matching
+- `Paragraph`s only containing link reference definitions will be kept in the AST until the `Document` is finalized
+ - (These were previously removed immediately after parsing the `Paragraph`)
+
+### Fixed
+
+- Fixed list tightness not being determined properly in some edge cases
+- Fixed incorrect ending line numbers for several block types in various scenarios
+- Fixed lowercase inline HTML declarations not being accepted
+
+## [2.4.4] - 2024-07-22
+
+### Fixed
+
+- Fixed SmartPunct extension changing already-formatted quotation marks (#1030)
+
+## [2.4.3] - 2024-07-22
+
+### Fixed
+
+- Fixed the Attributes extension not supporting CSS level 3 selectors (#1013)
+- Fixed `UrlAutolinkParser` incorrectly parsing text containing `www` anywhere before an autolink (#1025)
+
+
+## [2.4.2] - 2024-02-02
+
+### Fixed
+
+- Fixed declaration parser being too strict
+- `FencedCodeRenderer`: don't add `language-` to class if already prefixed
+
+### Deprecated
+
+- Returning dynamic values from `DelimiterProcessorInterface::getDelimiterUse()` is deprecated
+ - You should instead implement `CacheableDelimiterProcessorInterface` to help the engine perform caching to avoid performance issues.
+- Failing to set a delimiter's index (or returning `null` from `DelimiterInterface::getIndex()`) is deprecated and will not be supported in 3.0
+- Deprecated `DelimiterInterface::isActive()` and `DelimiterInterface::setActive()`, as these are no longer used by the engine
+- Deprecated `DelimiterStack::removeEarlierMatches()` and `DelimiterStack::searchByCharacter()`, as these are no longer used by the engine
+- Passing a `DelimiterInterface` as the `$stackBottom` argument to `DelimiterStack::processDelimiters()` or `::removeAll()` is deprecated and will not be supported in 3.0; pass the integer position instead.
+
+### Fixed
+
+- Fixed NUL characters not being replaced in the input
+- Fixed quadratic complexity parsing unclosed inline links
+- Fixed quadratic complexity parsing emphasis and strikethrough delimiters
+- Fixed issue where having 500,000+ delimiters could trigger a [known segmentation fault issue in PHP's garbage collection](https://bugs.php.net/bug.php?id=68606)
+- Fixed quadratic complexity deactivating link openers
+- Fixed quadratic complexity parsing long backtick code spans with no matching closers
+- Fixed catastrophic backtracking when parsing link labels/titles
+
+## [2.4.1] - 2023-08-30
+
+### Fixed
+
+- Fixed `ExternalLinkProcessor` not fully disabling the `rel` attribute when configured to do so (#992)
+
+## [2.4.0] - 2023-03-24
+
+### Added
+
+- Added generic `CommonMarkException` marker interface for all exceptions thrown by the library
+- Added several new specific exception types implementing that marker interface:
+ - `AlreadyInitializedException`
+ - `InvalidArgumentException`
+ - `IOException`
+ - `LogicException`
+ - `MissingDependencyException`
+ - `NoMatchingRendererException`
+ - `ParserLogicException`
+- Added more configuration options to the Heading Permalinks extension (#939):
+ - `heading_permalink/apply_id_to_heading` - When `true`, the `id` attribute will be applied to the heading element itself instead of the `` tag
+ - `heading_permalink/heading_class` - class to apply to the heading element
+ - `heading_permalink/insert` - now accepts `none` to prevent the creation of the ` ` link
+- Added new `table/alignment_attributes` configuration option to control how table cell alignment is rendered (#959)
+
+### Changed
+
+- Change several thrown exceptions from `RuntimeException` to `LogicException` (or something extending it), including:
+ - `CallbackGenerator`s that fail to set a URL or return an expected value
+ - `MarkdownParser` when deactivating the last block parser or attempting to get an active block parser when they've all been closed
+ - Adding items to an already-initialized `Environment`
+ - Rendering a `Node` when no renderer has been registered for it
+- `HeadingPermalinkProcessor` now throws `InvalidConfigurationException` instead of `RuntimeException` when invalid config values are given.
+- `HtmlElement::setAttribute()` no longer requires the second parameter for boolean attributes
+- Several small micro-optimizations
+- Changed Strikethrough to only allow 1 or 2 tildes per the updated GFM spec
+
+### Fixed
+
+- Fixed inaccurate `@throws` docblocks throughout the codebase, including `ConverterInterface`, `MarkdownConverter`, and `MarkdownConverterInterface`.
+ - These previously suggested that only `\RuntimeException`s were thrown, which was inaccurate as `\LogicException`s were also possible.
+
+## [2.3.9] - 2023-02-15
+
+### Fixed
+
+- Fixed autolink extension not detecting some URIs with underscores (#956)
+
+## [2.3.8] - 2022-12-10
+
+### Fixed
+
+- Fixed parsing issues when `mb_internal_encoding()` is set to something other than `UTF-8` (#951)
+
+## [2.3.7] - 2022-11-03
+
+### Fixed
+
+- Fixed `TaskListItemMarkerRenderer` not including HTML attributes set on the node by other extensions (#947)
+
+## [2.3.6] - 2022-10-30
+
+### Fixed
+
+- Fixed unquoted attribute parsing when closing curly brace is followed by certain characters (like a `.`) (#943)
+
+## [2.3.5] - 2022-07-29
+
+### Fixed
+
+- Fixed error using `InlineParserEngine` when no inline parsers are registered in the `Environment` (#908)
+
+## [2.3.4] - 2022-07-17
+
+### Changed
+
+- Made a number of small tweaks to the embed extension's parsing behavior to fix #898:
+ - Changed `EmbedStartParser` to always capture embed-like lines in container blocks, regardless of parent block type
+ - Changed `EmbedProcessor` to also remove `Embed` blocks that aren't direct children of the `Document`
+ - Increased the priority of `EmbedProcessor` to `1010`
+
+### Fixed
+
+- Fixed `EmbedExtension` not parsing embeds following a list block (#898)
+
+## [2.3.3] - 2022-06-07
+
+### Fixed
+
+- Fixed `DomainFilteringAdapter` not reindexing the embed list (#884, #885)
+
+## [2.3.2] - 2022-06-03
+
+### Fixed
+
+- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881)
+
+## [2.2.5] - 2022-06-03
+
+### Fixed
+
+- Fixed FootnoteExtension stripping extra characters from tab-indented footnotes (#881)
+
+## [2.3.1] - 2022-05-14
+
+### Fixed
+
+- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867)
+
+## [2.2.4] - 2022-05-14
+
+### Fixed
+
+- Fixed AutolinkExtension not ignoring trailing strikethrough syntax (#867)
+
+## [2.3.0] - 2022-04-07
+
+### Added
+
+- Added new `EmbedExtension` (#805)
+- Added `DocumentRendererInterface` as a replacement for the now-deprecated `MarkdownRendererInterface`
+
+### Deprecated
+
+- Deprecated `MarkdownRendererInterface`; use `DocumentRendererInterface` instead
+
+## [2.2.3] - 2022-02-26
+
+### Fixed
+
+- Fixed front matter parsing with Windows line endings (#821)
+
+## [2.1.3] - 2022-02-26
+
+### Fixed
+
+- Fixed front matter parsing with Windows line endings (#821)
+
+## [2.0.4] - 2022-02-26
+
+### Fixed
+
+- Fixed front matter parsing with Windows line endings (#821)
+
+## [2.2.2] - 2022-02-13
+
+### Fixed
+
+- Fixed double-escaping of image alt text (#806, #810)
+- Fixed Psalm typehints for event class names
+
+## [2.2.1] - 2022-01-25
+
+### Fixed
+
+ - Fixed `symfony/deprecation-contracts` constraint
+
+### Removed
+
+ - Removed deprecation trigger from `MarkdownConverterInterface` to reduce noise
+
+## [2.2.0] - 2022-01-22
+
+### Added
+
+ - Added new `ConverterInterface`
+ - Added new `MarkdownToXmlConverter` class
+ - Added new `HtmlDecorator` class which can wrap existing renderers with additional HTML tags
+ - Added new `table/wrap` config to apply an optional wrapping/container element around a table (#780)
+
+### Changed
+
+ - `HtmlElement` contents can now consist of any `Stringable`, not just `HtmlElement` and `string`
+
+### Deprecated
+
+ - Deprecated `MarkdownConverterInterface` and its `convertToHtml()` method; use `ConverterInterface` and `convert()` instead
+
+## [2.1.2] - 2022-02-13
+
+### Fixed
+
+- Fixed double-escaping of image alt text (#806, #810)
+- Fixed Psalm typehints for event class names
+
+## [2.1.1] - 2022-01-02
+
+### Added
+
+ - Added missing return type to `Environment::dispatch()` to fix deprecation warning (#778)
+
+## [2.1.0] - 2021-12-05
+
+### Added
+
+- Added support for ext-yaml in FrontMatterExtension (#715)
+- Added support for symfony/yaml v6.0 in FrontMatterExtension (#739)
+- Added new `heading_permalink/aria_hidden` config option (#741)
+
+### Fixed
+
+ - Fixed PHP 8.1 deprecation warning (#759, #762)
+
+## [2.0.3] - 2022-02-13
+
+### Fixed
+
+- Fixed double-escaping of image alt text (#806, #810)
+- Fixed Psalm typehints for event class names
+
+## [2.0.2] - 2021-08-14
+
+### Changed
+
+- Bumped minimum version of league/config to support PHP 8.1
+
+### Fixed
+
+- Fixed ability to register block parsers that identify lines starting with letters (#706)
+
+## [2.0.1] - 2021-07-31
+
+### Fixed
+
+- Fixed nested autolinks (#689)
+- Fixed description lists being parsed incorrectly (#692)
+- Fixed Table of Contents not respecting Heading Permalink prefixes (#690)
+
+## [2.0.0] - 2021-07-24
+
+No changes were introduced since the previous RC2 release.
+See all entries below for a list of changes between 1.x and 2.0.
+
+## [2.0.0-rc2] - 2021-07-17
+
+### Fixed
+
+- Fixed Mentions inside of links creating nested links against the spec's rules (#688)
+
+## [2.0.0-rc1] - 2021-07-10
+
+No changes were introduced since the previous release.
+
+## [2.0.0-beta3] - 2021-07-03
+
+### Changed
+
+ - Any leading UTF-8 BOM will be stripped from the input
+ - The `getEnvironment()` method of `CommonMarkConverter` and `GithubFlavoredMarkdownConverter` will always return the concrete, configurable `Environment` for upgrading convenience
+ - Optimized AST iteration
+ - Lots of small micro-optimizations
+
+## [2.0.0-beta2] - 2021-06-27
+
+### Added
+
+- Added new `Node::iterator()` method and `NodeIterator` class for faster AST iteration (#683, #684)
+
+### Changed
+
+- Made compatible with CommonMark spec 0.30.0
+- Optimized link label parsing
+- Optimized AST iteration for a 50% performance boost in some event listeners (#683, #684)
+
+### Fixed
+
+- Fixed processing instructions with EOLs
+- Fixed case-insensitive matching for HTML tag types
+- Fixed type 7 HTML blocks incorrectly interrupting lazy paragraphs
+- Fixed newlines in reference labels not collapsing into spaces
+- Fixed link label normalization with escaped newlines
+- Fixed unnecessary AST iteration when no default attributes are configured
+
+## [2.0.0-beta1] - 2021-06-20
+
+### Added
+
+ - **Added three new extensions:**
+ - `FrontMatterExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/front-matter/))
+ - `DescriptionListExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/description-lists/))
+ - `DefaultAttributesExtension` ([see documentation](https://commonmark.thephpleague.com/extensions/default-attributes/))
+ - **Added new `XmlRenderer` to simplify AST debugging** ([see documentation](https://commonmark.thephpleague.com/xml/)) (#431)
+ - **Added the ability to configure disallowed raw HTML tags** (#507)
+ - **Added the ability for Mentions to use multiple characters for their symbol** (#514, #550)
+ - **Added the ability to delegate event dispatching to PSR-14 compliant event dispatcher libraries**
+ - **Added new configuration options:**
+ - Added `heading_permalink/min_heading_level` and `heading_permalink/max_heading_level` options to control which headings get permalinks (#519)
+ - Added `heading_permalink/fragment_prefix` to allow customizing the URL fragment prefix (#602)
+ - Added `footnote/backref_symbol` option for customizing backreference link appearance (#522)
+ - Added `slug_normalizer/max_length` option to control the maximum length of generated URL slugs
+ - Added `slug_normalizer/unique` option to control whether unique slugs should be generated per-document or per-environment
+ - **Added purity markers throughout the codebase** (verified with Psalm)
+ - Added `Query` class to simplify Node traversal when looking to take action on certain Nodes
+ - Added new `HtmlFilter` and `StringContainerHelper` utility classes
+ - Added new `AbstractBlockContinueParser` class to simplify the creation of custom block parsers
+ - Added several new classes and interfaces:
+ - `BlockContinue`
+ - `BlockContinueParserInterface`
+ - `BlockContinueParserWithInlinesInterface`
+ - `BlockStart`
+ - `BlockStartParserInterface`
+ - `ChildNodeRendererInterface`
+ - `ConfigurableExtensionInterface`
+ - `CursorState`
+ - `DashParser` (extracted from `PunctuationParser`)
+ - `DelimiterParser`
+ - `DocumentBlockParser`
+ - `DocumentPreRenderEvent`
+ - `DocumentRenderedEvent`
+ - `EllipsesParser` (extracted from `PunctuationParser`)
+ - `ExpressionInterface`
+ - `FallbackNodeXmlRenderer`
+ - `InlineParserEngineInterface`
+ - `InlineParserMatch`
+ - `MarkdownParserState`
+ - `MarkdownParserStateInterface`
+ - `MarkdownRendererInterface`
+ - `Query`
+ - `RawMarkupContainerInterface`
+ - `ReferenceableInterface`
+ - `RenderedContent`
+ - `RenderedContentInterface`
+ - `ReplaceUnpairedQuotesListener`
+ - `SpecReader`
+ - `TableOfContentsRenderer`
+ - `UniqueSlugNormalizer`
+ - `UniqueSlugNormalizerInterface`
+ - `XmlRenderer`
+ - `XmlNodeRendererInterface`
+ - Added several new methods:
+ - `Cursor::getCurrentCharacter()`
+ - `Environment::createDefaultConfiguration()`
+ - `Environment::setEventDispatcher()`
+ - `EnvironmentInterface::getExtensions()`
+ - `EnvironmentInterface::getInlineParsers()`
+ - `EnvironmentInterface::getSlugNormalizer()`
+ - `FencedCode::setInfo()`
+ - `Heading::setLevel()`
+ - `HtmlRenderer::renderDocument()`
+ - `InlineParserContext::getFullMatch()`
+ - `InlineParserContext::getFullMatchLength()`
+ - `InlineParserContext::getMatches()`
+ - `InlineParserContext::getSubMatches()`
+ - `LinkParserHelper::parsePartialLinkLabel()`
+ - `LinkParserHelper::parsePartialLinkTitle()`
+ - `Node::assertInstanceOf()`
+ - `RegexHelper::isLetter()`
+ - `StringContainerInterface::setLiteral()`
+ - `TableCell::getType()`
+ - `TableCell::setType()`
+ - `TableCell::getAlign()`
+ - `TableCell::setAlign()`
+
+### Changed
+
+ - **Changed the converter return type**
+ - `CommonMarkConverter::convertToHtml()` now returns an instance of `RenderedContentInterface`. This can be cast to a string for backward compatibility with 1.x.
+ - **Table of Contents items are no longer wrapped with `` tags** (#613)
+ - **Heading Permalinks now link to element IDs instead of using `name` attributes** (#602)
+ - **Heading Permalink IDs and URL fragments now have a `content` prefix by default** (#602)
+ - **Changes to configuration options:**
+ - `enable_em` has been renamed to `commonmark/enable_em`
+ - `enable_strong` has been renamed to `commonmark/enable_strong`
+ - `use_asterisk` has been renamed to `commonmark/use_asterisk`
+ - `use_underscore` has been renamed to `commonmark/use_underscore`
+ - `unordered_list_markers` has been renamed to `commonmark/unordered_list_markers`
+ - `mentions/*/symbol` has been renamed to `mentions/*/prefix`
+ - `mentions/*/regex` has been renamed to `mentions/*/pattern` and requires partial regular expressions (without delimiters or flags)
+ - `max_nesting_level` now defaults to `PHP_INT_MAX` and no longer supports floats
+ - `heading_permalink/slug_normalizer` has been renamed to `slug_normalizer/instance`
+ - **Event dispatching is now fully PSR-14 compliant**
+ - **Moved and renamed several classes** - [see the full list here](https://commonmark.thephpleague.com/2.0/upgrading/#classesnamespaces-renamed)
+ - The `HeadingPermalinkExtension` and `FootnoteExtension` were modified to ensure they never produce a slug which conflicts with slugs created by the other extension
+ - `SlugNormalizer::normalizer()` now supports optional prefixes and max length options passed in via the `$context` argument
+ - The `AbstractBlock::$data` and `AbstractInline::$data` arrays were replaced with a `Data` array-like object on the base `Node` class
+ - **Implemented a new approach to block parsing.** This was a massive change, so here are the highlights:
+ - Functionality previously found in block parsers and node elements has moved to block parser factories and block parsers, respectively ([more details](https://commonmark.thephpleague.com/2.0/upgrading/#new-block-parsing-approach))
+ - `ConfigurableEnvironmentInterface::addBlockParser()` is now `EnvironmentBuilderInterface::addBlockParserFactory()`
+ - `ReferenceParser` was re-implemented and works completely different than before
+ - The paragraph parser no longer needs to be added manually to the environment
+ - **Implemented a new approach to inline parsing** where parsers can now specify longer strings or regular expressions they want to parse (instead of just single characters):
+ - `InlineParserInterface::getCharacters()` is now `getMatchDefinition()` and returns an instance of `InlineParserMatch`
+ - `InlineParserContext::__construct()` now requires the contents to be provided as a `Cursor` instead of a `string`
+ - **Implemented delimiter parsing as a special type of inline parser** (via the new `DelimiterParser` class)
+ - **Changed block and inline rendering to use common methods and interfaces**
+ - `BlockRendererInterface` and `InlineRendererInterface` were replaced by `NodeRendererInterface` with slightly different parameters. All core renderers now implement this interface.
+ - `ConfigurableEnvironmentInterface::addBlockRenderer()` and `addInlineRenderer()` were combined into `EnvironmentBuilderInterface::addRenderer()`
+ - `EnvironmentInterface::getBlockRenderersForClass()` and `getInlineRenderersForClass()` are now just `getRenderersForClass()`
+ - **Completely refactored the Configuration implementation**
+ - All configuration-specific classes have been moved into a new `league/config` package with a new namespace
+ - `Configuration` objects must now be configured with a schema and all options must match that schema - arbitrary keys are no longer permitted
+ - `Configuration::__construct()` no longer accepts the default configuration values - use `Configuration::merge()` instead
+ - `ConfigurationInterface` now only contains a `get(string $key)`; this method no longer allows arbitrary default values to be returned if the option is missing
+ - `ConfigurableEnvironmentInterface` was renamed to `EnvironmentBuilderInterface`
+ - `ExtensionInterface::register()` now requires an `EnvironmentBuilderInterface` param instead of `ConfigurableEnvironmentInterface`
+ - **Added missing return types to virtually every class and interface method**
+ - Re-implemented the GFM Autolink extension using the new inline parser approach instead of document processors
+ - `EmailAutolinkProcessor` is now `EmailAutolinkParser`
+ - `UrlAutolinkProcessor` is now `UrlAutolinkParser`
+ - `HtmlElement` can now properly handle array (i.e. `class`) and boolean (i.e. `checked`) attribute values
+ - `HtmlElement` automatically flattens any attributes with array values into space-separated strings, removing duplicate entries
+ - Combined separate classes/interfaces into one:
+ - `DisallowedRawHtmlRenderer` replaces `DisallowedRawHtmlBlockRenderer` and `DisallowedRawHtmlInlineRenderer`
+ - `NodeRendererInterface` replaces `BlockRendererInterface` and `InlineRendererInterface`
+ - Renamed the following methods:
+ - `Environment` and `ConfigurableEnvironmentInterface`:
+ - `addBlockParser()` is now `addBlockStartParser()`
+ - `ReferenceMap` and `ReferenceMapInterface`:
+ - `addReference()` is now `add()`
+ - `getReference()` is now `get()`
+ - `listReferences()` is now `getIterator()`
+ - Various node (block/inline) classes:
+ - `getContent()` is now `getLiteral()`
+ - `setContent()` is now `setLiteral()`
+ - Moved and renamed the following constants:
+ - `EnvironmentInterface::HTML_INPUT_ALLOW` is now `HtmlFilter::ALLOW`
+ - `EnvironmentInterface::HTML_INPUT_ESCAPE` is now `HtmlFilter::ESCAPE`
+ - `EnvironmentInterface::HTML_INPUT_STRIP` is now `HtmlFilter::STRIP`
+ - `TableCell::TYPE_HEAD` is now `TableCell::TYPE_HEADER`
+ - `TableCell::TYPE_BODY` is now `TableCell::TYPE_DATA`
+ - Changed the visibility of the following properties:
+ - `AttributesInline::$attributes` is now `private`
+ - `AttributesInline::$block` is now `private`
+ - `TableCell::$align` is now `private`
+ - `TableCell::$type` is now `private`
+ - `TableSection::$type` is now `private`
+ - Several methods which previously returned `$this` now return `void`
+ - `Delimiter::setPrevious()`
+ - `Node::replaceChildren()`
+ - `Context::setTip()`
+ - `Context::setContainer()`
+ - `Context::setBlocksParsed()`
+ - `AbstractStringContainer::setContent()`
+ - `AbstractWebResource::setUrl()`
+ - Several classes are now marked `final`:
+ - `ArrayCollection`
+ - `Emphasis`
+ - `FencedCode`
+ - `Heading`
+ - `HtmlBlock`
+ - `HtmlElement`
+ - `HtmlInline`
+ - `IndentedCode`
+ - `Newline`
+ - `Strikethrough`
+ - `Strong`
+ - `Text`
+ - `Heading` nodes no longer directly contain a copy of their inner text
+ - `StringContainerInterface` can now be used for inlines, not just blocks
+ - `ArrayCollection` only supports integer keys
+ - `HtmlElement` now implements `Stringable`
+ - `Cursor::saveState()` and `Cursor::restoreState()` now use `CursorState` objects instead of arrays
+ - `NodeWalker::next()` now enters, traverses any children, and leaves all elements which may have children (basically all blocks plus any inlines with children). Previously, it only did this for elements explicitly marked as "containers".
+ - `InvalidOptionException` was removed
+ - Anything with a `getReference(): ReferenceInterface` method now implements `ReferencableInterface`
+ - The `SmartPunct` extension now replaces all unpaired `Quote` elements with `Text` elements towards the end of parsing, making the `QuoteRenderer` unnecessary
+ - Several changes made to the Footnote extension:
+ - Footnote identifiers can no longer contain spaces
+ - Anonymous footnotes can now span subsequent lines
+ - Footnotes can now contain multiple lines of content, including sub-blocks, by indenting them
+ - Footnote event listeners now have numbered priorities (but still execute in the same order)
+ - Footnotes must now be separated from previous content by a blank line
+ - The line numbers (keys) returned via `MarkdownInput::getLines()` now start at 1 instead of 0
+ - `DelimiterProcessorCollectionInterface` now extends `Countable`
+ - `RegexHelper::PARTIAL_` constants must always be used in case-insensitive contexts
+ - `HeadingPermalinkProcessor` no longer accepts text normalizers via the constructor - these must be provided via configuration instead
+ - Blocks which can't contain inlines will no longer be asked to render inlines
+ - `AnonymousFootnoteRefParser` and `HeadingPermalinkProcessor` now implement `EnvironmentAwareInterface` instead of `ConfigurationAwareInterface`
+ - The second argument to `TextNormalizerInterface::normalize()` must now be an array
+ - The `title` attribute for `Link` and `Image` nodes is now stored using a dedicated property instead of stashing it in `$data`
+ - `ListData::$delimiter` now returns either `ListBlock::DELIM_PERIOD` or `ListBlock::DELIM_PAREN` instead of the literal delimiter
+
+### Fixed
+
+ - **Fixed parsing of footnotes without content**
+ - **Fixed rendering of orphaned footnotes and footnote refs**
+ - **Fixed some URL autolinks breaking too early** (#492)
+ - Fixed `AbstractStringContainer` not actually being `abstract`
+
+### Removed
+
+ - **Removed support for PHP 7.1, 7.2, and 7.3** (#625, #671)
+ - **Removed all previously-deprecated functionality:**
+ - Removed the ability to pass custom `Environment` instances into the `CommonMarkConverter` and `GithubFlavoredMarkdownConverter` constructors
+ - Removed the `Converter` class and `ConverterInterface`
+ - Removed the `bin/commonmark` script
+ - Removed the `Html5Entities` utility class
+ - Removed the `InlineMentionParser` (use `MentionParser` instead)
+ - Removed `DefaultSlugGenerator` and `SlugGeneratorInterface` from the `Extension/HeadingPermalink/Slug` sub-namespace (use the new ones under `./SlugGenerator` instead)
+ - Removed the following `ArrayCollection` methods:
+ - `add()`
+ - `set()`
+ - `get()`
+ - `remove()`
+ - `isEmpty()`
+ - `contains()`
+ - `indexOf()`
+ - `containsKey()`
+ - `replaceWith()`
+ - `removeGaps()`
+ - Removed the `ConfigurableEnvironmentInterface::setConfig()` method
+ - Removed the `ListBlock::TYPE_UNORDERED` constant
+ - Removed the `CommonMarkConverter::VERSION` constant
+ - Removed the `HeadingPermalinkRenderer::DEFAULT_INNER_CONTENTS` constant
+ - Removed the `heading_permalink/inner_contents` configuration option
+ - **Removed now-unused classes:**
+ - `AbstractStringContainerBlock`
+ - `BlockRendererInterface`
+ - `Context`
+ - `ContextInterface`
+ - `Converter`
+ - `ConverterInterface`
+ - `InlineRendererInterface`
+ - `PunctuationParser` (was split into two classes: `DashParser` and `EllipsesParser`)
+ - `QuoteRenderer`
+ - `UnmatchedBlockCloser`
+ - Removed the following methods, properties, and constants:
+ - `AbstractBlock::$open`
+ - `AbstractBlock::$lastLineBlank`
+ - `AbstractBlock::isContainer()`
+ - `AbstractBlock::canContain()`
+ - `AbstractBlock::isCode()`
+ - `AbstractBlock::matchesNextLine()`
+ - `AbstractBlock::endsWithBlankLine()`
+ - `AbstractBlock::setLastLineBlank()`
+ - `AbstractBlock::shouldLastLineBeBlank()`
+ - `AbstractBlock::isOpen()`
+ - `AbstractBlock::finalize()`
+ - `AbstractBlock::getData()`
+ - `AbstractInline::getData()`
+ - `ConfigurableEnvironmentInterface::addBlockParser()`
+ - `ConfigurableEnvironmentInterface::mergeConfig()`
+ - `Delimiter::setCanClose()`
+ - `EnvironmentInterface::getConfig()`
+ - `EnvironmentInterface::getInlineParsersForCharacter()`
+ - `EnvironmentInterface::getInlineParserCharacterRegex()`
+ - `HtmlRenderer::renderBlock()`
+ - `HtmlRenderer::renderBlocks()`
+ - `HtmlRenderer::renderInline()`
+ - `HtmlRenderer::renderInlines()`
+ - `Node::isContainer()`
+ - `RegexHelper::matchAll()` (use the new `matchFirst()` method instead)
+ - `RegexHelper::REGEX_WHITESPACE`
+ - Removed the second `$contents` argument from the `Heading` constructor
+
+### Deprecated
+
+**The following things have been deprecated and will not be supported in v3.0:**
+
+ - `Environment::mergeConfig()` (set configuration before instantiation instead)
+ - `Environment::createCommonMarkEnvironment()` and `Environment::createGFMEnvironment()`
+ - Alternative 1: Use `CommonMarkConverter` or `GithubFlavoredMarkdownConverter` if you don't need to customize the environment
+ - Alternative 2: Instantiate a new `Environment` and add the necessary extensions yourself
+
+[unreleased]: https://github.com/thephpleague/commonmark/compare/2.7.1...HEAD
+[2.7.1]: https://github.com/thephpleague/commonmark/compare/2.7.0...2.7.1
+[2.7.0]: https://github.com/thephpleague/commonmark/compare/2.6.2...2.7.0
+[2.6.2]: https://github.com/thephpleague/commonmark/compare/2.6.1...2.6.2
+[2.6.1]: https://github.com/thephpleague/commonmark/compare/2.6.0...2.6.1
+[2.6.0]: https://github.com/thephpleague/commonmark/compare/2.5.3...2.6.0
+[2.5.3]: https://github.com/thephpleague/commonmark/compare/2.5.2...2.5.3
+[2.5.2]: https://github.com/thephpleague/commonmark/compare/2.5.1...2.5.2
+[2.5.1]: https://github.com/thephpleague/commonmark/compare/2.5.0...2.5.1
+[2.5.0]: https://github.com/thephpleague/commonmark/compare/2.4.4...2.5.0
+[2.4.4]: https://github.com/thephpleague/commonmark/compare/2.4.3...2.4.4
+[2.4.3]: https://github.com/thephpleague/commonmark/compare/2.4.2...2.4.3
+[2.4.2]: https://github.com/thephpleague/commonmark/compare/2.4.1...2.4.2
+[2.4.1]: https://github.com/thephpleague/commonmark/compare/2.4.0...2.4.1
+[2.4.0]: https://github.com/thephpleague/commonmark/compare/2.3.9...2.4.0
+[2.3.9]: https://github.com/thephpleague/commonmark/compare/2.3.8...2.3.9
+[2.3.8]: https://github.com/thephpleague/commonmark/compare/2.3.7...2.3.8
+[2.3.7]: https://github.com/thephpleague/commonmark/compare/2.3.6...2.3.7
+[2.3.6]: https://github.com/thephpleague/commonmark/compare/2.3.5...2.3.6
+[2.3.5]: https://github.com/thephpleague/commonmark/compare/2.3.4...2.3.5
+[2.3.4]: https://github.com/thephpleague/commonmark/compare/2.3.3...2.3.4
+[2.3.3]: https://github.com/thephpleague/commonmark/compare/2.3.2...2.3.3
+[2.3.2]: https://github.com/thephpleague/commonmark/compare/2.3.2...main
+[2.3.1]: https://github.com/thephpleague/commonmark/compare/2.3.0...2.3.1
+[2.3.0]: https://github.com/thephpleague/commonmark/compare/2.2.3...2.3.0
+[2.2.5]: https://github.com/thephpleague/commonmark/compare/2.2.4...2.2.5
+[2.2.4]: https://github.com/thephpleague/commonmark/compare/2.2.3...2.2.4
+[2.2.3]: https://github.com/thephpleague/commonmark/compare/2.2.2...2.2.3
+[2.2.2]: https://github.com/thephpleague/commonmark/compare/2.2.1...2.2.2
+[2.2.1]: https://github.com/thephpleague/commonmark/compare/2.2.0...2.2.1
+[2.2.0]: https://github.com/thephpleague/commonmark/compare/2.1.1...2.2.0
+[2.1.3]: https://github.com/thephpleague/commonmark/compare/2.1.2...2.1.3
+[2.1.2]: https://github.com/thephpleague/commonmark/compare/2.1.1...2.1.2
+[2.1.1]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.1.1
+[2.1.0]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.1.0
+[2.0.4]: https://github.com/thephpleague/commonmark/compare/2.0.3...2.0.4
+[2.0.3]: https://github.com/thephpleague/commonmark/compare/2.0.2...2.0.3
+[2.0.2]: https://github.com/thephpleague/commonmark/compare/2.0.1...2.0.2
+[2.0.1]: https://github.com/thephpleague/commonmark/compare/2.0.0...2.0.1
+[2.0.0]: https://github.com/thephpleague/commonmark/compare/2.0.0-rc2...2.0.0
+[2.0.0-rc2]: https://github.com/thephpleague/commonmark/compare/2.0.0-rc1...2.0.0-rc2
+[2.0.0-rc1]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta3...2.0.0-rc1
+[2.0.0-beta3]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta2...2.0.0-beta3
+[2.0.0-beta2]: https://github.com/thephpleague/commonmark/compare/2.0.0-beta1...2.0.0-beta2
+[2.0.0-beta1]: https://github.com/thephpleague/commonmark/compare/1.6...2.0.0-beta1
diff --git a/vendor/league/commonmark/LICENSE b/vendor/league/commonmark/LICENSE
new file mode 100644
index 0000000..5f04fad
--- /dev/null
+++ b/vendor/league/commonmark/LICENSE
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2014-2022, Colin O'Dell. All rights reserved. Some code based on commonmark.js (copyright 2014-2018, John MacFarlane) and commonmark-java (copyright 2015-2016, Atlassian Pty Ltd)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/league/commonmark/README.md b/vendor/league/commonmark/README.md
new file mode 100644
index 0000000..a794d39
--- /dev/null
+++ b/vendor/league/commonmark/README.md
@@ -0,0 +1,224 @@
+# league/commonmark
+
+[](https://packagist.org/packages/league/commonmark)
+[](https://packagist.org/packages/league/commonmark)
+[](LICENSE)
+[](https://github.com/thephpleague/commonmark/actions?query=workflow%3ATests+branch%3Amain)
+[](https://scrutinizer-ci.com/g/thephpleague/commonmark/code-structure)
+[](https://scrutinizer-ci.com/g/thephpleague/commonmark)
+[](https://shepherd.dev/github/thephpleague/commonmark)
+[](https://bestpractices.coreinfrastructure.org/projects/126)
+[](https://www.colinodell.com/sponsor)
+
+
+
+**league/commonmark** is a highly-extensible PHP Markdown parser created by [Colin O'Dell][@colinodell] which supports the full [CommonMark] spec and [GitHub-Flavored Markdown]. It is based on the [CommonMark JS reference implementation][commonmark.js] by [John MacFarlane] \([@jgm]\).
+
+## 📦 Installation & Basic Usage
+
+This project requires PHP 7.4 or higher with the `mbstring` extension. To install it via [Composer] simply run:
+
+``` bash
+$ composer require league/commonmark
+```
+
+The `CommonMarkConverter` class provides a simple wrapper for converting CommonMark to HTML:
+
+```php
+use League\CommonMark\CommonMarkConverter;
+
+$converter = new CommonMarkConverter([
+ 'html_input' => 'strip',
+ 'allow_unsafe_links' => false,
+]);
+
+echo $converter->convert('# Hello World!');
+
+//
Hello World!
+```
+
+Or if you want GitHub-Flavored Markdown, use the `GithubFlavoredMarkdownConverter` class instead:
+
+```php
+use League\CommonMark\GithubFlavoredMarkdownConverter;
+
+$converter = new GithubFlavoredMarkdownConverter([
+ 'html_input' => 'strip',
+ 'allow_unsafe_links' => false,
+]);
+
+echo $converter->convert('# Hello World!');
+
+// Hello World!
+```
+
+Please note that only UTF-8 and ASCII encodings are supported. If your Markdown uses a different encoding please convert it to UTF-8 before running it through this library.
+
+> [!CAUTION]
+> If you will be parsing untrusted input from users, please consider setting the `html_input` and `allow_unsafe_links` options per the example above. See for more details. If you also do choose to allow raw HTML input from untrusted users, consider using a library (like [HTML Purifier](https://github.com/ezyang/htmlpurifier)) to provide additional HTML filtering.
+
+## 📓 Documentation
+
+Full documentation on advanced usage, configuration, and customization can be found at [commonmark.thephpleague.com][docs].
+
+## ⏫ Upgrading
+
+Information on how to upgrade to newer versions of this library can be found at .
+
+## 💻 GitHub-Flavored Markdown
+
+The `GithubFlavoredMarkdownConverter` shown earlier is a drop-in replacement for the `CommonMarkConverter` which adds additional features found in the GFM spec:
+
+ - Autolinks
+ - Disallowed raw HTML
+ - Strikethrough
+ - Tables
+ - Task Lists
+
+See the [Extensions documentation](https://commonmark.thephpleague.com/customization/extensions/) for more details on how to include only certain GFM features if you don't want them all.
+
+## 🗃️ Related Packages
+
+### Integrations
+
+- [CakePHP 3](https://github.com/gourmet/common-mark)
+- [Drupal](https://www.drupal.org/project/markdown)
+- [Laravel 4+](https://github.com/GrahamCampbell/Laravel-Markdown)
+- [Sculpin](https://github.com/bcremer/sculpin-commonmark-bundle)
+- [Symfony 2 & 3](https://github.com/webuni/commonmark-bundle)
+- [Symfony 4](https://github.com/avensome/commonmark-bundle)
+- [Twig Markdown extension](https://github.com/twigphp/markdown-extension)
+- [Twig filter and tag](https://github.com/aptoma/twig-markdown)
+- [Laravel CommonMark Blog](https://github.com/spekulatius/laravel-commonmark-blog)
+
+### Included Extensions
+
+See [our extension documentation](https://commonmark.thephpleague.com/extensions/overview) for a full list of extensions bundled with this library.
+
+### Community Extensions
+
+Custom parsers/renderers can be bundled into extensions which extend CommonMark. Here are some that you may find interesting:
+
+ - [Emoji extension](https://github.com/ElGigi/CommonMarkEmoji) - UTF-8 emoji extension with Github tag.
+ - [Sup Sub extensions](https://github.com/OWS/commonmark-sup-sub-extensions) - Adds support of superscript and subscript (`` and `` HTML tags)
+ - [YouTube iframe extension](https://github.com/zoonru/commonmark-ext-youtube-iframe) - Replaces youtube link with iframe.
+ - [Lazy Image extension](https://github.com/simonvomeyser/commonmark-ext-lazy-image) - Adds various options for lazy loading of images.
+ - [Marker Extension](https://github.com/noah1400/commonmark-marker-extension) - Adds support of highlighted text (`` HTML tag)
+ - [Pygments Highlighter extension](https://github.com/DanielEScherzer/commonmark-ext-pygments-highlighter) - Adds support for highlighting code with the Pygments library
+
+Others can be found on [Packagist under the `commonmark-extension` package type](https://packagist.org/packages/league/commonmark?type=commonmark-extension).
+
+If you build your own, feel free to submit a PR to add it to this list!
+
+### Others
+
+Check out the other cool things people are doing with `league/commonmark`:
+
+## 🏷️ Versioning
+
+[SemVer](http://semver.org/) is followed closely. Minor and patch releases should not introduce breaking changes to the codebase; however, they might change the resulting AST or HTML output of parsed Markdown (due to bug fixes, spec changes, etc.) As a result, you might get slightly different HTML, but any custom code built onto this library should still function correctly.
+
+Any classes or methods marked `@internal` are not intended for use outside of this library and are subject to breaking changes at any time, so please avoid using them.
+
+## 🛠️ Maintenance & Support
+
+When a new **minor** version (e.g. `2.0` -> `2.1`) is released, the previous one (`2.0`) will continue to receive security and critical bug fixes for *at least* 3 months.
+
+When a new **major** version is released (e.g. `1.6` -> `2.0`), the previous one (`1.6`) will receive critical bug fixes for *at least* 3 months and security updates for 6 months after that new release comes out.
+
+(This policy may change in the future and exceptions may be made on a case-by-case basis.)
+
+**Professional support, including notification of new releases and security updates, is available through a [Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-league-commonmark?utm_source=packagist-league-commonmark&utm_medium=referral&utm_campaign=readme).**
+
+## 👷♀️ Contributing
+
+To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure with us.
+
+If you encounter a bug in the spec, please report it to the [CommonMark] project. Any resulting fix will eventually be implemented in this project as well.
+
+Contributions to this library are **welcome**, especially ones that:
+
+ * Improve usability or flexibility without compromising our ability to adhere to the [CommonMark spec]
+ * Mirror fixes made to the [reference implementation][commonmark.js]
+ * Optimize performance
+ * Fix issues with adhering to the [CommonMark spec]
+
+Major refactoring to core parsing logic should be avoided if possible so that we can easily follow updates made to [the reference implementation][commonmark.js]. That being said, we will absolutely consider changes which don't deviate too far from the reference spec or which are favored by other popular CommonMark implementations.
+
+Please see [CONTRIBUTING](https://github.com/thephpleague/commonmark/blob/main/.github/CONTRIBUTING.md) for additional details.
+
+## 🧪 Testing
+
+``` bash
+$ composer test
+```
+
+This will also test league/commonmark against the latest supported spec.
+
+## 🚀 Performance Benchmarks
+
+You can compare the performance of **league/commonmark** to other popular parsers by running the included benchmark tool:
+
+``` bash
+$ ./tests/benchmark/benchmark.php
+```
+
+## 👥 Credits & Acknowledgements
+
+This code was originally based on the [CommonMark JS reference implementation][commonmark.js] which is written, maintained, and copyrighted by [John MacFarlane]. This project simply wouldn't exist without his work.
+
+And a huge thanks to all of our amazing contributors:
+
+
+
+
+
+### Sponsors
+
+We'd also like to extend our sincere thanks the following sponsors who support ongoing development of this project:
+
+ - [Tidelift](https://tidelift.com/subscription/pkg/packagist-league-commonmark?utm_source=packagist-league-commonmark&utm_medium=referral&utm_campaign=readme) for offering support to both the maintainers and end-users through their [professional support](https://tidelift.com/subscription/pkg/packagist-league-commonmark?utm_source=packagist-league-commonmark&utm_medium=referral&utm_campaign=readme) program
+ - [Blackfire](https://www.blackfire.io/) for providing an Open-Source Profiler subscription
+ - [JetBrains](https://www.jetbrains.com/) for supporting this project with complimentary [PhpStorm](https://www.jetbrains.com/phpstorm/) licenses
+
+Are you interested in sponsoring development of this project? See for a list of ways to contribute.
+
+## 📄 License
+
+**league/commonmark** is licensed under the BSD-3 license. See the [`LICENSE`](LICENSE) file for more details.
+
+## 🏛️ Governance
+
+This project is primarily maintained by [Colin O'Dell][@colinodell]. Members of the [PHP League] Leadership Team may occasionally assist with some of these duties.
+
+## 🗺️ Who Uses It?
+
+This project is used by [Drupal](https://www.drupal.org/project/markdown), [Laravel Framework](https://laravel.com/), [Cachet](https://cachethq.io/), [Firefly III](https://firefly-iii.org/), [Neos](https://www.neos.io/), [Daux.io](https://daux.io/), and [more](https://packagist.org/packages/league/commonmark/dependents)!
+
+---
+
+
+
+[CommonMark]: http://commonmark.org/
+[CommonMark spec]: http://spec.commonmark.org/
+[commonmark.js]: https://github.com/jgm/commonmark.js
+[GitHub-Flavored Markdown]: https://github.github.com/gfm/
+[John MacFarlane]: http://johnmacfarlane.net
+[docs]: https://commonmark.thephpleague.com/
+[docs-examples]: https://commonmark.thephpleague.com/customization/overview/#examples
+[docs-example-twitter]: https://commonmark.thephpleague.com/customization/inline-parsing#example-1---twitter-handles
+[docs-example-smilies]: https://commonmark.thephpleague.com/customization/inline-parsing#example-2---emoticons
+[All Contributors]: https://github.com/thephpleague/commonmark/contributors
+[@colinodell]: https://www.twitter.com/colinodell
+[@jgm]: https://github.com/jgm
+[jgm/stmd]: https://github.com/jgm/stmd
+[Composer]: https://getcomposer.org/
+[PHP League]: https://thephpleague.com
diff --git a/vendor/league/commonmark/composer.json b/vendor/league/commonmark/composer.json
new file mode 100644
index 0000000..c914034
--- /dev/null
+++ b/vendor/league/commonmark/composer.json
@@ -0,0 +1,129 @@
+{
+ "name": "league/commonmark",
+ "type": "library",
+ "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)",
+ "keywords": ["markdown","parser","commonmark","gfm","github","flavored","github-flavored","md"],
+ "homepage": "https://commonmark.thephpleague.com",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "support": {
+ "docs": "https://commonmark.thephpleague.com/",
+ "forum": "https://github.com/thephpleague/commonmark/discussions",
+ "issues": "https://github.com/thephpleague/commonmark/issues",
+ "rss": "https://github.com/thephpleague/commonmark/releases.atom",
+ "source": "https://github.com/thephpleague/commonmark"
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "ext-mbstring": "*",
+ "league/config": "^1.1.1",
+ "psr/event-dispatcher": "^1.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "cebe/markdown": "^1.0",
+ "commonmark/cmark": "0.31.1",
+ "commonmark/commonmark.js": "0.31.1",
+ "composer/package-versions-deprecated": "^1.8",
+ "embed/embed": "^4.4",
+ "erusev/parsedown": "^1.0",
+ "github/gfm": "0.29.0",
+ "michelf/php-markdown": "^1.4 || ^2.0",
+ "nyholm/psr7": "^1.5",
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0",
+ "scrutinizer/ocular": "^1.8.1",
+ "symfony/finder": "^5.3 | ^6.0 | ^7.0",
+ "symfony/process": "^5.4 | ^6.0 | ^7.0",
+ "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0",
+ "unleashedtech/php-coding-standard": "^3.1.1",
+ "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0"
+ },
+ "minimum-stability": "beta",
+ "suggest": {
+ "symfony/yaml": "v2.3+ required if using the Front Matter extension"
+ },
+ "repositories": [
+ {
+ "type": "package",
+ "package": {
+ "name": "commonmark/commonmark.js",
+ "version": "0.31.1",
+ "dist": {
+ "url": "https://github.com/commonmark/commonmark.js/archive/0.31.1.zip",
+ "type": "zip"
+ }
+ }
+ },
+ {
+ "type": "package",
+ "package": {
+ "name": "commonmark/cmark",
+ "version": "0.31.1",
+ "dist": {
+ "url": "https://github.com/commonmark/cmark/archive/0.31.1.zip",
+ "type": "zip"
+ }
+ }
+ },
+ {
+ "type": "package",
+ "package": {
+ "name": "github/gfm",
+ "version": "0.29.0",
+ "dist": {
+ "url": "https://github.com/github/cmark-gfm/archive/0.29.0.gfm.13.zip",
+ "type": "zip"
+ }
+ }
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "League\\CommonMark\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "League\\CommonMark\\Tests\\Unit\\": "tests/unit",
+ "League\\CommonMark\\Tests\\Functional\\": "tests/functional",
+ "League\\CommonMark\\Tests\\PHPStan\\": "tests/phpstan"
+ }
+ },
+ "scripts": {
+ "phpcs": "phpcs",
+ "phpcbf": "phpcbf",
+ "phpstan": "phpstan analyse",
+ "phpunit": "phpunit --no-coverage",
+ "psalm": "psalm --stats",
+ "pathological": "tests/pathological/test.php",
+ "test": [
+ "@phpcs",
+ "@phpstan",
+ "@psalm",
+ "@phpunit",
+ "@pathological"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.8-dev"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true,
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ },
+ "sort-packages": true
+ }
+}
diff --git a/vendor/league/commonmark/src/CommonMarkConverter.php b/vendor/league/commonmark/src/CommonMarkConverter.php
new file mode 100644
index 0000000..4d70053
--- /dev/null
+++ b/vendor/league/commonmark/src/CommonMarkConverter.php
@@ -0,0 +1,46 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark;
+
+use League\CommonMark\Environment\Environment;
+use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
+
+/**
+ * Converts CommonMark-compatible Markdown to HTML.
+ */
+final class CommonMarkConverter extends MarkdownConverter
+{
+ /**
+ * Create a new Markdown converter pre-configured for CommonMark
+ *
+ * @param array $config
+ */
+ public function __construct(array $config = [])
+ {
+ $environment = new Environment($config);
+ $environment->addExtension(new CommonMarkCoreExtension());
+
+ parent::__construct($environment);
+ }
+
+ public function getEnvironment(): Environment
+ {
+ \assert($this->environment instanceof Environment);
+
+ return $this->environment;
+ }
+}
diff --git a/vendor/league/commonmark/src/ConverterInterface.php b/vendor/league/commonmark/src/ConverterInterface.php
new file mode 100644
index 0000000..8192b0f
--- /dev/null
+++ b/vendor/league/commonmark/src/ConverterInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark;
+
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Output\RenderedContentInterface;
+use League\Config\Exception\ConfigurationExceptionInterface;
+
+/**
+ * Interface for a service which converts content from one format (like Markdown) to another (like HTML).
+ */
+interface ConverterInterface
+{
+ /**
+ * @throws CommonMarkException
+ * @throws ConfigurationExceptionInterface
+ */
+ public function convert(string $input): RenderedContentInterface;
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Bracket.php b/vendor/league/commonmark/src/Delimiter/Bracket.php
new file mode 100644
index 0000000..3a86859
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Bracket.php
@@ -0,0 +1,83 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter;
+
+use League\CommonMark\Node\Node;
+
+final class Bracket
+{
+ private Node $node;
+ private ?Bracket $previous;
+ private bool $hasNext = false;
+ private int $position;
+ private bool $image;
+ private bool $active = true;
+
+ public function __construct(Node $node, ?Bracket $previous, int $position, bool $image)
+ {
+ $this->node = $node;
+ $this->previous = $previous;
+ $this->position = $position;
+ $this->image = $image;
+ }
+
+ public function getNode(): Node
+ {
+ return $this->node;
+ }
+
+ public function getPrevious(): ?Bracket
+ {
+ return $this->previous;
+ }
+
+ public function hasNext(): bool
+ {
+ return $this->hasNext;
+ }
+
+ public function getPosition(): int
+ {
+ return $this->position;
+ }
+
+ public function isImage(): bool
+ {
+ return $this->image;
+ }
+
+ /**
+ * Only valid in the context of non-images (links)
+ */
+ public function isActive(): bool
+ {
+ return $this->active;
+ }
+
+ /**
+ * @internal
+ */
+ public function setHasNext(bool $hasNext): void
+ {
+ $this->hasNext = $hasNext;
+ }
+
+ /**
+ * @internal
+ */
+ public function setActive(bool $active): void
+ {
+ $this->active = $active;
+ }
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Delimiter.php b/vendor/league/commonmark/src/Delimiter/Delimiter.php
new file mode 100644
index 0000000..2f04f24
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Delimiter.php
@@ -0,0 +1,134 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter;
+
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+final class Delimiter implements DelimiterInterface
+{
+ /** @psalm-readonly */
+ private string $char;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $length;
+
+ /** @psalm-readonly */
+ private int $originalLength;
+
+ /** @psalm-readonly */
+ private AbstractStringContainer $inlineNode;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?DelimiterInterface $previous = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?DelimiterInterface $next = null;
+
+ /** @psalm-readonly */
+ private bool $canOpen;
+
+ /** @psalm-readonly */
+ private bool $canClose;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $active;
+
+ /** @psalm-readonly */
+ private ?int $index = null;
+
+ public function __construct(string $char, int $numDelims, AbstractStringContainer $node, bool $canOpen, bool $canClose, ?int $index = null)
+ {
+ $this->char = $char;
+ $this->length = $numDelims;
+ $this->originalLength = $numDelims;
+ $this->inlineNode = $node;
+ $this->canOpen = $canOpen;
+ $this->canClose = $canClose;
+ $this->active = true;
+ $this->index = $index;
+ }
+
+ public function canClose(): bool
+ {
+ return $this->canClose;
+ }
+
+ public function canOpen(): bool
+ {
+ return $this->canOpen;
+ }
+
+ public function isActive(): bool
+ {
+ return $this->active;
+ }
+
+ public function setActive(bool $active): void
+ {
+ $this->active = $active;
+ }
+
+ public function getChar(): string
+ {
+ return $this->char;
+ }
+
+ public function getIndex(): ?int
+ {
+ return $this->index;
+ }
+
+ public function getNext(): ?DelimiterInterface
+ {
+ return $this->next;
+ }
+
+ public function setNext(?DelimiterInterface $next): void
+ {
+ $this->next = $next;
+ }
+
+ public function getLength(): int
+ {
+ return $this->length;
+ }
+
+ public function setLength(int $length): void
+ {
+ $this->length = $length;
+ }
+
+ public function getOriginalLength(): int
+ {
+ return $this->originalLength;
+ }
+
+ public function getInlineNode(): AbstractStringContainer
+ {
+ return $this->inlineNode;
+ }
+
+ public function getPrevious(): ?DelimiterInterface
+ {
+ return $this->previous;
+ }
+
+ public function setPrevious(?DelimiterInterface $previous): void
+ {
+ $this->previous = $previous;
+ }
+}
diff --git a/vendor/league/commonmark/src/Delimiter/DelimiterInterface.php b/vendor/league/commonmark/src/Delimiter/DelimiterInterface.php
new file mode 100644
index 0000000..0cefba7
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/DelimiterInterface.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter;
+
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+interface DelimiterInterface
+{
+ public function canClose(): bool;
+
+ public function canOpen(): bool;
+
+ /**
+ * @deprecated This method is no longer used internally and will be removed in 3.0
+ */
+ public function isActive(): bool;
+
+ /**
+ * @deprecated This method is no longer used internally and will be removed in 3.0
+ */
+ public function setActive(bool $active): void;
+
+ public function getChar(): string;
+
+ public function getIndex(): ?int;
+
+ public function getNext(): ?DelimiterInterface;
+
+ public function setNext(?DelimiterInterface $next): void;
+
+ public function getLength(): int;
+
+ public function setLength(int $length): void;
+
+ public function getOriginalLength(): int;
+
+ public function getInlineNode(): AbstractStringContainer;
+
+ public function getPrevious(): ?DelimiterInterface;
+
+ public function setPrevious(?DelimiterInterface $previous): void;
+}
diff --git a/vendor/league/commonmark/src/Delimiter/DelimiterParser.php b/vendor/league/commonmark/src/Delimiter/DelimiterParser.php
new file mode 100644
index 0000000..fdfe093
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/DelimiterParser.php
@@ -0,0 +1,106 @@
+collection = $collection;
+ }
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::oneOf(...$this->collection->getDelimiterCharacters());
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $character = $inlineContext->getFullMatch();
+ $numDelims = 0;
+ $cursor = $inlineContext->getCursor();
+ $processor = $this->collection->getDelimiterProcessor($character);
+
+ \assert($processor !== null); // Delimiter processor should never be null here
+
+ $charBefore = $cursor->peek(-1);
+ if ($charBefore === null) {
+ $charBefore = "\n";
+ }
+
+ while ($cursor->peek($numDelims) === $character) {
+ ++$numDelims;
+ }
+
+ if ($numDelims < $processor->getMinLength()) {
+ return false;
+ }
+
+ $cursor->advanceBy($numDelims);
+
+ $charAfter = $cursor->getCurrentCharacter();
+ if ($charAfter === null) {
+ $charAfter = "\n";
+ }
+
+ [$canOpen, $canClose] = self::determineCanOpenOrClose($charBefore, $charAfter, $character, $processor);
+
+ if (! ($canOpen || $canClose)) {
+ $inlineContext->getContainer()->appendChild(new Text(\str_repeat($character, $numDelims)));
+
+ return true;
+ }
+
+ $node = new Text(\str_repeat($character, $numDelims), [
+ 'delim' => true,
+ ]);
+ $inlineContext->getContainer()->appendChild($node);
+
+ // Add entry to stack to this opener
+ $delimiter = new Delimiter($character, $numDelims, $node, $canOpen, $canClose, $inlineContext->getCursor()->getPosition());
+ $inlineContext->getDelimiterStack()->push($delimiter);
+
+ return true;
+ }
+
+ /**
+ * @return bool[]
+ */
+ private static function determineCanOpenOrClose(string $charBefore, string $charAfter, string $character, DelimiterProcessorInterface $delimiterProcessor): array
+ {
+ $afterIsWhitespace = \preg_match(RegexHelper::REGEX_UNICODE_WHITESPACE_CHAR, $charAfter);
+ $afterIsPunctuation = \preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter);
+ $beforeIsWhitespace = \preg_match(RegexHelper::REGEX_UNICODE_WHITESPACE_CHAR, $charBefore);
+ $beforeIsPunctuation = \preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore);
+
+ $leftFlanking = ! $afterIsWhitespace && (! $afterIsPunctuation || $beforeIsWhitespace || $beforeIsPunctuation);
+ $rightFlanking = ! $beforeIsWhitespace && (! $beforeIsPunctuation || $afterIsWhitespace || $afterIsPunctuation);
+
+ if ($character === '_') {
+ $canOpen = $leftFlanking && (! $rightFlanking || $beforeIsPunctuation);
+ $canClose = $rightFlanking && (! $leftFlanking || $afterIsPunctuation);
+ } else {
+ $canOpen = $leftFlanking && $character === $delimiterProcessor->getOpeningCharacter();
+ $canClose = $rightFlanking && $character === $delimiterProcessor->getClosingCharacter();
+ }
+
+ return [$canOpen, $canClose];
+ }
+}
diff --git a/vendor/league/commonmark/src/Delimiter/DelimiterStack.php b/vendor/league/commonmark/src/Delimiter/DelimiterStack.php
new file mode 100644
index 0000000..cf2a41e
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/DelimiterStack.php
@@ -0,0 +1,396 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter;
+
+use League\CommonMark\Delimiter\Processor\CacheableDelimiterProcessorInterface;
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorCollection;
+use League\CommonMark\Node\Inline\AdjacentTextMerger;
+use League\CommonMark\Node\Node;
+
+final class DelimiterStack
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ?DelimiterInterface $top = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?Bracket $brackets = null;
+
+ /**
+ * @deprecated This property will be removed in 3.0 once all delimiters MUST have an index/position
+ *
+ * @var \SplObjectStorage|\WeakMap
+ */
+ private $missingIndexCache;
+
+
+ private int $remainingDelimiters = 0;
+
+ public function __construct(int $maximumStackSize = PHP_INT_MAX)
+ {
+ $this->remainingDelimiters = $maximumStackSize;
+
+ if (\PHP_VERSION_ID >= 80000) {
+ /** @psalm-suppress PropertyTypeCoercion */
+ $this->missingIndexCache = new \WeakMap(); // @phpstan-ignore-line
+ } else {
+ $this->missingIndexCache = new \SplObjectStorage(); // @phpstan-ignore-line
+ }
+ }
+
+ public function push(DelimiterInterface $newDelimiter): void
+ {
+ if ($this->remainingDelimiters-- <= 0) {
+ return;
+ }
+
+ $newDelimiter->setPrevious($this->top);
+
+ if ($this->top !== null) {
+ $this->top->setNext($newDelimiter);
+ }
+
+ $this->top = $newDelimiter;
+ }
+
+ /**
+ * @internal
+ */
+ public function addBracket(Node $node, int $index, bool $image): void
+ {
+ if ($this->brackets !== null) {
+ $this->brackets->setHasNext(true);
+ }
+
+ $this->brackets = new Bracket($node, $this->brackets, $index, $image);
+ }
+
+ /**
+ * @psalm-immutable
+ */
+ public function getLastBracket(): ?Bracket
+ {
+ return $this->brackets;
+ }
+
+ private function findEarliest(int $stackBottom): ?DelimiterInterface
+ {
+ // Move back to first relevant delim.
+ $delimiter = $this->top;
+ $lastChecked = null;
+
+ while ($delimiter !== null && self::getIndex($delimiter) > $stackBottom) {
+ $lastChecked = $delimiter;
+ $delimiter = $delimiter->getPrevious();
+ }
+
+ return $lastChecked;
+ }
+
+ /**
+ * @internal
+ */
+ public function removeBracket(): void
+ {
+ if ($this->brackets === null) {
+ return;
+ }
+
+ $this->brackets = $this->brackets->getPrevious();
+
+ if ($this->brackets !== null) {
+ $this->brackets->setHasNext(false);
+ }
+ }
+
+ public function removeDelimiter(DelimiterInterface $delimiter): void
+ {
+ if ($delimiter->getPrevious() !== null) {
+ /** @psalm-suppress PossiblyNullReference */
+ $delimiter->getPrevious()->setNext($delimiter->getNext());
+ }
+
+ if ($delimiter->getNext() === null) {
+ // top of stack
+ $this->top = $delimiter->getPrevious();
+ } else {
+ /** @psalm-suppress PossiblyNullReference */
+ $delimiter->getNext()->setPrevious($delimiter->getPrevious());
+ }
+
+ // Nullify all references from the removed delimiter to other delimiters.
+ // All references to this particular delimiter in the linked list should be gone,
+ // but it's possible we're still hanging on to other references to things that
+ // have been (or soon will be) removed, which may interfere with efficient
+ // garbage collection by the PHP runtime.
+ // Explicitly releasing these references should help to avoid possible
+ // segfaults like in https://bugs.php.net/bug.php?id=68606.
+ $delimiter->setPrevious(null);
+ $delimiter->setNext(null);
+
+ // TODO: Remove the line below once PHP 7.4 support is dropped, as WeakMap won't hold onto the reference, making this unnecessary
+ unset($this->missingIndexCache[$delimiter]);
+ }
+
+ private function removeDelimiterAndNode(DelimiterInterface $delimiter): void
+ {
+ $delimiter->getInlineNode()->detach();
+ $this->removeDelimiter($delimiter);
+ }
+
+ private function removeDelimitersBetween(DelimiterInterface $opener, DelimiterInterface $closer): void
+ {
+ $delimiter = $closer->getPrevious();
+ $openerPosition = self::getIndex($opener);
+ while ($delimiter !== null && self::getIndex($delimiter) > $openerPosition) {
+ $previous = $delimiter->getPrevious();
+ $this->removeDelimiter($delimiter);
+ $delimiter = $previous;
+ }
+ }
+
+ /**
+ * @param DelimiterInterface|int|null $stackBottom
+ */
+ public function removeAll($stackBottom = null): void
+ {
+ $stackBottomPosition = \is_int($stackBottom) ? $stackBottom : self::getIndex($stackBottom);
+
+ while ($this->top && $this->getIndex($this->top) > $stackBottomPosition) {
+ $this->removeDelimiter($this->top);
+ }
+ }
+
+ /**
+ * @deprecated This method is no longer used internally and will be removed in 3.0
+ */
+ public function removeEarlierMatches(string $character): void
+ {
+ $opener = $this->top;
+ while ($opener !== null) {
+ if ($opener->getChar() === $character) {
+ $opener->setActive(false);
+ }
+
+ $opener = $opener->getPrevious();
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function deactivateLinkOpeners(): void
+ {
+ $opener = $this->brackets;
+ while ($opener !== null && $opener->isActive()) {
+ $opener->setActive(false);
+ $opener = $opener->getPrevious();
+ }
+ }
+
+ /**
+ * @deprecated This method is no longer used internally and will be removed in 3.0
+ *
+ * @param string|string[] $characters
+ */
+ public function searchByCharacter($characters): ?DelimiterInterface
+ {
+ if (! \is_array($characters)) {
+ $characters = [$characters];
+ }
+
+ $opener = $this->top;
+ while ($opener !== null) {
+ if (\in_array($opener->getChar(), $characters, true)) {
+ break;
+ }
+
+ $opener = $opener->getPrevious();
+ }
+
+ return $opener;
+ }
+
+ /**
+ * @param DelimiterInterface|int|null $stackBottom
+ *
+ * @todo change $stackBottom to an int in 3.0
+ */
+ public function processDelimiters($stackBottom, DelimiterProcessorCollection $processors): void
+ {
+ /** @var array $openersBottom */
+ $openersBottom = [];
+
+ $stackBottomPosition = \is_int($stackBottom) ? $stackBottom : self::getIndex($stackBottom);
+
+ // Find first closer above stackBottom
+ $closer = $this->findEarliest($stackBottomPosition);
+
+ // Move forward, looking for closers, and handling each
+ while ($closer !== null) {
+ $closingDelimiterChar = $closer->getChar();
+
+ $delimiterProcessor = $processors->getDelimiterProcessor($closingDelimiterChar);
+ if (! $closer->canClose() || $delimiterProcessor === null) {
+ $closer = $closer->getNext();
+ continue;
+ }
+
+ if ($delimiterProcessor instanceof CacheableDelimiterProcessorInterface) {
+ $openersBottomCacheKey = $delimiterProcessor->getCacheKey($closer);
+ } else {
+ $openersBottomCacheKey = $closingDelimiterChar;
+ }
+
+ $openingDelimiterChar = $delimiterProcessor->getOpeningCharacter();
+
+ $useDelims = 0;
+ $openerFound = false;
+ $potentialOpenerFound = false;
+ $opener = $closer->getPrevious();
+ while ($opener !== null && ($openerPosition = self::getIndex($opener)) > $stackBottomPosition && $openerPosition >= ($openersBottom[$openersBottomCacheKey] ?? 0)) {
+ if ($opener->canOpen() && $opener->getChar() === $openingDelimiterChar) {
+ $potentialOpenerFound = true;
+ $useDelims = $delimiterProcessor->getDelimiterUse($opener, $closer);
+ if ($useDelims > 0) {
+ $openerFound = true;
+ break;
+ }
+ }
+
+ $opener = $opener->getPrevious();
+ }
+
+ if (! $openerFound) {
+ // Set lower bound for future searches
+ // TODO: Remove this conditional check in 3.0. It only exists to prevent behavioral BC breaks in 2.x.
+ if ($potentialOpenerFound === false || $delimiterProcessor instanceof CacheableDelimiterProcessorInterface) {
+ $openersBottom[$openersBottomCacheKey] = self::getIndex($closer);
+ }
+
+ if (! $potentialOpenerFound && ! $closer->canOpen()) {
+ // We can remove a closer that can't be an opener,
+ // once we've seen there's no matching opener.
+ $next = $closer->getNext();
+ $this->removeDelimiter($closer);
+ $closer = $next;
+ } else {
+ $closer = $closer->getNext();
+ }
+
+ continue;
+ }
+
+ \assert($opener !== null);
+
+ $openerNode = $opener->getInlineNode();
+ $closerNode = $closer->getInlineNode();
+
+ // Remove number of used delimiters from stack and inline nodes.
+ $opener->setLength($opener->getLength() - $useDelims);
+ $closer->setLength($closer->getLength() - $useDelims);
+
+ $openerNode->setLiteral(\substr($openerNode->getLiteral(), 0, -$useDelims));
+ $closerNode->setLiteral(\substr($closerNode->getLiteral(), 0, -$useDelims));
+
+ $this->removeDelimitersBetween($opener, $closer);
+ // The delimiter processor can re-parent the nodes between opener and closer,
+ // so make sure they're contiguous already. Exclusive because we want to keep opener/closer themselves.
+ AdjacentTextMerger::mergeTextNodesBetweenExclusive($openerNode, $closerNode);
+ $delimiterProcessor->process($openerNode, $closerNode, $useDelims);
+
+ // No delimiter characters left to process, so we can remove delimiter and the now empty node.
+ if ($opener->getLength() === 0) {
+ $this->removeDelimiterAndNode($opener);
+ }
+
+ // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
+ if ($closer->getLength() === 0) {
+ $next = $closer->getNext();
+ $this->removeDelimiterAndNode($closer);
+ $closer = $next;
+ }
+ }
+
+ // Remove all delimiters
+ $this->removeAll($stackBottomPosition);
+ }
+
+ /**
+ * @internal
+ */
+ public function __destruct()
+ {
+ while ($this->top) {
+ $this->removeDelimiter($this->top);
+ }
+
+ while ($this->brackets) {
+ $this->removeBracket();
+ }
+ }
+
+ /**
+ * @deprecated This method will be dropped in 3.0 once all delimiters MUST have an index/position
+ */
+ private function getIndex(?DelimiterInterface $delimiter): int
+ {
+ if ($delimiter === null) {
+ return -1;
+ }
+
+ if (($index = $delimiter->getIndex()) !== null) {
+ return $index;
+ }
+
+ if (isset($this->missingIndexCache[$delimiter])) {
+ return $this->missingIndexCache[$delimiter];
+ }
+
+ $prev = $delimiter->getPrevious();
+ $next = $delimiter->getNext();
+
+ $i = 0;
+ do {
+ $i++;
+ if ($prev === null) {
+ break;
+ }
+
+ if ($prev->getIndex() !== null) {
+ return $this->missingIndexCache[$delimiter] = $prev->getIndex() + $i;
+ }
+ } while ($prev = $prev->getPrevious());
+
+ $j = 0;
+ do {
+ $j++;
+ if ($next === null) {
+ break;
+ }
+
+ if ($next->getIndex() !== null) {
+ return $this->missingIndexCache[$delimiter] = $next->getIndex() - $j;
+ }
+ } while ($next = $next->getNext());
+
+ // No index was defined on this delimiter, and none could be guesstimated based on the stack.
+ return $this->missingIndexCache[$delimiter] = $this->getIndex($delimiter->getPrevious()) + 1;
+ }
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Processor/CacheableDelimiterProcessorInterface.php b/vendor/league/commonmark/src/Delimiter/Processor/CacheableDelimiterProcessorInterface.php
new file mode 100644
index 0000000..a2a9b7e
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Processor/CacheableDelimiterProcessorInterface.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+
+/**
+ * Special marker interface for delimiter processors that return dynamic values from getDelimiterUse()
+ *
+ * In order to guarantee linear performance of delimiter processing, the delimiter stack must be able to
+ * cache the lower bound when searching for a matching opener. This gets complicated for delimiter processors
+ * that use a dynamic number of characters (like with emphasis and its "multiple of 3" rule).
+ */
+interface CacheableDelimiterProcessorInterface extends DelimiterProcessorInterface
+{
+ /**
+ * Returns a cache key of the factors that determine the number of characters to use.
+ *
+ * In order to guarantee linear performance of delimiter processing, the delimiter stack must be able to
+ * cache the lower bound when searching for a matching opener. This lower bound is usually quite simple;
+ * for example, with quotes, it's just the last opener with that characted. However, this gets complicated
+ * for delimiter processors that use a dynamic number of characters (like with emphasis and its "multiple
+ * of 3" rule), because the delimiter length being considered may change during processing because of that
+ * dynamic logic in getDelimiterUse(). Therefore, we cannot safely cache the lower bound for these dynamic
+ * processors without knowing the factors that determine the number of characters to use.
+ *
+ * At a minimum, this should include the delimiter character, plus any other factors used to determine
+ * the result of getDelimiterUse(). The format of the string is not important so long as it is unique
+ * (compared to other processors) and consistent for a given set of factors.
+ *
+ * If getDelimiterUse() always returns the same hard-coded value, this method should return just
+ * the delimiter character.
+ */
+ public function getCacheKey(DelimiterInterface $closer): string;
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollection.php b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollection.php
new file mode 100644
index 0000000..6e9f336
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollection.php
@@ -0,0 +1,89 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+
+final class DelimiterProcessorCollection implements DelimiterProcessorCollectionInterface
+{
+ /**
+ * @var array|DelimiterProcessorInterface[]
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $processorsByChar = [];
+
+ public function add(DelimiterProcessorInterface $processor): void
+ {
+ $opening = $processor->getOpeningCharacter();
+ $closing = $processor->getClosingCharacter();
+
+ if ($opening === $closing) {
+ $old = $this->processorsByChar[$opening] ?? null;
+ if ($old !== null && $old->getOpeningCharacter() === $old->getClosingCharacter()) {
+ $this->addStaggeredDelimiterProcessorForChar($opening, $old, $processor);
+ } else {
+ $this->addDelimiterProcessorForChar($opening, $processor);
+ }
+ } else {
+ $this->addDelimiterProcessorForChar($opening, $processor);
+ $this->addDelimiterProcessorForChar($closing, $processor);
+ }
+ }
+
+ public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface
+ {
+ return $this->processorsByChar[$char] ?? null;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getDelimiterCharacters(): array
+ {
+ return \array_keys($this->processorsByChar);
+ }
+
+ private function addDelimiterProcessorForChar(string $delimiterChar, DelimiterProcessorInterface $processor): void
+ {
+ if (isset($this->processorsByChar[$delimiterChar])) {
+ throw new InvalidArgumentException(\sprintf('Delim processor for character "%s" already exists', $processor->getOpeningCharacter()));
+ }
+
+ $this->processorsByChar[$delimiterChar] = $processor;
+ }
+
+ private function addStaggeredDelimiterProcessorForChar(string $opening, DelimiterProcessorInterface $old, DelimiterProcessorInterface $new): void
+ {
+ if ($old instanceof StaggeredDelimiterProcessor) {
+ $s = $old;
+ } else {
+ $s = new StaggeredDelimiterProcessor($opening, $old);
+ }
+
+ $s->add($new);
+ $this->processorsByChar[$opening] = $s;
+ }
+
+ public function count(): int
+ {
+ return \count($this->processorsByChar);
+ }
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollectionInterface.php b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollectionInterface.php
new file mode 100644
index 0000000..fea3ddb
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorCollectionInterface.php
@@ -0,0 +1,46 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+
+interface DelimiterProcessorCollectionInterface extends \Countable
+{
+ /**
+ * Add the given delim processor to the collection
+ *
+ * @param DelimiterProcessorInterface $processor The delim processor to add
+ *
+ * @throws InvalidArgumentException Exception will be thrown if attempting to add multiple processors for the same character
+ */
+ public function add(DelimiterProcessorInterface $processor): void;
+
+ /**
+ * Returns the delim processor which handles the given character if one exists
+ */
+ public function getDelimiterProcessor(string $char): ?DelimiterProcessorInterface;
+
+ /**
+ * Returns an array of delimiter characters who have associated processors
+ *
+ * @return string[]
+ */
+ public function getDelimiterCharacters(): array;
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorInterface.php b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorInterface.php
new file mode 100644
index 0000000..5e88ddc
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Processor/DelimiterProcessorInterface.php
@@ -0,0 +1,81 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+/**
+ * Interface for a delimiter processor
+ */
+interface DelimiterProcessorInterface
+{
+ /**
+ * Returns the character that marks the beginning of a delimited node.
+ *
+ * This must not clash with any other processors being added to the environment.
+ */
+ public function getOpeningCharacter(): string;
+
+ /**
+ * Returns the character that marks the ending of a delimited node.
+ *
+ * This must not clash with any other processors being added to the environment.
+ *
+ * Note that for a symmetric delimiter such as "*", this is the same as the opening.
+ */
+ public function getClosingCharacter(): string;
+
+ /**
+ * Minimum number of delimiter characters that are needed to active this.
+ *
+ * Must be at least 1.
+ */
+ public function getMinLength(): int;
+
+ /**
+ * Determine how many (if any) of the delimiter characters should be used.
+ *
+ * This allows implementations to decide how many characters to be used
+ * based on the properties of the delimiter runs. An implementation can also
+ * return 0 when it doesn't want to allow this particular combination of
+ * delimiter runs.
+ *
+ * IMPORTANT: Unless this method returns the same hard-coded value in all cases,
+ * you MUST implement the CacheableDelimiterProcessorInterface interface instead.
+ *
+ * @param DelimiterInterface $opener The opening delimiter run
+ * @param DelimiterInterface $closer The closing delimiter run
+ */
+ public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int;
+
+ /**
+ * Process the matched delimiters, e.g. by wrapping the nodes between opener
+ * and closer in a new node, or appending a new node after the opener.
+ *
+ * Note that removal of the delimiter from the delimiter nodes and detaching
+ * them is done by the caller.
+ *
+ * @param AbstractStringContainer $opener The node that contained the opening delimiter
+ * @param AbstractStringContainer $closer The node that contained the closing delimiter
+ * @param int $delimiterUse The number of delimiters that were used
+ */
+ public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void;
+}
diff --git a/vendor/league/commonmark/src/Delimiter/Processor/StaggeredDelimiterProcessor.php b/vendor/league/commonmark/src/Delimiter/Processor/StaggeredDelimiterProcessor.php
new file mode 100644
index 0000000..7d33e83
--- /dev/null
+++ b/vendor/league/commonmark/src/Delimiter/Processor/StaggeredDelimiterProcessor.php
@@ -0,0 +1,111 @@
+
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+use League\CommonMark\Exception\InvalidArgumentException;
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+/**
+ * An implementation of DelimiterProcessorInterface that dispatches all calls to two or more other DelimiterProcessors
+ * depending on the length of the delimiter run. All child DelimiterProcessors must have different minimum
+ * lengths. A given delimiter run is dispatched to the child with the largest acceptable minimum length. If no
+ * child is applicable, the one with the largest minimum length is chosen.
+ *
+ * @internal
+ */
+final class StaggeredDelimiterProcessor implements DelimiterProcessorInterface
+{
+ /** @psalm-readonly */
+ private string $delimiterChar;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $minLength = 0;
+
+ /**
+ * @var array|DelimiterProcessorInterface[]
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $processors = []; // keyed by minLength in reverse order
+
+ public function __construct(string $char, DelimiterProcessorInterface $processor)
+ {
+ $this->delimiterChar = $char;
+ $this->add($processor);
+ }
+
+ public function getOpeningCharacter(): string
+ {
+ return $this->delimiterChar;
+ }
+
+ public function getClosingCharacter(): string
+ {
+ return $this->delimiterChar;
+ }
+
+ public function getMinLength(): int
+ {
+ return $this->minLength;
+ }
+
+ /**
+ * Adds the given processor to this staggered delimiter processor
+ *
+ * @throws InvalidArgumentException if attempting to add another processors for the same character and minimum length
+ */
+ public function add(DelimiterProcessorInterface $processor): void
+ {
+ $len = $processor->getMinLength();
+
+ if (isset($this->processors[$len])) {
+ throw new InvalidArgumentException(\sprintf('Cannot add two delimiter processors for char "%s" and minimum length %d', $this->delimiterChar, $len));
+ }
+
+ $this->processors[$len] = $processor;
+ \krsort($this->processors);
+
+ $this->minLength = \min($this->minLength, $len);
+ }
+
+ public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
+ {
+ return $this->findProcessor($opener->getLength())->getDelimiterUse($opener, $closer);
+ }
+
+ public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
+ {
+ $this->findProcessor($delimiterUse)->process($opener, $closer, $delimiterUse);
+ }
+
+ private function findProcessor(int $len): DelimiterProcessorInterface
+ {
+ // Find the "longest" processor which can handle this length
+ foreach ($this->processors as $processor) {
+ if ($processor->getMinLength() <= $len) {
+ return $processor;
+ }
+ }
+
+ // Just use the first one in our list
+ $first = \reset($this->processors);
+ \assert($first instanceof DelimiterProcessorInterface);
+
+ return $first;
+ }
+}
diff --git a/vendor/league/commonmark/src/Environment/Environment.php b/vendor/league/commonmark/src/Environment/Environment.php
new file mode 100644
index 0000000..a811296
--- /dev/null
+++ b/vendor/league/commonmark/src/Environment/Environment.php
@@ -0,0 +1,448 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Environment;
+
+use League\CommonMark\Delimiter\DelimiterParser;
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorCollection;
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Event\ListenerData;
+use League\CommonMark\Exception\AlreadyInitializedException;
+use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
+use League\CommonMark\Normalizer\SlugNormalizer;
+use League\CommonMark\Normalizer\TextNormalizerInterface;
+use League\CommonMark\Normalizer\UniqueSlugNormalizer;
+use League\CommonMark\Normalizer\UniqueSlugNormalizerInterface;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Block\SkipLinesStartingWithLettersParser;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlFilter;
+use League\CommonMark\Util\PrioritizedList;
+use League\Config\Configuration;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+use Nette\Schema\Expect;
+use Psr\EventDispatcher\EventDispatcherInterface;
+use Psr\EventDispatcher\ListenerProviderInterface;
+use Psr\EventDispatcher\StoppableEventInterface;
+
+final class Environment implements EnvironmentInterface, EnvironmentBuilderInterface, ListenerProviderInterface
+{
+ /**
+ * @var ExtensionInterface[]
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $extensions = [];
+
+ /**
+ * @var ExtensionInterface[]
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $uninitializedExtensions = [];
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $extensionsInitialized = false;
+
+ /**
+ * @var PrioritizedList
+ *
+ * @psalm-readonly
+ */
+ private PrioritizedList $blockStartParsers;
+
+ /**
+ * @var PrioritizedList
+ *
+ * @psalm-readonly
+ */
+ private PrioritizedList $inlineParsers;
+
+ /** @psalm-readonly */
+ private DelimiterProcessorCollection $delimiterProcessors;
+
+ /**
+ * @var array>
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $renderersByClass = [];
+
+ /**
+ * @var PrioritizedList
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private PrioritizedList $listenerData;
+
+ private ?EventDispatcherInterface $eventDispatcher = null;
+
+ /** @psalm-readonly */
+ private Configuration $config;
+
+ private ?TextNormalizerInterface $slugNormalizer = null;
+
+ /**
+ * @param array $config
+ */
+ public function __construct(array $config = [])
+ {
+ $this->config = self::createDefaultConfiguration();
+ $this->config->merge($config);
+
+ $this->blockStartParsers = new PrioritizedList();
+ $this->inlineParsers = new PrioritizedList();
+ $this->listenerData = new PrioritizedList();
+ $this->delimiterProcessors = new DelimiterProcessorCollection();
+
+ // Performance optimization: always include a block "parser" that aborts parsing if a line starts with a letter
+ // and is therefore unlikely to match any lines as a block start.
+ $this->addBlockStartParser(new SkipLinesStartingWithLettersParser(), 249);
+ }
+
+ public function getConfiguration(): ConfigurationInterface
+ {
+ return $this->config->reader();
+ }
+
+ /**
+ * @deprecated Environment::mergeConfig() is deprecated since league/commonmark v2.0 and will be removed in v3.0. Configuration should be set when instantiating the environment instead.
+ *
+ * @param array $config
+ */
+ public function mergeConfig(array $config): void
+ {
+ @\trigger_error('Environment::mergeConfig() is deprecated since league/commonmark v2.0 and will be removed in v3.0. Configuration should be set when instantiating the environment instead.', \E_USER_DEPRECATED);
+
+ $this->assertUninitialized('Failed to modify configuration.');
+
+ $this->config->merge($config);
+ }
+
+ public function addBlockStartParser(BlockStartParserInterface $parser, int $priority = 0): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add block start parser.');
+
+ $this->blockStartParsers->add($parser, $priority);
+ $this->injectEnvironmentAndConfigurationIfNeeded($parser);
+
+ return $this;
+ }
+
+ public function addInlineParser(InlineParserInterface $parser, int $priority = 0): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add inline parser.');
+
+ $this->inlineParsers->add($parser, $priority);
+ $this->injectEnvironmentAndConfigurationIfNeeded($parser);
+
+ return $this;
+ }
+
+ public function addDelimiterProcessor(DelimiterProcessorInterface $processor): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add delimiter processor.');
+ $this->delimiterProcessors->add($processor);
+ $this->injectEnvironmentAndConfigurationIfNeeded($processor);
+
+ return $this;
+ }
+
+ public function addRenderer(string $nodeClass, NodeRendererInterface $renderer, int $priority = 0): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add renderer.');
+
+ if (! isset($this->renderersByClass[$nodeClass])) {
+ $this->renderersByClass[$nodeClass] = new PrioritizedList();
+ }
+
+ $this->renderersByClass[$nodeClass]->add($renderer, $priority);
+ $this->injectEnvironmentAndConfigurationIfNeeded($renderer);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getBlockStartParsers(): iterable
+ {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ return $this->blockStartParsers->getIterator();
+ }
+
+ public function getDelimiterProcessors(): DelimiterProcessorCollection
+ {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ return $this->delimiterProcessors;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getRenderersForClass(string $nodeClass): iterable
+ {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ // If renderers are defined for this specific class, return them immediately
+ if (isset($this->renderersByClass[$nodeClass])) {
+ return $this->renderersByClass[$nodeClass];
+ }
+
+ /** @psalm-suppress TypeDoesNotContainType -- Bug: https://github.com/vimeo/psalm/issues/3332 */
+ while (\class_exists($parent ??= $nodeClass) && $parent = \get_parent_class($parent)) {
+ if (! isset($this->renderersByClass[$parent])) {
+ continue;
+ }
+
+ // "Cache" this result to avoid future loops
+ return $this->renderersByClass[$nodeClass] = $this->renderersByClass[$parent];
+ }
+
+ return [];
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getExtensions(): iterable
+ {
+ return $this->extensions;
+ }
+
+ /**
+ * Add a single extension
+ *
+ * @return $this
+ */
+ public function addExtension(ExtensionInterface $extension): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add extension.');
+
+ $this->extensions[] = $extension;
+ $this->uninitializedExtensions[] = $extension;
+
+ if ($extension instanceof ConfigurableExtensionInterface) {
+ $extension->configureSchema($this->config);
+ }
+
+ return $this;
+ }
+
+ private function initializeExtensions(): void
+ {
+ // Initialize the slug normalizer
+ $this->getSlugNormalizer();
+
+ // Ask all extensions to register their components
+ while (\count($this->uninitializedExtensions) > 0) {
+ foreach ($this->uninitializedExtensions as $i => $extension) {
+ $extension->register($this);
+ unset($this->uninitializedExtensions[$i]);
+ }
+ }
+
+ $this->extensionsInitialized = true;
+
+ // Create the special delimiter parser if any processors were registered
+ if ($this->delimiterProcessors->count() > 0) {
+ $this->inlineParsers->add(new DelimiterParser($this->delimiterProcessors), PHP_INT_MIN);
+ }
+ }
+
+ private function injectEnvironmentAndConfigurationIfNeeded(object $object): void
+ {
+ if ($object instanceof EnvironmentAwareInterface) {
+ $object->setEnvironment($this);
+ }
+
+ if ($object instanceof ConfigurationAwareInterface) {
+ $object->setConfiguration($this->config->reader());
+ }
+ }
+
+ /**
+ * @deprecated Instantiate the environment and add the extension yourself
+ *
+ * @param array $config
+ */
+ public static function createCommonMarkEnvironment(array $config = []): Environment
+ {
+ $environment = new self($config);
+ $environment->addExtension(new CommonMarkCoreExtension());
+
+ return $environment;
+ }
+
+ /**
+ * @deprecated Instantiate the environment and add the extension yourself
+ *
+ * @param array $config
+ */
+ public static function createGFMEnvironment(array $config = []): Environment
+ {
+ $environment = new self($config);
+ $environment->addExtension(new CommonMarkCoreExtension());
+ $environment->addExtension(new GithubFlavoredMarkdownExtension());
+
+ return $environment;
+ }
+
+ public function addEventListener(string $eventClass, callable $listener, int $priority = 0): EnvironmentBuilderInterface
+ {
+ $this->assertUninitialized('Failed to add event listener.');
+
+ $this->listenerData->add(new ListenerData($eventClass, $listener), $priority);
+
+ if (\is_object($listener)) {
+ $this->injectEnvironmentAndConfigurationIfNeeded($listener);
+ } elseif (\is_array($listener) && \is_object($listener[0])) {
+ $this->injectEnvironmentAndConfigurationIfNeeded($listener[0]);
+ }
+
+ return $this;
+ }
+
+ public function dispatch(object $event): object
+ {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ if ($this->eventDispatcher !== null) {
+ return $this->eventDispatcher->dispatch($event);
+ }
+
+ foreach ($this->getListenersForEvent($event) as $listener) {
+ if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
+ return $event;
+ }
+
+ $listener($event);
+ }
+
+ return $event;
+ }
+
+ public function setEventDispatcher(EventDispatcherInterface $dispatcher): void
+ {
+ $this->eventDispatcher = $dispatcher;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return iterable
+ */
+ public function getListenersForEvent(object $event): iterable
+ {
+ foreach ($this->listenerData as $listenerData) {
+ \assert($listenerData instanceof ListenerData);
+
+ /** @psalm-suppress ArgumentTypeCoercion */
+ if (! \is_a($event, $listenerData->getEvent())) {
+ continue;
+ }
+
+ yield function (object $event) use ($listenerData) {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ return \call_user_func($listenerData->getListener(), $event);
+ };
+ }
+ }
+
+ /**
+ * @return iterable
+ */
+ public function getInlineParsers(): iterable
+ {
+ if (! $this->extensionsInitialized) {
+ $this->initializeExtensions();
+ }
+
+ return $this->inlineParsers->getIterator();
+ }
+
+ public function getSlugNormalizer(): TextNormalizerInterface
+ {
+ if ($this->slugNormalizer === null) {
+ $normalizer = $this->config->get('slug_normalizer/instance');
+ \assert($normalizer instanceof TextNormalizerInterface);
+ $this->injectEnvironmentAndConfigurationIfNeeded($normalizer);
+
+ if ($this->config->get('slug_normalizer/unique') !== UniqueSlugNormalizerInterface::DISABLED && ! $normalizer instanceof UniqueSlugNormalizer) {
+ $normalizer = new UniqueSlugNormalizer($normalizer);
+ }
+
+ if ($normalizer instanceof UniqueSlugNormalizer) {
+ if ($this->config->get('slug_normalizer/unique') === UniqueSlugNormalizerInterface::PER_DOCUMENT) {
+ $this->addEventListener(DocumentParsedEvent::class, [$normalizer, 'clearHistory'], -1000);
+ }
+ }
+
+ $this->slugNormalizer = $normalizer;
+ }
+
+ return $this->slugNormalizer;
+ }
+
+ /**
+ * @throws AlreadyInitializedException
+ */
+ private function assertUninitialized(string $message): void
+ {
+ if ($this->extensionsInitialized) {
+ throw new AlreadyInitializedException($message . ' Extensions have already been initialized.');
+ }
+ }
+
+ public static function createDefaultConfiguration(): Configuration
+ {
+ return new Configuration([
+ 'html_input' => Expect::anyOf(HtmlFilter::STRIP, HtmlFilter::ALLOW, HtmlFilter::ESCAPE)->default(HtmlFilter::ALLOW),
+ 'allow_unsafe_links' => Expect::bool(true),
+ 'max_nesting_level' => Expect::type('int')->default(PHP_INT_MAX),
+ 'max_delimiters_per_line' => Expect::type('int')->default(PHP_INT_MAX),
+ 'renderer' => Expect::structure([
+ 'block_separator' => Expect::string("\n"),
+ 'inner_separator' => Expect::string("\n"),
+ 'soft_break' => Expect::string("\n"),
+ ]),
+ 'slug_normalizer' => Expect::structure([
+ 'instance' => Expect::type(TextNormalizerInterface::class)->default(new SlugNormalizer()),
+ 'max_length' => Expect::int()->min(0)->default(255),
+ 'unique' => Expect::anyOf(UniqueSlugNormalizerInterface::DISABLED, UniqueSlugNormalizerInterface::PER_ENVIRONMENT, UniqueSlugNormalizerInterface::PER_DOCUMENT)->default(UniqueSlugNormalizerInterface::PER_DOCUMENT),
+ ]),
+ ]);
+ }
+}
diff --git a/vendor/league/commonmark/src/Environment/EnvironmentAwareInterface.php b/vendor/league/commonmark/src/Environment/EnvironmentAwareInterface.php
new file mode 100644
index 0000000..44b9d3e
--- /dev/null
+++ b/vendor/league/commonmark/src/Environment/EnvironmentAwareInterface.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Environment;
+
+interface EnvironmentAwareInterface
+{
+ public function setEnvironment(EnvironmentInterface $environment): void;
+}
diff --git a/vendor/league/commonmark/src/Environment/EnvironmentBuilderInterface.php b/vendor/league/commonmark/src/Environment/EnvironmentBuilderInterface.php
new file mode 100644
index 0000000..4df9761
--- /dev/null
+++ b/vendor/league/commonmark/src/Environment/EnvironmentBuilderInterface.php
@@ -0,0 +1,97 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Environment;
+
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorInterface;
+use League\CommonMark\Exception\AlreadyInitializedException;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\Config\ConfigurationProviderInterface;
+
+/**
+ * Interface for building the Environment with any extensions, parsers, listeners, etc. that it may need
+ */
+interface EnvironmentBuilderInterface extends ConfigurationProviderInterface
+{
+ /**
+ * Registers the given extension with the Environment
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addExtension(ExtensionInterface $extension): EnvironmentBuilderInterface;
+
+ /**
+ * Registers the given block start parser with the Environment
+ *
+ * @param BlockStartParserInterface $parser Block parser instance
+ * @param int $priority Priority (a higher number will be executed earlier)
+ *
+ * @return $this
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addBlockStartParser(BlockStartParserInterface $parser, int $priority = 0): EnvironmentBuilderInterface;
+
+ /**
+ * Registers the given inline parser with the Environment
+ *
+ * @param InlineParserInterface $parser Inline parser instance
+ * @param int $priority Priority (a higher number will be executed earlier)
+ *
+ * @return $this
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addInlineParser(InlineParserInterface $parser, int $priority = 0): EnvironmentBuilderInterface;
+
+ /**
+ * Registers the given delimiter processor with the Environment
+ *
+ * @param DelimiterProcessorInterface $processor Delimiter processors instance
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addDelimiterProcessor(DelimiterProcessorInterface $processor): EnvironmentBuilderInterface;
+
+ /**
+ * Registers the given node renderer with the Environment
+ *
+ * @param string $nodeClass The fully-qualified node element class name the renderer below should handle
+ * @param NodeRendererInterface $renderer The renderer responsible for rendering the type of element given above
+ * @param int $priority Priority (a higher number will be executed earlier)
+ *
+ * @psalm-param class-string $nodeClass
+ *
+ * @return $this
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addRenderer(string $nodeClass, NodeRendererInterface $renderer, int $priority = 0): EnvironmentBuilderInterface;
+
+ /**
+ * Registers the given event listener
+ *
+ * @param class-string $eventClass Fully-qualified class name of the event this listener should respond to
+ * @param callable $listener Listener to be executed
+ * @param int $priority Priority (a higher number will be executed earlier)
+ *
+ * @return $this
+ *
+ * @throws AlreadyInitializedException if the Environment has already been initialized
+ */
+ public function addEventListener(string $eventClass, callable $listener, int $priority = 0): EnvironmentBuilderInterface;
+}
diff --git a/vendor/league/commonmark/src/Environment/EnvironmentInterface.php b/vendor/league/commonmark/src/Environment/EnvironmentInterface.php
new file mode 100644
index 0000000..8e19a52
--- /dev/null
+++ b/vendor/league/commonmark/src/Environment/EnvironmentInterface.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Environment;
+
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorCollection;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Normalizer\TextNormalizerInterface;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\Config\ConfigurationProviderInterface;
+use Psr\EventDispatcher\EventDispatcherInterface;
+
+interface EnvironmentInterface extends ConfigurationProviderInterface, EventDispatcherInterface
+{
+ /**
+ * Get all registered extensions
+ *
+ * @return ExtensionInterface[]
+ */
+ public function getExtensions(): iterable;
+
+ /**
+ * @return iterable
+ */
+ public function getBlockStartParsers(): iterable;
+
+ /**
+ * @return iterable
+ */
+ public function getInlineParsers(): iterable;
+
+ public function getDelimiterProcessors(): DelimiterProcessorCollection;
+
+ /**
+ * @psalm-param class-string $nodeClass
+ *
+ * @return iterable
+ */
+ public function getRenderersForClass(string $nodeClass): iterable;
+
+ public function getSlugNormalizer(): TextNormalizerInterface;
+}
diff --git a/vendor/league/commonmark/src/Event/AbstractEvent.php b/vendor/league/commonmark/src/Event/AbstractEvent.php
new file mode 100644
index 0000000..8c83f92
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/AbstractEvent.php
@@ -0,0 +1,54 @@
+
+ *
+ * Original code based on the Symfony EventDispatcher "Event" contract
+ * - (c) 2018-2019 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Event;
+
+use Psr\EventDispatcher\StoppableEventInterface;
+
+/**
+ * Base class for classes containing event data.
+ *
+ * This class contains no event data. It is used by events that do not pass
+ * state information to an event handler when an event is raised.
+ *
+ * You can call the method stopPropagation() to abort the execution of
+ * further listeners in your event listener.
+ */
+abstract class AbstractEvent implements StoppableEventInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $propagationStopped = false;
+
+ /**
+ * Returns whether further event listeners should be triggered.
+ */
+ final public function isPropagationStopped(): bool
+ {
+ return $this->propagationStopped;
+ }
+
+ /**
+ * Stops the propagation of the event to further event listeners.
+ *
+ * If multiple event listeners are connected to the same event, no
+ * further event listener will be triggered once any trigger calls
+ * stopPropagation().
+ */
+ final public function stopPropagation(): void
+ {
+ $this->propagationStopped = true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Event/DocumentParsedEvent.php b/vendor/league/commonmark/src/Event/DocumentParsedEvent.php
new file mode 100644
index 0000000..04664c5
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/DocumentParsedEvent.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Event;
+
+use League\CommonMark\Node\Block\Document;
+
+/**
+ * Event dispatched when the document has been fully parsed
+ */
+final class DocumentParsedEvent extends AbstractEvent
+{
+ /** @psalm-readonly */
+ private Document $document;
+
+ public function __construct(Document $document)
+ {
+ $this->document = $document;
+ }
+
+ public function getDocument(): Document
+ {
+ return $this->document;
+ }
+}
diff --git a/vendor/league/commonmark/src/Event/DocumentPreParsedEvent.php b/vendor/league/commonmark/src/Event/DocumentPreParsedEvent.php
new file mode 100644
index 0000000..ad72512
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/DocumentPreParsedEvent.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Event;
+
+use League\CommonMark\Input\MarkdownInputInterface;
+use League\CommonMark\Node\Block\Document;
+
+/**
+ * Event dispatched when the document is about to be parsed
+ */
+final class DocumentPreParsedEvent extends AbstractEvent
+{
+ /** @psalm-readonly */
+ private Document $document;
+
+ private MarkdownInputInterface $markdown;
+
+ public function __construct(Document $document, MarkdownInputInterface $markdown)
+ {
+ $this->document = $document;
+ $this->markdown = $markdown;
+ }
+
+ public function getDocument(): Document
+ {
+ return $this->document;
+ }
+
+ public function getMarkdown(): MarkdownInputInterface
+ {
+ return $this->markdown;
+ }
+
+ public function replaceMarkdown(MarkdownInputInterface $markdownInput): void
+ {
+ $this->markdown = $markdownInput;
+ }
+}
diff --git a/vendor/league/commonmark/src/Event/DocumentPreRenderEvent.php b/vendor/league/commonmark/src/Event/DocumentPreRenderEvent.php
new file mode 100644
index 0000000..c569ca3
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/DocumentPreRenderEvent.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Event;
+
+use League\CommonMark\Node\Block\Document;
+
+/**
+ * Event dispatched just before rendering begins
+ */
+final class DocumentPreRenderEvent extends AbstractEvent
+{
+ /** @psalm-readonly */
+ private Document $document;
+
+ /** @psalm-readonly */
+ private string $format;
+
+ public function __construct(Document $document, string $format)
+ {
+ $this->document = $document;
+ $this->format = $format;
+ }
+
+ public function getDocument(): Document
+ {
+ return $this->document;
+ }
+
+ public function getFormat(): string
+ {
+ return $this->format;
+ }
+}
diff --git a/vendor/league/commonmark/src/Event/DocumentRenderedEvent.php b/vendor/league/commonmark/src/Event/DocumentRenderedEvent.php
new file mode 100644
index 0000000..7e49d01
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/DocumentRenderedEvent.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Event;
+
+use League\CommonMark\Output\RenderedContentInterface;
+
+final class DocumentRenderedEvent extends AbstractEvent
+{
+ private RenderedContentInterface $output;
+
+ public function __construct(RenderedContentInterface $output)
+ {
+ $this->output = $output;
+ }
+
+ /**
+ * @psalm-mutation-free
+ */
+ public function getOutput(): RenderedContentInterface
+ {
+ return $this->output;
+ }
+
+ /**
+ * @psalm-external-mutation-free
+ */
+ public function replaceOutput(RenderedContentInterface $output): void
+ {
+ $this->output = $output;
+ }
+}
diff --git a/vendor/league/commonmark/src/Event/ListenerData.php b/vendor/league/commonmark/src/Event/ListenerData.php
new file mode 100644
index 0000000..4cf3b3a
--- /dev/null
+++ b/vendor/league/commonmark/src/Event/ListenerData.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Event;
+
+/**
+ * @internal
+ *
+ * @psalm-immutable
+ */
+final class ListenerData
+{
+ /** @var class-string */
+ private string $event;
+
+ /** @var callable */
+ private $listener;
+
+ /**
+ * @param class-string $event
+ */
+ public function __construct(string $event, callable $listener)
+ {
+ $this->event = $event;
+ $this->listener = $listener;
+ }
+
+ /**
+ * @return class-string
+ */
+ public function getEvent(): string
+ {
+ return $this->event;
+ }
+
+ public function getListener(): callable
+ {
+ return $this->listener;
+ }
+}
diff --git a/vendor/league/commonmark/src/Exception/AlreadyInitializedException.php b/vendor/league/commonmark/src/Exception/AlreadyInitializedException.php
new file mode 100644
index 0000000..5faa6f8
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/AlreadyInitializedException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+class AlreadyInitializedException extends LogicException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/CommonMarkException.php b/vendor/league/commonmark/src/Exception/CommonMarkException.php
new file mode 100644
index 0000000..9fb349e
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/CommonMarkException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+/**
+ * Marker interface for all exceptions thrown by this library.
+ */
+interface CommonMarkException extends \Throwable
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/IOException.php b/vendor/league/commonmark/src/Exception/IOException.php
new file mode 100644
index 0000000..09a5578
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/IOException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+class IOException extends \RuntimeException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/InvalidArgumentException.php b/vendor/league/commonmark/src/Exception/InvalidArgumentException.php
new file mode 100644
index 0000000..fc67ac4
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/InvalidArgumentException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+class InvalidArgumentException extends \InvalidArgumentException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/LogicException.php b/vendor/league/commonmark/src/Exception/LogicException.php
new file mode 100644
index 0000000..c1d00df
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/LogicException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+class LogicException extends \LogicException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/MissingDependencyException.php b/vendor/league/commonmark/src/Exception/MissingDependencyException.php
new file mode 100644
index 0000000..b8eb841
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/MissingDependencyException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+class MissingDependencyException extends \RuntimeException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Exception/UnexpectedEncodingException.php b/vendor/league/commonmark/src/Exception/UnexpectedEncodingException.php
new file mode 100644
index 0000000..0f4e399
--- /dev/null
+++ b/vendor/league/commonmark/src/Exception/UnexpectedEncodingException.php
@@ -0,0 +1,18 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Exception;
+
+final class UnexpectedEncodingException extends \RuntimeException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/AttributesExtension.php b/vendor/league/commonmark/src/Extension/Attributes/AttributesExtension.php
new file mode 100644
index 0000000..b29606d
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/AttributesExtension.php
@@ -0,0 +1,44 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Attributes\Event\AttributesListener;
+use League\CommonMark\Extension\Attributes\Parser\AttributesBlockStartParser;
+use League\CommonMark\Extension\Attributes\Parser\AttributesInlineParser;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class AttributesExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('attributes', Expect::structure([
+ 'allow' => Expect::arrayOf('string')->default([]),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $allowList = $environment->getConfiguration()->get('attributes.allow');
+ $allowUnsafeLinks = $environment->getConfiguration()->get('allow_unsafe_links');
+
+ $environment->addBlockStartParser(new AttributesBlockStartParser());
+ $environment->addInlineParser(new AttributesInlineParser());
+ $environment->addEventListener(DocumentParsedEvent::class, [new AttributesListener($allowList, $allowUnsafeLinks), 'processDocument']);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Event/AttributesListener.php b/vendor/league/commonmark/src/Extension/Attributes/Event/AttributesListener.php
new file mode 100644
index 0000000..fa068f4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Event/AttributesListener.php
@@ -0,0 +1,152 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Attributes\Node\Attributes;
+use League\CommonMark\Extension\Attributes\Node\AttributesInline;
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Node\Node;
+
+final class AttributesListener
+{
+ private const DIRECTION_PREFIX = 'prefix';
+ private const DIRECTION_SUFFIX = 'suffix';
+
+ /** @var list */
+ private array $allowList;
+ private bool $allowUnsafeLinks;
+
+ /**
+ * @param list $allowList
+ */
+ public function __construct(array $allowList = [], bool $allowUnsafeLinks = true)
+ {
+ $this->allowList = $allowList;
+ $this->allowUnsafeLinks = $allowUnsafeLinks;
+ }
+
+ public function processDocument(DocumentParsedEvent $event): void
+ {
+ foreach ($event->getDocument()->iterator() as $node) {
+ if (! ($node instanceof Attributes || $node instanceof AttributesInline)) {
+ continue;
+ }
+
+ [$target, $direction] = self::findTargetAndDirection($node);
+
+ if ($target instanceof Node) {
+ $parent = $target->parent();
+ if ($parent instanceof ListItem && $parent->parent() instanceof ListBlock && $parent->parent()->isTight()) {
+ $target = $parent;
+ }
+
+ if ($direction === self::DIRECTION_SUFFIX) {
+ $attributes = AttributesHelper::mergeAttributes($target, $node->getAttributes());
+ } else {
+ $attributes = AttributesHelper::mergeAttributes($node->getAttributes(), $target);
+ }
+
+ $target->data->set('attributes', AttributesHelper::filterAttributes($attributes, $this->allowList, $this->allowUnsafeLinks));
+ }
+
+ $node->detach();
+ }
+ }
+
+ /**
+ * @param Attributes|AttributesInline $node
+ *
+ * @return array
+ */
+ private static function findTargetAndDirection($node): array
+ {
+ $target = null;
+ $direction = null;
+ $previous = $next = $node;
+ while (true) {
+ $previous = self::getPrevious($previous);
+ $next = self::getNext($next);
+
+ if ($previous === null && $next === null) {
+ if (! $node->parent() instanceof FencedCode) {
+ $target = $node->parent();
+ $direction = self::DIRECTION_SUFFIX;
+ }
+
+ break;
+ }
+
+ if ($node instanceof AttributesInline && ($previous === null || ($previous instanceof AbstractInline && $node->isBlock()))) {
+ continue;
+ }
+
+ if ($previous !== null && ! self::isAttributesNode($previous)) {
+ $target = $previous;
+ $direction = self::DIRECTION_SUFFIX;
+
+ break;
+ }
+
+ if ($next !== null && ! self::isAttributesNode($next)) {
+ $target = $next;
+ $direction = self::DIRECTION_PREFIX;
+
+ break;
+ }
+ }
+
+ return [$target, $direction];
+ }
+
+ /**
+ * Get any previous block (sibling or parent) this might apply to
+ */
+ private static function getPrevious(?Node $node = null): ?Node
+ {
+ if ($node instanceof Attributes) {
+ if ($node->getTarget() === Attributes::TARGET_NEXT) {
+ return null;
+ }
+
+ if ($node->getTarget() === Attributes::TARGET_PARENT) {
+ return $node->parent();
+ }
+ }
+
+ return $node instanceof Node ? $node->previous() : null;
+ }
+
+ /**
+ * Get any previous block (sibling or parent) this might apply to
+ */
+ private static function getNext(?Node $node = null): ?Node
+ {
+ if ($node instanceof Attributes && $node->getTarget() !== Attributes::TARGET_NEXT) {
+ return null;
+ }
+
+ return $node instanceof Node ? $node->next() : null;
+ }
+
+ private static function isAttributesNode(Node $node): bool
+ {
+ return $node instanceof Attributes || $node instanceof AttributesInline;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Node/Attributes.php b/vendor/league/commonmark/src/Extension/Attributes/Node/Attributes.php
new file mode 100644
index 0000000..096f04a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Node/Attributes.php
@@ -0,0 +1,65 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class Attributes extends AbstractBlock
+{
+ public const TARGET_PARENT = 0;
+ public const TARGET_PREVIOUS = 1;
+ public const TARGET_NEXT = 2;
+
+ /** @var array */
+ private array $attributes;
+
+ private int $target = self::TARGET_NEXT;
+
+ /**
+ * @param array $attributes
+ */
+ public function __construct(array $attributes)
+ {
+ parent::__construct();
+
+ $this->attributes = $attributes;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * @param array $attributes
+ */
+ public function setAttributes(array $attributes): void
+ {
+ $this->attributes = $attributes;
+ }
+
+ public function getTarget(): int
+ {
+ return $this->target;
+ }
+
+ public function setTarget(int $target): void
+ {
+ $this->target = $target;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Node/AttributesInline.php b/vendor/league/commonmark/src/Extension/Attributes/Node/AttributesInline.php
new file mode 100644
index 0000000..d8b0d08
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Node/AttributesInline.php
@@ -0,0 +1,57 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Node;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+
+final class AttributesInline extends AbstractInline
+{
+ /** @var array */
+ private array $attributes;
+
+ private bool $block;
+
+ /**
+ * @param array $attributes
+ */
+ public function __construct(array $attributes, bool $block)
+ {
+ parent::__construct();
+
+ $this->attributes = $attributes;
+ $this->block = $block;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * @param array $attributes
+ */
+ public function setAttributes(array $attributes): void
+ {
+ $this->attributes = $attributes;
+ }
+
+ public function isBlock(): bool
+ {
+ return $this->block;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockContinueParser.php b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockContinueParser.php
new file mode 100644
index 0000000..6e0cdc6
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockContinueParser.php
@@ -0,0 +1,92 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Parser;
+
+use League\CommonMark\Extension\Attributes\Node\Attributes;
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class AttributesBlockContinueParser extends AbstractBlockContinueParser
+{
+ private Attributes $block;
+
+ private AbstractBlock $container;
+
+ private bool $hasSubsequentLine = false;
+
+ /**
+ * @param array $attributes The attributes identified by the block start parser
+ * @param AbstractBlock $container The node we were in when these attributes were discovered
+ */
+ public function __construct(array $attributes, AbstractBlock $container)
+ {
+ $this->block = new Attributes($attributes);
+
+ $this->container = $container;
+ }
+
+ public function getBlock(): AbstractBlock
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ $this->hasSubsequentLine = true;
+
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ // Does this next line also have attributes?
+ $attributes = AttributesHelper::parseAttributes($cursor);
+ $cursor->advanceToNextNonSpaceOrTab();
+ if ($cursor->isAtEnd() && $attributes !== []) {
+ // It does! Merge them into what we parsed previously
+ $this->block->setAttributes(AttributesHelper::mergeAttributes(
+ $this->block->getAttributes(),
+ $attributes
+ ));
+
+ // Tell the core parser we've consumed everything
+ return BlockContinue::at($cursor);
+ }
+
+ // Okay, so there are no attributes on the next line
+ // If this next line is blank we know we can't target the next node, it must be a previous one
+ if ($cursor->isBlank()) {
+ $this->block->setTarget(Attributes::TARGET_PREVIOUS);
+ }
+
+ return BlockContinue::none();
+ }
+
+ public function closeBlock(): void
+ {
+ // Attributes appearing at the very end of the document won't have any last lines to check
+ // so we can make that determination here
+ if (! $this->hasSubsequentLine) {
+ $this->block->setTarget(Attributes::TARGET_PREVIOUS);
+ }
+
+ // We know this block must apply to the "previous" block, but that could be a sibling or parent,
+ // so we check the containing block to see which one it might be.
+ if ($this->block->getTarget() === Attributes::TARGET_PREVIOUS && $this->block->parent() === $this->container) {
+ $this->block->setTarget(Attributes::TARGET_PARENT);
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockStartParser.php b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockStartParser.php
new file mode 100644
index 0000000..299ccd4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesBlockStartParser.php
@@ -0,0 +1,40 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Parser;
+
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class AttributesBlockStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ $originalPosition = $cursor->getPosition();
+ $attributes = AttributesHelper::parseAttributes($cursor);
+
+ if ($attributes === [] && $originalPosition === $cursor->getPosition()) {
+ return BlockStart::none();
+ }
+
+ if ($cursor->getNextNonSpaceCharacter() !== null) {
+ return BlockStart::none();
+ }
+
+ return BlockStart::of(new AttributesBlockContinueParser($attributes, $parserState->getActiveBlockParser()->getBlock()))->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesInlineParser.php b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesInlineParser.php
new file mode 100644
index 0000000..26af3ca
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Parser/AttributesInlineParser.php
@@ -0,0 +1,54 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Parser;
+
+use League\CommonMark\Extension\Attributes\Node\AttributesInline;
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\CommonMark\Node\StringContainerInterface;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class AttributesInlineParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::string('{');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $cursor = $inlineContext->getCursor();
+ $char = (string) $cursor->peek(-1);
+
+ $attributes = AttributesHelper::parseAttributes($cursor);
+ if ($attributes === []) {
+ return false;
+ }
+
+ if ($char === ' ' && ($prev = $inlineContext->getContainer()->lastChild()) instanceof StringContainerInterface) {
+ $prev->setLiteral(\rtrim($prev->getLiteral(), ' '));
+ }
+
+ if ($char === '') {
+ $cursor->advanceToNextNonSpaceOrNewline();
+ }
+
+ $node = new AttributesInline($attributes, $char === ' ' || $char === '');
+ $inlineContext->getContainer()->appendChild($node);
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Attributes/Util/AttributesHelper.php b/vendor/league/commonmark/src/Extension/Attributes/Util/AttributesHelper.php
new file mode 100644
index 0000000..53a8287
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Attributes/Util/AttributesHelper.php
@@ -0,0 +1,180 @@
+
+ * (c) 2015 Martin Hasoň
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Attributes\Util;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Util\RegexHelper;
+
+/**
+ * @internal
+ */
+final class AttributesHelper
+{
+ private const SINGLE_ATTRIBUTE = '\s*([.]-?[_a-z][^\s.}]*|[#][^\s}]+|' . RegexHelper::PARTIAL_ATTRIBUTENAME . RegexHelper::PARTIAL_ATTRIBUTEVALUESPEC . ')\s*';
+ private const ATTRIBUTE_LIST = '/^{:?(' . self::SINGLE_ATTRIBUTE . ')+}/i';
+
+ /**
+ * @return array
+ */
+ public static function parseAttributes(Cursor $cursor): array
+ {
+ $state = $cursor->saveState();
+ $cursor->advanceToNextNonSpaceOrNewline();
+
+ // Quick check to see if we might have attributes
+ if ($cursor->getCharacter() !== '{') {
+ $cursor->restoreState($state);
+
+ return [];
+ }
+
+ // Attempt to match the entire attribute list expression
+ // While this is less performant than checking for '{' now and '}' later, it simplifies
+ // matching individual attributes since they won't need to look ahead for the closing '}'
+ // while dealing with the fact that attributes can technically contain curly braces.
+ // So we'll just match the start and end braces up front.
+ $attributeExpression = $cursor->match(self::ATTRIBUTE_LIST);
+ if ($attributeExpression === null) {
+ $cursor->restoreState($state);
+
+ return [];
+ }
+
+ // Trim the leading '{' or '{:' and the trailing '}'
+ $attributeExpression = \ltrim(\substr($attributeExpression, 1, -1), ':');
+ $attributeCursor = new Cursor($attributeExpression);
+
+ /** @var array $attributes */
+ $attributes = [];
+ while ($attribute = \trim((string) $attributeCursor->match('/^' . self::SINGLE_ATTRIBUTE . '/i'))) {
+ if ($attribute[0] === '#') {
+ $attributes['id'] = \substr($attribute, 1);
+
+ continue;
+ }
+
+ if ($attribute[0] === '.') {
+ $attributes['class'][] = \substr($attribute, 1);
+
+ continue;
+ }
+
+ /** @psalm-suppress PossiblyUndefinedArrayOffset */
+ [$name, $value] = \explode('=', $attribute, 2);
+
+ if ($value === 'true') {
+ $attributes[$name] = true;
+ continue;
+ }
+
+ $first = $value[0];
+ $last = \substr($value, -1);
+ if (($first === '"' && $last === '"') || ($first === "'" && $last === "'") && \strlen($value) > 1) {
+ $value = \substr($value, 1, -1);
+ }
+
+ if (\strtolower(\trim($name)) === 'class') {
+ foreach (\array_filter(\explode(' ', \trim($value))) as $class) {
+ $attributes['class'][] = $class;
+ }
+ } else {
+ $attributes[\trim($name)] = \trim($value);
+ }
+ }
+
+ if (isset($attributes['class'])) {
+ $attributes['class'] = \implode(' ', (array) $attributes['class']);
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * @param Node|array $attributes1
+ * @param Node|array $attributes2
+ *
+ * @return array
+ */
+ public static function mergeAttributes($attributes1, $attributes2): array
+ {
+ $attributes = [];
+ foreach ([$attributes1, $attributes2] as $arg) {
+ if ($arg instanceof Node) {
+ $arg = $arg->data->get('attributes');
+ }
+
+ /** @var array $arg */
+ $arg = (array) $arg;
+ if (isset($arg['class'])) {
+ if (\is_string($arg['class'])) {
+ $arg['class'] = \array_filter(\explode(' ', \trim($arg['class'])));
+ }
+
+ foreach ($arg['class'] as $class) {
+ $attributes['class'][] = $class;
+ }
+
+ unset($arg['class']);
+ }
+
+ $attributes = \array_merge($attributes, $arg);
+ }
+
+ if (isset($attributes['class'])) {
+ $attributes['class'] = \implode(' ', $attributes['class']);
+ }
+
+ return $attributes;
+ }
+
+ /**
+ * @param array $attributes
+ * @param list $allowList
+ *
+ * @return array
+ */
+ public static function filterAttributes(array $attributes, array $allowList, bool $allowUnsafeLinks): array
+ {
+ $allowList = \array_fill_keys($allowList, true);
+
+ foreach ($attributes as $name => $value) {
+ $attrNameLower = \strtolower($name);
+
+ // Remove any unsafe links
+ if (! $allowUnsafeLinks && ($attrNameLower === 'href' || $attrNameLower === 'src') && \is_string($value) && RegexHelper::isLinkPotentiallyUnsafe($value)) {
+ unset($attributes[$name]);
+ continue;
+ }
+
+ // No allowlist?
+ if ($allowList === []) {
+ // Just remove JS event handlers
+ if (\str_starts_with($attrNameLower, 'on')) {
+ unset($attributes[$name]);
+ }
+
+ continue;
+ }
+
+ // Remove any attributes not in that allowlist (case-sensitive)
+ if (! isset($allowList[$name])) {
+ unset($attributes[$name]);
+ }
+ }
+
+ return $attributes;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Autolink/AutolinkExtension.php b/vendor/league/commonmark/src/Extension/Autolink/AutolinkExtension.php
new file mode 100644
index 0000000..54aafd4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Autolink/AutolinkExtension.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Autolink;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class AutolinkExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('autolink', Expect::structure([
+ 'allowed_protocols' => Expect::listOf('string')->default(['http', 'https', 'ftp'])->mergeDefaults(false),
+ 'default_protocol' => Expect::string()->default('http'),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addInlineParser(new EmailAutolinkParser());
+ $environment->addInlineParser(new UrlAutolinkParser(
+ $environment->getConfiguration()->get('autolink.allowed_protocols'),
+ $environment->getConfiguration()->get('autolink.default_protocol'),
+ ));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Autolink/EmailAutolinkParser.php b/vendor/league/commonmark/src/Extension/Autolink/EmailAutolinkParser.php
new file mode 100644
index 0000000..15a7d34
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Autolink/EmailAutolinkParser.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Autolink;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class EmailAutolinkParser implements InlineParserInterface
+{
+ private const REGEX = '[A-Za-z0-9.\-_+]+@[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_.]+';
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex(self::REGEX);
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $email = $inlineContext->getFullMatch();
+ // The last character cannot be - or _
+ if (\in_array(\substr($email, -1), ['-', '_'], true)) {
+ return false;
+ }
+
+ // Does the URL end with punctuation that should be stripped?
+ if (\substr($email, -1) === '.') {
+ $email = \substr($email, 0, -1);
+ }
+
+ $inlineContext->getCursor()->advanceBy(\strlen($email));
+ $inlineContext->getContainer()->appendChild(new Link('mailto:' . $email, $email));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Autolink/UrlAutolinkParser.php b/vendor/league/commonmark/src/Extension/Autolink/UrlAutolinkParser.php
new file mode 100644
index 0000000..f487616
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Autolink/UrlAutolinkParser.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Autolink;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class UrlAutolinkParser implements InlineParserInterface
+{
+ private const ALLOWED_AFTER = [null, ' ', "\t", "\n", "\x0b", "\x0c", "\x0d", '*', '_', '~', '('];
+
+ // RegEx adapted from https://github.com/symfony/symfony/blob/6.3/src/Symfony/Component/Validator/Constraints/UrlValidator.php
+ private const REGEX = '~
+ (
+ # Must start with a supported scheme + auth, or "www"
+ (?:
+ (?:%s):// # protocol
+ (?:(?:(?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+:)?((?:[\_\.\pL\pN-]|%%[0-9A-Fa-f]{2})+)@)? # basic auth
+ |www\.)
+ (?:
+ (?:
+ (?:xn--[a-z0-9-]++\.)*+xn--[a-z0-9-]++ # a domain name using punycode
+ |
+ (?:[\pL\pN\pS\pM\-\_]++\.){1,127}[\pL\pN\pM]++ # a multi-level domain name; total length must be 253 bytes or less
+ |
+ [a-z0-9\-\_]++ # a single-level domain name
+ )\.?
+ | # or
+ \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # an IP address
+ | # or
+ \[
+ (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
+ \] # an IPv6 address
+ )
+ (?::[0-9]+)? # a port (optional)
+ (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path
+ (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional)
+ (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional)
+ )~ixu';
+
+ /**
+ * @var string[]
+ *
+ * @psalm-readonly
+ */
+ private array $prefixes = ['www.'];
+
+ /**
+ * @psalm-var non-empty-string
+ *
+ * @psalm-readonly
+ */
+ private string $finalRegex;
+
+ private string $defaultProtocol;
+
+ /**
+ * @param array $allowedProtocols
+ */
+ public function __construct(array $allowedProtocols = ['http', 'https', 'ftp'], string $defaultProtocol = 'http')
+ {
+ /**
+ * @psalm-suppress PropertyTypeCoercion
+ */
+ $this->finalRegex = \sprintf(self::REGEX, \implode('|', $allowedProtocols));
+
+ foreach ($allowedProtocols as $protocol) {
+ $this->prefixes[] = $protocol . '://';
+ }
+
+ $this->defaultProtocol = $defaultProtocol;
+ }
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::oneOf(...$this->prefixes);
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $cursor = $inlineContext->getCursor();
+
+ // Autolinks can only come at the beginning of a line, after whitespace, or certain delimiting characters
+ $previousChar = $cursor->peek(-1);
+ if (! \in_array($previousChar, self::ALLOWED_AFTER, true)) {
+ return false;
+ }
+
+ // Check if we have a valid URL
+ if (! \preg_match($this->finalRegex, $cursor->getRemainder(), $matches)) {
+ return false;
+ }
+
+ $url = $matches[0];
+
+ // Does the URL end with punctuation that should be stripped?
+ if (\preg_match('/(.+?)([?!.,:*_~]+)$/', $url, $matches)) {
+ // Add the punctuation later
+ $url = $matches[1];
+ }
+
+ // Does the URL end with something that looks like an entity reference?
+ if (\preg_match('/(.+)(&[A-Za-z0-9]+;)$/', $url, $matches)) {
+ $url = $matches[1];
+ }
+
+ // Does the URL need unmatched parens chopped off?
+ if (\substr($url, -1) === ')' && ($diff = self::diffParens($url)) > 0) {
+ $url = \substr($url, 0, -$diff);
+ }
+
+ $cursor->advanceBy(\mb_strlen($url, 'UTF-8'));
+
+ // Auto-prefix 'http(s)://' onto 'www' URLs
+ if (\substr($url, 0, 4) === 'www.') {
+ $inlineContext->getContainer()->appendChild(new Link($this->defaultProtocol . '://' . $url, $url));
+
+ return true;
+ }
+
+ $inlineContext->getContainer()->appendChild(new Link($url, $url));
+
+ return true;
+ }
+
+ /**
+ * @psalm-pure
+ */
+ private static function diffParens(string $content): int
+ {
+ // Scan the entire autolink for the total number of parentheses.
+ // If there is a greater number of closing parentheses than opening ones,
+ // we don’t consider ANY of the last characters as part of the autolink,
+ // in order to facilitate including an autolink inside a parenthesis.
+ \preg_match_all('/[()]/', $content, $matches);
+
+ $charCount = ['(' => 0, ')' => 0];
+ foreach ($matches[0] as $char) {
+ $charCount[$char]++;
+ }
+
+ return $charCount[')'] - $charCount['('];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/CommonMarkCoreExtension.php b/vendor/league/commonmark/src/Extension/CommonMark/CommonMarkCoreExtension.php
new file mode 100644
index 0000000..91f7a22
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/CommonMarkCoreExtension.php
@@ -0,0 +1,92 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\CommonMark\Delimiter\Processor\EmphasisDelimiterProcessor;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Node as CoreNode;
+use League\CommonMark\Parser as CoreParser;
+use League\CommonMark\Renderer as CoreRenderer;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class CommonMarkCoreExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('commonmark', Expect::structure([
+ 'use_asterisk' => Expect::bool(true),
+ 'use_underscore' => Expect::bool(true),
+ 'enable_strong' => Expect::bool(true),
+ 'enable_em' => Expect::bool(true),
+ 'unordered_list_markers' => Expect::listOf('string')->min(1)->default(['*', '+', '-'])->mergeDefaults(false),
+ ]));
+ }
+
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma,Squiz.WhiteSpace.SemicolonSpacing.Incorrect
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment
+ ->addBlockStartParser(new Parser\Block\BlockQuoteStartParser(), 70)
+ ->addBlockStartParser(new Parser\Block\HeadingStartParser(), 60)
+ ->addBlockStartParser(new Parser\Block\FencedCodeStartParser(), 50)
+ ->addBlockStartParser(new Parser\Block\HtmlBlockStartParser(), 40)
+ ->addBlockStartParser(new Parser\Block\ThematicBreakStartParser(), 20)
+ ->addBlockStartParser(new Parser\Block\ListBlockStartParser(), 10)
+ ->addBlockStartParser(new Parser\Block\IndentedCodeStartParser(), -100)
+
+ ->addInlineParser(new CoreParser\Inline\NewlineParser(), 200)
+ ->addInlineParser(new Parser\Inline\BacktickParser(), 150)
+ ->addInlineParser(new Parser\Inline\EscapableParser(), 80)
+ ->addInlineParser(new Parser\Inline\EntityParser(), 70)
+ ->addInlineParser(new Parser\Inline\AutolinkParser(), 50)
+ ->addInlineParser(new Parser\Inline\HtmlInlineParser(), 40)
+ ->addInlineParser(new Parser\Inline\CloseBracketParser(), 30)
+ ->addInlineParser(new Parser\Inline\OpenBracketParser(), 20)
+ ->addInlineParser(new Parser\Inline\BangParser(), 10)
+
+ ->addRenderer(Node\Block\BlockQuote::class, new Renderer\Block\BlockQuoteRenderer(), 0)
+ ->addRenderer(CoreNode\Block\Document::class, new CoreRenderer\Block\DocumentRenderer(), 0)
+ ->addRenderer(Node\Block\FencedCode::class, new Renderer\Block\FencedCodeRenderer(), 0)
+ ->addRenderer(Node\Block\Heading::class, new Renderer\Block\HeadingRenderer(), 0)
+ ->addRenderer(Node\Block\HtmlBlock::class, new Renderer\Block\HtmlBlockRenderer(), 0)
+ ->addRenderer(Node\Block\IndentedCode::class, new Renderer\Block\IndentedCodeRenderer(), 0)
+ ->addRenderer(Node\Block\ListBlock::class, new Renderer\Block\ListBlockRenderer(), 0)
+ ->addRenderer(Node\Block\ListItem::class, new Renderer\Block\ListItemRenderer(), 0)
+ ->addRenderer(CoreNode\Block\Paragraph::class, new CoreRenderer\Block\ParagraphRenderer(), 0)
+ ->addRenderer(Node\Block\ThematicBreak::class, new Renderer\Block\ThematicBreakRenderer(), 0)
+
+ ->addRenderer(Node\Inline\Code::class, new Renderer\Inline\CodeRenderer(), 0)
+ ->addRenderer(Node\Inline\Emphasis::class, new Renderer\Inline\EmphasisRenderer(), 0)
+ ->addRenderer(Node\Inline\HtmlInline::class, new Renderer\Inline\HtmlInlineRenderer(), 0)
+ ->addRenderer(Node\Inline\Image::class, new Renderer\Inline\ImageRenderer(), 0)
+ ->addRenderer(Node\Inline\Link::class, new Renderer\Inline\LinkRenderer(), 0)
+ ->addRenderer(CoreNode\Inline\Newline::class, new CoreRenderer\Inline\NewlineRenderer(), 0)
+ ->addRenderer(Node\Inline\Strong::class, new Renderer\Inline\StrongRenderer(), 0)
+ ->addRenderer(CoreNode\Inline\Text::class, new CoreRenderer\Inline\TextRenderer(), 0)
+ ;
+
+ if ($environment->getConfiguration()->get('commonmark/use_asterisk')) {
+ $environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('*'));
+ }
+
+ if ($environment->getConfiguration()->get('commonmark/use_underscore')) {
+ $environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('_'));
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Delimiter/Processor/EmphasisDelimiterProcessor.php b/vendor/league/commonmark/src/Extension/CommonMark/Delimiter/Processor/EmphasisDelimiterProcessor.php
new file mode 100644
index 0000000..9a6be13
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Delimiter/Processor/EmphasisDelimiterProcessor.php
@@ -0,0 +1,119 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Delimiter\Processor;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+use League\CommonMark\Delimiter\Processor\CacheableDelimiterProcessorInterface;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Strong;
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class EmphasisDelimiterProcessor implements CacheableDelimiterProcessorInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly */
+ private string $char;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ /**
+ * @param string $char The emphasis character to use (typically '*' or '_')
+ */
+ public function __construct(string $char)
+ {
+ $this->char = $char;
+ }
+
+ public function getOpeningCharacter(): string
+ {
+ return $this->char;
+ }
+
+ public function getClosingCharacter(): string
+ {
+ return $this->char;
+ }
+
+ public function getMinLength(): int
+ {
+ return 1;
+ }
+
+ public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
+ {
+ // "Multiple of 3" rule for internal delimiter runs
+ if (($opener->canClose() || $closer->canOpen()) && $closer->getOriginalLength() % 3 !== 0 && ($opener->getOriginalLength() + $closer->getOriginalLength()) % 3 === 0) {
+ return 0;
+ }
+
+ // Calculate actual number of delimiters used from this closer
+ if ($opener->getLength() >= 2 && $closer->getLength() >= 2) {
+ if ($this->config->get('commonmark/enable_strong')) {
+ return 2;
+ }
+
+ return 0;
+ }
+
+ if ($this->config->get('commonmark/enable_em')) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
+ {
+ if ($delimiterUse === 1) {
+ $emphasis = new Emphasis($this->char);
+ } elseif ($delimiterUse === 2) {
+ $emphasis = new Strong($this->char . $this->char);
+ } else {
+ return;
+ }
+
+ $next = $opener->next();
+ while ($next !== null && $next !== $closer) {
+ $tmp = $next->next();
+ $emphasis->appendChild($next);
+ $next = $tmp;
+ }
+
+ $opener->insertAfter($emphasis);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getCacheKey(DelimiterInterface $closer): string
+ {
+ return \sprintf(
+ '%s-%s-%d-%d',
+ $this->char,
+ $closer->canOpen() ? 'canOpen' : 'cannotOpen',
+ $closer->getOriginalLength() % 3,
+ $closer->getLength(),
+ );
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/BlockQuote.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/BlockQuote.php
new file mode 100644
index 0000000..11094b9
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/BlockQuote.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+class BlockQuote extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/FencedCode.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/FencedCode.php
new file mode 100644
index 0000000..b50b407
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/FencedCode.php
@@ -0,0 +1,100 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\StringContainerInterface;
+
+final class FencedCode extends AbstractBlock implements StringContainerInterface
+{
+ private ?string $info = null;
+
+ private string $literal = '';
+
+ private int $length;
+
+ private string $char;
+
+ private int $offset;
+
+ public function __construct(int $length, string $char, int $offset)
+ {
+ parent::__construct();
+
+ $this->length = $length;
+ $this->char = $char;
+ $this->offset = $offset;
+ }
+
+ public function getInfo(): ?string
+ {
+ return $this->info;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getInfoWords(): array
+ {
+ return \preg_split('/\s+/', $this->info ?? '') ?: [];
+ }
+
+ public function setInfo(string $info): void
+ {
+ $this->info = $info;
+ }
+
+ public function getLiteral(): string
+ {
+ return $this->literal;
+ }
+
+ public function setLiteral(string $literal): void
+ {
+ $this->literal = $literal;
+ }
+
+ public function getChar(): string
+ {
+ return $this->char;
+ }
+
+ public function setChar(string $char): void
+ {
+ $this->char = $char;
+ }
+
+ public function getLength(): int
+ {
+ return $this->length;
+ }
+
+ public function setLength(int $length): void
+ {
+ $this->length = $length;
+ }
+
+ public function getOffset(): int
+ {
+ return $this->offset;
+ }
+
+ public function setOffset(int $offset): void
+ {
+ $this->offset = $offset;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/Heading.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/Heading.php
new file mode 100644
index 0000000..1cf1184
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/Heading.php
@@ -0,0 +1,41 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class Heading extends AbstractBlock
+{
+ private int $level;
+
+ public function __construct(int $level)
+ {
+ parent::__construct();
+
+ $this->level = $level;
+ }
+
+ public function getLevel(): int
+ {
+ return $this->level;
+ }
+
+ public function setLevel(int $level): void
+ {
+ $this->level = $level;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/HtmlBlock.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/HtmlBlock.php
new file mode 100644
index 0000000..9879a89
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/HtmlBlock.php
@@ -0,0 +1,79 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\RawMarkupContainerInterface;
+
+final class HtmlBlock extends AbstractBlock implements RawMarkupContainerInterface
+{
+ // Any changes to these constants should be reflected in .phpstorm.meta.php
+ public const TYPE_1_CODE_CONTAINER = 1;
+ public const TYPE_2_COMMENT = 2;
+ public const TYPE_3 = 3;
+ public const TYPE_4 = 4;
+ public const TYPE_5_CDATA = 5;
+ public const TYPE_6_BLOCK_ELEMENT = 6;
+ public const TYPE_7_MISC_ELEMENT = 7;
+
+ /**
+ * @psalm-var self::TYPE_* $type
+ * @phpstan-var self::TYPE_* $type
+ */
+ private int $type;
+
+ private string $literal = '';
+
+ /**
+ * @psalm-param self::TYPE_* $type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function __construct(int $type)
+ {
+ parent::__construct();
+
+ $this->type = $type;
+ }
+
+ /**
+ * @psalm-return self::TYPE_*
+ *
+ * @phpstan-return self::TYPE_*
+ */
+ public function getType(): int
+ {
+ return $this->type;
+ }
+
+ /**
+ * @psalm-param self::TYPE_* $type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function setType(int $type): void
+ {
+ $this->type = $type;
+ }
+
+ public function getLiteral(): string
+ {
+ return $this->literal;
+ }
+
+ public function setLiteral(string $literal): void
+ {
+ $this->literal = $literal;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/IndentedCode.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/IndentedCode.php
new file mode 100644
index 0000000..d18be15
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/IndentedCode.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\StringContainerInterface;
+
+final class IndentedCode extends AbstractBlock implements StringContainerInterface
+{
+ private string $literal = '';
+
+ public function getLiteral(): string
+ {
+ return $this->literal;
+ }
+
+ public function setLiteral(string $literal): void
+ {
+ $this->literal = $literal;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListBlock.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListBlock.php
new file mode 100644
index 0000000..504a38a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListBlock.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Block\TightBlockInterface;
+
+class ListBlock extends AbstractBlock implements TightBlockInterface
+{
+ public const TYPE_BULLET = 'bullet';
+ public const TYPE_ORDERED = 'ordered';
+
+ public const DELIM_PERIOD = 'period';
+ public const DELIM_PAREN = 'paren';
+
+ protected bool $tight = false; // TODO Make lists tight by default in v3
+
+ /** @psalm-readonly */
+ protected ListData $listData;
+
+ public function __construct(ListData $listData)
+ {
+ parent::__construct();
+
+ $this->listData = $listData;
+ }
+
+ public function getListData(): ListData
+ {
+ return $this->listData;
+ }
+
+ public function isTight(): bool
+ {
+ return $this->tight;
+ }
+
+ public function setTight(bool $tight): void
+ {
+ $this->tight = $tight;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListData.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListData.php
new file mode 100644
index 0000000..7108a93
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListData.php
@@ -0,0 +1,47 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+class ListData
+{
+ public ?int $start = null;
+
+ public int $padding = 0;
+
+ /**
+ * @psalm-var ListBlock::TYPE_*
+ * @phpstan-var ListBlock::TYPE_*
+ */
+ public string $type;
+
+ /**
+ * @psalm-var ListBlock::DELIM_*|null
+ * @phpstan-var ListBlock::DELIM_*|null
+ */
+ public ?string $delimiter = null;
+
+ public ?string $bulletChar = null;
+
+ public int $markerOffset;
+
+ public function equals(ListData $data): bool
+ {
+ return $this->type === $data->type &&
+ $this->delimiter === $data->delimiter &&
+ $this->bulletChar === $data->bulletChar;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListItem.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListItem.php
new file mode 100644
index 0000000..f136b7e
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ListItem.php
@@ -0,0 +1,37 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+class ListItem extends AbstractBlock
+{
+ /** @psalm-readonly */
+ protected ListData $listData;
+
+ public function __construct(ListData $listData)
+ {
+ parent::__construct();
+
+ $this->listData = $listData;
+ }
+
+ public function getListData(): ListData
+ {
+ return $this->listData;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ThematicBreak.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ThematicBreak.php
new file mode 100644
index 0000000..bb6cea0
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Block/ThematicBreak.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+class ThematicBreak extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/AbstractWebResource.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/AbstractWebResource.php
new file mode 100644
index 0000000..dc0ed0a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/AbstractWebResource.php
@@ -0,0 +1,41 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+
+abstract class AbstractWebResource extends AbstractInline
+{
+ protected string $url;
+
+ public function __construct(string $url)
+ {
+ parent::__construct();
+
+ $this->url = $url;
+ }
+
+ public function getUrl(): string
+ {
+ return $this->url;
+ }
+
+ public function setUrl(string $url): void
+ {
+ $this->url = $url;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Code.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Code.php
new file mode 100644
index 0000000..3a6aca2
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Code.php
@@ -0,0 +1,23 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+class Code extends AbstractStringContainer
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Emphasis.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Emphasis.php
new file mode 100644
index 0000000..fab6869
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Emphasis.php
@@ -0,0 +1,42 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Node\Inline\DelimitedInterface;
+
+final class Emphasis extends AbstractInline implements DelimitedInterface
+{
+ private string $delimiter;
+
+ public function __construct(string $delimiter = '_')
+ {
+ parent::__construct();
+
+ $this->delimiter = $delimiter;
+ }
+
+ public function getOpeningDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+
+ public function getClosingDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/HtmlInline.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/HtmlInline.php
new file mode 100644
index 0000000..8594a06
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/HtmlInline.php
@@ -0,0 +1,24 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+use League\CommonMark\Node\RawMarkupContainerInterface;
+
+final class HtmlInline extends AbstractStringContainer implements RawMarkupContainerInterface
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Image.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Image.php
new file mode 100644
index 0000000..20e3f87
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Image.php
@@ -0,0 +1,49 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+
+class Image extends AbstractWebResource
+{
+ protected ?string $title = null;
+
+ public function __construct(string $url, ?string $label = null, ?string $title = null)
+ {
+ parent::__construct($url);
+
+ if ($label !== null && $label !== '') {
+ $this->appendChild(new Text($label));
+ }
+
+ $this->title = $title;
+ }
+
+ public function getTitle(): ?string
+ {
+ if ($this->title === '') {
+ return null;
+ }
+
+ return $this->title;
+ }
+
+ public function setTitle(?string $title): void
+ {
+ $this->title = $title;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Link.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Link.php
new file mode 100644
index 0000000..76d5609
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Link.php
@@ -0,0 +1,49 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+
+class Link extends AbstractWebResource
+{
+ protected ?string $title = null;
+
+ public function __construct(string $url, ?string $label = null, ?string $title = null)
+ {
+ parent::__construct($url);
+
+ if ($label !== null && $label !== '') {
+ $this->appendChild(new Text($label));
+ }
+
+ $this->title = $title;
+ }
+
+ public function getTitle(): ?string
+ {
+ if ($this->title === '') {
+ return null;
+ }
+
+ return $this->title;
+ }
+
+ public function setTitle(?string $title): void
+ {
+ $this->title = $title;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Strong.php b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Strong.php
new file mode 100644
index 0000000..827960f
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Node/Inline/Strong.php
@@ -0,0 +1,42 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Node\Inline\DelimitedInterface;
+
+final class Strong extends AbstractInline implements DelimitedInterface
+{
+ private string $delimiter;
+
+ public function __construct(string $delimiter = '**')
+ {
+ parent::__construct();
+
+ $this->delimiter = $delimiter;
+ }
+
+ public function getOpeningDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+
+ public function getClosingDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteParser.php
new file mode 100644
index 0000000..78db6c5
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteParser.php
@@ -0,0 +1,60 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class BlockQuoteParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private BlockQuote $block;
+
+ public function __construct()
+ {
+ $this->block = new BlockQuote();
+ }
+
+ public function getBlock(): BlockQuote
+ {
+ return $this->block;
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return true;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === '>') {
+ $cursor->advanceToNextNonSpaceOrTab();
+ $cursor->advanceBy(1);
+ $cursor->advanceBySpaceOrTab();
+
+ return BlockContinue::at($cursor);
+ }
+
+ return BlockContinue::none();
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteStartParser.php
new file mode 100644
index 0000000..de9a6bc
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/BlockQuoteStartParser.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class BlockQuoteStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented()) {
+ return BlockStart::none();
+ }
+
+ if ($cursor->getNextNonSpaceCharacter() !== '>') {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+ $cursor->advanceBy(1);
+ $cursor->advanceBySpaceOrTab();
+
+ return BlockStart::of(new BlockQuoteParser())->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeParser.php
new file mode 100644
index 0000000..96a5baa
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeParser.php
@@ -0,0 +1,84 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Util\ArrayCollection;
+use League\CommonMark\Util\RegexHelper;
+
+final class FencedCodeParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private FencedCode $block;
+
+ /** @var ArrayCollection */
+ private ArrayCollection $strings;
+
+ public function __construct(int $fenceLength, string $fenceChar, int $fenceOffset)
+ {
+ $this->block = new FencedCode($fenceLength, $fenceChar, $fenceOffset);
+ $this->strings = new ArrayCollection();
+ }
+
+ public function getBlock(): FencedCode
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ // Check for closing code fence
+ if (! $cursor->isIndented() && $cursor->getNextNonSpaceCharacter() === $this->block->getChar()) {
+ $match = RegexHelper::matchFirst('/^(?:`{3,}|~{3,})(?=[ \t]*$)/', $cursor->getLine(), $cursor->getNextNonSpacePosition());
+ if ($match !== null && \strlen($match[0]) >= $this->block->getLength()) {
+ // closing fence - we're at end of line, so we can finalize now
+ return BlockContinue::finished();
+ }
+ }
+
+ // Skip optional spaces of fence offset
+ // Optimization: don't attempt to match if we're at a non-space position
+ if ($cursor->getNextNonSpacePosition() > $cursor->getPosition()) {
+ $cursor->match('/^ {0,' . $this->block->getOffset() . '}/');
+ }
+
+ return BlockContinue::at($cursor);
+ }
+
+ public function addLine(string $line): void
+ {
+ $this->strings[] = $line;
+ }
+
+ public function closeBlock(): void
+ {
+ // first line becomes info string
+ $firstLine = $this->strings->first();
+ if ($firstLine === false) {
+ $firstLine = '';
+ }
+
+ $this->block->setInfo(RegexHelper::unescape(\trim($firstLine)));
+
+ if ($this->strings->count() === 1) {
+ $this->block->setLiteral('');
+ } else {
+ $this->block->setLiteral(\implode("\n", $this->strings->slice(1)) . "\n");
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeStartParser.php
new file mode 100644
index 0000000..be1b1dc
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/FencedCodeStartParser.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class FencedCodeStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented() || ! \in_array($cursor->getNextNonSpaceCharacter(), ['`', '~'], true)) {
+ return BlockStart::none();
+ }
+
+ $indent = $cursor->getIndent();
+ $fence = $cursor->match('/^[ \t]*(?:`{3,}(?!.*`)|~{3,})/');
+ if ($fence === null) {
+ return BlockStart::none();
+ }
+
+ // fenced code block
+ $fence = \ltrim($fence, " \t");
+
+ return BlockStart::of(new FencedCodeParser(\strlen($fence), $fence[0], $indent))->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingParser.php
new file mode 100644
index 0000000..c3e3108
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingParser.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\InlineParserEngineInterface;
+
+final class HeadingParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
+{
+ /** @psalm-readonly */
+ private Heading $block;
+
+ private string $content;
+
+ public function __construct(int $level, string $content)
+ {
+ $this->block = new Heading($level);
+ $this->content = $content;
+ }
+
+ public function getBlock(): Heading
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::none();
+ }
+
+ public function parseInlines(InlineParserEngineInterface $inlineParser): void
+ {
+ $inlineParser->parse($this->content, $this->block);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingStartParser.php
new file mode 100644
index 0000000..404f403
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HeadingStartParser.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\RegexHelper;
+
+class HeadingStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented() || ! \in_array($cursor->getNextNonSpaceCharacter(), ['#', '-', '='], true)) {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ if ($atxHeading = self::getAtxHeader($cursor)) {
+ return BlockStart::of($atxHeading)->at($cursor);
+ }
+
+ $setextHeadingLevel = self::getSetextHeadingLevel($cursor);
+ if ($setextHeadingLevel > 0) {
+ $content = $parserState->getParagraphContent();
+ if ($content !== null) {
+ $cursor->advanceToEnd();
+
+ return BlockStart::of(new HeadingParser($setextHeadingLevel, $content))
+ ->at($cursor)
+ ->replaceActiveBlockParser();
+ }
+ }
+
+ return BlockStart::none();
+ }
+
+ private static function getAtxHeader(Cursor $cursor): ?HeadingParser
+ {
+ $match = RegexHelper::matchFirst('/^#{1,6}(?:[ \t]+|$)/', $cursor->getRemainder());
+ if (! $match) {
+ return null;
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+ $cursor->advanceBy(\strlen($match[0]));
+
+ $level = \strlen(\trim($match[0]));
+ $str = $cursor->getRemainder();
+ $str = \preg_replace('/^[ \t]*#+[ \t]*$/', '', $str);
+ \assert(\is_string($str));
+ $str = \preg_replace('/[ \t]+#+[ \t]*$/', '', $str);
+ \assert(\is_string($str));
+
+ return new HeadingParser($level, $str);
+ }
+
+ private static function getSetextHeadingLevel(Cursor $cursor): int
+ {
+ $match = RegexHelper::matchFirst('/^(?:=+|-+)[ \t]*$/', $cursor->getRemainder());
+ if ($match === null) {
+ return 0;
+ }
+
+ return $match[0][0] === '=' ? 1 : 2;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockParser.php
new file mode 100644
index 0000000..6778676
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockParser.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Util\RegexHelper;
+
+final class HtmlBlockParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private HtmlBlock $block;
+
+ private string $content = '';
+
+ private bool $finished = false;
+
+ /**
+ * @psalm-param HtmlBlock::TYPE_* $blockType
+ *
+ * @phpstan-param HtmlBlock::TYPE_* $blockType
+ */
+ public function __construct(int $blockType)
+ {
+ $this->block = new HtmlBlock($blockType);
+ }
+
+ public function getBlock(): HtmlBlock
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($this->finished) {
+ return BlockContinue::none();
+ }
+
+ if ($cursor->isBlank() && \in_array($this->block->getType(), [HtmlBlock::TYPE_6_BLOCK_ELEMENT, HtmlBlock::TYPE_7_MISC_ELEMENT], true)) {
+ return BlockContinue::none();
+ }
+
+ return BlockContinue::at($cursor);
+ }
+
+ public function addLine(string $line): void
+ {
+ if ($this->content !== '') {
+ $this->content .= "\n";
+ }
+
+ $this->content .= $line;
+
+ // Check for end condition
+ // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
+ if ($this->block->getType() <= HtmlBlock::TYPE_5_CDATA) {
+ if (\preg_match(RegexHelper::getHtmlBlockCloseRegex($this->block->getType()), $line) === 1) {
+ $this->finished = true;
+ }
+ }
+ }
+
+ public function closeBlock(): void
+ {
+ $this->block->setLiteral($this->content);
+ $this->content = '';
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockStartParser.php
new file mode 100644
index 0000000..bcef0af
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/HtmlBlockStartParser.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\RegexHelper;
+
+final class HtmlBlockStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented() || $cursor->getNextNonSpaceCharacter() !== '<') {
+ return BlockStart::none();
+ }
+
+ $tmpCursor = clone $cursor;
+ $tmpCursor->advanceToNextNonSpaceOrTab();
+ $line = $tmpCursor->getRemainder();
+
+ for ($blockType = 1; $blockType <= 7; $blockType++) {
+ /** @psalm-var HtmlBlock::TYPE_* $blockType */
+ /** @phpstan-var HtmlBlock::TYPE_* $blockType */
+ $match = RegexHelper::matchAt(
+ RegexHelper::getHtmlBlockOpenRegex($blockType),
+ $line
+ );
+
+ if ($match !== null && ($blockType < 7 || $this->isType7BlockAllowed($cursor, $parserState))) {
+ return BlockStart::of(new HtmlBlockParser($blockType))->at($cursor);
+ }
+ }
+
+ return BlockStart::none();
+ }
+
+ private function isType7BlockAllowed(Cursor $cursor, MarkdownParserStateInterface $parserState): bool
+ {
+ // Type 7 blocks can't interrupt paragraphs
+ if ($parserState->getLastMatchedBlockParser()->getBlock() instanceof Paragraph) {
+ return false;
+ }
+
+ // Even lazy ones
+ return ! $parserState->getActiveBlockParser()->canHaveLazyContinuationLines();
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeParser.php
new file mode 100644
index 0000000..ac6406f
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeParser.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Util\ArrayCollection;
+
+final class IndentedCodeParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private IndentedCode $block;
+
+ /** @var ArrayCollection */
+ private ArrayCollection $strings;
+
+ public function __construct()
+ {
+ $this->block = new IndentedCode();
+ $this->strings = new ArrayCollection();
+ }
+
+ public function getBlock(): IndentedCode
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($cursor->isIndented()) {
+ $cursor->advanceBy(Cursor::INDENT_LEVEL, true);
+
+ return BlockContinue::at($cursor);
+ }
+
+ if ($cursor->isBlank()) {
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ return BlockContinue::at($cursor);
+ }
+
+ return BlockContinue::none();
+ }
+
+ public function addLine(string $line): void
+ {
+ $this->strings[] = $line;
+ }
+
+ public function closeBlock(): void
+ {
+ $lines = $this->strings->toArray();
+
+ // Note that indented code block cannot be empty, so $lines will always have at least one non-empty element
+ while (\preg_match('/^[ \t]*$/', \end($lines))) { // @phpstan-ignore-line
+ \array_pop($lines);
+ }
+
+ $this->block->setLiteral(\implode("\n", $lines) . "\n");
+ $this->block->setEndLine($this->block->getStartLine() + \count($lines) - 1);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeStartParser.php
new file mode 100644
index 0000000..bea4bde
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/IndentedCodeStartParser.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class IndentedCodeStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if (! $cursor->isIndented()) {
+ return BlockStart::none();
+ }
+
+ if ($parserState->getActiveBlockParser()->getBlock() instanceof Paragraph) {
+ return BlockStart::none();
+ }
+
+ if ($cursor->isBlank()) {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceBy(Cursor::INDENT_LEVEL, true);
+
+ return BlockStart::of(new IndentedCodeParser())->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockParser.php
new file mode 100644
index 0000000..5a7ee45
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockParser.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class ListBlockParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private ListBlock $block;
+
+ public function __construct(ListData $listData)
+ {
+ $this->block = new ListBlock($listData);
+ }
+
+ public function getBlock(): ListBlock
+ {
+ return $this->block;
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return $childBlock instanceof ListItem;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ // List blocks themselves don't have any markers, only list items. So try to stay in the list.
+ // If there is a block start other than list item, canContain makes sure that this list is closed.
+ return BlockContinue::at($cursor);
+ }
+
+ public function closeBlock(): void
+ {
+ $item = $this->block->firstChild();
+ while ($item instanceof AbstractBlock) {
+ // check for non-final list item ending with blank line:
+ if ($item->next() !== null && self::endsWithBlankLine($item)) {
+ $this->block->setTight(false);
+ break;
+ }
+
+ // recurse into children of list item, to see if there are spaces between any of them
+ $subitem = $item->firstChild();
+ while ($subitem instanceof AbstractBlock) {
+ if ($subitem->next() && self::endsWithBlankLine($subitem)) {
+ $this->block->setTight(false);
+ break 2;
+ }
+
+ $subitem = $subitem->next();
+ }
+
+ $item = $item->next();
+ }
+
+ $lastChild = $this->block->lastChild();
+ if ($lastChild instanceof AbstractBlock) {
+ $this->block->setEndLine($lastChild->getEndLine());
+ }
+ }
+
+ private static function endsWithBlankLine(AbstractBlock $block): bool
+ {
+ $next = $block->next();
+
+ return $next instanceof AbstractBlock && $block->getEndLine() !== $next->getStartLine() - 1;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockStartParser.php
new file mode 100644
index 0000000..a55f6f9
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListBlockStartParser.php
@@ -0,0 +1,154 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\RegexHelper;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class ListBlockStartParser implements BlockStartParserInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ?ConfigurationInterface $config = null;
+
+ /**
+ * @psalm-var non-empty-string|null
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private ?string $listMarkerRegex = null;
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented()) {
+ return BlockStart::none();
+ }
+
+ $listData = $this->parseList($cursor, $parserState->getParagraphContent() !== null);
+ if ($listData === null) {
+ return BlockStart::none();
+ }
+
+ $listItemParser = new ListItemParser($listData);
+
+ // prepend the list block if needed
+ $matched = $parserState->getLastMatchedBlockParser();
+ if (! ($matched instanceof ListBlockParser) || ! $listData->equals($matched->getBlock()->getListData())) {
+ $listBlockParser = new ListBlockParser($listData);
+ // We start out with assuming a list is tight. If we find a blank line, we set it to loose later.
+ // TODO for 3.0: Just make them tight by default in the block so we can remove this call
+ $listBlockParser->getBlock()->setTight(true);
+
+ return BlockStart::of($listBlockParser, $listItemParser)->at($cursor);
+ }
+
+ return BlockStart::of($listItemParser)->at($cursor);
+ }
+
+ private function parseList(Cursor $cursor, bool $inParagraph): ?ListData
+ {
+ $indent = $cursor->getIndent();
+
+ $tmpCursor = clone $cursor;
+ $tmpCursor->advanceToNextNonSpaceOrTab();
+ $rest = $tmpCursor->getRemainder();
+
+ if (\preg_match($this->listMarkerRegex ?? $this->generateListMarkerRegex(), $rest) === 1) {
+ $data = new ListData();
+ $data->markerOffset = $indent;
+ $data->type = ListBlock::TYPE_BULLET;
+ $data->delimiter = null;
+ $data->bulletChar = $rest[0];
+ $markerLength = 1;
+ } elseif (($matches = RegexHelper::matchFirst('/^(\d{1,9})([.)])/', $rest)) && (! $inParagraph || $matches[1] === '1')) {
+ $data = new ListData();
+ $data->markerOffset = $indent;
+ $data->type = ListBlock::TYPE_ORDERED;
+ $data->start = (int) $matches[1];
+ $data->delimiter = $matches[2] === '.' ? ListBlock::DELIM_PERIOD : ListBlock::DELIM_PAREN;
+ $data->bulletChar = null;
+ $markerLength = \strlen($matches[0]);
+ } else {
+ return null;
+ }
+
+ // Make sure we have spaces after
+ $nextChar = $tmpCursor->peek($markerLength);
+ if (! ($nextChar === null || $nextChar === "\t" || $nextChar === ' ')) {
+ return null;
+ }
+
+ // If it interrupts paragraph, make sure first line isn't blank
+ if ($inParagraph && ! RegexHelper::matchAt(RegexHelper::REGEX_NON_SPACE, $rest, $markerLength)) {
+ return null;
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab(); // to start of marker
+ $cursor->advanceBy($markerLength, true); // to end of marker
+ $data->padding = self::calculateListMarkerPadding($cursor, $markerLength);
+
+ return $data;
+ }
+
+ private static function calculateListMarkerPadding(Cursor $cursor, int $markerLength): int
+ {
+ $start = $cursor->saveState();
+ $spacesStartCol = $cursor->getColumn();
+
+ while ($cursor->getColumn() - $spacesStartCol < 5) {
+ if (! $cursor->advanceBySpaceOrTab()) {
+ break;
+ }
+ }
+
+ $blankItem = $cursor->peek() === null;
+ $spacesAfterMarker = $cursor->getColumn() - $spacesStartCol;
+
+ if ($spacesAfterMarker >= 5 || $spacesAfterMarker < 1 || $blankItem) {
+ $cursor->restoreState($start);
+ $cursor->advanceBySpaceOrTab();
+
+ return $markerLength + 1;
+ }
+
+ return $markerLength + $spacesAfterMarker;
+ }
+
+ /**
+ * @psalm-return non-empty-string
+ */
+ private function generateListMarkerRegex(): string
+ {
+ // No configuration given - use the defaults
+ if ($this->config === null) {
+ return $this->listMarkerRegex = '/^[*+-]/';
+ }
+
+ $markers = $this->config->get('commonmark/unordered_list_markers');
+ \assert(\is_array($markers));
+
+ return $this->listMarkerRegex = '/^[' . \preg_quote(\implode('', $markers), '/') . ']/';
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListItemParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListItemParser.php
new file mode 100644
index 0000000..739eefc
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ListItemParser.php
@@ -0,0 +1,82 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class ListItemParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private ListItem $block;
+
+ public function __construct(ListData $listData)
+ {
+ $this->block = new ListItem($listData);
+ }
+
+ public function getBlock(): ListItem
+ {
+ return $this->block;
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return ! $childBlock instanceof ListItem;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($cursor->isBlank()) {
+ if ($this->block->firstChild() === null) {
+ // Blank line after empty list item
+ return BlockContinue::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ return BlockContinue::at($cursor);
+ }
+
+ $contentIndent = $this->block->getListData()->markerOffset + $this->getBlock()->getListData()->padding;
+ if ($cursor->getIndent() >= $contentIndent) {
+ $cursor->advanceBy($contentIndent, true);
+
+ return BlockContinue::at($cursor);
+ }
+
+ // Note: We'll hit this case for lazy continuation lines, they will get added later.
+ return BlockContinue::none();
+ }
+
+ public function closeBlock(): void
+ {
+ if (($lastChild = $this->block->lastChild()) instanceof AbstractBlock) {
+ $this->block->setEndLine($lastChild->getEndLine());
+ } else {
+ // Empty list item
+ $this->block->setEndLine($this->block->getStartLine());
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakParser.php
new file mode 100644
index 0000000..fb46637
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakParser.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class ThematicBreakParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private ThematicBreak $block;
+
+ public function __construct()
+ {
+ $this->block = new ThematicBreak();
+ }
+
+ public function getBlock(): ThematicBreak
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ // a horizontal rule can never container > 1 line, so fail to match
+ return BlockContinue::none();
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakStartParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakStartParser.php
new file mode 100644
index 0000000..ba7ddf3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Block/ThematicBreakStartParser.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\RegexHelper;
+
+final class ThematicBreakStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented()) {
+ return BlockStart::none();
+ }
+
+ $match = RegexHelper::matchAt(RegexHelper::REGEX_THEMATIC_BREAK, $cursor->getLine(), $cursor->getNextNonSpacePosition());
+ if ($match === null) {
+ return BlockStart::none();
+ }
+
+ // Advance to the end of the string, consuming the entire line (of the thematic break)
+ $cursor->advanceToEnd();
+
+ return BlockStart::of(new ThematicBreakParser())->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/AutolinkParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/AutolinkParser.php
new file mode 100644
index 0000000..810769d
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/AutolinkParser.php
@@ -0,0 +1,54 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Util\UrlEncoder;
+
+final class AutolinkParser implements InlineParserInterface
+{
+ private const EMAIL_REGEX = '<([a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>';
+ private const OTHER_LINK_REGEX = '<([A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]*)>';
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex(self::EMAIL_REGEX . '|' . self::OTHER_LINK_REGEX);
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+ $matches = $inlineContext->getMatches();
+
+ if ($matches[1] !== '') {
+ $inlineContext->getContainer()->appendChild(new Link('mailto:' . UrlEncoder::unescapeAndEncode($matches[1]), $matches[1]));
+
+ return true;
+ }
+
+ if ($matches[2] !== '') {
+ $inlineContext->getContainer()->appendChild(new Link(UrlEncoder::unescapeAndEncode($matches[2]), $matches[2]));
+
+ return true;
+ }
+
+ return false; // This should never happen
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BacktickParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BacktickParser.php
new file mode 100644
index 0000000..3324fe3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BacktickParser.php
@@ -0,0 +1,132 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Code;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class BacktickParser implements InlineParserInterface
+{
+ /**
+ * Max bound for backtick code span delimiters.
+ *
+ * @see https://github.com/commonmark/cmark/commit/8ed5c9d
+ */
+ private const MAX_BACKTICKS = 1000;
+
+ /** @var \WeakReference|null */
+ private ?\WeakReference $lastCursor = null;
+ private bool $lastCursorScanned = false;
+
+ /** @var array backtick count => position of known ender */
+ private array $seenBackticks = [];
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex('`+');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $ticks = $inlineContext->getFullMatch();
+ $cursor = $inlineContext->getCursor();
+ $cursor->advanceBy($inlineContext->getFullMatchLength());
+
+ $currentPosition = $cursor->getPosition();
+ $previousState = $cursor->saveState();
+
+ if ($this->findMatchingTicks(\strlen($ticks), $cursor)) {
+ $code = $cursor->getSubstring($currentPosition, $cursor->getPosition() - $currentPosition - \strlen($ticks));
+
+ $c = \preg_replace('/\n/m', ' ', $code) ?? '';
+
+ if (
+ $c !== '' &&
+ $c[0] === ' ' &&
+ \substr($c, -1, 1) === ' ' &&
+ \preg_match('/[^ ]/', $c)
+ ) {
+ $c = \substr($c, 1, -1);
+ }
+
+ $inlineContext->getContainer()->appendChild(new Code($c));
+
+ return true;
+ }
+
+ // If we got here, we didn't match a closing backtick sequence
+ $cursor->restoreState($previousState);
+ $inlineContext->getContainer()->appendChild(new Text($ticks));
+
+ return true;
+ }
+
+ /**
+ * Locates the matching closer for a backtick code span.
+ *
+ * Leverages some caching to avoid traversing the same cursor multiple times when
+ * we've already seen all the potential backtick closers.
+ *
+ * @see https://github.com/commonmark/cmark/commit/8ed5c9d
+ *
+ * @param int $openTickLength Number of backticks in the opening sequence
+ * @param Cursor $cursor Cursor to scan
+ *
+ * @return bool True if a matching closer was found, false otherwise
+ */
+ private function findMatchingTicks(int $openTickLength, Cursor $cursor): bool
+ {
+ // Reset the seenBackticks cache if this is a new cursor
+ if ($this->lastCursor === null || $this->lastCursor->get() !== $cursor) {
+ $this->seenBackticks = [];
+ $this->lastCursor = \WeakReference::create($cursor);
+ $this->lastCursorScanned = false;
+ }
+
+ if ($openTickLength > self::MAX_BACKTICKS) {
+ return false;
+ }
+
+ // Return if we already know there's no closer
+ if ($this->lastCursorScanned && isset($this->seenBackticks[$openTickLength]) && $this->seenBackticks[$openTickLength] <= $cursor->getPosition()) {
+ return false;
+ }
+
+ while ($ticks = $cursor->match('/`{1,' . self::MAX_BACKTICKS . '}/m')) {
+ $numTicks = \strlen($ticks);
+
+ // Did we find the closer?
+ if ($numTicks === $openTickLength) {
+ return true;
+ }
+
+ // Store position of closer
+ if ($numTicks <= self::MAX_BACKTICKS) {
+ $this->seenBackticks[$numTicks] = $cursor->getPosition() - $numTicks;
+ }
+ }
+
+ // Got through whole input without finding closer
+ $this->lastCursorScanned = true;
+
+ return false;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BangParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BangParser.php
new file mode 100644
index 0000000..cbf6ca3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/BangParser.php
@@ -0,0 +1,44 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class BangParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::string('![');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $cursor = $inlineContext->getCursor();
+ $cursor->advanceBy(2);
+
+ $node = new Text('![', ['delim' => true]);
+ $inlineContext->getContainer()->appendChild($node);
+
+ // Add entry to stack for this opener
+ $inlineContext->getDelimiterStack()->addBracket($node, $cursor->getPosition(), true);
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/CloseBracketParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/CloseBracketParser.php
new file mode 100644
index 0000000..f3b83fd
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/CloseBracketParser.php
@@ -0,0 +1,214 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Delimiter\Bracket;
+use League\CommonMark\Environment\EnvironmentAwareInterface;
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Extension\CommonMark\Node\Inline\AbstractWebResource;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Extension\Mention\Mention;
+use League\CommonMark\Node\Inline\AdjacentTextMerger;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceMapInterface;
+use League\CommonMark\Util\LinkParserHelper;
+use League\CommonMark\Util\RegexHelper;
+
+final class CloseBracketParser implements InlineParserInterface, EnvironmentAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private EnvironmentInterface $environment;
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::string(']');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ // Look through stack of delimiters for a [ or !
+ $opener = $inlineContext->getDelimiterStack()->getLastBracket();
+ if ($opener === null) {
+ return false;
+ }
+
+ if (! $opener->isImage() && ! $opener->isActive()) {
+ // no matched opener; remove from stack
+ $inlineContext->getDelimiterStack()->removeBracket();
+
+ return false;
+ }
+
+ $cursor = $inlineContext->getCursor();
+
+ $startPos = $cursor->getPosition();
+ $previousState = $cursor->saveState();
+
+ $cursor->advanceBy(1);
+
+ // Check to see if we have a link/image
+
+ // Inline link?
+ if ($result = $this->tryParseInlineLinkAndTitle($cursor)) {
+ $link = $result;
+ } elseif ($link = $this->tryParseReference($cursor, $inlineContext->getReferenceMap(), $opener, $startPos)) {
+ $reference = $link;
+ $link = ['url' => $link->getDestination(), 'title' => $link->getTitle()];
+ } else {
+ // No match; remove this opener from stack
+ $inlineContext->getDelimiterStack()->removeBracket();
+ $cursor->restoreState($previousState);
+
+ return false;
+ }
+
+ $inline = $this->createInline($link['url'], $link['title'], $opener->isImage(), $reference ?? null);
+ $opener->getNode()->replaceWith($inline);
+ while (($label = $inline->next()) !== null) {
+ // Is there a Mention or Link contained within this link?
+ // CommonMark does not allow nested links, so we'll restore the original text.
+ if ($label instanceof Mention) {
+ $label->replaceWith($replacement = new Text($label->getPrefix() . $label->getIdentifier()));
+ $inline->appendChild($replacement);
+ } elseif ($label instanceof Link) {
+ foreach ($label->children() as $child) {
+ $label->insertBefore($child);
+ }
+
+ $label->detach();
+ } else {
+ $inline->appendChild($label);
+ }
+ }
+
+ // Process delimiters such as emphasis inside link/image
+ $delimiterStack = $inlineContext->getDelimiterStack();
+ $stackBottom = $opener->getPosition();
+ $delimiterStack->processDelimiters($stackBottom, $this->environment->getDelimiterProcessors());
+ $delimiterStack->removeBracket();
+ $delimiterStack->removeAll($stackBottom);
+
+ // Merge any adjacent Text nodes together
+ AdjacentTextMerger::mergeChildNodes($inline);
+
+ // processEmphasis will remove this and later delimiters.
+ // Now, for a link, we also remove earlier link openers (no links in links)
+ if (! $opener->isImage()) {
+ $inlineContext->getDelimiterStack()->deactivateLinkOpeners();
+ }
+
+ return true;
+ }
+
+ public function setEnvironment(EnvironmentInterface $environment): void
+ {
+ $this->environment = $environment;
+ }
+
+ /**
+ * @return array|null
+ */
+ private function tryParseInlineLinkAndTitle(Cursor $cursor): ?array
+ {
+ if ($cursor->getCurrentCharacter() !== '(') {
+ return null;
+ }
+
+ $previousState = $cursor->saveState();
+
+ $cursor->advanceBy(1);
+ $cursor->advanceToNextNonSpaceOrNewline();
+ if (($dest = LinkParserHelper::parseLinkDestination($cursor)) === null) {
+ $cursor->restoreState($previousState);
+
+ return null;
+ }
+
+ $cursor->advanceToNextNonSpaceOrNewline();
+ $previousCharacter = $cursor->peek(-1);
+ // We know from previous lines that we've advanced at least one space so far, so this next call should never be null
+ \assert(\is_string($previousCharacter));
+
+ $title = '';
+ // make sure there's a space before the title:
+ if (\preg_match(RegexHelper::REGEX_WHITESPACE_CHAR, $previousCharacter)) {
+ $title = LinkParserHelper::parseLinkTitle($cursor) ?? '';
+ }
+
+ $cursor->advanceToNextNonSpaceOrNewline();
+
+ if ($cursor->getCurrentCharacter() !== ')') {
+ $cursor->restoreState($previousState);
+
+ return null;
+ }
+
+ $cursor->advanceBy(1);
+
+ return ['url' => $dest, 'title' => $title];
+ }
+
+ private function tryParseReference(Cursor $cursor, ReferenceMapInterface $referenceMap, Bracket $opener, int $startPos): ?ReferenceInterface
+ {
+ $savePos = $cursor->saveState();
+ $beforeLabel = $cursor->getPosition();
+ $n = LinkParserHelper::parseLinkLabel($cursor);
+ if ($n > 2) {
+ $start = $beforeLabel + 1;
+ $length = $n - 2;
+ } elseif (! $opener->hasNext()) {
+ // Empty or missing second label means to use the first label as the reference.
+ // The reference must not contain a bracket. If we know there's a bracket, we don't even bother checking it.
+ $start = $opener->getPosition();
+ $length = $startPos - $start;
+ } else {
+ $cursor->restoreState($savePos);
+
+ return null;
+ }
+
+ $referenceLabel = $cursor->getSubstring($start, $length);
+
+ if ($n === 0) {
+ // If shortcut reference link, rewind before spaces we skipped
+ $cursor->restoreState($savePos);
+ }
+
+ return $referenceMap->get($referenceLabel);
+ }
+
+ private function createInline(string $url, string $title, bool $isImage, ?ReferenceInterface $reference = null): AbstractWebResource
+ {
+ if ($isImage) {
+ $inline = new Image($url, null, $title);
+ } else {
+ $inline = new Link($url, null, $title);
+ }
+
+ if ($reference) {
+ $inline->data->set('reference', $reference);
+ }
+
+ return $inline;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EntityParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EntityParser.php
new file mode 100644
index 0000000..4122ff7
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EntityParser.php
@@ -0,0 +1,42 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Util\Html5EntityDecoder;
+use League\CommonMark\Util\RegexHelper;
+
+final class EntityParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex(RegexHelper::PARTIAL_ENTITY);
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $entity = $inlineContext->getFullMatch();
+
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+ $inlineContext->getContainer()->appendChild(new Text(Html5EntityDecoder::decode($entity)));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EscapableParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EscapableParser.php
new file mode 100644
index 0000000..64e6fab
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/EscapableParser.php
@@ -0,0 +1,57 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Node\Inline\Newline;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Util\RegexHelper;
+
+final class EscapableParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::string('\\');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $cursor = $inlineContext->getCursor();
+ $nextChar = $cursor->peek();
+
+ if ($nextChar === "\n") {
+ $cursor->advanceBy(2);
+ $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK));
+
+ return true;
+ }
+
+ if ($nextChar !== null && RegexHelper::isEscapable($nextChar)) {
+ $cursor->advanceBy(2);
+ $inlineContext->getContainer()->appendChild(new Text($nextChar));
+
+ return true;
+ }
+
+ $cursor->advanceBy(1);
+ $inlineContext->getContainer()->appendChild(new Text('\\'));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/HtmlInlineParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/HtmlInlineParser.php
new file mode 100644
index 0000000..f38db13
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/HtmlInlineParser.php
@@ -0,0 +1,41 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Util\RegexHelper;
+
+final class HtmlInlineParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex(RegexHelper::PARTIAL_HTMLTAG)->caseSensitive();
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inline = $inlineContext->getFullMatch();
+
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+ $inlineContext->getContainer()->appendChild(new HtmlInline($inline));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/OpenBracketParser.php b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/OpenBracketParser.php
new file mode 100644
index 0000000..1ba8c13
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Parser/Inline/OpenBracketParser.php
@@ -0,0 +1,42 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Parser\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class OpenBracketParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::string('[');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy(1);
+ $node = new Text('[', ['delim' => true]);
+ $inlineContext->getContainer()->appendChild($node);
+
+ // Add entry to stack for this opener
+ $inlineContext->getDelimiterStack()->addBracket($node, $inlineContext->getCursor()->getPosition(), false);
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/BlockQuoteRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/BlockQuoteRenderer.php
new file mode 100644
index 0000000..4a59bd3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/BlockQuoteRenderer.php
@@ -0,0 +1,70 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class BlockQuoteRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param BlockQuote $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ BlockQuote::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ $filling = $childRenderer->renderNodes($node->children());
+ $innerSeparator = $childRenderer->getInnerSeparator();
+ if ($filling === '') {
+ return new HtmlElement('blockquote', $attrs, $innerSeparator);
+ }
+
+ return new HtmlElement(
+ 'blockquote',
+ $attrs,
+ $innerSeparator . $filling . $innerSeparator
+ );
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'block_quote';
+ }
+
+ /**
+ * @param BlockQuote $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/FencedCodeRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/FencedCodeRenderer.php
new file mode 100644
index 0000000..8df9a40
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/FencedCodeRenderer.php
@@ -0,0 +1,81 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Util\Xml;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class FencedCodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param FencedCode $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ FencedCode::assertInstanceOf($node);
+
+ $attrs = $node->data->getData('attributes');
+
+ $infoWords = $node->getInfoWords();
+ if (\count($infoWords) !== 0 && $infoWords[0] !== '') {
+ $class = $infoWords[0];
+ if (! \str_starts_with($class, 'language-')) {
+ $class = 'language-' . $class;
+ }
+
+ $attrs->append('class', $class);
+ }
+
+ return new HtmlElement(
+ 'pre',
+ [],
+ new HtmlElement('code', $attrs->export(), Xml::escape($node->getLiteral()))
+ );
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'code_block';
+ }
+
+ /**
+ * @param FencedCode $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ FencedCode::assertInstanceOf($node);
+
+ if (($info = $node->getInfo()) === null || $info === '') {
+ return [];
+ }
+
+ return ['info' => $info];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HeadingRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HeadingRenderer.php
new file mode 100644
index 0000000..8718b8c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HeadingRenderer.php
@@ -0,0 +1,64 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class HeadingRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Heading $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Heading::assertInstanceOf($node);
+
+ $tag = 'h' . $node->getLevel();
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement($tag, $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'heading';
+ }
+
+ /**
+ * @param Heading $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ Heading::assertInstanceOf($node);
+
+ return ['level' => $node->getLevel()];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HtmlBlockRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HtmlBlockRenderer.php
new file mode 100644
index 0000000..63a1907
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/HtmlBlockRenderer.php
@@ -0,0 +1,66 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlFilter;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class HtmlBlockRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ /**
+ * @param HtmlBlock $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ HtmlBlock::assertInstanceOf($node);
+
+ $htmlInput = $this->config->get('html_input');
+
+ return HtmlFilter::filter($node->getLiteral(), $htmlInput);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'html_block';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/IndentedCodeRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/IndentedCodeRenderer.php
new file mode 100644
index 0000000..c4bd4eb
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/IndentedCodeRenderer.php
@@ -0,0 +1,61 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\IndentedCode;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Util\Xml;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class IndentedCodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param IndentedCode $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ IndentedCode::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement(
+ 'pre',
+ [],
+ new HtmlElement('code', $attrs, Xml::escape($node->getLiteral()))
+ );
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'code_block';
+ }
+
+ /**
+ * @return array
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListBlockRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListBlockRenderer.php
new file mode 100644
index 0000000..f79b44d
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListBlockRenderer.php
@@ -0,0 +1,86 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class ListBlockRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param ListBlock $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ ListBlock::assertInstanceOf($node);
+
+ $listData = $node->getListData();
+
+ $tag = $listData->type === ListBlock::TYPE_BULLET ? 'ul' : 'ol';
+
+ $attrs = $node->data->get('attributes');
+
+ if ($listData->start !== null && $listData->start !== 1) {
+ $attrs['start'] = (string) $listData->start;
+ }
+
+ $innerSeparator = $childRenderer->getInnerSeparator();
+
+ return new HtmlElement($tag, $attrs, $innerSeparator . $childRenderer->renderNodes($node->children()) . $innerSeparator);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'list';
+ }
+
+ /**
+ * @param ListBlock $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ ListBlock::assertInstanceOf($node);
+
+ $data = $node->getListData();
+
+ if ($data->type === ListBlock::TYPE_BULLET) {
+ return [
+ 'type' => $data->type,
+ 'tight' => $node->isTight() ? 'true' : 'false',
+ ];
+ }
+
+ return [
+ 'type' => $data->type,
+ 'start' => $data->start ?? 1,
+ 'tight' => $node->isTight(),
+ 'delimiter' => $data->delimiter ?? ListBlock::DELIM_PERIOD,
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php
new file mode 100644
index 0000000..543baad
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ListItemRenderer.php
@@ -0,0 +1,80 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Block\TightBlockInterface;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class ListItemRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param ListItem $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ ListItem::assertInstanceOf($node);
+
+ $contents = $childRenderer->renderNodes($node->children());
+
+ $inTightList = ($parent = $node->parent()) && $parent instanceof TightBlockInterface && $parent->isTight();
+
+ if ($this->needsBlockSeparator($node->firstChild(), $inTightList)) {
+ $contents = "\n" . $contents;
+ }
+
+ if ($this->needsBlockSeparator($node->lastChild(), $inTightList)) {
+ $contents .= "\n";
+ }
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('li', $attrs, $contents);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'item';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+
+ private function needsBlockSeparator(?Node $child, bool $inTightList): bool
+ {
+ if ($child instanceof Paragraph && $inTightList) {
+ return false;
+ }
+
+ return $child instanceof AbstractBlock;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ThematicBreakRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ThematicBreakRenderer.php
new file mode 100644
index 0000000..392bfee
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Block/ThematicBreakRenderer.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Block;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ThematicBreak;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class ThematicBreakRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param ThematicBreak $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ ThematicBreak::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('hr', $attrs, '', true);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'thematic_break';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/CodeRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/CodeRenderer.php
new file mode 100644
index 0000000..de030e8
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/CodeRenderer.php
@@ -0,0 +1,57 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Code;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Util\Xml;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class CodeRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Code $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Code::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('code', $attrs, Xml::escape($node->getLiteral()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'code';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/EmphasisRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/EmphasisRenderer.php
new file mode 100644
index 0000000..41169c4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/EmphasisRenderer.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Emphasis;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class EmphasisRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Emphasis $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Emphasis::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('em', $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'emph';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/HtmlInlineRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/HtmlInlineRenderer.php
new file mode 100644
index 0000000..69f0fd5
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/HtmlInlineRenderer.php
@@ -0,0 +1,66 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlFilter;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class HtmlInlineRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ /**
+ * @param HtmlInline $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ HtmlInline::assertInstanceOf($node);
+
+ $htmlInput = $this->config->get('html_input');
+
+ return HtmlFilter::filter($node->getLiteral(), $htmlInput);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'html_inline';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/ImageRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/ImageRenderer.php
new file mode 100644
index 0000000..7bf09ac
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/ImageRenderer.php
@@ -0,0 +1,107 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
+use League\CommonMark\Node\Inline\Newline;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Node\NodeIterator;
+use League\CommonMark\Node\StringContainerInterface;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Util\RegexHelper;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class ImageRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ /**
+ * @param Image $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Image::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ $forbidUnsafeLinks = ! $this->config->get('allow_unsafe_links');
+ if ($forbidUnsafeLinks && RegexHelper::isLinkPotentiallyUnsafe($node->getUrl())) {
+ $attrs['src'] = '';
+ } else {
+ $attrs['src'] = $node->getUrl();
+ }
+
+ $attrs['alt'] = $this->getAltText($node);
+
+ if (($title = $node->getTitle()) !== null) {
+ $attrs['title'] = $title;
+ }
+
+ return new HtmlElement('img', $attrs, '', true);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'image';
+ }
+
+ /**
+ * @param Image $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ Image::assertInstanceOf($node);
+
+ return [
+ 'destination' => $node->getUrl(),
+ 'title' => $node->getTitle() ?? '',
+ ];
+ }
+
+ private function getAltText(Image $node): string
+ {
+ $altText = '';
+
+ foreach ((new NodeIterator($node)) as $n) {
+ if ($n instanceof StringContainerInterface) {
+ $altText .= $n->getLiteral();
+ } elseif ($n instanceof Newline) {
+ $altText .= "\n";
+ }
+ }
+
+ return $altText;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/LinkRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/LinkRenderer.php
new file mode 100644
index 0000000..4ef9645
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/LinkRenderer.php
@@ -0,0 +1,89 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Util\RegexHelper;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class LinkRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ /**
+ * @param Link $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Link::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ $forbidUnsafeLinks = ! $this->config->get('allow_unsafe_links');
+ if (! ($forbidUnsafeLinks && RegexHelper::isLinkPotentiallyUnsafe($node->getUrl()))) {
+ $attrs['href'] = $node->getUrl();
+ }
+
+ if (($title = $node->getTitle()) !== null) {
+ $attrs['title'] = $title;
+ }
+
+ if (isset($attrs['target']) && $attrs['target'] === '_blank' && ! isset($attrs['rel'])) {
+ $attrs['rel'] = 'noopener noreferrer';
+ }
+
+ return new HtmlElement('a', $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'link';
+ }
+
+ /**
+ * @param Link $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ Link::assertInstanceOf($node);
+
+ return [
+ 'destination' => $node->getUrl(),
+ 'title' => $node->getTitle() ?? '',
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/StrongRenderer.php b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/StrongRenderer.php
new file mode 100644
index 0000000..f0bb8f9
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/CommonMark/Renderer/Inline/StrongRenderer.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Strong;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class StrongRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Strong $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Strong::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('strong', $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'strong';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/ConfigurableExtensionInterface.php b/vendor/league/commonmark/src/Extension/ConfigurableExtensionInterface.php
new file mode 100644
index 0000000..63e467c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/ConfigurableExtensionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension;
+
+use League\Config\ConfigurationBuilderInterface;
+
+interface ConfigurableExtensionInterface extends ExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void;
+}
diff --git a/vendor/league/commonmark/src/Extension/DefaultAttributes/ApplyDefaultAttributesProcessor.php b/vendor/league/commonmark/src/Extension/DefaultAttributes/ApplyDefaultAttributesProcessor.php
new file mode 100644
index 0000000..6b519f8
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DefaultAttributes/ApplyDefaultAttributesProcessor.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DefaultAttributes;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class ApplyDefaultAttributesProcessor implements ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ /** @var array> $map */
+ $map = $this->config->get('default_attributes');
+
+ // Don't bother iterating if no default attributes are configured
+ if (! $map) {
+ return;
+ }
+
+ foreach ($event->getDocument()->iterator() as $node) {
+ // Check to see if any default attributes were defined
+ if (($attributesToApply = $map[\get_class($node)] ?? []) === []) {
+ continue;
+ }
+
+ $newAttributes = [];
+ foreach ($attributesToApply as $name => $value) {
+ if (\is_callable($value)) {
+ $value = $value($node);
+ // Callables are allowed to return `null` indicating that no changes should be made
+ if ($value !== null) {
+ $newAttributes[$name] = $value;
+ }
+ } else {
+ $newAttributes[$name] = $value;
+ }
+ }
+
+ // Merge these attributes into the node
+ if (\count($newAttributes) > 0) {
+ $node->data->set('attributes', AttributesHelper::mergeAttributes($node, $newAttributes));
+ }
+ }
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DefaultAttributes/DefaultAttributesExtension.php b/vendor/league/commonmark/src/Extension/DefaultAttributes/DefaultAttributesExtension.php
new file mode 100644
index 0000000..152c29a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DefaultAttributes/DefaultAttributesExtension.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DefaultAttributes;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class DefaultAttributesExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('default_attributes', Expect::arrayOf(
+ Expect::arrayOf(
+ Expect::type('string|string[]|bool|callable'), // attribute value(s)
+ 'string' // attribute name
+ ),
+ 'string' // node FQCN
+ )->default([]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addEventListener(DocumentParsedEvent::class, [new ApplyDefaultAttributesProcessor(), 'onDocumentParsed']);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/DescriptionListExtension.php b/vendor/league/commonmark/src/Extension/DescriptionList/DescriptionListExtension.php
new file mode 100644
index 0000000..9ddd2a8
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/DescriptionListExtension.php
@@ -0,0 +1,42 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\DescriptionList\Event\ConsecutiveDescriptionListMerger;
+use League\CommonMark\Extension\DescriptionList\Event\LooseDescriptionHandler;
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionList;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm;
+use League\CommonMark\Extension\DescriptionList\Parser\DescriptionStartParser;
+use League\CommonMark\Extension\DescriptionList\Renderer\DescriptionListRenderer;
+use League\CommonMark\Extension\DescriptionList\Renderer\DescriptionRenderer;
+use League\CommonMark\Extension\DescriptionList\Renderer\DescriptionTermRenderer;
+use League\CommonMark\Extension\ExtensionInterface;
+
+final class DescriptionListExtension implements ExtensionInterface
+{
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addBlockStartParser(new DescriptionStartParser());
+
+ $environment->addEventListener(DocumentParsedEvent::class, new LooseDescriptionHandler(), 1001);
+ $environment->addEventListener(DocumentParsedEvent::class, new ConsecutiveDescriptionListMerger(), 1000);
+
+ $environment->addRenderer(DescriptionList::class, new DescriptionListRenderer());
+ $environment->addRenderer(DescriptionTerm::class, new DescriptionTermRenderer());
+ $environment->addRenderer(Description::class, new DescriptionRenderer());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Event/ConsecutiveDescriptionListMerger.php b/vendor/league/commonmark/src/Extension/DescriptionList/Event/ConsecutiveDescriptionListMerger.php
new file mode 100644
index 0000000..15210e7
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Event/ConsecutiveDescriptionListMerger.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionList;
+use League\CommonMark\Node\NodeIterator;
+
+final class ConsecutiveDescriptionListMerger
+{
+ public function __invoke(DocumentParsedEvent $event): void
+ {
+ foreach ($event->getDocument()->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ if (! $node instanceof DescriptionList) {
+ continue;
+ }
+
+ if (! ($prev = $node->previous()) instanceof DescriptionList) {
+ continue;
+ }
+
+ // There's another description list behind this one; merge the current one into that
+ foreach ($node->children() as $child) {
+ $prev->appendChild($child);
+ }
+
+ $node->detach();
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Event/LooseDescriptionHandler.php b/vendor/league/commonmark/src/Extension/DescriptionList/Event/LooseDescriptionHandler.php
new file mode 100644
index 0000000..a8823fa
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Event/LooseDescriptionHandler.php
@@ -0,0 +1,66 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionList;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Inline\Newline;
+use League\CommonMark\Node\NodeIterator;
+
+final class LooseDescriptionHandler
+{
+ public function __invoke(DocumentParsedEvent $event): void
+ {
+ foreach ($event->getDocument()->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $description) {
+ if (! $description instanceof Description) {
+ continue;
+ }
+
+ // Does this description need to be added to a list?
+ if (! $description->parent() instanceof DescriptionList) {
+ $list = new DescriptionList();
+ // Taking any preceding paragraphs with it
+ if (($paragraph = $description->previous()) instanceof Paragraph) {
+ $list->appendChild($paragraph);
+ }
+
+ $description->replaceWith($list);
+ $list->appendChild($description);
+ }
+
+ // Is this description preceded by a paragraph that should really be a term?
+ if (! (($paragraph = $description->previous()) instanceof Paragraph)) {
+ continue;
+ }
+
+ // Convert the paragraph into one or more terms
+ $term = new DescriptionTerm();
+ $paragraph->replaceWith($term);
+
+ foreach ($paragraph->children() as $child) {
+ if ($child instanceof Newline) {
+ $newTerm = new DescriptionTerm();
+ $term->insertAfter($newTerm);
+ $term = $newTerm;
+ continue;
+ }
+
+ $term->appendChild($child);
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Node/Description.php b/vendor/league/commonmark/src/Extension/DescriptionList/Node/Description.php
new file mode 100644
index 0000000..ccef962
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Node/Description.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Block\TightBlockInterface;
+
+class Description extends AbstractBlock implements TightBlockInterface
+{
+ private bool $tight;
+
+ public function __construct(bool $tight = false)
+ {
+ parent::__construct();
+
+ $this->tight = $tight;
+ }
+
+ public function isTight(): bool
+ {
+ return $this->tight;
+ }
+
+ public function setTight(bool $tight): void
+ {
+ $this->tight = $tight;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionList.php b/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionList.php
new file mode 100644
index 0000000..90d026c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionList.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+class DescriptionList extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionTerm.php b/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionTerm.php
new file mode 100644
index 0000000..b13ec75
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Node/DescriptionTerm.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+class DescriptionTerm extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionContinueParser.php b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionContinueParser.php
new file mode 100644
index 0000000..0cdd9d5
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionContinueParser.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\DescriptionList\Parser;
+
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class DescriptionContinueParser extends AbstractBlockContinueParser
+{
+ private Description $block;
+
+ private int $indentation;
+
+ public function __construct(bool $tight, int $indentation)
+ {
+ $this->block = new Description($tight);
+ $this->indentation = $indentation;
+ }
+
+ public function getBlock(): Description
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($cursor->isBlank()) {
+ if ($this->block->firstChild() === null) {
+ // Blank line after empty item
+ return BlockContinue::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ return BlockContinue::at($cursor);
+ }
+
+ if ($cursor->getIndent() >= $this->indentation) {
+ $cursor->advanceBy($this->indentation, true);
+
+ return BlockContinue::at($cursor);
+ }
+
+ return BlockContinue::none();
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionListContinueParser.php b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionListContinueParser.php
new file mode 100644
index 0000000..1d446a7
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionListContinueParser.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\DescriptionList\Parser;
+
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionList;
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+final class DescriptionListContinueParser extends AbstractBlockContinueParser
+{
+ private DescriptionList $block;
+
+ public function __construct()
+ {
+ $this->block = new DescriptionList();
+ }
+
+ public function getBlock(): DescriptionList
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::at($cursor);
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return $childBlock instanceof DescriptionTerm || $childBlock instanceof Description;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionStartParser.php b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionStartParser.php
new file mode 100644
index 0000000..b4e8c98
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionStartParser.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\DescriptionList\Parser;
+
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class DescriptionStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented()) {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+ if ($cursor->match('/^:[ \t]+/') === null) {
+ return BlockStart::none();
+ }
+
+ $terms = $parserState->getParagraphContent();
+
+ $activeBlock = $parserState->getActiveBlockParser()->getBlock();
+
+ if ($terms !== null && $terms !== '') {
+ // New description; tight; term(s) sitting in pending block that we will replace
+ return BlockStart::of(...[new DescriptionListContinueParser()], ...self::splitTerms($terms), ...[new DescriptionContinueParser(true, $cursor->getPosition())])
+ ->at($cursor)
+ ->replaceActiveBlockParser();
+ }
+
+ if ($activeBlock instanceof Paragraph && $activeBlock->parent() instanceof Description) {
+ // Additional description in the same list as the parent description
+ return BlockStart::of(new DescriptionContinueParser(true, $cursor->getPosition()))->at($cursor);
+ }
+
+ if ($activeBlock->lastChild() instanceof Paragraph) {
+ // New description; loose; term(s) sitting in previous closed paragraph block
+ return BlockStart::of(new DescriptionContinueParser(false, $cursor->getPosition()))->at($cursor);
+ }
+
+ // No preceding terms
+ return BlockStart::none();
+ }
+
+ /**
+ * @return array
+ */
+ private static function splitTerms(string $terms): array
+ {
+ $ret = [];
+ foreach (\explode("\n", $terms) as $term) {
+ $ret[] = new DescriptionTermContinueParser($term);
+ }
+
+ return $ret;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionTermContinueParser.php b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionTermContinueParser.php
new file mode 100644
index 0000000..7b43882
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Parser/DescriptionTermContinueParser.php
@@ -0,0 +1,52 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Parser;
+
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\InlineParserEngineInterface;
+
+final class DescriptionTermContinueParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
+{
+ private DescriptionTerm $block;
+
+ private string $term;
+
+ public function __construct(string $term)
+ {
+ $this->block = new DescriptionTerm();
+ $this->term = $term;
+ }
+
+ public function getBlock(): DescriptionTerm
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::finished();
+ }
+
+ public function parseInlines(InlineParserEngineInterface $inlineParser): void
+ {
+ if ($this->term !== '') {
+ $inlineParser->parse($this->term, $this->block);
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionListRenderer.php b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionListRenderer.php
new file mode 100644
index 0000000..7723038
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionListRenderer.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Renderer;
+
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionList;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+
+final class DescriptionListRenderer implements NodeRendererInterface
+{
+ /**
+ * @param DescriptionList $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): HtmlElement
+ {
+ DescriptionList::assertInstanceOf($node);
+
+ $separator = $childRenderer->getBlockSeparator();
+
+ return new HtmlElement('dl', [], $separator . $childRenderer->renderNodes($node->children()) . $separator);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionRenderer.php b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionRenderer.php
new file mode 100644
index 0000000..5fcffd6
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionRenderer.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Renderer;
+
+use League\CommonMark\Extension\DescriptionList\Node\Description;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+
+final class DescriptionRenderer implements NodeRendererInterface
+{
+ /**
+ * @param Description $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Description::assertInstanceOf($node);
+
+ return new HtmlElement('dd', [], $childRenderer->renderNodes($node->children()));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionTermRenderer.php b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionTermRenderer.php
new file mode 100644
index 0000000..ce8a1c4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DescriptionList/Renderer/DescriptionTermRenderer.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DescriptionList\Renderer;
+
+use League\CommonMark\Extension\DescriptionList\Node\DescriptionTerm;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+
+final class DescriptionTermRenderer implements NodeRendererInterface
+{
+ /**
+ * @param DescriptionTerm $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ DescriptionTerm::assertInstanceOf($node);
+
+ return new HtmlElement('dt', [], $childRenderer->renderNodes($node->children()));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlExtension.php b/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlExtension.php
new file mode 100644
index 0000000..0ece0c2
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlExtension.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DisallowedRawHtml;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
+use League\CommonMark\Extension\CommonMark\Node\Inline\HtmlInline;
+use League\CommonMark\Extension\CommonMark\Renderer\Block\HtmlBlockRenderer;
+use League\CommonMark\Extension\CommonMark\Renderer\Inline\HtmlInlineRenderer;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class DisallowedRawHtmlExtension implements ConfigurableExtensionInterface
+{
+ private const DEFAULT_DISALLOWED_TAGS = [
+ 'title',
+ 'textarea',
+ 'style',
+ 'xmp',
+ 'iframe',
+ 'noembed',
+ 'noframes',
+ 'script',
+ 'plaintext',
+ ];
+
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('disallowed_raw_html', Expect::structure([
+ 'disallowed_tags' => Expect::listOf('string')->default(self::DEFAULT_DISALLOWED_TAGS)->mergeDefaults(false),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addRenderer(HtmlBlock::class, new DisallowedRawHtmlRenderer(new HtmlBlockRenderer()), 50);
+ $environment->addRenderer(HtmlInline::class, new DisallowedRawHtmlRenderer(new HtmlInlineRenderer()), 50);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php b/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php
new file mode 100644
index 0000000..06252a3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/DisallowedRawHtml/DisallowedRawHtmlRenderer.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\DisallowedRawHtml;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class DisallowedRawHtmlRenderer implements NodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly */
+ private NodeRendererInterface $innerRenderer;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function __construct(NodeRendererInterface $innerRenderer)
+ {
+ $this->innerRenderer = $innerRenderer;
+ }
+
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): ?string
+ {
+ $rendered = (string) $this->innerRenderer->render($node, $childRenderer);
+
+ if ($rendered === '') {
+ return '';
+ }
+
+ $tags = (array) $this->config->get('disallowed_raw_html/disallowed_tags');
+ if (\count($tags) === 0) {
+ return $rendered;
+ }
+
+ $regex = \sprintf('/<(\/?(?:%s)[ \/>])/i', \implode('|', \array_map('preg_quote', $tags)));
+
+ // Match these types of tags:
+ return \preg_replace($regex, '<$1', $rendered);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+
+ if ($this->innerRenderer instanceof ConfigurationAwareInterface) {
+ $this->innerRenderer->setConfiguration($configuration);
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/Bridge/OscaroteroEmbedAdapter.php b/vendor/league/commonmark/src/Extension/Embed/Bridge/OscaroteroEmbedAdapter.php
new file mode 100644
index 0000000..06b8190
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/Bridge/OscaroteroEmbedAdapter.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed\Bridge;
+
+use Embed\Embed as EmbedLib;
+use League\CommonMark\Exception\MissingDependencyException;
+use League\CommonMark\Extension\Embed\Embed;
+use League\CommonMark\Extension\Embed\EmbedAdapterInterface;
+
+final class OscaroteroEmbedAdapter implements EmbedAdapterInterface
+{
+ private EmbedLib $embedLib;
+
+ public function __construct(?EmbedLib $embed = null)
+ {
+ if ($embed === null) {
+ if (! \class_exists(EmbedLib::class)) {
+ throw new MissingDependencyException('The embed/embed package is not installed. Please install it with Composer to use this adapter.');
+ }
+
+ $embed = new EmbedLib();
+ }
+
+ $this->embedLib = $embed;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function updateEmbeds(array $embeds): void
+ {
+ $extractors = $this->embedLib->getMulti(...\array_map(static fn (Embed $embed) => $embed->getUrl(), $embeds));
+ foreach ($extractors as $i => $extractor) {
+ if ($extractor->code !== null) {
+ $embeds[$i]->setEmbedCode($extractor->code->html);
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/DomainFilteringAdapter.php b/vendor/league/commonmark/src/Extension/Embed/DomainFilteringAdapter.php
new file mode 100644
index 0000000..d150764
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/DomainFilteringAdapter.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+class DomainFilteringAdapter implements EmbedAdapterInterface
+{
+ private EmbedAdapterInterface $decorated;
+
+ /** @psalm-var non-empty-string */
+ private string $regex;
+
+ /**
+ * @param string[] $allowedDomains
+ */
+ public function __construct(EmbedAdapterInterface $decorated, array $allowedDomains)
+ {
+ $this->decorated = $decorated;
+ $this->regex = self::createRegex($allowedDomains);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function updateEmbeds(array $embeds): void
+ {
+ $this->decorated->updateEmbeds(\array_values(\array_filter($embeds, function (Embed $embed): bool {
+ return \preg_match($this->regex, $embed->getUrl()) === 1;
+ })));
+ }
+
+ /**
+ * @param string[] $allowedDomains
+ *
+ * @psalm-return non-empty-string
+ */
+ private static function createRegex(array $allowedDomains): string
+ {
+ $allowedDomains = \array_map('preg_quote', $allowedDomains);
+
+ return '/^(?:https?:\/\/)?(?:[^.]+\.)*(' . \implode('|', $allowedDomains) . ')/';
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/Embed.php b/vendor/league/commonmark/src/Extension/Embed/Embed.php
new file mode 100644
index 0000000..94c1980
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/Embed.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class Embed extends AbstractBlock
+{
+ private string $url;
+ private ?string $embedCode;
+
+ public function __construct(string $url, ?string $embedCode = null)
+ {
+ parent::__construct();
+
+ $this->url = $url;
+ $this->embedCode = $embedCode;
+ }
+
+ public function getUrl(): string
+ {
+ return $this->url;
+ }
+
+ public function setUrl(string $url): void
+ {
+ $this->url = $url;
+ }
+
+ public function getEmbedCode(): ?string
+ {
+ return $this->embedCode;
+ }
+
+ public function setEmbedCode(?string $embedCode): void
+ {
+ $this->embedCode = $embedCode;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedAdapterInterface.php b/vendor/league/commonmark/src/Extension/Embed/EmbedAdapterInterface.php
new file mode 100644
index 0000000..9880a43
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedAdapterInterface.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+/**
+ * Interface for a service which updates the embed code(s) for the given array of embeds
+ */
+interface EmbedAdapterInterface
+{
+ /**
+ * @param Embed[] $embeds
+ */
+ public function updateEmbeds(array $embeds): void;
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedExtension.php b/vendor/league/commonmark/src/Extension/Embed/EmbedExtension.php
new file mode 100644
index 0000000..babf048
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedExtension.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class EmbedExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('embed', Expect::structure([
+ 'adapter' => Expect::type(EmbedAdapterInterface::class),
+ 'allowed_domains' => Expect::arrayOf('string')->default([]),
+ 'fallback' => Expect::anyOf('link', 'remove')->default('link'),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $adapter = $environment->getConfiguration()->get('embed.adapter');
+ \assert($adapter instanceof EmbedAdapterInterface);
+
+ $allowedDomains = $environment->getConfiguration()->get('embed.allowed_domains');
+ if ($allowedDomains !== []) {
+ $adapter = new DomainFilteringAdapter($adapter, $allowedDomains);
+ }
+
+ $environment
+ ->addBlockStartParser(new EmbedStartParser(), 300)
+ ->addEventListener(DocumentParsedEvent::class, new EmbedProcessor($adapter, $environment->getConfiguration()->get('embed.fallback')), 1010)
+ ->addRenderer(Embed::class, new EmbedRenderer());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedParser.php b/vendor/league/commonmark/src/Extension/Embed/EmbedParser.php
new file mode 100644
index 0000000..e957caf
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedParser.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+
+class EmbedParser implements BlockContinueParserInterface
+{
+ private Embed $embed;
+
+ public function __construct(string $url)
+ {
+ $this->embed = new Embed($url);
+ }
+
+ public function getBlock(): AbstractBlock
+ {
+ return $this->embed;
+ }
+
+ public function isContainer(): bool
+ {
+ return false;
+ }
+
+ public function canHaveLazyContinuationLines(): bool
+ {
+ return false;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return false;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::none();
+ }
+
+ public function addLine(string $line): void
+ {
+ }
+
+ public function closeBlock(): void
+ {
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedProcessor.php b/vendor/league/commonmark/src/Extension/Embed/EmbedProcessor.php
new file mode 100644
index 0000000..035b583
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedProcessor.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Node\NodeIterator;
+
+final class EmbedProcessor
+{
+ public const FALLBACK_REMOVE = 'remove';
+ public const FALLBACK_LINK = 'link';
+
+ private EmbedAdapterInterface $adapter;
+ private string $fallback;
+
+ public function __construct(EmbedAdapterInterface $adapter, string $fallback = self::FALLBACK_REMOVE)
+ {
+ $this->adapter = $adapter;
+ $this->fallback = $fallback;
+ }
+
+ public function __invoke(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+ $embeds = [];
+ foreach (new NodeIterator($document) as $node) {
+ if (! ($node instanceof Embed)) {
+ continue;
+ }
+
+ if ($node->parent() !== $document) {
+ $replacement = new Paragraph();
+ $replacement->appendChild(new Text($node->getUrl()));
+ $node->replaceWith($replacement);
+ } else {
+ $embeds[] = $node;
+ }
+ }
+
+ if ($embeds) {
+ $this->adapter->updateEmbeds($embeds);
+ }
+
+ foreach ($embeds as $embed) {
+ if ($embed->getEmbedCode() !== null) {
+ continue;
+ }
+
+ if ($this->fallback === self::FALLBACK_REMOVE) {
+ $embed->detach();
+ } elseif ($this->fallback === self::FALLBACK_LINK) {
+ $paragraph = new Paragraph();
+ $paragraph->appendChild(new Link($embed->getUrl(), $embed->getUrl()));
+ $embed->replaceWith($paragraph);
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedRenderer.php b/vendor/league/commonmark/src/Extension/Embed/EmbedRenderer.php
new file mode 100644
index 0000000..91655d8
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedRenderer.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+
+class EmbedRenderer implements NodeRendererInterface
+{
+ /**
+ * @param Embed $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer)
+ {
+ Embed::assertInstanceOf($node);
+
+ return $node->getEmbedCode() ?? '';
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Embed/EmbedStartParser.php b/vendor/league/commonmark/src/Extension/Embed/EmbedStartParser.php
new file mode 100644
index 0000000..5ff3808
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Embed/EmbedStartParser.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Embed;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\LinkParserHelper;
+
+class EmbedStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented() || $parserState->getParagraphContent() !== null || ! ($parserState->getActiveBlockParser()->isContainer())) {
+ return BlockStart::none();
+ }
+
+ // 0-3 leading spaces are okay
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ // The line must begin with "https://"
+ if (! str_starts_with($cursor->getRemainder(), 'https://')) {
+ return BlockStart::none();
+ }
+
+ // A valid link must be found next
+ if (($dest = LinkParserHelper::parseLinkDestination($cursor)) === null) {
+ return BlockStart::none();
+ }
+
+ // Skip any trailing whitespace
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ // We must be at the end of the line; otherwise, this link was not by itself
+ if (! $cursor->isAtEnd()) {
+ return BlockStart::none();
+ }
+
+ return BlockStart::of(new EmbedParser($dest))->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/ExtensionInterface.php b/vendor/league/commonmark/src/Extension/ExtensionInterface.php
new file mode 100644
index 0000000..01a9f2e
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/ExtensionInterface.php
@@ -0,0 +1,24 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+
+interface ExtensionInterface
+{
+ public function register(EnvironmentBuilderInterface $environment): void;
+}
diff --git a/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkExtension.php b/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkExtension.php
new file mode 100644
index 0000000..df0079c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkExtension.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\ExternalLink;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class ExternalLinkExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $applyOptions = [
+ ExternalLinkProcessor::APPLY_NONE,
+ ExternalLinkProcessor::APPLY_ALL,
+ ExternalLinkProcessor::APPLY_INTERNAL,
+ ExternalLinkProcessor::APPLY_EXTERNAL,
+ ];
+
+ $builder->addSchema('external_link', Expect::structure([
+ 'internal_hosts' => Expect::type('string|string[]'),
+ 'open_in_new_window' => Expect::bool(false),
+ 'html_class' => Expect::string()->default(''),
+ 'nofollow' => Expect::anyOf(...$applyOptions)->default(ExternalLinkProcessor::APPLY_NONE),
+ 'noopener' => Expect::anyOf(...$applyOptions)->default(ExternalLinkProcessor::APPLY_EXTERNAL),
+ 'noreferrer' => Expect::anyOf(...$applyOptions)->default(ExternalLinkProcessor::APPLY_EXTERNAL),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addEventListener(DocumentParsedEvent::class, new ExternalLinkProcessor($environment->getConfiguration()), -50);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkProcessor.php b/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkProcessor.php
new file mode 100644
index 0000000..4a0aa89
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/ExternalLink/ExternalLinkProcessor.php
@@ -0,0 +1,119 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\ExternalLink;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\Config\ConfigurationInterface;
+
+final class ExternalLinkProcessor
+{
+ public const APPLY_NONE = '';
+ public const APPLY_ALL = 'all';
+ public const APPLY_EXTERNAL = 'external';
+ public const APPLY_INTERNAL = 'internal';
+
+ /** @psalm-readonly */
+ private ConfigurationInterface $config;
+
+ public function __construct(ConfigurationInterface $config)
+ {
+ $this->config = $config;
+ }
+
+ public function __invoke(DocumentParsedEvent $e): void
+ {
+ $internalHosts = $this->config->get('external_link/internal_hosts');
+ $openInNewWindow = $this->config->get('external_link/open_in_new_window');
+ $classes = $this->config->get('external_link/html_class');
+
+ foreach ($e->getDocument()->iterator() as $link) {
+ if (! ($link instanceof Link)) {
+ continue;
+ }
+
+ $host = \parse_url($link->getUrl(), PHP_URL_HOST);
+ if (! \is_string($host)) {
+ // Something is terribly wrong with this URL
+ continue;
+ }
+
+ if (self::hostMatches($host, $internalHosts)) {
+ $link->data->set('external', false);
+ $this->applyRelAttribute($link, false);
+ continue;
+ }
+
+ // Host does not match our list
+ $this->markLinkAsExternal($link, $openInNewWindow, $classes);
+ }
+ }
+
+ private function markLinkAsExternal(Link $link, bool $openInNewWindow, string $classes): void
+ {
+ $link->data->set('external', true);
+ $this->applyRelAttribute($link, true);
+
+ if ($openInNewWindow) {
+ $link->data->set('attributes/target', '_blank');
+ }
+
+ if ($classes !== '') {
+ $link->data->append('attributes/class', $classes);
+ }
+ }
+
+ private function applyRelAttribute(Link $link, bool $isExternal): void
+ {
+ $options = [
+ 'nofollow' => $this->config->get('external_link/nofollow'),
+ 'noopener' => $this->config->get('external_link/noopener'),
+ 'noreferrer' => $this->config->get('external_link/noreferrer'),
+ ];
+
+ foreach ($options as $type => $option) {
+ switch (true) {
+ case $option === self::APPLY_ALL:
+ case $isExternal && $option === self::APPLY_EXTERNAL:
+ case ! $isExternal && $option === self::APPLY_INTERNAL:
+ $link->data->append('attributes/rel', $type);
+ }
+ }
+
+ // No rel attributes? Mark the attribute as 'false' so LinkRenderer doesn't add defaults
+ if (! $link->data->has('attributes/rel')) {
+ $link->data->set('attributes/rel', false);
+ }
+ }
+
+ /**
+ * @internal This method is only public so we can easily test it. DO NOT USE THIS OUTSIDE OF THIS EXTENSION!
+ *
+ * @param non-empty-string|list $compareTo
+ */
+ public static function hostMatches(string $host, $compareTo): bool
+ {
+ foreach ((array) $compareTo as $c) {
+ if (\strpos($c, '/') === 0) {
+ if (\preg_match($c, $host)) {
+ return true;
+ }
+ } elseif ($c === $host) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Event/AnonymousFootnotesListener.php b/vendor/league/commonmark/src/Extension/Footnote/Event/AnonymousFootnotesListener.php
new file mode 100644
index 0000000..401613a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Event/AnonymousFootnotesListener.php
@@ -0,0 +1,62 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Extension\Footnote\Node\FootnoteBackref;
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Reference\Reference;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class AnonymousFootnotesListener implements ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+ foreach ($document->iterator() as $node) {
+ if (! $node instanceof FootnoteRef || ($text = $node->getContent()) === null) {
+ continue;
+ }
+
+ // Anonymous footnote needs to create a footnote from its content
+ $existingReference = $node->getReference();
+ $newReference = new Reference(
+ $existingReference->getLabel(),
+ '#' . $this->config->get('footnote/ref_id_prefix') . $existingReference->getLabel(),
+ $existingReference->getTitle()
+ );
+
+ $paragraph = new Paragraph();
+ $paragraph->appendChild(new Text($text));
+ $paragraph->appendChild(new FootnoteBackref($newReference));
+
+ $footnote = new Footnote($newReference);
+ $footnote->appendChild($paragraph);
+
+ $document->appendChild($footnote);
+ }
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Event/FixOrphanedFootnotesAndRefsListener.php b/vendor/league/commonmark/src/Extension/Footnote/Event/FixOrphanedFootnotesAndRefsListener.php
new file mode 100644
index 0000000..a0295b5
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Event/FixOrphanedFootnotesAndRefsListener.php
@@ -0,0 +1,68 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Inline\Text;
+
+final class FixOrphanedFootnotesAndRefsListener
+{
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+ $map = $this->buildMapOfKnownFootnotesAndRefs($document);
+
+ foreach ($map['_flat'] as $node) {
+ if ($node instanceof FootnoteRef && ! isset($map[Footnote::class][$node->getReference()->getLabel()])) {
+ // Found an orphaned FootnoteRef without a corresponding Footnote
+ // Restore the original footnote ref text
+ $node->replaceWith(new Text(\sprintf('[^%s]', $node->getReference()->getLabel())));
+ }
+
+ // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
+ if ($node instanceof Footnote && ! isset($map[FootnoteRef::class][$node->getReference()->getLabel()])) {
+ // Found an orphaned Footnote without a corresponding FootnoteRef
+ // Remove the footnote
+ $node->detach();
+ }
+ }
+ }
+
+ /** @phpstan-ignore-next-line */
+ private function buildMapOfKnownFootnotesAndRefs(Document $document): array // @phpcs:ignore
+ {
+ $map = [
+ Footnote::class => [],
+ FootnoteRef::class => [],
+ '_flat' => [],
+ ];
+
+ foreach ($document->iterator() as $node) {
+ if ($node instanceof Footnote) {
+ $map[Footnote::class][$node->getReference()->getLabel()] = true;
+
+ $map['_flat'][] = $node;
+ } elseif ($node instanceof FootnoteRef) {
+ $map[FootnoteRef::class][$node->getReference()->getLabel()] = true;
+
+ $map['_flat'][] = $node;
+ }
+ }
+
+ return $map;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Event/GatherFootnotesListener.php b/vendor/league/commonmark/src/Extension/Footnote/Event/GatherFootnotesListener.php
new file mode 100644
index 0000000..ae8d00b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Event/GatherFootnotesListener.php
@@ -0,0 +1,106 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Extension\Footnote\Node\FootnoteBackref;
+use League\CommonMark\Extension\Footnote\Node\FootnoteContainer;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\NodeIterator;
+use League\CommonMark\Reference\Reference;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class GatherFootnotesListener implements ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+ $footnotes = [];
+
+ foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ if (! $node instanceof Footnote) {
+ continue;
+ }
+
+ // Look for existing reference with footnote label
+ $ref = $document->getReferenceMap()->get($node->getReference()->getLabel());
+ if ($ref !== null) {
+ // Use numeric title to get footnotes order
+ $footnotes[(int) $ref->getTitle()] = $node;
+ } else {
+ // Footnote call is missing, append footnote at the end
+ $footnotes[\PHP_INT_MAX] = $node;
+ }
+
+ $key = '#' . $this->config->get('footnote/footnote_id_prefix') . $node->getReference()->getDestination();
+ if ($document->data->has($key)) {
+ $this->createBackrefs($node, $document->data->get($key));
+ }
+ }
+
+ // Only add a footnote container if there are any
+ if (\count($footnotes) === 0) {
+ return;
+ }
+
+ $container = $this->getFootnotesContainer($document);
+
+ \ksort($footnotes);
+ foreach ($footnotes as $footnote) {
+ $container->appendChild($footnote);
+ }
+ }
+
+ private function getFootnotesContainer(Document $document): FootnoteContainer
+ {
+ $footnoteContainer = new FootnoteContainer();
+ $document->appendChild($footnoteContainer);
+
+ return $footnoteContainer;
+ }
+
+ /**
+ * Look for all footnote refs pointing to this footnote and create each footnote backrefs.
+ *
+ * @param Footnote $node The target footnote
+ * @param Reference[] $backrefs References to create backrefs for
+ */
+ private function createBackrefs(Footnote $node, array $backrefs): void
+ {
+ // Backrefs should be added to the child paragraph
+ $target = $node->lastChild();
+ if ($target === null) {
+ // This should never happen, but you never know
+ $target = $node;
+ }
+
+ foreach ($backrefs as $backref) {
+ $target->appendChild(new FootnoteBackref(new Reference(
+ $backref->getLabel(),
+ '#' . $this->config->get('footnote/ref_id_prefix') . $backref->getLabel(),
+ $backref->getTitle()
+ )));
+ }
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Event/NumberFootnotesListener.php b/vendor/league/commonmark/src/Extension/Footnote/Event/NumberFootnotesListener.php
new file mode 100644
index 0000000..65600fa
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Event/NumberFootnotesListener.php
@@ -0,0 +1,75 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Event;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Reference\Reference;
+
+final class NumberFootnotesListener
+{
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+ $nextCounter = 1;
+ $usedLabels = [];
+ $usedCounters = [];
+
+ foreach ($document->iterator() as $node) {
+ if (! $node instanceof FootnoteRef) {
+ continue;
+ }
+
+ $existingReference = $node->getReference();
+ $label = $existingReference->getLabel();
+ $counter = $nextCounter;
+ $canIncrementCounter = true;
+
+ if (\array_key_exists($label, $usedLabels)) {
+ /*
+ * Reference is used again, we need to point
+ * to the same footnote. But with a different ID
+ */
+ $counter = $usedCounters[$label];
+ $label .= '__' . ++$usedLabels[$label];
+ $canIncrementCounter = false;
+ }
+
+ // rewrite reference title to use a numeric link
+ $newReference = new Reference(
+ $label,
+ $existingReference->getDestination(),
+ (string) $counter
+ );
+
+ // Override reference with numeric link
+ $node->setReference($newReference);
+ $document->getReferenceMap()->add($newReference);
+
+ /*
+ * Store created references in document for
+ * creating FootnoteBackrefs
+ */
+ $document->data->append($existingReference->getDestination(), $newReference);
+
+ $usedLabels[$label] = 1;
+ $usedCounters[$label] = $nextCounter;
+
+ if ($canIncrementCounter) {
+ $nextCounter++;
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/FootnoteExtension.php b/vendor/league/commonmark/src/Extension/Footnote/FootnoteExtension.php
new file mode 100644
index 0000000..0fa8038
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/FootnoteExtension.php
@@ -0,0 +1,70 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Extension\Footnote\Event\AnonymousFootnotesListener;
+use League\CommonMark\Extension\Footnote\Event\FixOrphanedFootnotesAndRefsListener;
+use League\CommonMark\Extension\Footnote\Event\GatherFootnotesListener;
+use League\CommonMark\Extension\Footnote\Event\NumberFootnotesListener;
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Extension\Footnote\Node\FootnoteBackref;
+use League\CommonMark\Extension\Footnote\Node\FootnoteContainer;
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Extension\Footnote\Parser\AnonymousFootnoteRefParser;
+use League\CommonMark\Extension\Footnote\Parser\FootnoteRefParser;
+use League\CommonMark\Extension\Footnote\Parser\FootnoteStartParser;
+use League\CommonMark\Extension\Footnote\Renderer\FootnoteBackrefRenderer;
+use League\CommonMark\Extension\Footnote\Renderer\FootnoteContainerRenderer;
+use League\CommonMark\Extension\Footnote\Renderer\FootnoteRefRenderer;
+use League\CommonMark\Extension\Footnote\Renderer\FootnoteRenderer;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class FootnoteExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('footnote', Expect::structure([
+ 'backref_class' => Expect::string('footnote-backref'),
+ 'backref_symbol' => Expect::string('↩'),
+ 'container_add_hr' => Expect::bool(true),
+ 'container_class' => Expect::string('footnotes'),
+ 'ref_class' => Expect::string('footnote-ref'),
+ 'ref_id_prefix' => Expect::string('fnref:'),
+ 'footnote_class' => Expect::string('footnote'),
+ 'footnote_id_prefix' => Expect::string('fn:'),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addBlockStartParser(new FootnoteStartParser(), 51);
+ $environment->addInlineParser(new AnonymousFootnoteRefParser(), 35);
+ $environment->addInlineParser(new FootnoteRefParser(), 51);
+
+ $environment->addRenderer(FootnoteContainer::class, new FootnoteContainerRenderer());
+ $environment->addRenderer(Footnote::class, new FootnoteRenderer());
+ $environment->addRenderer(FootnoteRef::class, new FootnoteRefRenderer());
+ $environment->addRenderer(FootnoteBackref::class, new FootnoteBackrefRenderer());
+
+ $environment->addEventListener(DocumentParsedEvent::class, [new AnonymousFootnotesListener(), 'onDocumentParsed'], 40);
+ $environment->addEventListener(DocumentParsedEvent::class, [new FixOrphanedFootnotesAndRefsListener(), 'onDocumentParsed'], 30);
+ $environment->addEventListener(DocumentParsedEvent::class, [new NumberFootnotesListener(), 'onDocumentParsed'], 20);
+ $environment->addEventListener(DocumentParsedEvent::class, [new GatherFootnotesListener(), 'onDocumentParsed'], 10);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Node/Footnote.php b/vendor/league/commonmark/src/Extension/Footnote/Node/Footnote.php
new file mode 100644
index 0000000..c3f77ca
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Node/Footnote.php
@@ -0,0 +1,37 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceableInterface;
+
+final class Footnote extends AbstractBlock implements ReferenceableInterface
+{
+ /** @psalm-readonly */
+ private ReferenceInterface $reference;
+
+ public function __construct(ReferenceInterface $reference)
+ {
+ parent::__construct();
+
+ $this->reference = $reference;
+ }
+
+ public function getReference(): ReferenceInterface
+ {
+ return $this->reference;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteBackref.php b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteBackref.php
new file mode 100644
index 0000000..f56daa5
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteBackref.php
@@ -0,0 +1,40 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Node;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceableInterface;
+
+/**
+ * Link from the footnote on the bottom of the document back to the reference
+ */
+final class FootnoteBackref extends AbstractInline implements ReferenceableInterface
+{
+ /** @psalm-readonly */
+ private ReferenceInterface $reference;
+
+ public function __construct(ReferenceInterface $reference)
+ {
+ parent::__construct();
+
+ $this->reference = $reference;
+ }
+
+ public function getReference(): ReferenceInterface
+ {
+ return $this->reference;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteContainer.php b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteContainer.php
new file mode 100644
index 0000000..af4ee35
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteContainer.php
@@ -0,0 +1,21 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class FootnoteContainer extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteRef.php b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteRef.php
new file mode 100644
index 0000000..429a1dc
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Node/FootnoteRef.php
@@ -0,0 +1,57 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Node;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceableInterface;
+
+final class FootnoteRef extends AbstractInline implements ReferenceableInterface
+{
+ private ReferenceInterface $reference;
+
+ /** @psalm-readonly */
+ private ?string $content = null;
+
+ /**
+ * @param array $data
+ */
+ public function __construct(ReferenceInterface $reference, ?string $content = null, array $data = [])
+ {
+ parent::__construct();
+
+ $this->reference = $reference;
+ $this->content = $content;
+
+ if (\count($data) > 0) {
+ $this->data->import($data);
+ }
+ }
+
+ public function getReference(): ReferenceInterface
+ {
+ return $this->reference;
+ }
+
+ public function setReference(ReferenceInterface $reference): void
+ {
+ $this->reference = $reference;
+ }
+
+ public function getContent(): ?string
+ {
+ return $this->content;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Parser/AnonymousFootnoteRefParser.php b/vendor/league/commonmark/src/Extension/Footnote/Parser/AnonymousFootnoteRefParser.php
new file mode 100644
index 0000000..4ed93da
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Parser/AnonymousFootnoteRefParser.php
@@ -0,0 +1,66 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Parser;
+
+use League\CommonMark\Environment\EnvironmentAwareInterface;
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Normalizer\TextNormalizerInterface;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Reference\Reference;
+use League\Config\ConfigurationInterface;
+
+final class AnonymousFootnoteRefParser implements InlineParserInterface, EnvironmentAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private TextNormalizerInterface $slugNormalizer;
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex('\^\[([^\]]+)\]');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+
+ [$label] = $inlineContext->getSubMatches();
+ $reference = $this->createReference($label);
+ $inlineContext->getContainer()->appendChild(new FootnoteRef($reference, $label));
+
+ return true;
+ }
+
+ private function createReference(string $label): Reference
+ {
+ $refLabel = $this->slugNormalizer->normalize($label, ['length' => 20]);
+
+ return new Reference(
+ $refLabel,
+ '#' . $this->config->get('footnote/footnote_id_prefix') . $refLabel,
+ $label
+ );
+ }
+
+ public function setEnvironment(EnvironmentInterface $environment): void
+ {
+ $this->config = $environment->getConfiguration();
+ $this->slugNormalizer = $environment->getSlugNormalizer();
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteParser.php b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteParser.php
new file mode 100644
index 0000000..2192546
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteParser.php
@@ -0,0 +1,68 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Parser;
+
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Reference\ReferenceInterface;
+
+final class FootnoteParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private Footnote $block;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?int $indentation = null;
+
+ public function __construct(ReferenceInterface $reference)
+ {
+ $this->block = new Footnote($reference);
+ }
+
+ public function getBlock(): Footnote
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($cursor->isBlank()) {
+ return BlockContinue::at($cursor);
+ }
+
+ if ($cursor->isIndented()) {
+ $this->indentation ??= $cursor->getIndent();
+ $cursor->advanceBy($this->indentation, true);
+
+ return BlockContinue::at($cursor);
+ }
+
+ return BlockContinue::none();
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteRefParser.php b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteRefParser.php
new file mode 100644
index 0000000..4032abd
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteRefParser.php
@@ -0,0 +1,57 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Parser;
+
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Reference\Reference;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class FootnoteRefParser implements InlineParserInterface, ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex('\[\^([^\s\]]+)\]');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+
+ [$label] = $inlineContext->getSubMatches();
+ $inlineContext->getContainer()->appendChild(new FootnoteRef($this->createReference($label)));
+
+ return true;
+ }
+
+ private function createReference(string $label): Reference
+ {
+ return new Reference(
+ $label,
+ '#' . $this->config->get('footnote/footnote_id_prefix') . $label,
+ $label
+ );
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteStartParser.php b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteStartParser.php
new file mode 100644
index 0000000..734e678
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Parser/FootnoteStartParser.php
@@ -0,0 +1,56 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Parser;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Reference\Reference;
+use League\CommonMark\Util\RegexHelper;
+
+final class FootnoteStartParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if ($cursor->isIndented() || $parserState->getLastMatchedBlockParser()->canHaveLazyContinuationLines()) {
+ return BlockStart::none();
+ }
+
+ $match = RegexHelper::matchFirst(
+ '/^\[\^([^\s^\]]+)\]\:(?:\s|$)/',
+ $cursor->getLine(),
+ $cursor->getNextNonSpacePosition()
+ );
+
+ if (! $match) {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+ $cursor->advanceBy(\strlen($match[0]));
+ $str = $cursor->getRemainder();
+ \preg_replace('/^\[\^([^\s^\]]+)\]\:(?:\s|$)/', '', $str);
+
+ if (\preg_match('/^\[\^([^\s^\]]+)\]\:(?:\s|$)/', $match[0], $matches) !== 1) {
+ return BlockStart::none();
+ }
+
+ $reference = new Reference($matches[1], $matches[1], $matches[1]);
+ $footnoteParser = new FootnoteParser($reference);
+
+ return BlockStart::of($footnoteParser)->at($cursor);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteBackrefRenderer.php b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteBackrefRenderer.php
new file mode 100644
index 0000000..3b7bc3c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteBackrefRenderer.php
@@ -0,0 +1,81 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Renderer;
+
+use League\CommonMark\Extension\Footnote\Node\FootnoteBackref;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class FootnoteBackrefRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ public const DEFAULT_SYMBOL = '↩';
+
+ private ConfigurationInterface $config;
+
+ /**
+ * @param FootnoteBackref $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ FootnoteBackref::assertInstanceOf($node);
+
+ $attrs = $node->data->getData('attributes');
+
+ $attrs->append('class', $this->config->get('footnote/backref_class'));
+ $attrs->set('rev', 'footnote');
+ $attrs->set('href', \mb_strtolower($node->getReference()->getDestination(), 'UTF-8'));
+ $attrs->set('role', 'doc-backlink');
+
+ $symbol = $this->config->get('footnote/backref_symbol');
+ \assert(\is_string($symbol));
+
+ return ' ' . new HtmlElement('a', $attrs->export(), \htmlspecialchars($symbol), true);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'footnote_backref';
+ }
+
+ /**
+ * @param FootnoteBackref $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ FootnoteBackref::assertInstanceOf($node);
+
+ return [
+ 'reference' => $node->getReference()->getLabel(),
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteContainerRenderer.php b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteContainerRenderer.php
new file mode 100644
index 0000000..74d35ef
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteContainerRenderer.php
@@ -0,0 +1,71 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Renderer;
+
+use League\CommonMark\Extension\Footnote\Node\FootnoteContainer;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class FootnoteContainerRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ /**
+ * @param FootnoteContainer $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ FootnoteContainer::assertInstanceOf($node);
+
+ $attrs = $node->data->getData('attributes');
+
+ $attrs->append('class', $this->config->get('footnote/container_class'));
+ $attrs->set('role', 'doc-endnotes');
+
+ $contents = new HtmlElement('ol', [], $childRenderer->renderNodes($node->children()));
+ if ($this->config->get('footnote/container_add_hr')) {
+ $contents = [new HtmlElement('hr', [], null, true), $contents];
+ }
+
+ return new HtmlElement('div', $attrs->export(), $contents);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'footnote_container';
+ }
+
+ /**
+ * @return array
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRefRenderer.php b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRefRenderer.php
new file mode 100644
index 0000000..c0c07d7
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRefRenderer.php
@@ -0,0 +1,87 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Renderer;
+
+use League\CommonMark\Extension\Footnote\Node\FootnoteRef;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class FootnoteRefRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ /**
+ * @param FootnoteRef $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ FootnoteRef::assertInstanceOf($node);
+
+ $attrs = $node->data->getData('attributes');
+ $attrs->append('class', $this->config->get('footnote/ref_class'));
+ $attrs->set('href', \mb_strtolower($node->getReference()->getDestination(), 'UTF-8'));
+ $attrs->set('role', 'doc-noteref');
+
+ $idPrefix = $this->config->get('footnote/ref_id_prefix');
+
+ return new HtmlElement(
+ 'sup',
+ [
+ 'id' => $idPrefix . \mb_strtolower($node->getReference()->getLabel(), 'UTF-8'),
+ ],
+ new HtmlElement(
+ 'a',
+ $attrs->export(),
+ $node->getReference()->getTitle()
+ ),
+ true
+ );
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'footnote_ref';
+ }
+
+ /**
+ * @param FootnoteRef $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ FootnoteRef::assertInstanceOf($node);
+
+ return [
+ 'reference' => $node->getReference()->getLabel(),
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRenderer.php b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRenderer.php
new file mode 100644
index 0000000..cdd027e
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Footnote/Renderer/FootnoteRenderer.php
@@ -0,0 +1,80 @@
+
+ * (c) Rezo Zero / Ambroise Maupate
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\Footnote\Renderer;
+
+use League\CommonMark\Extension\Footnote\Node\Footnote;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class FootnoteRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ private ConfigurationInterface $config;
+
+ /**
+ * @param Footnote $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Footnote::assertInstanceOf($node);
+
+ $attrs = $node->data->getData('attributes');
+
+ $attrs->append('class', $this->config->get('footnote/footnote_class'));
+ $attrs->set('id', $this->config->get('footnote/footnote_id_prefix') . \mb_strtolower($node->getReference()->getLabel(), 'UTF-8'));
+ $attrs->set('role', 'doc-endnote');
+
+ return new HtmlElement(
+ 'li',
+ $attrs->export(),
+ $childRenderer->renderNodes($node->children()),
+ true
+ );
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'footnote';
+ }
+
+ /**
+ * @param Footnote $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ Footnote::assertInstanceOf($node);
+
+ return [
+ 'reference' => $node->getReference()->getLabel(),
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Data/FrontMatterDataParserInterface.php b/vendor/league/commonmark/src/Extension/FrontMatter/Data/FrontMatterDataParserInterface.php
new file mode 100644
index 0000000..6e9db40
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Data/FrontMatterDataParserInterface.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\FrontMatter\Data;
+
+use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException;
+
+interface FrontMatterDataParserInterface
+{
+ /**
+ * @return mixed|null The parsed data (which may be null, if the input represents a null value)
+ *
+ * @throws InvalidFrontMatterException if parsing fails
+ */
+ public function parse(string $frontMatter);
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Data/LibYamlFrontMatterParser.php b/vendor/league/commonmark/src/Extension/FrontMatter/Data/LibYamlFrontMatterParser.php
new file mode 100644
index 0000000..b7194f4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Data/LibYamlFrontMatterParser.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\FrontMatter\Data;
+
+use League\CommonMark\Exception\MissingDependencyException;
+use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException;
+
+final class LibYamlFrontMatterParser implements FrontMatterDataParserInterface
+{
+ public static function capable(): ?LibYamlFrontMatterParser
+ {
+ if (! \extension_loaded('yaml')) {
+ return null;
+ }
+
+ return new LibYamlFrontMatterParser();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function parse(string $frontMatter)
+ {
+ if (! \extension_loaded('yaml')) {
+ throw new MissingDependencyException('Failed to parse yaml: "ext-yaml" extension is missing');
+ }
+
+ $result = @\yaml_parse($frontMatter);
+
+ if ($result === false) {
+ throw new InvalidFrontMatterException('Failed to parse front matter');
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Data/SymfonyYamlFrontMatterParser.php b/vendor/league/commonmark/src/Extension/FrontMatter/Data/SymfonyYamlFrontMatterParser.php
new file mode 100644
index 0000000..8d99d33
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Data/SymfonyYamlFrontMatterParser.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\FrontMatter\Data;
+
+use League\CommonMark\Exception\MissingDependencyException;
+use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+
+final class SymfonyYamlFrontMatterParser implements FrontMatterDataParserInterface
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function parse(string $frontMatter)
+ {
+ if (! \class_exists(Yaml::class)) {
+ throw new MissingDependencyException('Failed to parse yaml: "symfony/yaml" library is missing');
+ }
+
+ try {
+ /** @psalm-suppress ReservedWord */
+ return Yaml::parse($frontMatter);
+ } catch (ParseException $ex) {
+ throw InvalidFrontMatterException::wrap($ex);
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Exception/InvalidFrontMatterException.php b/vendor/league/commonmark/src/Extension/FrontMatter/Exception/InvalidFrontMatterException.php
new file mode 100644
index 0000000..ffe0c28
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Exception/InvalidFrontMatterException.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter\Exception;
+
+use League\CommonMark\Exception\CommonMarkException;
+
+class InvalidFrontMatterException extends \RuntimeException implements CommonMarkException
+{
+ public static function wrap(\Throwable $t): self
+ {
+ return new InvalidFrontMatterException('Failed to parse front matter: ' . $t->getMessage(), 0, $t);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterExtension.php b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterExtension.php
new file mode 100644
index 0000000..019ecb4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterExtension.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\FrontMatter;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentPreParsedEvent;
+use League\CommonMark\Event\DocumentRenderedEvent;
+use League\CommonMark\Extension\ExtensionInterface;
+use League\CommonMark\Extension\FrontMatter\Data\FrontMatterDataParserInterface;
+use League\CommonMark\Extension\FrontMatter\Data\LibYamlFrontMatterParser;
+use League\CommonMark\Extension\FrontMatter\Data\SymfonyYamlFrontMatterParser;
+use League\CommonMark\Extension\FrontMatter\Listener\FrontMatterPostRenderListener;
+use League\CommonMark\Extension\FrontMatter\Listener\FrontMatterPreParser;
+
+final class FrontMatterExtension implements ExtensionInterface
+{
+ /** @psalm-readonly */
+ private FrontMatterParserInterface $frontMatterParser;
+
+ public function __construct(?FrontMatterDataParserInterface $dataParser = null)
+ {
+ $this->frontMatterParser = new FrontMatterParser($dataParser ?? LibYamlFrontMatterParser::capable() ?? new SymfonyYamlFrontMatterParser());
+ }
+
+ public function getFrontMatterParser(): FrontMatterParserInterface
+ {
+ return $this->frontMatterParser;
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addEventListener(DocumentPreParsedEvent::class, new FrontMatterPreParser($this->frontMatterParser));
+ $environment->addEventListener(DocumentRenderedEvent::class, new FrontMatterPostRenderListener(), -500);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParser.php b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParser.php
new file mode 100644
index 0000000..69c41d1
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParser.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\FrontMatter;
+
+use League\CommonMark\Extension\FrontMatter\Data\FrontMatterDataParserInterface;
+use League\CommonMark\Extension\FrontMatter\Exception\InvalidFrontMatterException;
+use League\CommonMark\Extension\FrontMatter\Input\MarkdownInputWithFrontMatter;
+use League\CommonMark\Parser\Cursor;
+
+final class FrontMatterParser implements FrontMatterParserInterface
+{
+ /** @psalm-readonly */
+ private FrontMatterDataParserInterface $frontMatterParser;
+
+ private const REGEX_FRONT_MATTER = '/^---\\R.*?\\R---\\R/s';
+
+ public function __construct(FrontMatterDataParserInterface $frontMatterParser)
+ {
+ $this->frontMatterParser = $frontMatterParser;
+ }
+
+ /**
+ * @throws InvalidFrontMatterException if the front matter cannot be parsed
+ */
+ public function parse(string $markdownContent): MarkdownInputWithFrontMatter
+ {
+ $cursor = new Cursor($markdownContent);
+
+ // Locate the front matter
+ $frontMatter = $cursor->match(self::REGEX_FRONT_MATTER);
+ if ($frontMatter === null) {
+ return new MarkdownInputWithFrontMatter($markdownContent);
+ }
+
+ // Trim the last line (ending ---s and newline)
+ $frontMatter = \preg_replace('/---\R$/', '', $frontMatter);
+ if ($frontMatter === null) {
+ return new MarkdownInputWithFrontMatter($markdownContent);
+ }
+
+ // Parse the resulting YAML data
+ $data = $this->frontMatterParser->parse($frontMatter);
+
+ // Advance through any remaining newlines which separated the front matter from the Markdown text
+ $trailingNewlines = $cursor->match('/^\R+/');
+
+ // Calculate how many lines the Markdown is offset from the front matter by counting the number of newlines
+ // Don't forget to add 1 because we stripped one out when trimming the trailing delims
+ $lineOffset = \preg_match_all('/\R/', $frontMatter . $trailingNewlines) + 1;
+
+ return new MarkdownInputWithFrontMatter($cursor->getRemainder(), $lineOffset, $data);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParserInterface.php b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParserInterface.php
new file mode 100644
index 0000000..197a33b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterParserInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter;
+
+use League\CommonMark\Extension\FrontMatter\Input\MarkdownInputWithFrontMatter;
+
+interface FrontMatterParserInterface
+{
+ public function parse(string $markdownContent): MarkdownInputWithFrontMatter;
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterProviderInterface.php b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterProviderInterface.php
new file mode 100644
index 0000000..b5a7278
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/FrontMatterProviderInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter;
+
+interface FrontMatterProviderInterface
+{
+ /**
+ * @return mixed|null
+ */
+ public function getFrontMatter();
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Input/MarkdownInputWithFrontMatter.php b/vendor/league/commonmark/src/Extension/FrontMatter/Input/MarkdownInputWithFrontMatter.php
new file mode 100644
index 0000000..86c982b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Input/MarkdownInputWithFrontMatter.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter\Input;
+
+use League\CommonMark\Extension\FrontMatter\FrontMatterProviderInterface;
+use League\CommonMark\Input\MarkdownInput;
+
+final class MarkdownInputWithFrontMatter extends MarkdownInput implements FrontMatterProviderInterface
+{
+ /** @var mixed|null */
+ private $frontMatter;
+
+ /**
+ * @param string $content Markdown content without the raw front matter
+ * @param int $lineOffset Line offset (based on number of front matter lines removed)
+ * @param mixed|null $frontMatter Parsed front matter
+ */
+ public function __construct(string $content, int $lineOffset = 0, $frontMatter = null)
+ {
+ parent::__construct($content, $lineOffset);
+
+ $this->frontMatter = $frontMatter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getFrontMatter()
+ {
+ return $this->frontMatter;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPostRenderListener.php b/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPostRenderListener.php
new file mode 100644
index 0000000..14b7191
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPostRenderListener.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter\Listener;
+
+use League\CommonMark\Event\DocumentRenderedEvent;
+use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
+
+final class FrontMatterPostRenderListener
+{
+ public function __invoke(DocumentRenderedEvent $event): void
+ {
+ if ($event->getOutput()->getDocument()->data->get('front_matter', null) === null) {
+ return;
+ }
+
+ $frontMatter = $event->getOutput()->getDocument()->data->get('front_matter');
+
+ $event->replaceOutput(new RenderedContentWithFrontMatter(
+ $event->getOutput()->getDocument(),
+ $event->getOutput()->getContent(),
+ $frontMatter
+ ));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPreParser.php b/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPreParser.php
new file mode 100644
index 0000000..b0afbee
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Listener/FrontMatterPreParser.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter\Listener;
+
+use League\CommonMark\Event\DocumentPreParsedEvent;
+use League\CommonMark\Extension\FrontMatter\FrontMatterParserInterface;
+
+final class FrontMatterPreParser
+{
+ private FrontMatterParserInterface $parser;
+
+ public function __construct(FrontMatterParserInterface $parser)
+ {
+ $this->parser = $parser;
+ }
+
+ public function __invoke(DocumentPreParsedEvent $event): void
+ {
+ $content = $event->getMarkdown()->getContent();
+
+ $parsed = $this->parser->parse($content);
+
+ $event->getDocument()->data->set('front_matter', $parsed->getFrontMatter());
+ $event->replaceMarkdown($parsed);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/FrontMatter/Output/RenderedContentWithFrontMatter.php b/vendor/league/commonmark/src/Extension/FrontMatter/Output/RenderedContentWithFrontMatter.php
new file mode 100644
index 0000000..efaa342
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/FrontMatter/Output/RenderedContentWithFrontMatter.php
@@ -0,0 +1,51 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Extension\FrontMatter\Output;
+
+use League\CommonMark\Extension\FrontMatter\FrontMatterProviderInterface;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Output\RenderedContent;
+
+/**
+ * @psalm-immutable
+ */
+final class RenderedContentWithFrontMatter extends RenderedContent implements FrontMatterProviderInterface
+{
+ /**
+ * @var mixed
+ *
+ * @psalm-readonly
+ */
+ private $frontMatter;
+
+ /**
+ * @param Document $document The parsed Document object
+ * @param string $content The final HTML
+ * @param mixed|null $frontMatter Any parsed front matter
+ */
+ public function __construct(Document $document, string $content, $frontMatter)
+ {
+ parent::__construct($document, $content);
+
+ $this->frontMatter = $frontMatter;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getFrontMatter()
+ {
+ return $this->frontMatter;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/GithubFlavoredMarkdownExtension.php b/vendor/league/commonmark/src/Extension/GithubFlavoredMarkdownExtension.php
new file mode 100644
index 0000000..b3920aa
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/GithubFlavoredMarkdownExtension.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\Autolink\AutolinkExtension;
+use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension;
+use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
+use League\CommonMark\Extension\Table\TableExtension;
+use League\CommonMark\Extension\TaskList\TaskListExtension;
+
+final class GithubFlavoredMarkdownExtension implements ExtensionInterface
+{
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addExtension(new AutolinkExtension());
+ $environment->addExtension(new DisallowedRawHtmlExtension());
+ $environment->addExtension(new StrikethroughExtension());
+ $environment->addExtension(new TableExtension());
+ $environment->addExtension(new TaskListExtension());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalink.php b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalink.php
new file mode 100644
index 0000000..df9bded
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalink.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\HeadingPermalink;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+
+/**
+ * Represents an anchor link within a heading
+ */
+final class HeadingPermalink extends AbstractInline
+{
+ /** @psalm-readonly */
+ private string $slug;
+
+ public function __construct(string $slug)
+ {
+ parent::__construct();
+
+ $this->slug = $slug;
+ }
+
+ public function getSlug(): string
+ {
+ return $this->slug;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkExtension.php b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkExtension.php
new file mode 100644
index 0000000..96473a2
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkExtension.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\HeadingPermalink;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+/**
+ * Extension which automatically anchor links to heading elements
+ */
+final class HeadingPermalinkExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('heading_permalink', Expect::structure([
+ 'min_heading_level' => Expect::int()->min(1)->max(6)->default(1),
+ 'max_heading_level' => Expect::int()->min(1)->max(6)->default(6),
+ 'insert' => Expect::anyOf(HeadingPermalinkProcessor::INSERT_BEFORE, HeadingPermalinkProcessor::INSERT_AFTER, HeadingPermalinkProcessor::INSERT_NONE)->default(HeadingPermalinkProcessor::INSERT_BEFORE),
+ 'id_prefix' => Expect::string()->default('content'),
+ 'apply_id_to_heading' => Expect::bool()->default(false),
+ 'heading_class' => Expect::string()->default(''),
+ 'fragment_prefix' => Expect::string()->default('content'),
+ 'html_class' => Expect::string()->default('heading-permalink'),
+ 'title' => Expect::string()->default('Permalink'),
+ 'symbol' => Expect::string()->default(HeadingPermalinkRenderer::DEFAULT_SYMBOL),
+ 'aria_hidden' => Expect::bool()->default(true),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addEventListener(DocumentParsedEvent::class, new HeadingPermalinkProcessor(), -100);
+ $environment->addRenderer(HeadingPermalink::class, new HeadingPermalinkRenderer());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkProcessor.php b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkProcessor.php
new file mode 100644
index 0000000..871aa21
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkProcessor.php
@@ -0,0 +1,101 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\HeadingPermalink;
+
+use League\CommonMark\Environment\EnvironmentAwareInterface;
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
+use League\CommonMark\Node\NodeIterator;
+use League\CommonMark\Node\RawMarkupContainerInterface;
+use League\CommonMark\Node\StringContainerHelper;
+use League\CommonMark\Normalizer\TextNormalizerInterface;
+use League\Config\ConfigurationInterface;
+use League\Config\Exception\InvalidConfigurationException;
+
+/**
+ * Searches the Document for Heading elements and adds HeadingPermalinks to each one
+ */
+final class HeadingPermalinkProcessor implements EnvironmentAwareInterface
+{
+ public const INSERT_BEFORE = 'before';
+ public const INSERT_AFTER = 'after';
+ public const INSERT_NONE = 'none';
+
+ /** @psalm-readonly-allow-private-mutation */
+ private TextNormalizerInterface $slugNormalizer;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function setEnvironment(EnvironmentInterface $environment): void
+ {
+ $this->config = $environment->getConfiguration();
+ $this->slugNormalizer = $environment->getSlugNormalizer();
+ }
+
+ public function __invoke(DocumentParsedEvent $e): void
+ {
+ $min = (int) $this->config->get('heading_permalink/min_heading_level');
+ $max = (int) $this->config->get('heading_permalink/max_heading_level');
+ $applyToHeading = (bool) $this->config->get('heading_permalink/apply_id_to_heading');
+ $idPrefix = (string) $this->config->get('heading_permalink/id_prefix');
+ $slugLength = (int) $this->config->get('slug_normalizer/max_length');
+ $headingClass = (string) $this->config->get('heading_permalink/heading_class');
+
+ if ($idPrefix !== '') {
+ $idPrefix .= '-';
+ }
+
+ foreach ($e->getDocument()->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ if ($node instanceof Heading && $node->getLevel() >= $min && $node->getLevel() <= $max) {
+ $this->addHeadingLink($node, $slugLength, $idPrefix, $applyToHeading, $headingClass);
+ }
+ }
+ }
+
+ private function addHeadingLink(Heading $heading, int $slugLength, string $idPrefix, bool $applyToHeading, string $headingClass): void
+ {
+ $text = StringContainerHelper::getChildText($heading, [RawMarkupContainerInterface::class]);
+ $slug = $this->slugNormalizer->normalize($text, [
+ 'node' => $heading,
+ 'length' => $slugLength,
+ ]);
+
+ if ($applyToHeading) {
+ $heading->data->set('attributes/id', $idPrefix . $slug);
+ }
+
+ if ($headingClass !== '') {
+ $heading->data->append('attributes/class', $headingClass);
+ }
+
+ $headingLinkAnchor = new HeadingPermalink($slug);
+
+ switch ($this->config->get('heading_permalink/insert')) {
+ case self::INSERT_BEFORE:
+ $heading->prependChild($headingLinkAnchor);
+
+ return;
+ case self::INSERT_AFTER:
+ $heading->appendChild($headingLinkAnchor);
+
+ return;
+ case self::INSERT_NONE:
+ return;
+ default:
+ throw new InvalidConfigurationException("Invalid configuration value for heading_permalink/insert; expected 'before', 'after', or 'none'");
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkRenderer.php b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkRenderer.php
new file mode 100644
index 0000000..59a86a1
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/HeadingPermalink/HeadingPermalinkRenderer.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\HeadingPermalink;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+/**
+ * Renders the HeadingPermalink elements
+ */
+final class HeadingPermalinkRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ public const DEFAULT_SYMBOL = '¶';
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ /**
+ * @param HeadingPermalink $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ HeadingPermalink::assertInstanceOf($node);
+
+ $slug = $node->getSlug();
+
+ $fragmentPrefix = (string) $this->config->get('heading_permalink/fragment_prefix');
+ if ($fragmentPrefix !== '') {
+ $fragmentPrefix .= '-';
+ }
+
+ $attrs = $node->data->getData('attributes');
+ $appendId = ! $this->config->get('heading_permalink/apply_id_to_heading');
+
+ if ($appendId) {
+ $idPrefix = (string) $this->config->get('heading_permalink/id_prefix');
+
+ if ($idPrefix !== '') {
+ $idPrefix .= '-';
+ }
+
+ $attrs->set('id', $idPrefix . $slug);
+ }
+
+ $attrs->set('href', '#' . $fragmentPrefix . $slug);
+ $attrs->append('class', $this->config->get('heading_permalink/html_class'));
+
+ $hidden = $this->config->get('heading_permalink/aria_hidden');
+ if ($hidden) {
+ $attrs->set('aria-hidden', 'true');
+ }
+
+ $attrs->set('title', $this->config->get('heading_permalink/title'));
+
+ $symbol = $this->config->get('heading_permalink/symbol');
+ \assert(\is_string($symbol));
+
+ return new HtmlElement('a', $attrs->export(), \htmlspecialchars($symbol), false);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'heading_permalink';
+ }
+
+ /**
+ * @param HeadingPermalink $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ HeadingPermalink::assertInstanceOf($node);
+
+ return [
+ 'slug' => $node->getSlug(),
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/InlinesOnly/ChildRenderer.php b/vendor/league/commonmark/src/Extension/InlinesOnly/ChildRenderer.php
new file mode 100644
index 0000000..403e948
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/InlinesOnly/ChildRenderer.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\InlinesOnly;
+
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+
+/**
+ * Simply renders child elements as-is, adding newlines as needed.
+ */
+final class ChildRenderer implements NodeRendererInterface
+{
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ $out = $childRenderer->renderNodes($node->children());
+ if (! $node instanceof Document) {
+ $out .= $childRenderer->getBlockSeparator();
+ }
+
+ return $out;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/InlinesOnly/InlinesOnlyExtension.php b/vendor/league/commonmark/src/Extension/InlinesOnly/InlinesOnlyExtension.php
new file mode 100644
index 0000000..7777510
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/InlinesOnly/InlinesOnlyExtension.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\InlinesOnly;
+
+use League\CommonMark as Core;
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\CommonMark;
+use League\CommonMark\Extension\CommonMark\Delimiter\Processor\EmphasisDelimiterProcessor;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class InlinesOnlyExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('commonmark', Expect::structure([
+ 'use_asterisk' => Expect::bool(true),
+ 'use_underscore' => Expect::bool(true),
+ 'enable_strong' => Expect::bool(true),
+ 'enable_em' => Expect::bool(true),
+ ]));
+ }
+
+ // phpcs:disable Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma,Squiz.WhiteSpace.SemicolonSpacing.Incorrect
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $childRenderer = new ChildRenderer();
+
+ $environment
+ ->addInlineParser(new Core\Parser\Inline\NewlineParser(), 200)
+ ->addInlineParser(new CommonMark\Parser\Inline\BacktickParser(), 150)
+ ->addInlineParser(new CommonMark\Parser\Inline\EscapableParser(), 80)
+ ->addInlineParser(new CommonMark\Parser\Inline\EntityParser(), 70)
+ ->addInlineParser(new CommonMark\Parser\Inline\AutolinkParser(), 50)
+ ->addInlineParser(new CommonMark\Parser\Inline\HtmlInlineParser(), 40)
+ ->addInlineParser(new CommonMark\Parser\Inline\CloseBracketParser(), 30)
+ ->addInlineParser(new CommonMark\Parser\Inline\OpenBracketParser(), 20)
+ ->addInlineParser(new CommonMark\Parser\Inline\BangParser(), 10)
+
+ ->addRenderer(Core\Node\Block\Document::class, $childRenderer, 0)
+ ->addRenderer(Core\Node\Block\Paragraph::class, $childRenderer, 0)
+
+ ->addRenderer(CommonMark\Node\Inline\Code::class, new CommonMark\Renderer\Inline\CodeRenderer(), 0)
+ ->addRenderer(CommonMark\Node\Inline\Emphasis::class, new CommonMark\Renderer\Inline\EmphasisRenderer(), 0)
+ ->addRenderer(CommonMark\Node\Inline\HtmlInline::class, new CommonMark\Renderer\Inline\HtmlInlineRenderer(), 0)
+ ->addRenderer(CommonMark\Node\Inline\Image::class, new CommonMark\Renderer\Inline\ImageRenderer(), 0)
+ ->addRenderer(CommonMark\Node\Inline\Link::class, new CommonMark\Renderer\Inline\LinkRenderer(), 0)
+ ->addRenderer(Core\Node\Inline\Newline::class, new Core\Renderer\Inline\NewlineRenderer(), 0)
+ ->addRenderer(CommonMark\Node\Inline\Strong::class, new CommonMark\Renderer\Inline\StrongRenderer(), 0)
+ ->addRenderer(Core\Node\Inline\Text::class, new Core\Renderer\Inline\TextRenderer(), 0)
+ ;
+
+ if ($environment->getConfiguration()->get('commonmark/use_asterisk')) {
+ $environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('*'));
+ }
+
+ if ($environment->getConfiguration()->get('commonmark/use_underscore')) {
+ $environment->addDelimiterProcessor(new EmphasisDelimiterProcessor('_'));
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/Generator/CallbackGenerator.php b/vendor/league/commonmark/src/Extension/Mention/Generator/CallbackGenerator.php
new file mode 100644
index 0000000..d0b6292
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/Generator/CallbackGenerator.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention\Generator;
+
+use League\CommonMark\Exception\LogicException;
+use League\CommonMark\Extension\Mention\Mention;
+use League\CommonMark\Node\Inline\AbstractInline;
+
+final class CallbackGenerator implements MentionGeneratorInterface
+{
+ /**
+ * A callback function which sets the URL on the passed mention and returns the mention, return a new AbstractInline based object or null if the mention is not a match
+ *
+ * @var callable(Mention): ?AbstractInline
+ */
+ private $callback;
+
+ public function __construct(callable $callback)
+ {
+ $this->callback = $callback;
+ }
+
+ /**
+ * @throws LogicException
+ */
+ public function generateMention(Mention $mention): ?AbstractInline
+ {
+ $result = \call_user_func($this->callback, $mention);
+ if ($result === null) {
+ return null;
+ }
+
+ if ($result instanceof AbstractInline && ! ($result instanceof Mention)) {
+ return $result;
+ }
+
+ if ($result instanceof Mention && $result->hasUrl()) {
+ return $mention;
+ }
+
+ throw new LogicException('CallbackGenerator callable must set the URL on the passed mention and return the mention, return a new AbstractInline based object or null if the mention is not a match');
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/Generator/MentionGeneratorInterface.php b/vendor/league/commonmark/src/Extension/Mention/Generator/MentionGeneratorInterface.php
new file mode 100644
index 0000000..30d4a51
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/Generator/MentionGeneratorInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention\Generator;
+
+use League\CommonMark\Extension\Mention\Mention;
+use League\CommonMark\Node\Inline\AbstractInline;
+
+interface MentionGeneratorInterface
+{
+ public function generateMention(Mention $mention): ?AbstractInline;
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/Generator/StringTemplateLinkGenerator.php b/vendor/league/commonmark/src/Extension/Mention/Generator/StringTemplateLinkGenerator.php
new file mode 100644
index 0000000..5d92897
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/Generator/StringTemplateLinkGenerator.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention\Generator;
+
+use League\CommonMark\Extension\Mention\Mention;
+use League\CommonMark\Node\Inline\AbstractInline;
+
+final class StringTemplateLinkGenerator implements MentionGeneratorInterface
+{
+ private string $urlTemplate;
+
+ public function __construct(string $urlTemplate)
+ {
+ $this->urlTemplate = $urlTemplate;
+ }
+
+ public function generateMention(Mention $mention): ?AbstractInline
+ {
+ $mention->setUrl(\sprintf($this->urlTemplate, $mention->getIdentifier()));
+
+ return $mention;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/Mention.php b/vendor/league/commonmark/src/Extension/Mention/Mention.php
new file mode 100644
index 0000000..74eaee4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/Mention.php
@@ -0,0 +1,93 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention;
+
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Node\Inline\Text;
+
+class Mention extends Link
+{
+ private string $name;
+
+ private string $prefix;
+
+ private string $identifier;
+
+ public function __construct(string $name, string $prefix, string $identifier, ?string $label = null)
+ {
+ $this->name = $name;
+ $this->prefix = $prefix;
+ $this->identifier = $identifier;
+
+ parent::__construct('', $label ?? \sprintf('%s%s', $prefix, $identifier));
+ }
+
+ public function getLabel(): ?string
+ {
+ if (($labelNode = $this->findLabelNode()) === null) {
+ return null;
+ }
+
+ return $labelNode->getLiteral();
+ }
+
+ public function getIdentifier(): string
+ {
+ return $this->identifier;
+ }
+
+ public function getName(): ?string
+ {
+ return $this->name;
+ }
+
+ public function getPrefix(): string
+ {
+ return $this->prefix;
+ }
+
+ public function hasUrl(): bool
+ {
+ return $this->url !== '';
+ }
+
+ /**
+ * @return $this
+ */
+ public function setLabel(string $label): self
+ {
+ if (($labelNode = $this->findLabelNode()) === null) {
+ $labelNode = new Text();
+ $this->prependChild($labelNode);
+ }
+
+ $labelNode->setLiteral($label);
+
+ return $this;
+ }
+
+ private function findLabelNode(): ?Text
+ {
+ foreach ($this->children() as $child) {
+ if ($child instanceof Text) {
+ return $child;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/MentionExtension.php b/vendor/league/commonmark/src/Extension/Mention/MentionExtension.php
new file mode 100644
index 0000000..c848c26
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/MentionExtension.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Extension\Mention\Generator\MentionGeneratorInterface;
+use League\Config\ConfigurationBuilderInterface;
+use League\Config\Exception\InvalidConfigurationException;
+use Nette\Schema\Expect;
+
+final class MentionExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $isAValidPartialRegex = static function (string $regex): bool {
+ $regex = '/' . $regex . '/i';
+
+ return @\preg_match($regex, '') !== false;
+ };
+
+ $builder->addSchema('mentions', Expect::arrayOf(
+ Expect::structure([
+ 'prefix' => Expect::string()->required(),
+ 'pattern' => Expect::string()->assert($isAValidPartialRegex, 'Pattern must not include starting/ending delimiters (like "/")')->required(),
+ 'generator' => Expect::anyOf(
+ Expect::type(MentionGeneratorInterface::class),
+ Expect::string(),
+ Expect::type('callable')
+ )->required(),
+ ])
+ ));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $mentions = $environment->getConfiguration()->get('mentions');
+ foreach ($mentions as $name => $mention) {
+ if ($mention['generator'] instanceof MentionGeneratorInterface) {
+ $environment->addInlineParser(new MentionParser($name, $mention['prefix'], $mention['pattern'], $mention['generator']));
+ } elseif (\is_string($mention['generator'])) {
+ $environment->addInlineParser(MentionParser::createWithStringTemplate($name, $mention['prefix'], $mention['pattern'], $mention['generator']));
+ } elseif (\is_callable($mention['generator'])) {
+ $environment->addInlineParser(MentionParser::createWithCallback($name, $mention['prefix'], $mention['pattern'], $mention['generator']));
+ } else {
+ throw new InvalidConfigurationException(\sprintf('The "generator" provided for the "%s" MentionParser configuration must be a string template, callable, or an object that implements %s.', $name, MentionGeneratorInterface::class));
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Mention/MentionParser.php b/vendor/league/commonmark/src/Extension/Mention/MentionParser.php
new file mode 100644
index 0000000..a81c787
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Mention/MentionParser.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Mention;
+
+use League\CommonMark\Extension\Mention\Generator\CallbackGenerator;
+use League\CommonMark\Extension\Mention\Generator\MentionGeneratorInterface;
+use League\CommonMark\Extension\Mention\Generator\StringTemplateLinkGenerator;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class MentionParser implements InlineParserInterface
+{
+ /** @psalm-readonly */
+ private string $name;
+
+ /** @psalm-readonly */
+ private string $prefix;
+
+ /** @psalm-readonly */
+ private string $identifierPattern;
+
+ /** @psalm-readonly */
+ private MentionGeneratorInterface $mentionGenerator;
+
+ public function __construct(string $name, string $prefix, string $identifierPattern, MentionGeneratorInterface $mentionGenerator)
+ {
+ $this->name = $name;
+ $this->prefix = $prefix;
+ $this->identifierPattern = $identifierPattern;
+ $this->mentionGenerator = $mentionGenerator;
+ }
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::join(
+ InlineParserMatch::string($this->prefix),
+ InlineParserMatch::regex($this->identifierPattern)
+ );
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $cursor = $inlineContext->getCursor();
+
+ // The prefix must not have any other characters immediately prior
+ $previousChar = $cursor->peek(-1);
+ if ($previousChar !== null && \preg_match('/\w/', $previousChar)) {
+ // peek() doesn't modify the cursor, so no need to restore state first
+ return false;
+ }
+
+ [$prefix, $identifier] = $inlineContext->getSubMatches();
+
+ $mention = $this->mentionGenerator->generateMention(new Mention($this->name, $prefix, $identifier));
+
+ if ($mention === null) {
+ return false;
+ }
+
+ $cursor->advanceBy($inlineContext->getFullMatchLength());
+ $inlineContext->getContainer()->appendChild($mention);
+
+ return true;
+ }
+
+ public static function createWithStringTemplate(string $name, string $prefix, string $mentionRegex, string $urlTemplate): MentionParser
+ {
+ return new self($name, $prefix, $mentionRegex, new StringTemplateLinkGenerator($urlTemplate));
+ }
+
+ public static function createWithCallback(string $name, string $prefix, string $mentionRegex, callable $callback): MentionParser
+ {
+ return new self($name, $prefix, $mentionRegex, new CallbackGenerator($callback));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/DashParser.php b/vendor/league/commonmark/src/Extension/SmartPunct/DashParser.php
new file mode 100644
index 0000000..cf0e1af
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/DashParser.php
@@ -0,0 +1,59 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class DashParser implements InlineParserInterface
+{
+ private const EN_DASH = '–';
+ private const EM_DASH = '—';
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex('(?getFullMatchLength();
+ $inlineContext->getCursor()->advanceBy($count);
+
+ $enCount = 0;
+ $emCount = 0;
+ if ($count % 3 === 0) { // If divisible by 3, use all em dashes
+ $emCount = (int) ($count / 3);
+ } elseif ($count % 2 === 0) { // If divisible by 2, use all en dashes
+ $enCount = (int) ($count / 2);
+ } elseif ($count % 3 === 2) { // If 2 extra dashes, use en dash for last 2; em dashes for rest
+ $emCount = (int) (($count - 2) / 3);
+ $enCount = 1;
+ } else { // Use en dashes for last 4 hyphens; em dashes for rest
+ $emCount = (int) (($count - 4) / 3);
+ $enCount = 2;
+ }
+
+ $inlineContext->getContainer()->appendChild(new Text(
+ \str_repeat(self::EM_DASH, $emCount) . \str_repeat(self::EN_DASH, $enCount)
+ ));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/EllipsesParser.php b/vendor/league/commonmark/src/Extension/SmartPunct/EllipsesParser.php
new file mode 100644
index 0000000..9f5b3bd
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/EllipsesParser.php
@@ -0,0 +1,38 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class EllipsesParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::oneOf('...', '. . .');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy($inlineContext->getFullMatchLength());
+ $inlineContext->getContainer()->appendChild(new Text('…'));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/Quote.php b/vendor/league/commonmark/src/Extension/SmartPunct/Quote.php
new file mode 100644
index 0000000..dee9759
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/Quote.php
@@ -0,0 +1,30 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+final class Quote extends AbstractStringContainer
+{
+ public const DOUBLE_QUOTE = '"';
+ public const DOUBLE_QUOTE_OPENER = '“';
+ public const DOUBLE_QUOTE_CLOSER = '”';
+
+ public const SINGLE_QUOTE = "'";
+ public const SINGLE_QUOTE_OPENER = '‘';
+ public const SINGLE_QUOTE_CLOSER = '’';
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/QuoteParser.php b/vendor/league/commonmark/src/Extension/SmartPunct/QuoteParser.php
new file mode 100644
index 0000000..31ba8c7
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/QuoteParser.php
@@ -0,0 +1,98 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Delimiter\Delimiter;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+use League\CommonMark\Util\RegexHelper;
+
+final class QuoteParser implements InlineParserInterface
+{
+ /**
+ * @deprecated This constant is no longer used and will be removed in a future major release
+ */
+ public const DOUBLE_QUOTES = [Quote::DOUBLE_QUOTE, Quote::DOUBLE_QUOTE_OPENER, Quote::DOUBLE_QUOTE_CLOSER];
+
+ /**
+ * @deprecated This constant is no longer used and will be removed in a future major release
+ */
+ public const SINGLE_QUOTES = [Quote::SINGLE_QUOTE, Quote::SINGLE_QUOTE_OPENER, Quote::SINGLE_QUOTE_CLOSER];
+
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::oneOf(Quote::SINGLE_QUOTE, Quote::DOUBLE_QUOTE);
+ }
+
+ /**
+ * Normalizes any quote characters found and manually adds them to the delimiter stack
+ */
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $char = $inlineContext->getFullMatch();
+ $cursor = $inlineContext->getCursor();
+ $index = $cursor->getPosition();
+
+ $charBefore = $cursor->peek(-1);
+ if ($charBefore === null) {
+ $charBefore = "\n";
+ }
+
+ $cursor->advance();
+
+ $charAfter = $cursor->getCurrentCharacter();
+ if ($charAfter === null) {
+ $charAfter = "\n";
+ }
+
+ [$leftFlanking, $rightFlanking] = $this->determineFlanking($charBefore, $charAfter);
+ $canOpen = $leftFlanking && ! $rightFlanking;
+ $canClose = $rightFlanking;
+
+ $node = new Quote($char, ['delim' => true]);
+ $inlineContext->getContainer()->appendChild($node);
+
+ // Add entry to stack to this opener
+ $inlineContext->getDelimiterStack()->push(new Delimiter($char, 1, $node, $canOpen, $canClose, $index));
+
+ return true;
+ }
+
+ /**
+ * @return bool[]
+ */
+ private function determineFlanking(string $charBefore, string $charAfter): array
+ {
+ $afterIsWhitespace = \preg_match('/\pZ|\s/u', $charAfter);
+ $afterIsPunctuation = \preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter);
+ $beforeIsWhitespace = \preg_match('/\pZ|\s/u', $charBefore);
+ $beforeIsPunctuation = \preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore);
+
+ $leftFlanking = ! $afterIsWhitespace &&
+ ! ($afterIsPunctuation &&
+ ! $beforeIsWhitespace &&
+ ! $beforeIsPunctuation);
+
+ $rightFlanking = ! $beforeIsWhitespace &&
+ ! ($beforeIsPunctuation &&
+ ! $afterIsWhitespace &&
+ ! $afterIsPunctuation);
+
+ return [$leftFlanking, $rightFlanking];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/QuoteProcessor.php b/vendor/league/commonmark/src/Extension/SmartPunct/QuoteProcessor.php
new file mode 100644
index 0000000..1fc30d4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/QuoteProcessor.php
@@ -0,0 +1,82 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+use League\CommonMark\Delimiter\Processor\DelimiterProcessorInterface;
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+final class QuoteProcessor implements DelimiterProcessorInterface
+{
+ /** @psalm-readonly */
+ private string $normalizedCharacter;
+
+ /** @psalm-readonly */
+ private string $openerCharacter;
+
+ /** @psalm-readonly */
+ private string $closerCharacter;
+
+ private function __construct(string $char, string $opener, string $closer)
+ {
+ $this->normalizedCharacter = $char;
+ $this->openerCharacter = $opener;
+ $this->closerCharacter = $closer;
+ }
+
+ public function getOpeningCharacter(): string
+ {
+ return $this->normalizedCharacter;
+ }
+
+ public function getClosingCharacter(): string
+ {
+ return $this->normalizedCharacter;
+ }
+
+ public function getMinLength(): int
+ {
+ return 1;
+ }
+
+ public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
+ {
+ return 1;
+ }
+
+ public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
+ {
+ $opener->insertAfter(new Quote($this->openerCharacter));
+ $closer->insertBefore(new Quote($this->closerCharacter));
+ }
+
+ /**
+ * Create a double-quote processor
+ */
+ public static function createDoubleQuoteProcessor(string $opener = Quote::DOUBLE_QUOTE_OPENER, string $closer = Quote::DOUBLE_QUOTE_CLOSER): self
+ {
+ return new self(Quote::DOUBLE_QUOTE, $opener, $closer);
+ }
+
+ /**
+ * Create a single-quote processor
+ */
+ public static function createSingleQuoteProcessor(string $opener = Quote::SINGLE_QUOTE_OPENER, string $closer = Quote::SINGLE_QUOTE_CLOSER): self
+ {
+ return new self(Quote::SINGLE_QUOTE, $opener, $closer);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/ReplaceUnpairedQuotesListener.php b/vendor/league/commonmark/src/Extension/SmartPunct/ReplaceUnpairedQuotesListener.php
new file mode 100644
index 0000000..3536452
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/ReplaceUnpairedQuotesListener.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Node\Inline\AdjacentTextMerger;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Node\Query;
+
+/**
+ * Identifies any lingering Quote nodes that were missing pairs and converts them into Text nodes
+ */
+final class ReplaceUnpairedQuotesListener
+{
+ public function __invoke(DocumentParsedEvent $event): void
+ {
+ $query = (new Query())->where(Query::type(Quote::class));
+ foreach ($query->findAll($event->getDocument()) as $quote) {
+ \assert($quote instanceof Quote);
+
+ $literal = $quote->getLiteral();
+ if ($literal === Quote::SINGLE_QUOTE) {
+ $literal = Quote::SINGLE_QUOTE_CLOSER;
+ } elseif ($literal === Quote::DOUBLE_QUOTE) {
+ $literal = Quote::DOUBLE_QUOTE_OPENER;
+ }
+
+ $quote->replaceWith($new = new Text($literal));
+ AdjacentTextMerger::mergeWithDirectlyAdjacentNodes($new);
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/SmartPunct/SmartPunctExtension.php b/vendor/league/commonmark/src/Extension/SmartPunct/SmartPunctExtension.php
new file mode 100644
index 0000000..8524ca1
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/SmartPunct/SmartPunctExtension.php
@@ -0,0 +1,64 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (http://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\SmartPunct;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Renderer\Block as CoreBlockRenderer;
+use League\CommonMark\Renderer\Inline as CoreInlineRenderer;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class SmartPunctExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('smartpunct', Expect::structure([
+ 'double_quote_opener' => Expect::string(Quote::DOUBLE_QUOTE_OPENER),
+ 'double_quote_closer' => Expect::string(Quote::DOUBLE_QUOTE_CLOSER),
+ 'single_quote_opener' => Expect::string(Quote::SINGLE_QUOTE_OPENER),
+ 'single_quote_closer' => Expect::string(Quote::SINGLE_QUOTE_CLOSER),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment
+ ->addInlineParser(new QuoteParser(), 10)
+ ->addInlineParser(new DashParser(), 0)
+ ->addInlineParser(new EllipsesParser(), 0)
+
+ ->addDelimiterProcessor(QuoteProcessor::createDoubleQuoteProcessor(
+ $environment->getConfiguration()->get('smartpunct/double_quote_opener'),
+ $environment->getConfiguration()->get('smartpunct/double_quote_closer')
+ ))
+ ->addDelimiterProcessor(QuoteProcessor::createSingleQuoteProcessor(
+ $environment->getConfiguration()->get('smartpunct/single_quote_opener'),
+ $environment->getConfiguration()->get('smartpunct/single_quote_closer')
+ ))
+
+ ->addEventListener(DocumentParsedEvent::class, new ReplaceUnpairedQuotesListener())
+
+ ->addRenderer(Document::class, new CoreBlockRenderer\DocumentRenderer(), 0)
+ ->addRenderer(Paragraph::class, new CoreBlockRenderer\ParagraphRenderer(), 0)
+ ->addRenderer(Text::class, new CoreInlineRenderer\TextRenderer(), 0);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Strikethrough/Strikethrough.php b/vendor/league/commonmark/src/Extension/Strikethrough/Strikethrough.php
new file mode 100644
index 0000000..20ad161
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Strikethrough/Strikethrough.php
@@ -0,0 +1,39 @@
+ and uAfrica.com (http://uafrica.com)
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Strikethrough;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Node\Inline\DelimitedInterface;
+
+final class Strikethrough extends AbstractInline implements DelimitedInterface
+{
+ private string $delimiter;
+
+ public function __construct(string $delimiter = '~~')
+ {
+ parent::__construct();
+
+ $this->delimiter = $delimiter;
+ }
+
+ public function getOpeningDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+
+ public function getClosingDelimiter(): string
+ {
+ return $this->delimiter;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughDelimiterProcessor.php b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughDelimiterProcessor.php
new file mode 100644
index 0000000..a6c8d38
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughDelimiterProcessor.php
@@ -0,0 +1,69 @@
+ and uAfrica.com (http://uafrica.com)
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Strikethrough;
+
+use League\CommonMark\Delimiter\DelimiterInterface;
+use League\CommonMark\Delimiter\Processor\CacheableDelimiterProcessorInterface;
+use League\CommonMark\Node\Inline\AbstractStringContainer;
+
+final class StrikethroughDelimiterProcessor implements CacheableDelimiterProcessorInterface
+{
+ public function getOpeningCharacter(): string
+ {
+ return '~';
+ }
+
+ public function getClosingCharacter(): string
+ {
+ return '~';
+ }
+
+ public function getMinLength(): int
+ {
+ return 1;
+ }
+
+ public function getDelimiterUse(DelimiterInterface $opener, DelimiterInterface $closer): int
+ {
+ if ($opener->getLength() > 2 && $closer->getLength() > 2) {
+ return 0;
+ }
+
+ if ($opener->getLength() !== $closer->getLength()) {
+ return 0;
+ }
+
+ // $opener and $closer are the same length so we just return one of them
+ return $opener->getLength();
+ }
+
+ public function process(AbstractStringContainer $opener, AbstractStringContainer $closer, int $delimiterUse): void
+ {
+ $strikethrough = new Strikethrough(\str_repeat('~', $delimiterUse));
+
+ $tmp = $opener->next();
+ while ($tmp !== null && $tmp !== $closer) {
+ $next = $tmp->next();
+ $strikethrough->appendChild($tmp);
+ $tmp = $next;
+ }
+
+ $opener->insertAfter($strikethrough);
+ }
+
+ public function getCacheKey(DelimiterInterface $closer): string
+ {
+ return '~' . $closer->getLength();
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughExtension.php b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughExtension.php
new file mode 100644
index 0000000..96ffe7a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughExtension.php
@@ -0,0 +1,26 @@
+ and uAfrica.com (http://uafrica.com)
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Strikethrough;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+
+final class StrikethroughExtension implements ExtensionInterface
+{
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addDelimiterProcessor(new StrikethroughDelimiterProcessor());
+ $environment->addRenderer(Strikethrough::class, new StrikethroughRenderer());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughRenderer.php b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughRenderer.php
new file mode 100644
index 0000000..a50b895
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Strikethrough/StrikethroughRenderer.php
@@ -0,0 +1,50 @@
+ and uAfrica.com (http://uafrica.com)
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Strikethrough;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class StrikethroughRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Strikethrough $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Strikethrough::assertInstanceOf($node);
+
+ return new HtmlElement('del', $node->data->get('attributes'), $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'strikethrough';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/Table.php b/vendor/league/commonmark/src/Extension/Table/Table.php
new file mode 100644
index 0000000..2fe441d
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/Table.php
@@ -0,0 +1,22 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class Table extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableCell.php b/vendor/league/commonmark/src/Extension/Table/TableCell.php
new file mode 100644
index 0000000..6ed359a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableCell.php
@@ -0,0 +1,99 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class TableCell extends AbstractBlock
+{
+ public const TYPE_HEADER = 'header';
+ public const TYPE_DATA = 'data';
+
+ public const ALIGN_LEFT = 'left';
+ public const ALIGN_RIGHT = 'right';
+ public const ALIGN_CENTER = 'center';
+
+ /**
+ * @psalm-var self::TYPE_*
+ * @phpstan-var self::TYPE_*
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private string $type = self::TYPE_DATA;
+
+ /**
+ * @psalm-var self::ALIGN_*|null
+ * @phpstan-var self::ALIGN_*|null
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private ?string $align = null;
+
+ /**
+ * @psalm-param self::TYPE_* $type
+ * @psalm-param self::ALIGN_*|null $align
+ *
+ * @phpstan-param self::TYPE_* $type
+ * @phpstan-param self::ALIGN_*|null $align
+ */
+ public function __construct(string $type = self::TYPE_DATA, ?string $align = null)
+ {
+ parent::__construct();
+
+ $this->type = $type;
+ $this->align = $align;
+ }
+
+ /**
+ * @psalm-return self::TYPE_*
+ *
+ * @phpstan-return self::TYPE_*
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @psalm-param self::TYPE_* $type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function setType(string $type): void
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @psalm-return self::ALIGN_*|null
+ *
+ * @phpstan-return self::ALIGN_*|null
+ */
+ public function getAlign(): ?string
+ {
+ return $this->align;
+ }
+
+ /**
+ * @psalm-param self::ALIGN_*|null $align
+ *
+ * @phpstan-param self::ALIGN_*|null $align
+ */
+ public function setAlign(?string $align): void
+ {
+ $this->align = $align;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableCellRenderer.php b/vendor/league/commonmark/src/Extension/Table/TableCellRenderer.php
new file mode 100644
index 0000000..99512c3
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableCellRenderer.php
@@ -0,0 +1,89 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Extension\Attributes\Util\AttributesHelper;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableCellRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ private const DEFAULT_ATTRIBUTES = [
+ TableCell::ALIGN_LEFT => ['align' => 'left'],
+ TableCell::ALIGN_CENTER => ['align' => 'center'],
+ TableCell::ALIGN_RIGHT => ['align' => 'right'],
+ ];
+
+ /** @var array> */
+ private array $alignmentAttributes;
+
+ /**
+ * @param array> $alignmentAttributes
+ */
+ public function __construct(array $alignmentAttributes = self::DEFAULT_ATTRIBUTES)
+ {
+ $this->alignmentAttributes = $alignmentAttributes;
+ }
+
+ /**
+ * @param TableCell $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ TableCell::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+ if (($alignment = $node->getAlign()) !== null) {
+ $attrs = AttributesHelper::mergeAttributes($attrs, $this->alignmentAttributes[$alignment]);
+ }
+
+ $tag = $node->getType() === TableCell::TYPE_HEADER ? 'th' : 'td';
+
+ return new HtmlElement($tag, $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table_cell';
+ }
+
+ /**
+ * @param TableCell $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ TableCell::assertInstanceOf($node);
+
+ $ret = ['type' => $node->getType()];
+
+ if (($align = $node->getAlign()) !== null) {
+ $ret['align'] = $align;
+ }
+
+ return $ret;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableExtension.php b/vendor/league/commonmark/src/Extension/Table/TableExtension.php
new file mode 100644
index 0000000..0a8db3e
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableExtension.php
@@ -0,0 +1,63 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Renderer\HtmlDecorator;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class TableExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $attributeArraySchema = Expect::arrayOf(
+ Expect::type('string|string[]|bool'), // attribute value(s)
+ 'string' // attribute name
+ )->mergeDefaults(false);
+
+ $builder->addSchema('table', Expect::structure([
+ 'wrap' => Expect::structure([
+ 'enabled' => Expect::bool()->default(false),
+ 'tag' => Expect::string()->default('div'),
+ 'attributes' => Expect::arrayOf(Expect::string()),
+ ]),
+ 'alignment_attributes' => Expect::structure([
+ 'left' => (clone $attributeArraySchema)->default(['align' => 'left']),
+ 'center' => (clone $attributeArraySchema)->default(['align' => 'center']),
+ 'right' => (clone $attributeArraySchema)->default(['align' => 'right']),
+ ]),
+ 'max_autocompleted_cells' => Expect::int()->min(0)->default(TableParser::DEFAULT_MAX_AUTOCOMPLETED_CELLS),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $tableRenderer = new TableRenderer();
+ if ($environment->getConfiguration()->get('table/wrap/enabled')) {
+ $tableRenderer = new HtmlDecorator($tableRenderer, $environment->getConfiguration()->get('table/wrap/tag'), $environment->getConfiguration()->get('table/wrap/attributes'));
+ }
+
+ $environment
+ ->addBlockStartParser(new TableStartParser($environment->getConfiguration()->get('table/max_autocompleted_cells')))
+
+ ->addRenderer(Table::class, $tableRenderer)
+ ->addRenderer(TableSection::class, new TableSectionRenderer())
+ ->addRenderer(TableRow::class, new TableRowRenderer())
+ ->addRenderer(TableCell::class, new TableCellRenderer($environment->getConfiguration()->get('table/alignment_attributes')));
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableParser.php b/vendor/league/commonmark/src/Extension/Table/TableParser.php
new file mode 100644
index 0000000..a005f8a
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableParser.php
@@ -0,0 +1,212 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\InlineParserEngineInterface;
+use League\CommonMark\Util\ArrayCollection;
+
+final class TableParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
+{
+ /**
+ * @internal
+ */
+ public const DEFAULT_MAX_AUTOCOMPLETED_CELLS = 10_000;
+
+ /** @psalm-readonly */
+ private Table $block;
+
+ /**
+ * @var ArrayCollection
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private ArrayCollection $bodyLines;
+
+ /**
+ * @var array
+ * @psalm-var array
+ * @phpstan-var array
+ *
+ * @psalm-readonly
+ */
+ private array $columns;
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $headerCells;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $nextIsSeparatorLine = true;
+
+ private int $remainingAutocompletedCells;
+
+ /**
+ * @param array $columns
+ * @param array $headerCells
+ *
+ * @psalm-param array $columns
+ *
+ * @phpstan-param array $columns
+ */
+ public function __construct(array $columns, array $headerCells, int $remainingAutocompletedCells = self::DEFAULT_MAX_AUTOCOMPLETED_CELLS)
+ {
+ $this->block = new Table();
+ $this->bodyLines = new ArrayCollection();
+ $this->columns = $columns;
+ $this->headerCells = $headerCells;
+ $this->remainingAutocompletedCells = $remainingAutocompletedCells;
+ }
+
+ public function canHaveLazyContinuationLines(): bool
+ {
+ return true;
+ }
+
+ public function getBlock(): Table
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if (\strpos($cursor->getLine(), '|') === false) {
+ return BlockContinue::none();
+ }
+
+ return BlockContinue::at($cursor);
+ }
+
+ public function addLine(string $line): void
+ {
+ if ($this->nextIsSeparatorLine) {
+ $this->nextIsSeparatorLine = false;
+ } else {
+ $this->bodyLines[] = $line;
+ }
+ }
+
+ public function parseInlines(InlineParserEngineInterface $inlineParser): void
+ {
+ $headerColumns = \count($this->headerCells);
+
+ $head = new TableSection(TableSection::TYPE_HEAD);
+ $this->block->appendChild($head);
+
+ $headerRow = new TableRow();
+ $head->appendChild($headerRow);
+ for ($i = 0; $i < $headerColumns; $i++) {
+ $cell = $this->headerCells[$i];
+ $tableCell = $this->parseCell($cell, $i, $inlineParser);
+ $tableCell->setType(TableCell::TYPE_HEADER);
+ $headerRow->appendChild($tableCell);
+ }
+
+ $body = null;
+ foreach ($this->bodyLines as $rowLine) {
+ $cells = self::split($rowLine);
+ $row = new TableRow();
+
+ // Body can not have more columns than head
+ for ($i = 0; $i < $headerColumns; $i++) {
+ // It can have less columns though, in which case we'll autocomplete the empty ones (up to some limit)
+ if (! isset($cells[$i]) && $this->remainingAutocompletedCells-- <= 0) {
+ // Too many cells were auto-completed, so we'll just stop here
+ return;
+ }
+
+ $cell = $cells[$i] ?? '';
+ $tableCell = $this->parseCell($cell, $i, $inlineParser);
+ $row->appendChild($tableCell);
+ }
+
+ if ($body === null) {
+ // It's valid to have a table without body. In that case, don't add an empty TableBody node.
+ $body = new TableSection();
+ $this->block->appendChild($body);
+ }
+
+ $body->appendChild($row);
+ }
+ }
+
+ private function parseCell(string $cell, int $column, InlineParserEngineInterface $inlineParser): TableCell
+ {
+ $tableCell = new TableCell(TableCell::TYPE_DATA, $this->columns[$column] ?? null);
+
+ if ($cell !== '') {
+ $inlineParser->parse(\trim($cell), $tableCell);
+ }
+
+ return $tableCell;
+ }
+
+ /**
+ * @internal
+ *
+ * @return array
+ */
+ public static function split(string $line): array
+ {
+ $cursor = new Cursor(\trim($line));
+
+ if ($cursor->getCurrentCharacter() === '|') {
+ $cursor->advanceBy(1);
+ }
+
+ $cells = [];
+ $sb = '';
+
+ while (! $cursor->isAtEnd()) {
+ switch ($c = $cursor->getCurrentCharacter()) {
+ case '\\':
+ if ($cursor->peek() === '|') {
+ // Pipe is special for table parsing. An escaped pipe doesn't result in a new cell, but is
+ // passed down to inline parsing as an unescaped pipe. Note that that applies even for the `\|`
+ // in an input like `\\|` - in other words, table parsing doesn't support escaping backslashes.
+ $sb .= '|';
+ $cursor->advanceBy(1);
+ } else {
+ // Preserve backslash before other characters or at end of line.
+ $sb .= '\\';
+ }
+
+ break;
+ case '|':
+ $cells[] = $sb;
+ $sb = '';
+ break;
+ default:
+ $sb .= $c;
+ }
+
+ $cursor->advanceBy(1);
+ }
+
+ if ($sb !== '') {
+ $cells[] = $sb;
+ }
+
+ return $cells;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableRenderer.php b/vendor/league/commonmark/src/Extension/Table/TableRenderer.php
new file mode 100644
index 0000000..7799e22
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableRenderer.php
@@ -0,0 +1,58 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Table $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ Table::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ $separator = $childRenderer->getInnerSeparator();
+
+ $children = $childRenderer->renderNodes($node->children());
+
+ return new HtmlElement('table', $attrs, $separator . \trim($children) . $separator);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableRow.php b/vendor/league/commonmark/src/Extension/Table/TableRow.php
new file mode 100644
index 0000000..cd6ac99
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableRow.php
@@ -0,0 +1,22 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class TableRow extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableRowRenderer.php b/vendor/league/commonmark/src/Extension/Table/TableRowRenderer.php
new file mode 100644
index 0000000..dee72d2
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableRowRenderer.php
@@ -0,0 +1,56 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableRowRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param TableRow $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ TableRow::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+
+ $separator = $childRenderer->getInnerSeparator();
+
+ return new HtmlElement('tr', $attrs, $separator . $childRenderer->renderNodes($node->children()) . $separator);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table_row';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableSection.php b/vendor/league/commonmark/src/Extension/Table/TableSection.php
new file mode 100644
index 0000000..9edd63b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableSection.php
@@ -0,0 +1,64 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class TableSection extends AbstractBlock
+{
+ public const TYPE_HEAD = 'head';
+ public const TYPE_BODY = 'body';
+
+ /**
+ * @psalm-var self::TYPE_*
+ * @phpstan-var self::TYPE_*
+ *
+ * @psalm-readonly
+ */
+ private string $type;
+
+ /**
+ * @psalm-param self::TYPE_* $type
+ *
+ * @phpstan-param self::TYPE_* $type
+ */
+ public function __construct(string $type = self::TYPE_BODY)
+ {
+ parent::__construct();
+
+ $this->type = $type;
+ }
+
+ /**
+ * @psalm-return self::TYPE_*
+ *
+ * @phpstan-return self::TYPE_*
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ public function isHead(): bool
+ {
+ return $this->type === self::TYPE_HEAD;
+ }
+
+ public function isBody(): bool
+ {
+ return $this->type === self::TYPE_BODY;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableSectionRenderer.php b/vendor/league/commonmark/src/Extension/Table/TableSectionRenderer.php
new file mode 100644
index 0000000..cccf06c
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableSectionRenderer.php
@@ -0,0 +1,70 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableSectionRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param TableSection $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer)
+ {
+ TableSection::assertInstanceOf($node);
+
+ if (! $node->hasChildren()) {
+ return '';
+ }
+
+ $attrs = $node->data->get('attributes');
+
+ $separator = $childRenderer->getInnerSeparator();
+
+ $tag = $node->getType() === TableSection::TYPE_HEAD ? 'thead' : 'tbody';
+
+ return new HtmlElement($tag, $attrs, $separator . $childRenderer->renderNodes($node->children()) . $separator);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table_section';
+ }
+
+ /**
+ * @param TableSection $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ TableSection::assertInstanceOf($node);
+
+ return [
+ 'type' => $node->getType(),
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/Table/TableStartParser.php b/vendor/league/commonmark/src/Extension/Table/TableStartParser.php
new file mode 100644
index 0000000..7411951
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/Table/TableStartParser.php
@@ -0,0 +1,165 @@
+
+ * (c) Webuni s.r.o.
+ * (c) Colin O'Dell
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\Table;
+
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Block\ParagraphParser;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+final class TableStartParser implements BlockStartParserInterface
+{
+ private int $maxAutocompletedCells;
+
+ public function __construct(int $maxAutocompletedCells = TableParser::DEFAULT_MAX_AUTOCOMPLETED_CELLS)
+ {
+ $this->maxAutocompletedCells = $maxAutocompletedCells;
+ }
+
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ $paragraph = $parserState->getParagraphContent();
+ if ($paragraph === null || \strpos($paragraph, '|') === false) {
+ return BlockStart::none();
+ }
+
+ $columns = self::parseSeparator($cursor);
+ if (\count($columns) === 0) {
+ return BlockStart::none();
+ }
+
+ $lastLineBreak = \strrpos($paragraph, "\n");
+ $lastLine = $lastLineBreak === false ? $paragraph : \substr($paragraph, $lastLineBreak + 1);
+
+ $headerCells = TableParser::split($lastLine);
+ if (\count($headerCells) > \count($columns)) {
+ return BlockStart::none();
+ }
+
+ $cursor->advanceToEnd();
+
+ $parsers = [];
+
+ if ($lastLineBreak !== false) {
+ $p = new ParagraphParser();
+ $p->addLine(\substr($paragraph, 0, $lastLineBreak));
+ $parsers[] = $p;
+ }
+
+ $parsers[] = new TableParser($columns, $headerCells, $this->maxAutocompletedCells);
+
+ return BlockStart::of(...$parsers)
+ ->at($cursor)
+ ->replaceActiveBlockParser();
+ }
+
+ /**
+ * @return array
+ *
+ * @psalm-return array
+ *
+ * @phpstan-return array
+ */
+ private static function parseSeparator(Cursor $cursor): array
+ {
+ $columns = [];
+ $pipes = 0;
+ $valid = false;
+
+ while (! $cursor->isAtEnd()) {
+ switch ($c = $cursor->getCurrentCharacter()) {
+ case '|':
+ $cursor->advanceBy(1);
+ $pipes++;
+ if ($pipes > 1) {
+ // More than one adjacent pipe not allowed
+ return [];
+ }
+
+ // Need at least one pipe, even for a one-column table
+ $valid = true;
+ break;
+ case '-':
+ case ':':
+ if ($pipes === 0 && \count($columns) > 0) {
+ // Need a pipe after the first column (first column doesn't need to start with one)
+ return [];
+ }
+
+ $left = false;
+ $right = false;
+ if ($c === ':') {
+ $left = true;
+ $cursor->advanceBy(1);
+ }
+
+ if ($cursor->match('/^-+/') === null) {
+ // Need at least one dash
+ return [];
+ }
+
+ if ($cursor->getCurrentCharacter() === ':') {
+ $right = true;
+ $cursor->advanceBy(1);
+ }
+
+ $columns[] = self::getAlignment($left, $right);
+ // Next, need another pipe
+ $pipes = 0;
+ break;
+ case ' ':
+ case "\t":
+ // White space is allowed between pipes and columns
+ $cursor->advanceToNextNonSpaceOrTab();
+ break;
+ default:
+ // Any other character is invalid
+ return [];
+ }
+ }
+
+ if (! $valid) {
+ return [];
+ }
+
+ return $columns;
+ }
+
+ /**
+ * @psalm-return TableCell::ALIGN_*|null
+ *
+ * @phpstan-return TableCell::ALIGN_*|null
+ *
+ * @psalm-pure
+ */
+ private static function getAlignment(bool $left, bool $right): ?string
+ {
+ if ($left && $right) {
+ return TableCell::ALIGN_CENTER;
+ }
+
+ if ($left) {
+ return TableCell::ALIGN_LEFT;
+ }
+
+ if ($right) {
+ return TableCell::ALIGN_RIGHT;
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContents.php b/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContents.php
new file mode 100644
index 0000000..e040d86
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContents.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Node;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+
+final class TableOfContents extends ListBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContentsPlaceholder.php b/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContentsPlaceholder.php
new file mode 100644
index 0000000..6d6db10
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Node/TableOfContentsPlaceholder.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class TableOfContentsPlaceholder extends AbstractBlock
+{
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/AsIsNormalizerStrategy.php b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/AsIsNormalizerStrategy.php
new file mode 100644
index 0000000..f5bb9a4
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/AsIsNormalizerStrategy.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Normalizer;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+
+final class AsIsNormalizerStrategy implements NormalizerStrategyInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ListBlock $parentListBlock;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $parentLevel = 1;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?ListItem $lastListItem = null;
+
+ public function __construct(TableOfContents $toc)
+ {
+ $this->parentListBlock = $toc;
+ }
+
+ public function addItem(int $level, ListItem $listItemToAdd): void
+ {
+ while ($level > $this->parentLevel) {
+ // Descend downwards, creating new ListBlocks if needed, until we reach the correct depth
+ if ($this->lastListItem === null) {
+ $this->lastListItem = new ListItem($this->parentListBlock->getListData());
+ $this->parentListBlock->appendChild($this->lastListItem);
+ }
+
+ $newListBlock = new ListBlock($this->parentListBlock->getListData());
+ $newListBlock->setStartLine($listItemToAdd->getStartLine());
+ $newListBlock->setEndLine($listItemToAdd->getEndLine());
+ $this->lastListItem->appendChild($newListBlock);
+ $this->parentListBlock = $newListBlock;
+ $this->lastListItem = null;
+
+ $this->parentLevel++;
+ }
+
+ while ($level < $this->parentLevel) {
+ // Search upwards for the previous parent list block
+ $search = $this->parentListBlock;
+ while ($search = $search->parent()) {
+ if ($search instanceof ListBlock) {
+ $this->parentListBlock = $search;
+ break;
+ }
+ }
+
+ $this->parentLevel--;
+ }
+
+ $this->parentListBlock->appendChild($listItemToAdd);
+
+ $this->lastListItem = $listItemToAdd;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/FlatNormalizerStrategy.php b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/FlatNormalizerStrategy.php
new file mode 100644
index 0000000..8e805ae
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/FlatNormalizerStrategy.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Normalizer;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+
+final class FlatNormalizerStrategy implements NormalizerStrategyInterface
+{
+ /** @psalm-readonly */
+ private TableOfContents $toc;
+
+ public function __construct(TableOfContents $toc)
+ {
+ $this->toc = $toc;
+ }
+
+ public function addItem(int $level, ListItem $listItemToAdd): void
+ {
+ $this->toc->appendChild($listItemToAdd);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/NormalizerStrategyInterface.php b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/NormalizerStrategyInterface.php
new file mode 100644
index 0000000..f30afb1
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/NormalizerStrategyInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Normalizer;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+
+interface NormalizerStrategyInterface
+{
+ public function addItem(int $level, ListItem $listItemToAdd): void;
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/RelativeNormalizerStrategy.php b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/RelativeNormalizerStrategy.php
new file mode 100644
index 0000000..1b2197f
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/Normalizer/RelativeNormalizerStrategy.php
@@ -0,0 +1,67 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents\Normalizer;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+
+final class RelativeNormalizerStrategy implements NormalizerStrategyInterface
+{
+ /** @psalm-readonly */
+ private TableOfContents $toc;
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $listItemStack = [];
+
+ public function __construct(TableOfContents $toc)
+ {
+ $this->toc = $toc;
+ }
+
+ public function addItem(int $level, ListItem $listItemToAdd): void
+ {
+ $previousLevel = \array_key_last($this->listItemStack);
+
+ // Pop the stack if we're too deep
+ while ($previousLevel !== null && $level < $previousLevel) {
+ \array_pop($this->listItemStack);
+ $previousLevel = \array_key_last($this->listItemStack);
+ }
+
+ $lastListItem = \end($this->listItemStack);
+
+ // Need to go one level deeper? Add that level
+ if ($lastListItem !== false && $level > $previousLevel) {
+ $targetListBlock = new ListBlock($lastListItem->getListData());
+ $targetListBlock->setStartLine($listItemToAdd->getStartLine());
+ $targetListBlock->setEndLine($listItemToAdd->getEndLine());
+ $lastListItem->appendChild($targetListBlock);
+ // Otherwise we're at the right level
+ // If there's no stack we're adding this item directly to the TOC element
+ } elseif ($lastListItem === false) {
+ $targetListBlock = $this->toc;
+ // Otherwise add it to the last list item
+ } else {
+ $targetListBlock = $lastListItem->parent();
+ }
+
+ $targetListBlock->appendChild($listItemToAdd);
+ $this->listItemStack[$level] = $listItemToAdd;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsBuilder.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsBuilder.php
new file mode 100644
index 0000000..7fe2b09
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsBuilder.php
@@ -0,0 +1,106 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
+use League\CommonMark\Extension\HeadingPermalink\HeadingPermalink;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContentsPlaceholder;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\NodeIterator;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+use League\Config\Exception\InvalidConfigurationException;
+
+final class TableOfContentsBuilder implements ConfigurationAwareInterface
+{
+ public const POSITION_TOP = 'top';
+ public const POSITION_BEFORE_HEADINGS = 'before-headings';
+ public const POSITION_PLACEHOLDER = 'placeholder';
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function onDocumentParsed(DocumentParsedEvent $event): void
+ {
+ $document = $event->getDocument();
+
+ $generator = new TableOfContentsGenerator(
+ (string) $this->config->get('table_of_contents/style'),
+ (string) $this->config->get('table_of_contents/normalize'),
+ (int) $this->config->get('table_of_contents/min_heading_level'),
+ (int) $this->config->get('table_of_contents/max_heading_level'),
+ (string) $this->config->get('heading_permalink/fragment_prefix'),
+ );
+
+ $toc = $generator->generate($document);
+ if ($toc === null) {
+ // No linkable headers exist, so no TOC could be generated
+ return;
+ }
+
+ // Add custom CSS class(es), if defined
+ $class = $this->config->get('table_of_contents/html_class');
+ if ($class !== null) {
+ $toc->data->append('attributes/class', $class);
+ }
+
+ // Add the TOC to the Document
+ $position = $this->config->get('table_of_contents/position');
+ if ($position === self::POSITION_TOP) {
+ $document->prependChild($toc);
+ } elseif ($position === self::POSITION_BEFORE_HEADINGS) {
+ $this->insertBeforeFirstLinkedHeading($document, $toc);
+ } elseif ($position === self::POSITION_PLACEHOLDER) {
+ $this->replacePlaceholders($document, $toc);
+ } else {
+ throw InvalidConfigurationException::forConfigOption('table_of_contents/position', $position);
+ }
+ }
+
+ private function insertBeforeFirstLinkedHeading(Document $document, TableOfContents $toc): void
+ {
+ foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ if (! $node instanceof Heading) {
+ continue;
+ }
+
+ foreach ($node->children() as $child) {
+ if ($child instanceof HeadingPermalink) {
+ $node->insertBefore($toc);
+
+ return;
+ }
+ }
+ }
+ }
+
+ private function replacePlaceholders(Document $document, TableOfContents $toc): void
+ {
+ foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ // Add the block once we find a placeholder
+ if (! $node instanceof TableOfContentsPlaceholder) {
+ continue;
+ }
+
+ $node->replaceWith(clone $toc);
+ }
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsExtension.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsExtension.php
new file mode 100644
index 0000000..9c8223b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsExtension.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Renderer\Block\ListBlockRenderer;
+use League\CommonMark\Extension\ConfigurableExtensionInterface;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContentsPlaceholder;
+use League\Config\ConfigurationBuilderInterface;
+use Nette\Schema\Expect;
+
+final class TableOfContentsExtension implements ConfigurableExtensionInterface
+{
+ public function configureSchema(ConfigurationBuilderInterface $builder): void
+ {
+ $builder->addSchema('table_of_contents', Expect::structure([
+ 'position' => Expect::anyOf(TableOfContentsBuilder::POSITION_BEFORE_HEADINGS, TableOfContentsBuilder::POSITION_PLACEHOLDER, TableOfContentsBuilder::POSITION_TOP)->default(TableOfContentsBuilder::POSITION_TOP),
+ 'style' => Expect::anyOf(ListBlock::TYPE_BULLET, ListBlock::TYPE_ORDERED)->default(ListBlock::TYPE_BULLET),
+ 'normalize' => Expect::anyOf(TableOfContentsGenerator::NORMALIZE_RELATIVE, TableOfContentsGenerator::NORMALIZE_FLAT, TableOfContentsGenerator::NORMALIZE_DISABLED)->default(TableOfContentsGenerator::NORMALIZE_RELATIVE),
+ 'min_heading_level' => Expect::int()->min(1)->max(6)->default(1),
+ 'max_heading_level' => Expect::int()->min(1)->max(6)->default(6),
+ 'html_class' => Expect::string()->default('table-of-contents'),
+ 'placeholder' => Expect::anyOf(Expect::string(), Expect::null())->default(null),
+ ]));
+ }
+
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addRenderer(TableOfContents::class, new TableOfContentsRenderer(new ListBlockRenderer()));
+ $environment->addEventListener(DocumentParsedEvent::class, [new TableOfContentsBuilder(), 'onDocumentParsed'], -150);
+
+ // phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
+ if ($environment->getConfiguration()->get('table_of_contents/position') === TableOfContentsBuilder::POSITION_PLACEHOLDER) {
+ $environment->addBlockStartParser(TableOfContentsPlaceholderParser::blockStartParser(), 200);
+ // If a placeholder cannot be replaced with a TOC element this renderer will ensure the parser won't error out
+ $environment->addRenderer(TableOfContentsPlaceholder::class, new TableOfContentsPlaceholderRenderer());
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGenerator.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGenerator.php
new file mode 100644
index 0000000..f0df96b
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGenerator.php
@@ -0,0 +1,168 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\Heading;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListBlock;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListData;
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
+use League\CommonMark\Extension\HeadingPermalink\HeadingPermalink;
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+use League\CommonMark\Extension\TableOfContents\Normalizer\AsIsNormalizerStrategy;
+use League\CommonMark\Extension\TableOfContents\Normalizer\FlatNormalizerStrategy;
+use League\CommonMark\Extension\TableOfContents\Normalizer\NormalizerStrategyInterface;
+use League\CommonMark\Extension\TableOfContents\Normalizer\RelativeNormalizerStrategy;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\NodeIterator;
+use League\CommonMark\Node\RawMarkupContainerInterface;
+use League\CommonMark\Node\StringContainerHelper;
+use League\Config\Exception\InvalidConfigurationException;
+
+final class TableOfContentsGenerator implements TableOfContentsGeneratorInterface
+{
+ public const STYLE_BULLET = ListBlock::TYPE_BULLET;
+ public const STYLE_ORDERED = ListBlock::TYPE_ORDERED;
+
+ public const NORMALIZE_DISABLED = 'as-is';
+ public const NORMALIZE_RELATIVE = 'relative';
+ public const NORMALIZE_FLAT = 'flat';
+
+ /** @psalm-readonly */
+ private string $style;
+
+ /** @psalm-readonly */
+ private string $normalizationStrategy;
+
+ /** @psalm-readonly */
+ private int $minHeadingLevel;
+
+ /** @psalm-readonly */
+ private int $maxHeadingLevel;
+
+ /** @psalm-readonly */
+ private string $fragmentPrefix;
+
+ public function __construct(string $style, string $normalizationStrategy, int $minHeadingLevel, int $maxHeadingLevel, string $fragmentPrefix)
+ {
+ $this->style = $style;
+ $this->normalizationStrategy = $normalizationStrategy;
+ $this->minHeadingLevel = $minHeadingLevel;
+ $this->maxHeadingLevel = $maxHeadingLevel;
+ $this->fragmentPrefix = $fragmentPrefix;
+
+ if ($fragmentPrefix !== '') {
+ $this->fragmentPrefix .= '-';
+ }
+ }
+
+ public function generate(Document $document): ?TableOfContents
+ {
+ $toc = $this->createToc($document);
+
+ $normalizer = $this->getNormalizer($toc);
+
+ $firstHeading = null;
+
+ foreach ($this->getHeadingLinks($document) as $headingLink) {
+ $heading = $headingLink->parent();
+ // Make sure this is actually tied to a heading
+ if (! $heading instanceof Heading) {
+ continue;
+ }
+
+ // Skip any headings outside the configured min/max levels
+ if ($heading->getLevel() < $this->minHeadingLevel || $heading->getLevel() > $this->maxHeadingLevel) {
+ continue;
+ }
+
+ // Keep track of the first heading we see - we might need this later
+ $firstHeading ??= $heading;
+
+ // Keep track of the start and end lines
+ $toc->setStartLine($firstHeading->getStartLine());
+ $toc->setEndLine($heading->getEndLine());
+
+ // Create the new link
+ $link = new Link('#' . $this->fragmentPrefix . $headingLink->getSlug(), StringContainerHelper::getChildText($heading, [RawMarkupContainerInterface::class]));
+
+ $listItem = new ListItem($toc->getListData());
+ $listItem->setStartLine($heading->getStartLine());
+ $listItem->setEndLine($heading->getEndLine());
+ $listItem->appendChild($link);
+
+ // Add it to the correct place
+ $normalizer->addItem($heading->getLevel(), $listItem);
+ }
+
+ // Don't add the TOC if no headings were present
+ if (! $toc->hasChildren() || $firstHeading === null) {
+ return null;
+ }
+
+ return $toc;
+ }
+
+ private function createToc(Document $document): TableOfContents
+ {
+ $listData = new ListData();
+
+ if ($this->style === self::STYLE_BULLET) {
+ $listData->type = ListBlock::TYPE_BULLET;
+ } elseif ($this->style === self::STYLE_ORDERED) {
+ $listData->type = ListBlock::TYPE_ORDERED;
+ } else {
+ throw new InvalidConfigurationException(\sprintf('Invalid table of contents list style: "%s"', $this->style));
+ }
+
+ $toc = new TableOfContents($listData);
+
+ $toc->setStartLine($document->getStartLine());
+ $toc->setEndLine($document->getEndLine());
+
+ return $toc;
+ }
+
+ /**
+ * @return iterable
+ */
+ private function getHeadingLinks(Document $document): iterable
+ {
+ foreach ($document->iterator(NodeIterator::FLAG_BLOCKS_ONLY) as $node) {
+ if (! $node instanceof Heading) {
+ continue;
+ }
+
+ foreach ($node->children() as $child) {
+ if ($child instanceof HeadingPermalink) {
+ yield $child;
+ }
+ }
+ }
+ }
+
+ private function getNormalizer(TableOfContents $toc): NormalizerStrategyInterface
+ {
+ switch ($this->normalizationStrategy) {
+ case self::NORMALIZE_DISABLED:
+ return new AsIsNormalizerStrategy($toc);
+ case self::NORMALIZE_RELATIVE:
+ return new RelativeNormalizerStrategy($toc);
+ case self::NORMALIZE_FLAT:
+ return new FlatNormalizerStrategy($toc);
+ default:
+ throw new InvalidConfigurationException(\sprintf('Invalid table of contents normalization strategy: "%s"', $this->normalizationStrategy));
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGeneratorInterface.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGeneratorInterface.php
new file mode 100644
index 0000000..64ecb8e
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsGeneratorInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContents;
+use League\CommonMark\Node\Block\Document;
+
+interface TableOfContentsGeneratorInterface
+{
+ public function generate(Document $document): ?TableOfContents;
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderParser.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderParser.php
new file mode 100644
index 0000000..b27ddee
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderParser.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Extension\TableOfContents\Node\TableOfContentsPlaceholder;
+use League\CommonMark\Parser\Block\AbstractBlockContinueParser;
+use League\CommonMark\Parser\Block\BlockContinue;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class TableOfContentsPlaceholderParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private TableOfContentsPlaceholder $block;
+
+ public function __construct()
+ {
+ $this->block = new TableOfContentsPlaceholder();
+ }
+
+ public function getBlock(): TableOfContentsPlaceholder
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::none();
+ }
+
+ public static function blockStartParser(): BlockStartParserInterface
+ {
+ return new class () implements BlockStartParserInterface, ConfigurationAwareInterface {
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ $placeholder = $this->config->get('table_of_contents/placeholder');
+ if ($placeholder === null) {
+ return BlockStart::none();
+ }
+
+ // The placeholder must be the only thing on the line
+ if ($cursor->match('/^' . \preg_quote($placeholder, '/') . '$/') === null) {
+ return BlockStart::none();
+ }
+
+ return BlockStart::of(new TableOfContentsPlaceholderParser())->at($cursor);
+ }
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+ };
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderRenderer.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderRenderer.php
new file mode 100644
index 0000000..0366cb9
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsPlaceholderRenderer.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableOfContentsPlaceholderRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ return '';
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table_of_contents_placeholder';
+ }
+
+ /**
+ * @return array
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsRenderer.php b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsRenderer.php
new file mode 100644
index 0000000..da1b698
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TableOfContents/TableOfContentsRenderer.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TableOfContents;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TableOfContentsRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /** @var NodeRendererInterface&XmlNodeRendererInterface */
+ private $innerRenderer;
+
+ /**
+ * @psalm-param NodeRendererInterface&XmlNodeRendererInterface $innerRenderer
+ *
+ * @phpstan-param NodeRendererInterface&XmlNodeRendererInterface $innerRenderer
+ */
+ public function __construct(NodeRendererInterface $innerRenderer)
+ {
+ $this->innerRenderer = $innerRenderer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer)
+ {
+ return $this->innerRenderer->render($node, $childRenderer);
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'table_of_contents';
+ }
+
+ /**
+ * @return array
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return $this->innerRenderer->getXmlAttributes($node);
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TaskList/TaskListExtension.php b/vendor/league/commonmark/src/Extension/TaskList/TaskListExtension.php
new file mode 100644
index 0000000..bf4b0d2
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TaskList/TaskListExtension.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TaskList;
+
+use League\CommonMark\Environment\EnvironmentBuilderInterface;
+use League\CommonMark\Extension\ExtensionInterface;
+
+final class TaskListExtension implements ExtensionInterface
+{
+ public function register(EnvironmentBuilderInterface $environment): void
+ {
+ $environment->addInlineParser(new TaskListItemMarkerParser(), 35);
+ $environment->addRenderer(TaskListItemMarker::class, new TaskListItemMarkerRenderer());
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarker.php b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarker.php
new file mode 100644
index 0000000..125ae40
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarker.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TaskList;
+
+use League\CommonMark\Node\Inline\AbstractInline;
+
+final class TaskListItemMarker extends AbstractInline
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $checked;
+
+ public function __construct(bool $isCompleted)
+ {
+ parent::__construct();
+
+ $this->checked = $isCompleted;
+ }
+
+ public function isChecked(): bool
+ {
+ return $this->checked;
+ }
+
+ public function setChecked(bool $checked): void
+ {
+ $this->checked = $checked;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerParser.php b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerParser.php
new file mode 100644
index 0000000..30e2731
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerParser.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TaskList;
+
+use League\CommonMark\Extension\CommonMark\Node\Block\ListItem;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Parser\Inline\InlineParserMatch;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class TaskListItemMarkerParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::oneOf('[ ]', '[x]');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $container = $inlineContext->getContainer();
+
+ // Checkbox must come at the beginning of the first paragraph of the list item
+ if ($container->hasChildren() || ! ($container instanceof Paragraph && $container->parent() && $container->parent() instanceof ListItem)) {
+ return false;
+ }
+
+ $cursor = $inlineContext->getCursor();
+ $oldState = $cursor->saveState();
+
+ $cursor->advanceBy(3);
+
+ if ($cursor->getNextNonSpaceCharacter() === null) {
+ $cursor->restoreState($oldState);
+
+ return false;
+ }
+
+ $isChecked = $inlineContext->getFullMatch() !== '[ ]';
+
+ $container->appendChild(new TaskListItemMarker($isChecked));
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerRenderer.php b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerRenderer.php
new file mode 100644
index 0000000..a1eb745
--- /dev/null
+++ b/vendor/league/commonmark/src/Extension/TaskList/TaskListItemMarkerRenderer.php
@@ -0,0 +1,70 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Extension\TaskList;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TaskListItemMarkerRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param TaskListItemMarker $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \Stringable
+ {
+ TaskListItemMarker::assertInstanceOf($node);
+
+ $attrs = $node->data->get('attributes');
+ $checkbox = new HtmlElement('input', $attrs, '', true);
+
+ if ($node->isChecked()) {
+ $checkbox->setAttribute('checked', '');
+ }
+
+ $checkbox->setAttribute('disabled', '');
+ $checkbox->setAttribute('type', 'checkbox');
+
+ return $checkbox;
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'task_list_item_marker';
+ }
+
+ /**
+ * @param TaskListItemMarker $node
+ *
+ * @return array
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ TaskListItemMarker::assertInstanceOf($node);
+
+ if ($node->isChecked()) {
+ return ['checked' => 'checked'];
+ }
+
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/GithubFlavoredMarkdownConverter.php b/vendor/league/commonmark/src/GithubFlavoredMarkdownConverter.php
new file mode 100644
index 0000000..f2524b2
--- /dev/null
+++ b/vendor/league/commonmark/src/GithubFlavoredMarkdownConverter.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark;
+
+use League\CommonMark\Environment\Environment;
+use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
+use League\CommonMark\Extension\GithubFlavoredMarkdownExtension;
+
+/**
+ * Converts GitHub Flavored Markdown to HTML.
+ */
+final class GithubFlavoredMarkdownConverter extends MarkdownConverter
+{
+ /**
+ * Create a new Markdown converter pre-configured for GFM
+ *
+ * @param array $config
+ */
+ public function __construct(array $config = [])
+ {
+ $environment = new Environment($config);
+ $environment->addExtension(new CommonMarkCoreExtension());
+ $environment->addExtension(new GithubFlavoredMarkdownExtension());
+
+ parent::__construct($environment);
+ }
+
+ public function getEnvironment(): Environment
+ {
+ \assert($this->environment instanceof Environment);
+
+ return $this->environment;
+ }
+}
diff --git a/vendor/league/commonmark/src/Input/MarkdownInput.php b/vendor/league/commonmark/src/Input/MarkdownInput.php
new file mode 100644
index 0000000..bbe1618
--- /dev/null
+++ b/vendor/league/commonmark/src/Input/MarkdownInput.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Input;
+
+use League\CommonMark\Exception\UnexpectedEncodingException;
+
+class MarkdownInput implements MarkdownInputInterface
+{
+ /**
+ * @var array|null
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private ?array $lines = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private string $content;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?int $lineCount = null;
+
+ /** @psalm-readonly */
+ private int $lineOffset;
+
+ public function __construct(string $content, int $lineOffset = 0)
+ {
+ if (! \mb_check_encoding($content, 'UTF-8')) {
+ throw new UnexpectedEncodingException('Unexpected encoding - UTF-8 or ASCII was expected');
+ }
+
+ // Strip any leading UTF-8 BOM
+ if (\substr($content, 0, 3) === "\xEF\xBB\xBF") {
+ $content = \substr($content, 3);
+ }
+
+ $this->content = $content;
+ $this->lineOffset = $lineOffset;
+ }
+
+ public function getContent(): string
+ {
+ return $this->content;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getLines(): iterable
+ {
+ $this->splitLinesIfNeeded();
+
+ \assert($this->lines !== null);
+
+ /** @psalm-suppress PossiblyNullIterator */
+ foreach ($this->lines as $i => $line) {
+ yield $this->lineOffset + $i + 1 => $line;
+ }
+ }
+
+ public function getLineCount(): int
+ {
+ $this->splitLinesIfNeeded();
+
+ \assert($this->lineCount !== null);
+
+ return $this->lineCount;
+ }
+
+ private function splitLinesIfNeeded(): void
+ {
+ if ($this->lines !== null) {
+ return;
+ }
+
+ $lines = \preg_split('/\r\n|\n|\r/', $this->content);
+ if ($lines === false) {
+ throw new UnexpectedEncodingException('Failed to split Markdown content by line');
+ }
+
+ $this->lines = $lines;
+
+ // Remove any newline which appears at the very end of the string.
+ // We've already split the document by newlines, so we can simply drop
+ // any empty element which appears on the end.
+ if (\end($this->lines) === '') {
+ \array_pop($this->lines);
+ }
+
+ $this->lineCount = \count($this->lines);
+ }
+}
diff --git a/vendor/league/commonmark/src/Input/MarkdownInputInterface.php b/vendor/league/commonmark/src/Input/MarkdownInputInterface.php
new file mode 100644
index 0000000..bb8d6f1
--- /dev/null
+++ b/vendor/league/commonmark/src/Input/MarkdownInputInterface.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Input;
+
+interface MarkdownInputInterface
+{
+ public function getContent(): string;
+
+ /**
+ * @return iterable
+ */
+ public function getLines(): iterable;
+
+ public function getLineCount(): int;
+}
diff --git a/vendor/league/commonmark/src/MarkdownConverter.php b/vendor/league/commonmark/src/MarkdownConverter.php
new file mode 100644
index 0000000..037ecff
--- /dev/null
+++ b/vendor/league/commonmark/src/MarkdownConverter.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark;
+
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Output\RenderedContentInterface;
+use League\CommonMark\Parser\MarkdownParser;
+use League\CommonMark\Parser\MarkdownParserInterface;
+use League\CommonMark\Renderer\HtmlRenderer;
+use League\CommonMark\Renderer\MarkdownRendererInterface;
+
+class MarkdownConverter implements ConverterInterface, MarkdownConverterInterface
+{
+ /** @psalm-readonly */
+ protected EnvironmentInterface $environment;
+
+ /** @psalm-readonly */
+ protected MarkdownParserInterface $markdownParser;
+
+ /** @psalm-readonly */
+ protected MarkdownRendererInterface $htmlRenderer;
+
+ public function __construct(EnvironmentInterface $environment)
+ {
+ $this->environment = $environment;
+
+ $this->markdownParser = new MarkdownParser($environment);
+ $this->htmlRenderer = new HtmlRenderer($environment);
+ }
+
+ public function getEnvironment(): EnvironmentInterface
+ {
+ return $this->environment;
+ }
+
+ /**
+ * Converts Markdown to HTML.
+ *
+ * @param string $input The Markdown to convert
+ *
+ * @return RenderedContentInterface Rendered HTML
+ *
+ * @throws CommonMarkException
+ */
+ public function convert(string $input): RenderedContentInterface
+ {
+ $documentAST = $this->markdownParser->parse($input);
+
+ return $this->htmlRenderer->renderDocument($documentAST);
+ }
+
+ /**
+ * Converts Markdown to HTML.
+ *
+ * @deprecated since 2.2; use {@link convert()} instead
+ *
+ * @param string $markdown The Markdown to convert
+ *
+ * @return RenderedContentInterface Rendered HTML
+ *
+ * @throws CommonMarkException
+ */
+ public function convertToHtml(string $markdown): RenderedContentInterface
+ {
+ \trigger_deprecation('league/commonmark', '2.2.0', 'Calling "convertToHtml()" on a %s class is deprecated, use "convert()" instead.', self::class);
+
+ return $this->convert($markdown);
+ }
+
+ /**
+ * Converts CommonMark to HTML.
+ *
+ * @see MarkdownConverter::convert()
+ *
+ * @throws CommonMarkException
+ */
+ public function __invoke(string $markdown): RenderedContentInterface
+ {
+ return $this->convert($markdown);
+ }
+}
diff --git a/vendor/league/commonmark/src/MarkdownConverterInterface.php b/vendor/league/commonmark/src/MarkdownConverterInterface.php
new file mode 100644
index 0000000..a52a286
--- /dev/null
+++ b/vendor/league/commonmark/src/MarkdownConverterInterface.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark;
+
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Output\RenderedContentInterface;
+
+/**
+ * Interface for a service which converts Markdown to HTML.
+ *
+ * @deprecated since 2.2; use {@link ConverterInterface} instead
+ */
+interface MarkdownConverterInterface
+{
+ /**
+ * Converts Markdown to HTML.
+ *
+ * @deprecated since 2.2; use {@link ConverterInterface::convert()} instead
+ *
+ * @throws CommonMarkException
+ */
+ public function convertToHtml(string $markdown): RenderedContentInterface;
+}
diff --git a/vendor/league/commonmark/src/Node/Block/AbstractBlock.php b/vendor/league/commonmark/src/Node/Block/AbstractBlock.php
new file mode 100644
index 0000000..417f89b
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Block/AbstractBlock.php
@@ -0,0 +1,64 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Block;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+use League\CommonMark\Node\Node;
+
+/**
+ * Block-level element
+ *
+ * @method parent() ?AbstractBlock
+ */
+abstract class AbstractBlock extends Node
+{
+ protected ?int $startLine = null;
+
+ protected ?int $endLine = null;
+
+ protected function setParent(?Node $node = null): void
+ {
+ if ($node && ! $node instanceof self) {
+ throw new InvalidArgumentException('Parent of block must also be block (cannot be inline)');
+ }
+
+ parent::setParent($node);
+ }
+
+ public function setStartLine(?int $startLine): void
+ {
+ $this->startLine = $startLine;
+ if ($this->endLine === null) {
+ $this->endLine = $startLine;
+ }
+ }
+
+ public function getStartLine(): ?int
+ {
+ return $this->startLine;
+ }
+
+ public function setEndLine(?int $endLine): void
+ {
+ $this->endLine = $endLine;
+ }
+
+ public function getEndLine(): ?int
+ {
+ return $this->endLine;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Block/Document.php b/vendor/league/commonmark/src/Node/Block/Document.php
new file mode 100644
index 0000000..ee7ee44
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Block/Document.php
@@ -0,0 +1,56 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Block;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Reference\ReferenceMap;
+use League\CommonMark\Reference\ReferenceMapInterface;
+
+class Document extends AbstractBlock
+{
+ /** @psalm-readonly */
+ protected ReferenceMapInterface $referenceMap;
+
+ public function __construct(?ReferenceMapInterface $referenceMap = null)
+ {
+ parent::__construct();
+
+ $this->setStartLine(1);
+
+ $this->referenceMap = $referenceMap ?? new ReferenceMap();
+ }
+
+ public function getReferenceMap(): ReferenceMapInterface
+ {
+ return $this->referenceMap;
+ }
+
+ public function canContain(AbstractBlock $block): bool
+ {
+ return true;
+ }
+
+ public function isCode(): bool
+ {
+ return false;
+ }
+
+ public function matchesNextLine(Cursor $cursor): bool
+ {
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Block/Paragraph.php b/vendor/league/commonmark/src/Node/Block/Paragraph.php
new file mode 100644
index 0000000..d06d84e
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Block/Paragraph.php
@@ -0,0 +1,23 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Block;
+
+class Paragraph extends AbstractBlock
+{
+ /** @internal */
+ public bool $onlyContainsLinkReferenceDefinitions = false;
+}
diff --git a/vendor/league/commonmark/src/Node/Block/TightBlockInterface.php b/vendor/league/commonmark/src/Node/Block/TightBlockInterface.php
new file mode 100644
index 0000000..21a5868
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Block/TightBlockInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Block;
+
+interface TightBlockInterface
+{
+ public function isTight(): bool;
+
+ public function setTight(bool $tight): void;
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/AbstractInline.php b/vendor/league/commonmark/src/Node/Inline/AbstractInline.php
new file mode 100644
index 0000000..d3705b4
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/AbstractInline.php
@@ -0,0 +1,23 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Node;
+
+abstract class AbstractInline extends Node
+{
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/AbstractStringContainer.php b/vendor/league/commonmark/src/Node/Inline/AbstractStringContainer.php
new file mode 100644
index 0000000..f0aab84
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/AbstractStringContainer.php
@@ -0,0 +1,47 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\StringContainerInterface;
+
+abstract class AbstractStringContainer extends AbstractInline implements StringContainerInterface
+{
+ protected string $literal = '';
+
+ /**
+ * @param array $data
+ */
+ public function __construct(string $contents = '', array $data = [])
+ {
+ parent::__construct();
+
+ $this->literal = $contents;
+ if (\count($data) > 0) {
+ $this->data->import($data);
+ }
+ }
+
+ public function getLiteral(): string
+ {
+ return $this->literal;
+ }
+
+ public function setLiteral(string $literal): void
+ {
+ $this->literal = $literal;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/AdjacentTextMerger.php b/vendor/league/commonmark/src/Node/Inline/AdjacentTextMerger.php
new file mode 100644
index 0000000..43922d4
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/AdjacentTextMerger.php
@@ -0,0 +1,105 @@
+
+ *
+ * Additional emphasis processing code based on commonmark-java (https://github.com/atlassian/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+use League\CommonMark\Node\Node;
+
+/**
+ * @internal
+ */
+final class AdjacentTextMerger
+{
+ public static function mergeChildNodes(Node $node): void
+ {
+ // No children or just one child node, no need for merging
+ if ($node->firstChild() === $node->lastChild() || $node->firstChild() === null || $node->lastChild() === null) {
+ return;
+ }
+
+ /** @psalm-suppress PossiblyNullArgument */
+ self::mergeTextNodesInclusive($node->firstChild(), $node->lastChild());
+ }
+
+ public static function mergeTextNodesBetweenExclusive(Node $fromNode, Node $toNode): void
+ {
+ // No nodes between them
+ if ($fromNode === $toNode || $fromNode->next() === $toNode || $fromNode->next() === null || $toNode->previous() === null) {
+ return;
+ }
+
+ /** @psalm-suppress PossiblyNullArgument */
+ self::mergeTextNodesInclusive($fromNode->next(), $toNode->previous());
+ }
+
+ public static function mergeWithDirectlyAdjacentNodes(Text $node): void
+ {
+ $start = ($previous = $node->previous()) instanceof Text ? $previous : $node;
+ $end = ($next = $node->next()) instanceof Text ? $next : $node;
+
+ self::mergeIfNeeded($start, $end);
+ }
+
+ private static function mergeTextNodesInclusive(Node $fromNode, Node $toNode): void
+ {
+ $first = null;
+ $last = null;
+
+ $node = $fromNode;
+ while ($node !== null) {
+ if ($node instanceof Text) {
+ if ($first === null) {
+ $first = $node;
+ }
+
+ $last = $node;
+ } else {
+ self::mergeIfNeeded($first, $last);
+ $first = null;
+ $last = null;
+ }
+
+ if ($node === $toNode) {
+ break;
+ }
+
+ $node = $node->next();
+ }
+
+ self::mergeIfNeeded($first, $last);
+ }
+
+ private static function mergeIfNeeded(?Text $first, ?Text $last): void
+ {
+ if ($first === null || $last === null || $first === $last) {
+ // No merging needed
+ return;
+ }
+
+ $s = $first->getLiteral();
+
+ $node = $first->next();
+ $stop = $last->next();
+ while ($node !== $stop && $node instanceof Text) {
+ $s .= $node->getLiteral();
+ $unlink = $node;
+ $node = $node->next();
+ $unlink->detach();
+ }
+
+ $first->setLiteral($s);
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/DelimitedInterface.php b/vendor/league/commonmark/src/Node/Inline/DelimitedInterface.php
new file mode 100644
index 0000000..89773fa
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/DelimitedInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+interface DelimitedInterface
+{
+ public function getOpeningDelimiter(): string;
+
+ public function getClosingDelimiter(): string;
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/Newline.php b/vendor/league/commonmark/src/Node/Inline/Newline.php
new file mode 100644
index 0000000..68790de
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/Newline.php
@@ -0,0 +1,40 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+final class Newline extends AbstractInline
+{
+ // Any changes to these constants should be reflected in .phpstorm.meta.php
+ public const HARDBREAK = 0;
+ public const SOFTBREAK = 1;
+
+ /** @psalm-readonly */
+ private int $type;
+
+ public function __construct(int $breakType = self::HARDBREAK)
+ {
+ parent::__construct();
+
+ $this->type = $breakType;
+ }
+
+ /** @psalm-immutable */
+ public function getType(): int
+ {
+ return $this->type;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Inline/Text.php b/vendor/league/commonmark/src/Node/Inline/Text.php
new file mode 100644
index 0000000..31387f9
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Inline/Text.php
@@ -0,0 +1,25 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Inline;
+
+final class Text extends AbstractStringContainer
+{
+ public function append(string $literal): void
+ {
+ $this->literal .= $literal;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Node.php b/vendor/league/commonmark/src/Node/Node.php
new file mode 100644
index 0000000..484b39c
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Node.php
@@ -0,0 +1,262 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+use Dflydev\DotAccessData\Data;
+use League\CommonMark\Exception\InvalidArgumentException;
+
+abstract class Node
+{
+ /** @psalm-readonly */
+ public Data $data;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected int $depth = 0;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected ?Node $parent = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected ?Node $previous = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected ?Node $next = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected ?Node $firstChild = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ protected ?Node $lastChild = null;
+
+ public function __construct()
+ {
+ $this->data = new Data([
+ 'attributes' => [],
+ ]);
+ }
+
+ public function previous(): ?Node
+ {
+ return $this->previous;
+ }
+
+ public function next(): ?Node
+ {
+ return $this->next;
+ }
+
+ public function parent(): ?Node
+ {
+ return $this->parent;
+ }
+
+ protected function setParent(?Node $node = null): void
+ {
+ $this->parent = $node;
+ $this->depth = $node === null ? 0 : $node->depth + 1;
+ }
+
+ /**
+ * Inserts the $sibling node after $this
+ */
+ public function insertAfter(Node $sibling): void
+ {
+ $sibling->detach();
+ $sibling->next = $this->next;
+
+ if ($sibling->next) {
+ $sibling->next->previous = $sibling;
+ }
+
+ $sibling->previous = $this;
+ $this->next = $sibling;
+ $sibling->setParent($this->parent);
+
+ if (! $sibling->next && $sibling->parent) {
+ $sibling->parent->lastChild = $sibling;
+ }
+ }
+
+ /**
+ * Inserts the $sibling node before $this
+ */
+ public function insertBefore(Node $sibling): void
+ {
+ $sibling->detach();
+ $sibling->previous = $this->previous;
+
+ if ($sibling->previous) {
+ $sibling->previous->next = $sibling;
+ }
+
+ $sibling->next = $this;
+ $this->previous = $sibling;
+ $sibling->setParent($this->parent);
+
+ if (! $sibling->previous && $sibling->parent) {
+ $sibling->parent->firstChild = $sibling;
+ }
+ }
+
+ public function replaceWith(Node $replacement): void
+ {
+ $replacement->detach();
+ $this->insertAfter($replacement);
+ $this->detach();
+ }
+
+ public function detach(): void
+ {
+ if ($this->previous) {
+ $this->previous->next = $this->next;
+ } elseif ($this->parent) {
+ $this->parent->firstChild = $this->next;
+ }
+
+ if ($this->next) {
+ $this->next->previous = $this->previous;
+ } elseif ($this->parent) {
+ $this->parent->lastChild = $this->previous;
+ }
+
+ $this->parent = null;
+ $this->next = null;
+ $this->previous = null;
+ $this->depth = 0;
+ }
+
+ public function hasChildren(): bool
+ {
+ return $this->firstChild !== null;
+ }
+
+ public function firstChild(): ?Node
+ {
+ return $this->firstChild;
+ }
+
+ public function lastChild(): ?Node
+ {
+ return $this->lastChild;
+ }
+
+ /**
+ * @return Node[]
+ */
+ public function children(): iterable
+ {
+ $children = [];
+ for ($current = $this->firstChild; $current !== null; $current = $current->next) {
+ $children[] = $current;
+ }
+
+ return $children;
+ }
+
+ public function appendChild(Node $child): void
+ {
+ if ($this->lastChild) {
+ $this->lastChild->insertAfter($child);
+ } else {
+ $child->detach();
+ $child->setParent($this);
+ $this->lastChild = $this->firstChild = $child;
+ }
+ }
+
+ /**
+ * Adds $child as the very first child of $this
+ */
+ public function prependChild(Node $child): void
+ {
+ if ($this->firstChild) {
+ $this->firstChild->insertBefore($child);
+ } else {
+ $child->detach();
+ $child->setParent($this);
+ $this->lastChild = $this->firstChild = $child;
+ }
+ }
+
+ /**
+ * Detaches all child nodes of given node
+ */
+ public function detachChildren(): void
+ {
+ foreach ($this->children() as $children) {
+ $children->setParent(null);
+ }
+
+ $this->firstChild = $this->lastChild = null;
+ }
+
+ /**
+ * Replace all children of given node with collection of another
+ *
+ * @param iterable $children
+ */
+ public function replaceChildren(iterable $children): void
+ {
+ $this->detachChildren();
+ foreach ($children as $item) {
+ $this->appendChild($item);
+ }
+ }
+
+ public function getDepth(): int
+ {
+ return $this->depth;
+ }
+
+ public function walker(): NodeWalker
+ {
+ return new NodeWalker($this);
+ }
+
+ public function iterator(int $flags = 0): NodeIterator
+ {
+ return new NodeIterator($this, $flags);
+ }
+
+ /**
+ * Clone the current node and its children
+ *
+ * WARNING: This is a recursive function and should not be called on deeply-nested node trees!
+ */
+ public function __clone()
+ {
+ // Cloned nodes are detached from their parents, siblings, and children
+ $this->parent = null;
+ $this->previous = null;
+ $this->next = null;
+ // But save a copy of the children since we'll need that in a moment
+ $children = $this->children();
+ $this->detachChildren();
+
+ // The original children get cloned and re-added
+ foreach ($children as $child) {
+ $this->appendChild(clone $child);
+ }
+ }
+
+ public static function assertInstanceOf(Node $node): void
+ {
+ if (! $node instanceof static) {
+ throw new InvalidArgumentException(\sprintf('Incompatible node type: expected %s, got %s', static::class, \get_class($node)));
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/NodeIterator.php b/vendor/league/commonmark/src/Node/NodeIterator.php
new file mode 100644
index 0000000..3d295ef
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/NodeIterator.php
@@ -0,0 +1,58 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+/**
+ * @implements \IteratorAggregate
+ */
+final class NodeIterator implements \IteratorAggregate
+{
+ public const FLAG_BLOCKS_ONLY = 1;
+
+ private Node $node;
+ private bool $blocksOnly;
+
+ public function __construct(Node $node, int $flags = 0)
+ {
+ $this->node = $node;
+ $this->blocksOnly = ($flags & self::FLAG_BLOCKS_ONLY) === self::FLAG_BLOCKS_ONLY;
+ }
+
+ /**
+ * @return \Generator
+ */
+ public function getIterator(): \Generator
+ {
+ $stack = [$this->node];
+ $index = 0;
+
+ while ($stack) {
+ $node = \array_pop($stack);
+
+ yield $index++ => $node;
+
+ // Push all children onto the stack in reverse order
+ $child = $node->lastChild();
+ while ($child !== null) {
+ if (! $this->blocksOnly || $child instanceof AbstractBlock) {
+ $stack[] = $child;
+ }
+
+ $child = $child->previous();
+ }
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/NodeWalker.php b/vendor/league/commonmark/src/Node/NodeWalker.php
new file mode 100644
index 0000000..6f922e8
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/NodeWalker.php
@@ -0,0 +1,80 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+final class NodeWalker
+{
+ /** @psalm-readonly */
+ private Node $root;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?Node $current = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $entering;
+
+ public function __construct(Node $root)
+ {
+ $this->root = $root;
+ $this->current = $this->root;
+ $this->entering = true;
+ }
+
+ /**
+ * Returns an event which contains node and entering flag
+ * (entering is true when we enter a Node from a parent or sibling,
+ * and false when we reenter it from child)
+ */
+ public function next(): ?NodeWalkerEvent
+ {
+ $current = $this->current;
+ $entering = $this->entering;
+ if ($current === null) {
+ return null;
+ }
+
+ if ($entering && ($current instanceof AbstractBlock || $current->hasChildren())) {
+ if ($current->firstChild()) {
+ $this->current = $current->firstChild();
+ $this->entering = true;
+ } else {
+ $this->entering = false;
+ }
+ } elseif ($current === $this->root) {
+ $this->current = null;
+ } elseif ($current->next() === null) {
+ $this->current = $current->parent();
+ $this->entering = false;
+ } else {
+ $this->current = $current->next();
+ $this->entering = true;
+ }
+
+ return new NodeWalkerEvent($current, $entering);
+ }
+
+ /**
+ * Resets the iterator to resume at the specified node
+ */
+ public function resumeAt(Node $node, bool $entering = true): void
+ {
+ $this->current = $node;
+ $this->entering = $entering;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/NodeWalkerEvent.php b/vendor/league/commonmark/src/Node/NodeWalkerEvent.php
new file mode 100644
index 0000000..773ec3a
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/NodeWalkerEvent.php
@@ -0,0 +1,42 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+final class NodeWalkerEvent
+{
+ /** @psalm-readonly */
+ private Node $node;
+
+ /** @psalm-readonly */
+ private bool $isEntering;
+
+ public function __construct(Node $node, bool $isEntering = true)
+ {
+ $this->node = $node;
+ $this->isEntering = $isEntering;
+ }
+
+ public function getNode(): Node
+ {
+ return $this->node;
+ }
+
+ public function isEntering(): bool
+ {
+ return $this->isEntering;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Query.php b/vendor/league/commonmark/src/Node/Query.php
new file mode 100644
index 0000000..7e76fe3
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Query.php
@@ -0,0 +1,139 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+use League\CommonMark\Node\Query\AndExpr;
+use League\CommonMark\Node\Query\OrExpr;
+
+final class Query
+{
+ /** @var callable(Node): bool $condition */
+ private $condition;
+
+ public function __construct()
+ {
+ $this->condition = new AndExpr();
+ }
+
+ public function where(callable ...$conditions): self
+ {
+ return $this->andWhere(...$conditions);
+ }
+
+ public function andWhere(callable ...$conditions): self
+ {
+ if ($this->condition instanceof AndExpr) {
+ foreach ($conditions as $condition) {
+ $this->condition->add($condition);
+ }
+ } else {
+ $this->condition = new AndExpr($this->condition, ...$conditions);
+ }
+
+ return $this;
+ }
+
+ public function orWhere(callable ...$conditions): self
+ {
+ if ($this->condition instanceof OrExpr) {
+ foreach ($conditions as $condition) {
+ $this->condition->add($condition);
+ }
+ } else {
+ $this->condition = new OrExpr($this->condition, ...$conditions);
+ }
+
+ return $this;
+ }
+
+ public function findOne(Node $node): ?Node
+ {
+ foreach ($node->iterator() as $n) {
+ if (\call_user_func($this->condition, $n)) {
+ return $n;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return iterable
+ */
+ public function findAll(Node $node, ?int $limit = PHP_INT_MAX): iterable
+ {
+ $resultCount = 0;
+
+ foreach ($node->iterator() as $n) {
+ if ($resultCount >= $limit) {
+ break;
+ }
+
+ if (! \call_user_func($this->condition, $n)) {
+ continue;
+ }
+
+ ++$resultCount;
+
+ yield $n;
+ }
+ }
+
+ /**
+ * @return callable(Node): bool
+ */
+ public static function type(string $class): callable
+ {
+ return static fn (Node $node): bool => $node instanceof $class;
+ }
+
+ /**
+ * @psalm-param ?callable(Node): bool $condition
+ *
+ * @return callable(Node): bool
+ */
+ public static function hasChild(?callable $condition = null): callable
+ {
+ return static function (Node $node) use ($condition): bool {
+ foreach ($node->children() as $child) {
+ if ($condition === null || $condition($child)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ /**
+ * @psalm-param ?callable(Node): bool $condition
+ *
+ * @return callable(Node): bool
+ */
+ public static function hasParent(?callable $condition = null): callable
+ {
+ return static function (Node $node) use ($condition): bool {
+ $parent = $node->parent();
+ if ($parent === null) {
+ return false;
+ }
+
+ if ($condition === null) {
+ return true;
+ }
+
+ return $condition($parent);
+ };
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Query/AndExpr.php b/vendor/league/commonmark/src/Node/Query/AndExpr.php
new file mode 100644
index 0000000..d2cd615
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Query/AndExpr.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Query;
+
+use League\CommonMark\Node\Node;
+
+/**
+ * @internal
+ */
+final class AndExpr implements ExpressionInterface
+{
+ /**
+ * @var callable[]
+ * @psalm-var list
+ */
+ private array $conditions;
+
+ /**
+ * @psalm-param callable(Node): bool $expressions
+ */
+ public function __construct(callable ...$expressions)
+ {
+ $this->conditions = \array_values($expressions);
+ }
+
+ /**
+ * @param callable(Node): bool $expression
+ */
+ public function add(callable $expression): void
+ {
+ $this->conditions[] = $expression;
+ }
+
+ public function __invoke(Node $node): bool
+ {
+ foreach ($this->conditions as $condition) {
+ if (! $condition($node)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/Query/ExpressionInterface.php b/vendor/league/commonmark/src/Node/Query/ExpressionInterface.php
new file mode 100644
index 0000000..2bbbc7f
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Query/ExpressionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Query;
+
+use League\CommonMark\Node\Node;
+
+interface ExpressionInterface
+{
+ public function __invoke(Node $node): bool;
+}
diff --git a/vendor/league/commonmark/src/Node/Query/OrExpr.php b/vendor/league/commonmark/src/Node/Query/OrExpr.php
new file mode 100644
index 0000000..b0baad8
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/Query/OrExpr.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node\Query;
+
+use League\CommonMark\Node\Node;
+
+/**
+ * @internal
+ */
+final class OrExpr implements ExpressionInterface
+{
+ /**
+ * @var callable[]
+ * @psalm-var list
+ */
+ private array $conditions;
+
+ /**
+ * @psalm-param callable(Node): bool $expressions
+ */
+ public function __construct(callable ...$expressions)
+ {
+ $this->conditions = \array_values($expressions);
+ }
+
+ /**
+ * @param callable(Node): bool $expression
+ */
+ public function add(callable $expression): void
+ {
+ $this->conditions[] = $expression;
+ }
+
+ public function __invoke(Node $node): bool
+ {
+ foreach ($this->conditions as $condition) {
+ if ($condition($node)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/RawMarkupContainerInterface.php b/vendor/league/commonmark/src/Node/RawMarkupContainerInterface.php
new file mode 100644
index 0000000..1545285
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/RawMarkupContainerInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+/**
+ * Interface for a node which contains raw, unprocessed markup (like HTML)
+ */
+interface RawMarkupContainerInterface extends StringContainerInterface
+{
+}
diff --git a/vendor/league/commonmark/src/Node/StringContainerHelper.php b/vendor/league/commonmark/src/Node/StringContainerHelper.php
new file mode 100644
index 0000000..8e1ec34
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/StringContainerHelper.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+final class StringContainerHelper
+{
+ /**
+ * Extract text literals from all descendant nodes
+ *
+ * @param Node $node Parent node
+ * @param array $excludeTypes Optional list of node class types to exclude
+ *
+ * @return string Concatenated literals
+ */
+ public static function getChildText(Node $node, array $excludeTypes = []): string
+ {
+ $text = '';
+
+ foreach ($node->iterator() as $child) {
+ if ($child instanceof StringContainerInterface && ! self::isOneOf($child, $excludeTypes)) {
+ $text .= $child->getLiteral();
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * @param string[] $classesOrInterfacesToCheck
+ *
+ * @psalm-pure
+ */
+ private static function isOneOf(object $object, array $classesOrInterfacesToCheck): bool
+ {
+ foreach ($classesOrInterfacesToCheck as $type) {
+ if ($object instanceof $type) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/league/commonmark/src/Node/StringContainerInterface.php b/vendor/league/commonmark/src/Node/StringContainerInterface.php
new file mode 100644
index 0000000..23564ae
--- /dev/null
+++ b/vendor/league/commonmark/src/Node/StringContainerInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Node;
+
+/**
+ * Interface for a node which directly contains line(s) of text
+ */
+interface StringContainerInterface
+{
+ public function setLiteral(string $literal): void;
+
+ public function getLiteral(): string;
+}
diff --git a/vendor/league/commonmark/src/Normalizer/SlugNormalizer.php b/vendor/league/commonmark/src/Normalizer/SlugNormalizer.php
new file mode 100644
index 0000000..7cfb960
--- /dev/null
+++ b/vendor/league/commonmark/src/Normalizer/SlugNormalizer.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Normalizer;
+
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+/**
+ * Creates URL-friendly strings based on the given string input
+ */
+final class SlugNormalizer implements TextNormalizerInterface, ConfigurationAwareInterface
+{
+ /** @psalm-allow-private-mutation */
+ private int $defaultMaxLength = 255;
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->defaultMaxLength = $configuration->get('slug_normalizer/max_length');
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-immutable
+ */
+ public function normalize(string $text, array $context = []): string
+ {
+ // Add any requested prefix
+ $slug = ($context['prefix'] ?? '') . $text;
+ // Trim whitespace
+ $slug = \trim($slug);
+ // Convert to lowercase
+ $slug = \mb_strtolower($slug, 'UTF-8');
+ // Try replacing whitespace with a dash
+ $slug = \preg_replace('/\s+/u', '-', $slug) ?? $slug;
+ // Try removing characters other than letters, numbers, and marks.
+ $slug = \preg_replace('/[^\p{L}\p{Nd}\p{Nl}\p{M}-]+/u', '', $slug) ?? $slug;
+ // Trim to requested length if given
+ if ($length = $context['length'] ?? $this->defaultMaxLength) {
+ $slug = \mb_substr($slug, 0, $length, 'UTF-8');
+ }
+
+ // @phpstan-ignore-next-line Because it thinks mb_substr() returns false on PHP 7.4
+ return $slug;
+ }
+}
diff --git a/vendor/league/commonmark/src/Normalizer/TextNormalizer.php b/vendor/league/commonmark/src/Normalizer/TextNormalizer.php
new file mode 100644
index 0000000..43eb117
--- /dev/null
+++ b/vendor/league/commonmark/src/Normalizer/TextNormalizer.php
@@ -0,0 +1,44 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Normalizer;
+
+/***
+ * Normalize text input using the steps given by the CommonMark spec to normalize labels
+ *
+ * @see https://spec.commonmark.org/0.29/#matches
+ *
+ * @psalm-immutable
+ */
+final class TextNormalizer implements TextNormalizerInterface
+{
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-pure
+ */
+ public function normalize(string $text, array $context = []): string
+ {
+ // Collapse internal whitespace to single space and remove
+ // leading/trailing whitespace
+ $text = \preg_replace('/[ \t\r\n]+/', ' ', \trim($text));
+ \assert(\is_string($text));
+
+ // Is it strictly ASCII? If so, we can use strtolower() instead (faster)
+ if (\mb_check_encoding($text, 'ASCII')) {
+ return \strtolower($text);
+ }
+
+ return \mb_convert_case($text, \MB_CASE_FOLD, 'UTF-8');
+ }
+}
diff --git a/vendor/league/commonmark/src/Normalizer/TextNormalizerInterface.php b/vendor/league/commonmark/src/Normalizer/TextNormalizerInterface.php
new file mode 100644
index 0000000..f476234
--- /dev/null
+++ b/vendor/league/commonmark/src/Normalizer/TextNormalizerInterface.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Normalizer;
+
+/**
+ * Creates a normalized version of the given input text
+ */
+interface TextNormalizerInterface
+{
+ /**
+ * @param string $text The text to normalize
+ * @param array $context Additional context about the text being normalized (optional)
+ *
+ * $context may include (but is not required to include) the following:
+ * - `prefix` - A string prefix to prepend to each normalized result
+ * - `length` - The requested maximum length
+ * - `node` - The node we're normalizing text for
+ *
+ * Implementations do not have to use or respect any information within that $context
+ */
+ public function normalize(string $text, array $context = []): string;
+}
diff --git a/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizer.php b/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizer.php
new file mode 100644
index 0000000..591f19f
--- /dev/null
+++ b/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizer.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Normalizer;
+
+// phpcs:disable Squiz.Strings.DoubleQuoteUsage.ContainsVar
+final class UniqueSlugNormalizer implements UniqueSlugNormalizerInterface
+{
+ private TextNormalizerInterface $innerNormalizer;
+ /** @var array */
+ private array $alreadyUsed = [];
+
+ public function __construct(TextNormalizerInterface $innerNormalizer)
+ {
+ $this->innerNormalizer = $innerNormalizer;
+ }
+
+ public function clearHistory(): void
+ {
+ $this->alreadyUsed = [];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-allow-private-mutation
+ */
+ public function normalize(string $text, array $context = []): string
+ {
+ $normalized = $this->innerNormalizer->normalize($text, $context);
+
+ // If it's not unique, add an incremental number to the end until we get a unique version
+ if (\array_key_exists($normalized, $this->alreadyUsed)) {
+ $suffix = 0;
+ do {
+ ++$suffix;
+ } while (\array_key_exists("$normalized-$suffix", $this->alreadyUsed));
+
+ $normalized = "$normalized-$suffix";
+ }
+
+ $this->alreadyUsed[$normalized] = true;
+
+ return $normalized;
+ }
+}
diff --git a/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizerInterface.php b/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizerInterface.php
new file mode 100644
index 0000000..642edeb
--- /dev/null
+++ b/vendor/league/commonmark/src/Normalizer/UniqueSlugNormalizerInterface.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Normalizer;
+
+interface UniqueSlugNormalizerInterface extends TextNormalizerInterface
+{
+ public const DISABLED = false;
+ public const PER_ENVIRONMENT = 'environment';
+ public const PER_DOCUMENT = 'document';
+
+ /**
+ * Called by the Environment whenever the configured scope changes
+ *
+ * Currently, this will only be called PER_DOCUMENT.
+ */
+ public function clearHistory(): void;
+}
diff --git a/vendor/league/commonmark/src/Output/RenderedContent.php b/vendor/league/commonmark/src/Output/RenderedContent.php
new file mode 100644
index 0000000..4bf612d
--- /dev/null
+++ b/vendor/league/commonmark/src/Output/RenderedContent.php
@@ -0,0 +1,49 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Output;
+
+use League\CommonMark\Node\Block\Document;
+
+class RenderedContent implements RenderedContentInterface, \Stringable
+{
+ /** @psalm-readonly */
+ private Document $document;
+
+ /** @psalm-readonly */
+ private string $content;
+
+ public function __construct(Document $document, string $content)
+ {
+ $this->document = $document;
+ $this->content = $content;
+ }
+
+ public function getDocument(): Document
+ {
+ return $this->document;
+ }
+
+ public function getContent(): string
+ {
+ return $this->content;
+ }
+
+ /**
+ * @psalm-mutation-free
+ */
+ public function __toString(): string
+ {
+ return $this->content;
+ }
+}
diff --git a/vendor/league/commonmark/src/Output/RenderedContentInterface.php b/vendor/league/commonmark/src/Output/RenderedContentInterface.php
new file mode 100644
index 0000000..2179b1b
--- /dev/null
+++ b/vendor/league/commonmark/src/Output/RenderedContentInterface.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Output;
+
+use League\CommonMark\Node\Block\Document;
+
+interface RenderedContentInterface extends \Stringable
+{
+ /**
+ * @psalm-mutation-free
+ */
+ public function getDocument(): Document;
+
+ /**
+ * @psalm-mutation-free
+ */
+ public function getContent(): string;
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/AbstractBlockContinueParser.php b/vendor/league/commonmark/src/Parser/Block/AbstractBlockContinueParser.php
new file mode 100644
index 0000000..889532e
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/AbstractBlockContinueParser.php
@@ -0,0 +1,47 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+/**
+ * Base class for a block parser
+ *
+ * Slightly more convenient to extend from vs. implementing the interface
+ */
+abstract class AbstractBlockContinueParser implements BlockContinueParserInterface
+{
+ public function isContainer(): bool
+ {
+ return false;
+ }
+
+ public function canHaveLazyContinuationLines(): bool
+ {
+ return false;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return false;
+ }
+
+ public function addLine(string $line): void
+ {
+ }
+
+ public function closeBlock(): void
+ {
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/BlockContinue.php b/vendor/league/commonmark/src/Parser/Block/BlockContinue.php
new file mode 100644
index 0000000..4b5f37d
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/BlockContinue.php
@@ -0,0 +1,73 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\CursorState;
+
+/**
+ * Result object for continuing parsing of a block; see static methods for constructors.
+ *
+ * @psalm-immutable
+ */
+final class BlockContinue
+{
+ /** @psalm-readonly */
+ private ?CursorState $cursorState = null;
+
+ /** @psalm-readonly */
+ private bool $finalize;
+
+ private function __construct(?CursorState $cursorState = null, bool $finalize = false)
+ {
+ $this->cursorState = $cursorState;
+ $this->finalize = $finalize;
+ }
+
+ public function getCursorState(): ?CursorState
+ {
+ return $this->cursorState;
+ }
+
+ public function isFinalize(): bool
+ {
+ return $this->finalize;
+ }
+
+ /**
+ * Signal that we cannot continue here
+ *
+ * @return null
+ */
+ public static function none(): ?self
+ {
+ return null;
+ }
+
+ /**
+ * Signal that we're continuing at the given position
+ */
+ public static function at(Cursor $cursor): self
+ {
+ return new self($cursor->saveState(), false);
+ }
+
+ /**
+ * Signal that we want to finalize and close the block
+ */
+ public static function finished(): self
+ {
+ return new self(null, true);
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/BlockContinueParserInterface.php b/vendor/league/commonmark/src/Parser/Block/BlockContinueParserInterface.php
new file mode 100644
index 0000000..b6e5472
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/BlockContinueParserInterface.php
@@ -0,0 +1,64 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Parser\Cursor;
+
+/**
+ * Interface for a block continuation parser
+ *
+ * A block continue parser can only handle a single block instance. The current block being parsed is stored within this parser and
+ * can be returned once parsing has completed. If you need to parse multiple block continuations, instantiate a new parser for each one.
+ */
+interface BlockContinueParserInterface
+{
+ /**
+ * Return the current block being parsed by this parser
+ */
+ public function getBlock(): AbstractBlock;
+
+ /**
+ * Return whether we are parsing a container block
+ */
+ public function isContainer(): bool;
+
+ /**
+ * Return whether we are interested in possibly lazily parsing any subsequent lines
+ */
+ public function canHaveLazyContinuationLines(): bool;
+
+ /**
+ * Determine whether the current block being parsed can contain the given child block
+ */
+ public function canContain(AbstractBlock $childBlock): bool;
+
+ /**
+ * Attempt to parse the given line
+ */
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue;
+
+ /**
+ * Add the given line of text to the current block
+ */
+ public function addLine(string $line): void;
+
+ /**
+ * Close and finalize the current block
+ */
+ public function closeBlock(): void;
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/BlockContinueParserWithInlinesInterface.php b/vendor/league/commonmark/src/Parser/Block/BlockContinueParserWithInlinesInterface.php
new file mode 100644
index 0000000..6f826c9
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/BlockContinueParserWithInlinesInterface.php
@@ -0,0 +1,24 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\InlineParserEngineInterface;
+
+interface BlockContinueParserWithInlinesInterface extends BlockContinueParserInterface
+{
+ /**
+ * Parse any inlines inside of the current block
+ */
+ public function parseInlines(InlineParserEngineInterface $inlineParser): void;
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/BlockStart.php b/vendor/league/commonmark/src/Parser/Block/BlockStart.php
new file mode 100644
index 0000000..5576622
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/BlockStart.php
@@ -0,0 +1,124 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\CursorState;
+
+/**
+ * Result object for starting parsing of a block; see static methods for constructors
+ */
+final class BlockStart
+{
+ /**
+ * @var BlockContinueParserInterface[]
+ *
+ * @psalm-readonly
+ */
+ private array $blockParsers;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?CursorState $cursorState = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $replaceActiveBlockParser = false;
+
+ private bool $isAborting = false;
+
+ private function __construct(BlockContinueParserInterface ...$blockParsers)
+ {
+ $this->blockParsers = $blockParsers;
+ }
+
+ /**
+ * @return BlockContinueParserInterface[]
+ */
+ public function getBlockParsers(): iterable
+ {
+ return $this->blockParsers;
+ }
+
+ public function getCursorState(): ?CursorState
+ {
+ return $this->cursorState;
+ }
+
+ public function isReplaceActiveBlockParser(): bool
+ {
+ return $this->replaceActiveBlockParser;
+ }
+
+ /**
+ * @internal
+ */
+ public function isAborting(): bool
+ {
+ return $this->isAborting;
+ }
+
+ /**
+ * Signal that we want to parse at the given cursor position
+ *
+ * @return $this
+ */
+ public function at(Cursor $cursor): self
+ {
+ $this->cursorState = $cursor->saveState();
+
+ return $this;
+ }
+
+ /**
+ * Signal that we want to replace the active block parser with this one
+ *
+ * @return $this
+ */
+ public function replaceActiveBlockParser(): self
+ {
+ $this->replaceActiveBlockParser = true;
+
+ return $this;
+ }
+
+ /**
+ * Signal that we cannot parse whatever is here
+ *
+ * @return null
+ */
+ public static function none(): ?self
+ {
+ return null;
+ }
+
+ /**
+ * Signal that we'd like to register the given parser(s) so they can parse the current block
+ */
+ public static function of(BlockContinueParserInterface ...$blockParsers): self
+ {
+ return new self(...$blockParsers);
+ }
+
+ /**
+ * Signal that the block parsing process should be aborted (no other block starts should be checked)
+ *
+ * @internal
+ */
+ public static function abort(): self
+ {
+ $ret = new self();
+ $ret->isAborting = true;
+
+ return $ret;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/BlockStartParserInterface.php b/vendor/league/commonmark/src/Parser/Block/BlockStartParserInterface.php
new file mode 100644
index 0000000..90ed781
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/BlockStartParserInterface.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+
+/**
+ * Interface for a block parser which identifies block starts.
+ */
+interface BlockStartParserInterface
+{
+ /**
+ * Check whether we should handle the block at the current position
+ *
+ * @param Cursor $cursor A cloned copy of the cursor at the current parsing location
+ * @param MarkdownParserStateInterface $parserState Additional information about the state of the Markdown parser
+ *
+ * @return BlockStart|null The BlockStart that has been identified, or null if the block doesn't match here
+ */
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart;
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/DocumentBlockParser.php b/vendor/league/commonmark/src/Parser/Block/DocumentBlockParser.php
new file mode 100644
index 0000000..c03c24e
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/DocumentBlockParser.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Reference\ReferenceMapInterface;
+
+/**
+ * Parser implementation which ensures everything is added to the root-level Document
+ */
+final class DocumentBlockParser extends AbstractBlockContinueParser
+{
+ /** @psalm-readonly */
+ private Document $document;
+
+ public function __construct(ReferenceMapInterface $referenceMap)
+ {
+ $this->document = new Document($referenceMap);
+ }
+
+ public function getBlock(): Document
+ {
+ return $this->document;
+ }
+
+ public function isContainer(): bool
+ {
+ return true;
+ }
+
+ public function canContain(AbstractBlock $childBlock): bool
+ {
+ return true;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ return BlockContinue::at($cursor);
+ }
+
+ public function closeBlock(): void
+ {
+ $this->removeLinkReferenceDefinitions();
+ }
+
+ private function removeLinkReferenceDefinitions(): void
+ {
+ $emptyNodes = [];
+
+ $walker = $this->document->walker();
+ while ($event = $walker->next()) {
+ $node = $event->getNode();
+ // TODO for v3: It would be great if we could find an alternate way to identify such paragraphs.
+ // Unfortunately, we can't simply check for empty paragraphs here because inlines haven't been processed yet,
+ // meaning all paragraphs will appear blank here, and we don't have a way to check the status of the reference parser
+ // which is attached to the (already-closed) paragraph parser.
+ if ($event->isEntering() && $node instanceof Paragraph && $node->onlyContainsLinkReferenceDefinitions) {
+ $emptyNodes[] = $node;
+ }
+ }
+
+ foreach ($emptyNodes as $node) {
+ $node->detach();
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/ParagraphParser.php b/vendor/league/commonmark/src/Parser/Block/ParagraphParser.php
new file mode 100644
index 0000000..f9312be
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/ParagraphParser.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\InlineParserEngineInterface;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceParser;
+
+final class ParagraphParser extends AbstractBlockContinueParser implements BlockContinueParserWithInlinesInterface
+{
+ /** @psalm-readonly */
+ private Paragraph $block;
+
+ /** @psalm-readonly */
+ private ReferenceParser $referenceParser;
+
+ public function __construct()
+ {
+ $this->block = new Paragraph();
+ $this->referenceParser = new ReferenceParser();
+ }
+
+ public function canHaveLazyContinuationLines(): bool
+ {
+ return true;
+ }
+
+ public function getBlock(): Paragraph
+ {
+ return $this->block;
+ }
+
+ public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue
+ {
+ if ($cursor->isBlank()) {
+ return BlockContinue::none();
+ }
+
+ return BlockContinue::at($cursor);
+ }
+
+ public function addLine(string $line): void
+ {
+ $this->referenceParser->parse($line);
+ }
+
+ public function closeBlock(): void
+ {
+ $this->block->onlyContainsLinkReferenceDefinitions = $this->referenceParser->hasReferences() && $this->referenceParser->getParagraphContent() === '';
+ }
+
+ public function parseInlines(InlineParserEngineInterface $inlineParser): void
+ {
+ $content = $this->getContentString();
+ if ($content !== '') {
+ $inlineParser->parse($content, $this->block);
+ }
+ }
+
+ public function getContentString(): string
+ {
+ return $this->referenceParser->getParagraphContent();
+ }
+
+ /**
+ * @return ReferenceInterface[]
+ */
+ public function getReferences(): iterable
+ {
+ return $this->referenceParser->getReferences();
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Block/SkipLinesStartingWithLettersParser.php b/vendor/league/commonmark/src/Parser/Block/SkipLinesStartingWithLettersParser.php
new file mode 100644
index 0000000..95d8bd2
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Block/SkipLinesStartingWithLettersParser.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Block;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Parser\MarkdownParserStateInterface;
+use League\CommonMark\Util\RegexHelper;
+
+/**
+ * @internal
+ *
+ * This "parser" is actually a performance optimization.
+ *
+ * Most lines in a typical Markdown document probably won't match a block start. This is especially true for lines starting
+ * with letters - nothing in the core CommonMark spec or our supported extensions will match those lines as blocks. Therefore,
+ * if we can identify those lines and skip block start parsing, we can optimize performance by ~10%.
+ *
+ * Previously this optimization was hard-coded in the MarkdownParser but did not allow users to override this behavior.
+ * By implementing this optimization as a block parser instead, users wanting custom blocks starting with letters
+ * can instead register their block parser with a higher priority to ensure their parser is always called first.
+ */
+final class SkipLinesStartingWithLettersParser implements BlockStartParserInterface
+{
+ public function tryStart(Cursor $cursor, MarkdownParserStateInterface $parserState): ?BlockStart
+ {
+ if (! $cursor->isIndented() && RegexHelper::isLetter($cursor->getNextNonSpaceCharacter())) {
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ return BlockStart::abort();
+ }
+
+ return BlockStart::none();
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Cursor.php b/vendor/league/commonmark/src/Parser/Cursor.php
new file mode 100644
index 0000000..598cd75
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Cursor.php
@@ -0,0 +1,494 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Exception\UnexpectedEncodingException;
+
+class Cursor
+{
+ public const INDENT_LEVEL = 4;
+
+ /** @psalm-readonly */
+ private string $line;
+
+ /** @psalm-readonly */
+ private int $length;
+
+ /**
+ * @var int
+ *
+ * It's possible for this to be 1 char past the end, meaning we've parsed all chars and have
+ * reached the end. In this state, any character-returning method MUST return null.
+ */
+ private int $currentPosition = 0;
+
+ private int $column = 0;
+
+ private int $indent = 0;
+
+ private int $previousPosition = 0;
+
+ private ?int $nextNonSpaceCache = null;
+
+ private bool $partiallyConsumedTab = false;
+
+ /**
+ * @var int|false
+ *
+ * @psalm-readonly
+ */
+ private $lastTabPosition;
+
+ /** @psalm-readonly */
+ private bool $isMultibyte;
+
+ /** @var array */
+ private array $charCache = [];
+
+ /**
+ * @param string $line The line being parsed (ASCII or UTF-8)
+ */
+ public function __construct(string $line)
+ {
+ if (! \mb_check_encoding($line, 'UTF-8')) {
+ throw new UnexpectedEncodingException('Unexpected encoding - UTF-8 or ASCII was expected');
+ }
+
+ $this->line = $line;
+ $this->length = \mb_strlen($line, 'UTF-8') ?: 0;
+ $this->isMultibyte = $this->length !== \strlen($line);
+ $this->lastTabPosition = $this->isMultibyte ? \mb_strrpos($line, "\t", 0, 'UTF-8') : \strrpos($line, "\t");
+ }
+
+ /**
+ * Returns the position of the next character which is not a space (or tab)
+ */
+ public function getNextNonSpacePosition(): int
+ {
+ if ($this->nextNonSpaceCache !== null) {
+ return $this->nextNonSpaceCache;
+ }
+
+ if ($this->currentPosition >= $this->length) {
+ return $this->length;
+ }
+
+ $cols = $this->column;
+
+ for ($i = $this->currentPosition; $i < $this->length; $i++) {
+ // This if-else was copied out of getCharacter() for performance reasons
+ if ($this->isMultibyte) {
+ $c = $this->charCache[$i] ??= \mb_substr($this->line, $i, 1, 'UTF-8');
+ } else {
+ $c = $this->line[$i];
+ }
+
+ if ($c === ' ') {
+ $cols++;
+ } elseif ($c === "\t") {
+ $cols += 4 - ($cols % 4);
+ } else {
+ break;
+ }
+ }
+
+ $this->indent = $cols - $this->column;
+
+ return $this->nextNonSpaceCache = $i;
+ }
+
+ /**
+ * Returns the next character which isn't a space (or tab)
+ */
+ public function getNextNonSpaceCharacter(): ?string
+ {
+ $index = $this->getNextNonSpacePosition();
+ if ($index >= $this->length) {
+ return null;
+ }
+
+ if ($this->isMultibyte) {
+ return $this->charCache[$index] ??= \mb_substr($this->line, $index, 1, 'UTF-8');
+ }
+
+ return $this->line[$index];
+ }
+
+ /**
+ * Calculates the current indent (number of spaces after current position)
+ */
+ public function getIndent(): int
+ {
+ if ($this->nextNonSpaceCache === null) {
+ $this->getNextNonSpacePosition();
+ }
+
+ return $this->indent;
+ }
+
+ /**
+ * Whether the cursor is indented to INDENT_LEVEL
+ */
+ public function isIndented(): bool
+ {
+ if ($this->nextNonSpaceCache === null) {
+ $this->getNextNonSpacePosition();
+ }
+
+ return $this->indent >= self::INDENT_LEVEL;
+ }
+
+ public function getCharacter(?int $index = null): ?string
+ {
+ if ($index === null) {
+ $index = $this->currentPosition;
+ }
+
+ // Index out-of-bounds, or we're at the end
+ if ($index < 0 || $index >= $this->length) {
+ return null;
+ }
+
+ if ($this->isMultibyte) {
+ return $this->charCache[$index] ??= \mb_substr($this->line, $index, 1, 'UTF-8');
+ }
+
+ return $this->line[$index];
+ }
+
+ /**
+ * Slightly-optimized version of getCurrent(null)
+ */
+ public function getCurrentCharacter(): ?string
+ {
+ if ($this->currentPosition >= $this->length) {
+ return null;
+ }
+
+ if ($this->isMultibyte) {
+ return $this->charCache[$this->currentPosition] ??= \mb_substr($this->line, $this->currentPosition, 1, 'UTF-8');
+ }
+
+ return $this->line[$this->currentPosition];
+ }
+
+ /**
+ * Returns the next character (or null, if none) without advancing forwards
+ */
+ public function peek(int $offset = 1): ?string
+ {
+ return $this->getCharacter($this->currentPosition + $offset);
+ }
+
+ /**
+ * Whether the remainder is blank
+ */
+ public function isBlank(): bool
+ {
+ return $this->nextNonSpaceCache === $this->length || $this->getNextNonSpacePosition() === $this->length;
+ }
+
+ /**
+ * Move the cursor forwards
+ */
+ public function advance(): void
+ {
+ $this->advanceBy(1);
+ }
+
+ /**
+ * Move the cursor forwards
+ *
+ * @param int $characters Number of characters to advance by
+ * @param bool $advanceByColumns Whether to advance by columns instead of spaces
+ */
+ public function advanceBy(int $characters, bool $advanceByColumns = false): void
+ {
+ $this->previousPosition = $this->currentPosition;
+ $this->nextNonSpaceCache = null;
+
+ if ($this->currentPosition >= $this->length || $characters === 0) {
+ return;
+ }
+
+ // Optimization to avoid tab handling logic if we have no tabs
+ if ($this->lastTabPosition === false || $this->currentPosition > $this->lastTabPosition) {
+ $length = \min($characters, $this->length - $this->currentPosition);
+ $this->partiallyConsumedTab = false;
+ $this->currentPosition += $length;
+ $this->column += $length;
+
+ return;
+ }
+
+ $nextFewChars = $this->isMultibyte ?
+ \mb_substr($this->line, $this->currentPosition, $characters, 'UTF-8') :
+ \substr($this->line, $this->currentPosition, $characters);
+
+ if ($characters === 1) {
+ $asArray = [$nextFewChars];
+ } elseif ($this->isMultibyte) {
+ /** @var string[] $asArray */
+ $asArray = \mb_str_split($nextFewChars, 1, 'UTF-8');
+ } else {
+ $asArray = \str_split($nextFewChars);
+ }
+
+ foreach ($asArray as $c) {
+ if ($c === "\t") {
+ $charsToTab = 4 - ($this->column % 4);
+ if ($advanceByColumns) {
+ $this->partiallyConsumedTab = $charsToTab > $characters;
+ $charsToAdvance = $charsToTab > $characters ? $characters : $charsToTab;
+ $this->column += $charsToAdvance;
+ $this->currentPosition += $this->partiallyConsumedTab ? 0 : 1;
+ $characters -= $charsToAdvance;
+ } else {
+ $this->partiallyConsumedTab = false;
+ $this->column += $charsToTab;
+ $this->currentPosition++;
+ $characters--;
+ }
+ } else {
+ $this->partiallyConsumedTab = false;
+ $this->currentPosition++;
+ $this->column++;
+ $characters--;
+ }
+
+ if ($characters <= 0) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Advances the cursor by a single space or tab, if present
+ */
+ public function advanceBySpaceOrTab(): bool
+ {
+ $character = $this->getCurrentCharacter();
+
+ if ($character === ' ' || $character === "\t") {
+ $this->advanceBy(1, true);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Parse zero or more space/tab characters
+ *
+ * @return int Number of positions moved
+ */
+ public function advanceToNextNonSpaceOrTab(): int
+ {
+ $newPosition = $this->nextNonSpaceCache ?? $this->getNextNonSpacePosition();
+ if ($newPosition === $this->currentPosition) {
+ return 0;
+ }
+
+ $this->advanceBy($newPosition - $this->currentPosition);
+ $this->partiallyConsumedTab = false;
+
+ // We've just advanced to where that non-space is,
+ // so any subsequent calls to find the next one will
+ // always return the current position.
+ $this->nextNonSpaceCache = $this->currentPosition;
+ $this->indent = 0;
+
+ return $this->currentPosition - $this->previousPosition;
+ }
+
+ /**
+ * Parse zero or more space characters, including at most one newline.
+ *
+ * Tab characters are not parsed with this function.
+ *
+ * @return int Number of positions moved
+ */
+ public function advanceToNextNonSpaceOrNewline(): int
+ {
+ $currentCharacter = $this->getCurrentCharacter();
+
+ // Optimization: Avoid the regex if we know there are no spaces or newlines
+ if ($currentCharacter !== ' ' && $currentCharacter !== "\n") {
+ $this->previousPosition = $this->currentPosition;
+
+ return 0;
+ }
+
+ $matches = [];
+ \preg_match('/^ *(?:\n *)?/', $this->getRemainder(), $matches, \PREG_OFFSET_CAPTURE);
+
+ // [0][0] contains the matched text
+ // [0][1] contains the index of that match
+ \assert(isset($matches[0]));
+ $increment = $matches[0][1] + \strlen($matches[0][0]);
+
+ $this->advanceBy($increment);
+
+ return $this->currentPosition - $this->previousPosition;
+ }
+
+ /**
+ * Move the position to the very end of the line
+ *
+ * @return int The number of characters moved
+ */
+ public function advanceToEnd(): int
+ {
+ $this->previousPosition = $this->currentPosition;
+ $this->nextNonSpaceCache = null;
+
+ $this->currentPosition = $this->length;
+
+ return $this->currentPosition - $this->previousPosition;
+ }
+
+ public function getRemainder(): string
+ {
+ if ($this->currentPosition >= $this->length) {
+ return '';
+ }
+
+ $prefix = '';
+ $position = $this->currentPosition;
+ if ($this->partiallyConsumedTab) {
+ $position++;
+ $charsToTab = 4 - ($this->column % 4);
+ $prefix = \str_repeat(' ', $charsToTab);
+ }
+
+ $subString = $this->isMultibyte ?
+ \mb_substr($this->line, $position, null, 'UTF-8') :
+ \substr($this->line, $position);
+
+ return $prefix . $subString;
+ }
+
+ public function getLine(): string
+ {
+ return $this->line;
+ }
+
+ public function isAtEnd(): bool
+ {
+ return $this->currentPosition >= $this->length;
+ }
+
+ /**
+ * Try to match a regular expression
+ *
+ * Returns the matching text and advances to the end of that match
+ *
+ * @psalm-param non-empty-string $regex
+ */
+ public function match(string $regex): ?string
+ {
+ $subject = $this->getRemainder();
+
+ if (! \preg_match($regex, $subject, $matches, \PREG_OFFSET_CAPTURE)) {
+ return null;
+ }
+
+ // $matches[0][0] contains the matched text
+ // $matches[0][1] contains the index of that match
+
+ if ($this->isMultibyte) {
+ // PREG_OFFSET_CAPTURE always returns the byte offset, not the char offset, which is annoying
+ $offset = \mb_strlen(\substr($subject, 0, $matches[0][1]), 'UTF-8');
+ $matchLength = \mb_strlen($matches[0][0], 'UTF-8');
+ } else {
+ $offset = $matches[0][1];
+ $matchLength = \strlen($matches[0][0]);
+ }
+
+ // [0][0] contains the matched text
+ // [0][1] contains the index of that match
+ $this->advanceBy($offset + $matchLength);
+
+ return $matches[0][0];
+ }
+
+ /**
+ * Encapsulates the current state of this cursor in case you need to rollback later.
+ *
+ * WARNING: Do not parse or use the return value for ANYTHING except for
+ * passing it back into restoreState(), as the number of values and their
+ * contents may change in any future release without warning.
+ */
+ public function saveState(): CursorState
+ {
+ return new CursorState([
+ $this->currentPosition,
+ $this->previousPosition,
+ $this->nextNonSpaceCache,
+ $this->indent,
+ $this->column,
+ $this->partiallyConsumedTab,
+ ]);
+ }
+
+ /**
+ * Restore the cursor to a previous state.
+ *
+ * Pass in the value previously obtained by calling saveState().
+ */
+ public function restoreState(CursorState $state): void
+ {
+ [
+ $this->currentPosition,
+ $this->previousPosition,
+ $this->nextNonSpaceCache,
+ $this->indent,
+ $this->column,
+ $this->partiallyConsumedTab,
+ ] = $state->toArray();
+ }
+
+ public function getPosition(): int
+ {
+ return $this->currentPosition;
+ }
+
+ public function getPreviousText(): string
+ {
+ if ($this->isMultibyte) {
+ return \mb_substr($this->line, $this->previousPosition, $this->currentPosition - $this->previousPosition, 'UTF-8');
+ }
+
+ return \substr($this->line, $this->previousPosition, $this->currentPosition - $this->previousPosition);
+ }
+
+ public function getSubstring(int $start, ?int $length = null): string
+ {
+ if ($this->isMultibyte) {
+ return \mb_substr($this->line, $start, $length, 'UTF-8');
+ }
+
+ if ($length !== null) {
+ return \substr($this->line, $start, $length);
+ }
+
+ return \substr($this->line, $start);
+ }
+
+ public function getColumn(): int
+ {
+ return $this->column;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/CursorState.php b/vendor/league/commonmark/src/Parser/CursorState.php
new file mode 100644
index 0000000..4a6c2d9
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/CursorState.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+/**
+ * Encapsulates the current state of a cursor in case you need to rollback later.
+ *
+ * WARNING: Do not attempt to use this class for ANYTHING except for
+ * type hinting and passing this object back into restoreState().
+ * The constructor, methods, and inner contents may change in any
+ * future release without warning!
+ *
+ * @internal
+ *
+ * @psalm-immutable
+ */
+final class CursorState
+{
+ /**
+ * @var array
+ *
+ * @psalm-readonly
+ */
+ private array $state;
+
+ /**
+ * @internal
+ *
+ * @param array $state
+ */
+ public function __construct(array $state)
+ {
+ $this->state = $state;
+ }
+
+ /**
+ * @internal
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return $this->state;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Inline/InlineParserInterface.php b/vendor/league/commonmark/src/Parser/Inline/InlineParserInterface.php
new file mode 100644
index 0000000..fd13435
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Inline/InlineParserInterface.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Inline;
+
+use League\CommonMark\Parser\InlineParserContext;
+
+interface InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch;
+
+ public function parse(InlineParserContext $inlineContext): bool;
+}
diff --git a/vendor/league/commonmark/src/Parser/Inline/InlineParserMatch.php b/vendor/league/commonmark/src/Parser/Inline/InlineParserMatch.php
new file mode 100644
index 0000000..e433ed2
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Inline/InlineParserMatch.php
@@ -0,0 +1,87 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Inline;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+
+final class InlineParserMatch
+{
+ private string $regex;
+
+ private bool $caseSensitive;
+
+ private function __construct(string $regex, bool $caseSensitive = false)
+ {
+ $this->regex = $regex;
+ $this->caseSensitive = $caseSensitive;
+ }
+
+ public function caseSensitive(): self
+ {
+ $this->caseSensitive = true;
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ *
+ * @psalm-return non-empty-string
+ */
+ public function getRegex(): string
+ {
+ return '/' . $this->regex . '/' . ($this->caseSensitive ? '' : 'i');
+ }
+
+ /**
+ * Match the given string (case-insensitive)
+ */
+ public static function string(string $str): self
+ {
+ return new self(\preg_quote($str, '/'));
+ }
+
+ /**
+ * Match any of the given strings (case-insensitive)
+ */
+ public static function oneOf(string ...$str): self
+ {
+ return new self(\implode('|', \array_map(static fn (string $str): string => \preg_quote($str, '/'), $str)));
+ }
+
+ /**
+ * Match a partial regular expression without starting/ending delimiters, anchors, or flags
+ */
+ public static function regex(string $regex): self
+ {
+ return new self($regex);
+ }
+
+ public static function join(self ...$definitions): self
+ {
+ $regex = '';
+ $caseSensitive = null;
+ foreach ($definitions as $definition) {
+ $regex .= '(' . $definition->regex . ')';
+
+ if ($caseSensitive === null) {
+ $caseSensitive = $definition->caseSensitive;
+ } elseif ($caseSensitive !== $definition->caseSensitive) {
+ throw new InvalidArgumentException('Case-sensitive and case-insensitive definitions cannot be combined');
+ }
+ }
+
+ return new self($regex, $caseSensitive ?? false);
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/Inline/NewlineParser.php b/vendor/league/commonmark/src/Parser/Inline/NewlineParser.php
new file mode 100644
index 0000000..eb10d91
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/Inline/NewlineParser.php
@@ -0,0 +1,53 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser\Inline;
+
+use League\CommonMark\Node\Inline\Newline;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\InlineParserContext;
+
+final class NewlineParser implements InlineParserInterface
+{
+ public function getMatchDefinition(): InlineParserMatch
+ {
+ return InlineParserMatch::regex('\\n');
+ }
+
+ public function parse(InlineParserContext $inlineContext): bool
+ {
+ $inlineContext->getCursor()->advanceBy(1);
+
+ // Check previous inline for trailing spaces
+ $spaces = 0;
+ $lastInline = $inlineContext->getContainer()->lastChild();
+ if ($lastInline instanceof Text) {
+ $trimmed = \rtrim($lastInline->getLiteral(), ' ');
+ $spaces = \strlen($lastInline->getLiteral()) - \strlen($trimmed);
+ if ($spaces) {
+ $lastInline->setLiteral($trimmed);
+ }
+ }
+
+ if ($spaces >= 2) {
+ $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK));
+ } else {
+ $inlineContext->getContainer()->appendChild(new Newline(Newline::SOFTBREAK));
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/InlineParserContext.php b/vendor/league/commonmark/src/Parser/InlineParserContext.php
new file mode 100644
index 0000000..9372904
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/InlineParserContext.php
@@ -0,0 +1,120 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Delimiter\DelimiterStack;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Reference\ReferenceMapInterface;
+
+final class InlineParserContext
+{
+ /** @psalm-readonly */
+ private AbstractBlock $container;
+
+ /** @psalm-readonly */
+ private ReferenceMapInterface $referenceMap;
+
+ /** @psalm-readonly */
+ private Cursor $cursor;
+
+ /** @psalm-readonly */
+ private DelimiterStack $delimiterStack;
+
+ /**
+ * @var string[]
+ * @psalm-var non-empty-array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $matches;
+
+ public function __construct(Cursor $contents, AbstractBlock $container, ReferenceMapInterface $referenceMap, int $maxDelimitersPerLine = PHP_INT_MAX)
+ {
+ $this->referenceMap = $referenceMap;
+ $this->container = $container;
+ $this->cursor = $contents;
+ $this->delimiterStack = new DelimiterStack($maxDelimitersPerLine);
+ }
+
+ public function getContainer(): AbstractBlock
+ {
+ return $this->container;
+ }
+
+ public function getReferenceMap(): ReferenceMapInterface
+ {
+ return $this->referenceMap;
+ }
+
+ public function getCursor(): Cursor
+ {
+ return $this->cursor;
+ }
+
+ public function getDelimiterStack(): DelimiterStack
+ {
+ return $this->delimiterStack;
+ }
+
+ /**
+ * @return string The full text that matched the InlineParserMatch definition
+ */
+ public function getFullMatch(): string
+ {
+ return $this->matches[0];
+ }
+
+ /**
+ * @return int The length of the full match (in characters, not bytes)
+ */
+ public function getFullMatchLength(): int
+ {
+ return \mb_strlen($this->matches[0], 'UTF-8');
+ }
+
+ /**
+ * @return string[] Similar to preg_match(), index 0 will contain the full match, and any other array elements will be captured sub-matches
+ *
+ * @psalm-return non-empty-array
+ */
+ public function getMatches(): array
+ {
+ return $this->matches;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getSubMatches(): array
+ {
+ return \array_slice($this->matches, 1);
+ }
+
+ /**
+ * @param string[] $matches
+ *
+ * @psalm-param non-empty-array $matches
+ */
+ public function withMatches(array $matches): InlineParserContext
+ {
+ $ctx = clone $this;
+
+ $ctx->matches = $matches;
+
+ return $ctx;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/InlineParserEngine.php b/vendor/league/commonmark/src/Parser/InlineParserEngine.php
new file mode 100644
index 0000000..6a26979
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/InlineParserEngine.php
@@ -0,0 +1,177 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Inline\AdjacentTextMerger;
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Parser\Inline\InlineParserInterface;
+use League\CommonMark\Reference\ReferenceMapInterface;
+
+/**
+ * @internal
+ */
+final class InlineParserEngine implements InlineParserEngineInterface
+{
+ /** @psalm-readonly */
+ private EnvironmentInterface $environment;
+
+ /** @psalm-readonly */
+ private ReferenceMapInterface $referenceMap;
+
+ /**
+ * @var array
+ * @psalm-var list
+ * @phpstan-var array
+ */
+ private array $parsers = [];
+
+ public function __construct(EnvironmentInterface $environment, ReferenceMapInterface $referenceMap)
+ {
+ $this->environment = $environment;
+ $this->referenceMap = $referenceMap;
+
+ foreach ($environment->getInlineParsers() as $parser) {
+ \assert($parser instanceof InlineParserInterface);
+ $regex = $parser->getMatchDefinition()->getRegex();
+
+ $this->parsers[] = [$parser, $regex, \strlen($regex) !== \mb_strlen($regex, 'UTF-8')];
+ }
+ }
+
+ public function parse(string $contents, AbstractBlock $block): void
+ {
+ $contents = \trim($contents);
+ $cursor = new Cursor($contents);
+
+ $inlineParserContext = new InlineParserContext($cursor, $block, $this->referenceMap, $this->environment->getConfiguration()->get('max_delimiters_per_line'));
+
+ // Have all parsers look at the line to determine what they might want to parse and what positions they exist at
+ foreach ($this->matchParsers($contents) as $matchPosition => $parsers) {
+ $currentPosition = $cursor->getPosition();
+ // We've already gone past this point
+ if ($currentPosition > $matchPosition) {
+ continue;
+ }
+
+ // We've skipped over some uninteresting text that should be added as a plain text node
+ if ($currentPosition < $matchPosition) {
+ $cursor->advanceBy($matchPosition - $currentPosition);
+ $this->addPlainText($cursor->getPreviousText(), $block);
+ }
+
+ // We're now at a potential start - see which of the current parsers can handle it
+ $parsed = false;
+ foreach ($parsers as [$parser, $matches]) {
+ \assert($parser instanceof InlineParserInterface);
+ if ($parser->parse($inlineParserContext->withMatches($matches))) {
+ // A parser has successfully handled the text at the given position; don't consider any others at this position
+ $parsed = true;
+ break;
+ }
+ }
+
+ if ($parsed) {
+ continue;
+ }
+
+ // Despite potentially being interested, nothing actually parsed text here, so add the current character and continue onwards
+ $this->addPlainText((string) $cursor->getCurrentCharacter(), $block);
+ $cursor->advance();
+ }
+
+ // Add any remaining text that wasn't parsed
+ if (! $cursor->isAtEnd()) {
+ $this->addPlainText($cursor->getRemainder(), $block);
+ }
+
+ // Process any delimiters that were found
+ $delimiterStack = $inlineParserContext->getDelimiterStack();
+ $delimiterStack->processDelimiters(null, $this->environment->getDelimiterProcessors());
+ $delimiterStack->removeAll();
+
+ // Combine adjacent text notes into one
+ AdjacentTextMerger::mergeChildNodes($block);
+ }
+
+ private function addPlainText(string $text, AbstractBlock $container): void
+ {
+ $lastInline = $container->lastChild();
+ if ($lastInline instanceof Text && ! $lastInline->data->has('delim')) {
+ $lastInline->append($text);
+ } else {
+ $container->appendChild(new Text($text));
+ }
+ }
+
+ /**
+ * Given the current line, ask all the parsers which parts of the text they would be interested in parsing.
+ *
+ * The resulting array provides a list of character positions, which parsers are interested in trying to parse
+ * the text at those points, and (for convenience/optimization) what the matching text happened to be.
+ *
+ * @return array>
+ *
+ * @psalm-return array}>>
+ *
+ * @phpstan-return array}>>
+ */
+ private function matchParsers(string $contents): array
+ {
+ $contents = \trim($contents);
+ $isMultibyte = ! \mb_check_encoding($contents, 'ASCII');
+
+ $ret = [];
+
+ foreach ($this->parsers as [$parser, $regex, $isRegexMultibyte]) {
+ if ($isMultibyte || $isRegexMultibyte) {
+ $regex .= 'u';
+ }
+
+ // See if the parser's InlineParserMatch regex matched against any part of the string
+ if (! \preg_match_all($regex, $contents, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER)) {
+ continue;
+ }
+
+ // For each part that matched...
+ foreach ($matches as $match) {
+ if ($isMultibyte) {
+ // PREG_OFFSET_CAPTURE always returns the byte offset, not the char offset, which is annoying
+ $offset = \mb_strlen(\substr($contents, 0, $match[0][1]), 'UTF-8');
+ } else {
+ $offset = \intval($match[0][1]);
+ }
+
+ // Remove the offsets, keeping only the matched text
+ $m = \array_column($match, 0);
+
+ if ($m === []) {
+ continue;
+ }
+
+ // Add this match to the list of character positions to stop at
+ $ret[$offset][] = [$parser, $m];
+ }
+ }
+
+ // Sort matches by position so we visit them in order
+ \ksort($ret);
+
+ return $ret;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/InlineParserEngineInterface.php b/vendor/league/commonmark/src/Parser/InlineParserEngineInterface.php
new file mode 100644
index 0000000..8a0986d
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/InlineParserEngineInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+
+/**
+ * Parser for inline content (text, links, emphasized text, etc).
+ */
+interface InlineParserEngineInterface
+{
+ /**
+ * Parse the given contents as inlines and insert them into the given block
+ */
+ public function parse(string $contents, AbstractBlock $block): void;
+}
diff --git a/vendor/league/commonmark/src/Parser/MarkdownParser.php b/vendor/league/commonmark/src/Parser/MarkdownParser.php
new file mode 100644
index 0000000..904c7c4
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/MarkdownParser.php
@@ -0,0 +1,356 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * Additional code based on commonmark-java (https://github.com/commonmark/commonmark-java)
+ * - (c) Atlassian Pty Ltd
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Event\DocumentParsedEvent;
+use League\CommonMark\Event\DocumentPreParsedEvent;
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Input\MarkdownInput;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\BlockContinueParserWithInlinesInterface;
+use League\CommonMark\Parser\Block\BlockStart;
+use League\CommonMark\Parser\Block\BlockStartParserInterface;
+use League\CommonMark\Parser\Block\DocumentBlockParser;
+use League\CommonMark\Parser\Block\ParagraphParser;
+use League\CommonMark\Reference\MemoryLimitedReferenceMap;
+use League\CommonMark\Reference\ReferenceInterface;
+use League\CommonMark\Reference\ReferenceMap;
+
+final class MarkdownParser implements MarkdownParserInterface
+{
+ /** @psalm-readonly */
+ private EnvironmentInterface $environment;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $maxNestingLevel;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ReferenceMap $referenceMap;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $lineNumber = 0;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private Cursor $cursor;
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $activeBlockParsers = [];
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $closedBlockParsers = [];
+
+ public function __construct(EnvironmentInterface $environment)
+ {
+ $this->environment = $environment;
+ }
+
+ private function initialize(): void
+ {
+ $this->referenceMap = new ReferenceMap();
+ $this->lineNumber = 0;
+ $this->activeBlockParsers = [];
+ $this->closedBlockParsers = [];
+
+ $this->maxNestingLevel = $this->environment->getConfiguration()->get('max_nesting_level');
+ }
+
+ /**
+ * @throws CommonMarkException
+ */
+ public function parse(string $input): Document
+ {
+ $this->initialize();
+
+ $documentParser = new DocumentBlockParser($this->referenceMap);
+ $this->activateBlockParser($documentParser);
+
+ $preParsedEvent = new DocumentPreParsedEvent($documentParser->getBlock(), new MarkdownInput($input));
+ $this->environment->dispatch($preParsedEvent);
+ $markdownInput = $preParsedEvent->getMarkdown();
+
+ foreach ($markdownInput->getLines() as $lineNumber => $line) {
+ $this->lineNumber = $lineNumber;
+ $this->parseLine($line);
+ }
+
+ // finalizeAndProcess
+ $this->closeBlockParsers(\count($this->activeBlockParsers), $this->lineNumber);
+ $this->processInlines(\strlen($input));
+
+ $this->environment->dispatch(new DocumentParsedEvent($documentParser->getBlock()));
+
+ return $documentParser->getBlock();
+ }
+
+ /**
+ * Analyze a line of text and update the document appropriately. We parse markdown text by calling this on each
+ * line of input, then finalizing the document.
+ */
+ private function parseLine(string $line): void
+ {
+ // replace NUL characters for security
+ $line = \str_replace("\0", "\u{FFFD}", $line);
+
+ $this->cursor = new Cursor($line);
+
+ $matches = $this->parseBlockContinuation();
+ if ($matches === null) {
+ return;
+ }
+
+ $unmatchedBlocks = \count($this->activeBlockParsers) - $matches;
+ $blockParser = $this->activeBlockParsers[$matches - 1];
+ $startedNewBlock = false;
+
+ // Unless last matched container is a code block, try new container starts,
+ // adding children to the last matched container:
+ $tryBlockStarts = $blockParser->getBlock() instanceof Paragraph || $blockParser->isContainer();
+ while ($tryBlockStarts) {
+ // this is a little performance optimization
+ if ($this->cursor->isBlank()) {
+ $this->cursor->advanceToEnd();
+ break;
+ }
+
+ if ($blockParser->getBlock()->getDepth() >= $this->maxNestingLevel) {
+ break;
+ }
+
+ $blockStart = $this->findBlockStart($blockParser);
+ if ($blockStart === null || $blockStart->isAborting()) {
+ $this->cursor->advanceToNextNonSpaceOrTab();
+ break;
+ }
+
+ if (($state = $blockStart->getCursorState()) !== null) {
+ $this->cursor->restoreState($state);
+ }
+
+ $startedNewBlock = true;
+
+ // We're starting a new block. If we have any previous blocks that need to be closed, we need to do it now.
+ if ($unmatchedBlocks > 0) {
+ $this->closeBlockParsers($unmatchedBlocks, $this->lineNumber - 1);
+ $unmatchedBlocks = 0;
+ }
+
+ $oldBlockLineStart = null;
+ if ($blockStart->isReplaceActiveBlockParser()) {
+ $oldBlockLineStart = $this->prepareActiveBlockParserForReplacement();
+ }
+
+ foreach ($blockStart->getBlockParsers() as $newBlockParser) {
+ $blockParser = $this->addChild($newBlockParser, $oldBlockLineStart);
+ $tryBlockStarts = $newBlockParser->isContainer();
+ }
+ }
+
+ // What remains at the offset is a text line. Add the text to the appropriate block.
+
+ // First check for a lazy paragraph continuation:
+ if (! $startedNewBlock && ! $this->cursor->isBlank() && $this->getActiveBlockParser()->canHaveLazyContinuationLines()) {
+ $this->getActiveBlockParser()->addLine($this->cursor->getRemainder());
+ } else {
+ // finalize any blocks not matched
+ if ($unmatchedBlocks > 0) {
+ $this->closeBlockParsers($unmatchedBlocks, $this->lineNumber - 1);
+ }
+
+ if (! $blockParser->isContainer()) {
+ $this->getActiveBlockParser()->addLine($this->cursor->getRemainder());
+ } elseif (! $this->cursor->isBlank()) {
+ $this->addChild(new ParagraphParser());
+ $this->getActiveBlockParser()->addLine($this->cursor->getRemainder());
+ }
+ }
+ }
+
+ private function parseBlockContinuation(): ?int
+ {
+ // For each containing block, try to parse the associated line start.
+ // The document will always match, so we can skip the first block parser and start at 1 matches
+ $matches = 1;
+ for ($i = 1; $i < \count($this->activeBlockParsers); $i++) {
+ $blockParser = $this->activeBlockParsers[$i];
+ $blockContinue = $blockParser->tryContinue(clone $this->cursor, $this->getActiveBlockParser());
+ if ($blockContinue === null) {
+ break;
+ }
+
+ if ($blockContinue->isFinalize()) {
+ $this->closeBlockParsers(\count($this->activeBlockParsers) - $i, $this->lineNumber);
+
+ return null;
+ }
+
+ if (($state = $blockContinue->getCursorState()) !== null) {
+ $this->cursor->restoreState($state);
+ }
+
+ $matches++;
+ }
+
+ return $matches;
+ }
+
+ private function findBlockStart(BlockContinueParserInterface $lastMatchedBlockParser): ?BlockStart
+ {
+ $matchedBlockParser = new MarkdownParserState($this->getActiveBlockParser(), $lastMatchedBlockParser);
+
+ foreach ($this->environment->getBlockStartParsers() as $blockStartParser) {
+ \assert($blockStartParser instanceof BlockStartParserInterface);
+ if (($result = $blockStartParser->tryStart(clone $this->cursor, $matchedBlockParser)) !== null) {
+ return $result;
+ }
+ }
+
+ return null;
+ }
+
+ private function closeBlockParsers(int $count, int $endLineNumber): void
+ {
+ for ($i = 0; $i < $count; $i++) {
+ $blockParser = $this->deactivateBlockParser();
+ $this->finalize($blockParser, $endLineNumber);
+
+ // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed
+ if ($blockParser instanceof BlockContinueParserWithInlinesInterface) {
+ // Remember for inline parsing
+ $this->closedBlockParsers[] = $blockParser;
+ }
+ }
+ }
+
+ /**
+ * Finalize a block. Close it and do any necessary postprocessing, e.g. creating string_content from strings,
+ * setting the 'tight' or 'loose' status of a list, and parsing the beginnings of paragraphs for reference
+ * definitions.
+ */
+ private function finalize(BlockContinueParserInterface $blockParser, int $endLineNumber): void
+ {
+ if ($blockParser instanceof ParagraphParser) {
+ $this->updateReferenceMap($blockParser->getReferences());
+ }
+
+ $blockParser->getBlock()->setEndLine($endLineNumber);
+ $blockParser->closeBlock();
+ }
+
+ /**
+ * Walk through a block & children recursively, parsing string content into inline content where appropriate.
+ */
+ private function processInlines(int $inputSize): void
+ {
+ $p = new InlineParserEngine($this->environment, new MemoryLimitedReferenceMap($this->referenceMap, $inputSize));
+
+ foreach ($this->closedBlockParsers as $blockParser) {
+ $blockParser->parseInlines($p);
+ }
+ }
+
+ /**
+ * Add block of type tag as a child of the tip. If the tip can't accept children, close and finalize it and try
+ * its parent, and so on til we find a block that can accept children.
+ */
+ private function addChild(BlockContinueParserInterface $blockParser, ?int $startLineNumber = null): BlockContinueParserInterface
+ {
+ $blockParser->getBlock()->setStartLine($startLineNumber ?? $this->lineNumber);
+
+ while (! $this->getActiveBlockParser()->canContain($blockParser->getBlock())) {
+ $this->closeBlockParsers(1, ($startLineNumber ?? $this->lineNumber) - 1);
+ }
+
+ $this->getActiveBlockParser()->getBlock()->appendChild($blockParser->getBlock());
+ $this->activateBlockParser($blockParser);
+
+ return $blockParser;
+ }
+
+ private function activateBlockParser(BlockContinueParserInterface $blockParser): void
+ {
+ $this->activeBlockParsers[] = $blockParser;
+ }
+
+ /**
+ * @throws ParserLogicException
+ */
+ private function deactivateBlockParser(): BlockContinueParserInterface
+ {
+ $popped = \array_pop($this->activeBlockParsers);
+ if ($popped === null) {
+ throw new ParserLogicException('The last block parser should not be deactivated');
+ }
+
+ return $popped;
+ }
+
+ /**
+ * @return int|null The line number where the old block started
+ */
+ private function prepareActiveBlockParserForReplacement(): ?int
+ {
+ // Note that we don't want to parse inlines or finalize this block, as it's getting replaced.
+ $old = $this->deactivateBlockParser();
+
+ if ($old instanceof ParagraphParser) {
+ $this->updateReferenceMap($old->getReferences());
+ }
+
+ $old->getBlock()->detach();
+
+ return $old->getBlock()->getStartLine();
+ }
+
+ /**
+ * @param ReferenceInterface[] $references
+ */
+ private function updateReferenceMap(iterable $references): void
+ {
+ foreach ($references as $reference) {
+ if (! $this->referenceMap->contains($reference->getLabel())) {
+ $this->referenceMap->add($reference);
+ }
+ }
+ }
+
+ /**
+ * @throws ParserLogicException
+ */
+ public function getActiveBlockParser(): BlockContinueParserInterface
+ {
+ $active = \end($this->activeBlockParsers);
+ if ($active === false) {
+ throw new ParserLogicException('No active block parsers are available');
+ }
+
+ return $active;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/MarkdownParserInterface.php b/vendor/league/commonmark/src/Parser/MarkdownParserInterface.php
new file mode 100644
index 0000000..e0a6be4
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/MarkdownParserInterface.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Node\Block\Document;
+
+interface MarkdownParserInterface
+{
+ /**
+ * @throws CommonMarkException
+ */
+ public function parse(string $input): Document;
+}
diff --git a/vendor/league/commonmark/src/Parser/MarkdownParserState.php b/vendor/league/commonmark/src/Parser/MarkdownParserState.php
new file mode 100644
index 0000000..79abd42
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/MarkdownParserState.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+use League\CommonMark\Parser\Block\ParagraphParser;
+
+/**
+ * @internal You should rely on the interface instead
+ */
+final class MarkdownParserState implements MarkdownParserStateInterface
+{
+ /** @psalm-readonly */
+ private BlockContinueParserInterface $activeBlockParser;
+
+ /** @psalm-readonly */
+ private BlockContinueParserInterface $lastMatchedBlockParser;
+
+ public function __construct(BlockContinueParserInterface $activeBlockParser, BlockContinueParserInterface $lastMatchedBlockParser)
+ {
+ $this->activeBlockParser = $activeBlockParser;
+ $this->lastMatchedBlockParser = $lastMatchedBlockParser;
+ }
+
+ public function getActiveBlockParser(): BlockContinueParserInterface
+ {
+ return $this->activeBlockParser;
+ }
+
+ public function getLastMatchedBlockParser(): BlockContinueParserInterface
+ {
+ return $this->lastMatchedBlockParser;
+ }
+
+ public function getParagraphContent(): ?string
+ {
+ if (! $this->lastMatchedBlockParser instanceof ParagraphParser) {
+ return null;
+ }
+
+ $paragraphParser = $this->lastMatchedBlockParser;
+ $content = $paragraphParser->getContentString();
+
+ return $content === '' ? null : $content;
+ }
+}
diff --git a/vendor/league/commonmark/src/Parser/MarkdownParserStateInterface.php b/vendor/league/commonmark/src/Parser/MarkdownParserStateInterface.php
new file mode 100644
index 0000000..21a9d3a
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/MarkdownParserStateInterface.php
@@ -0,0 +1,36 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Parser\Block\BlockContinueParserInterface;
+
+interface MarkdownParserStateInterface
+{
+ /**
+ * Returns the deepest open block parser
+ */
+ public function getActiveBlockParser(): BlockContinueParserInterface;
+
+ /**
+ * Open block parser that was last matched during the continue phase. This is different from the currently active
+ * block parser, as an unmatched block is only closed when a new block is started.
+ */
+ public function getLastMatchedBlockParser(): BlockContinueParserInterface;
+
+ /**
+ * Returns the current content of the paragraph if the matched block is a paragraph. The content can be multiple
+ * lines separated by newlines.
+ */
+ public function getParagraphContent(): ?string;
+}
diff --git a/vendor/league/commonmark/src/Parser/ParserLogicException.php b/vendor/league/commonmark/src/Parser/ParserLogicException.php
new file mode 100644
index 0000000..592b1a2
--- /dev/null
+++ b/vendor/league/commonmark/src/Parser/ParserLogicException.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Parser;
+
+use League\CommonMark\Exception\CommonMarkException;
+
+class ParserLogicException extends \LogicException implements CommonMarkException
+{
+}
diff --git a/vendor/league/commonmark/src/Reference/MemoryLimitedReferenceMap.php b/vendor/league/commonmark/src/Reference/MemoryLimitedReferenceMap.php
new file mode 100644
index 0000000..d47bd6a
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/MemoryLimitedReferenceMap.php
@@ -0,0 +1,68 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+final class MemoryLimitedReferenceMap implements ReferenceMapInterface
+{
+ private ReferenceMapInterface $decorated;
+
+ private const MINIMUM_SIZE = 100_000;
+
+ private int $remaining;
+
+ public function __construct(ReferenceMapInterface $decorated, int $maxSize)
+ {
+ $this->decorated = $decorated;
+ $this->remaining = \max(self::MINIMUM_SIZE, $maxSize);
+ }
+
+ public function add(ReferenceInterface $reference): void
+ {
+ $this->decorated->add($reference);
+ }
+
+ public function contains(string $label): bool
+ {
+ return $this->decorated->contains($label);
+ }
+
+ public function get(string $label): ?ReferenceInterface
+ {
+ $reference = $this->decorated->get($label);
+ if ($reference === null) {
+ return null;
+ }
+
+ // Check for expansion limit
+ $this->remaining -= \strlen($reference->getDestination()) + \strlen($reference->getTitle());
+ if ($this->remaining < 0) {
+ return null;
+ }
+
+ return $reference;
+ }
+
+ /**
+ * @return \Traversable
+ */
+ public function getIterator(): \Traversable
+ {
+ return $this->decorated->getIterator();
+ }
+
+ public function count(): int
+ {
+ return $this->decorated->count();
+ }
+}
diff --git a/vendor/league/commonmark/src/Reference/Reference.php b/vendor/league/commonmark/src/Reference/Reference.php
new file mode 100644
index 0000000..a0d571d
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/Reference.php
@@ -0,0 +1,54 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+/**
+ * @psalm-immutable
+ */
+final class Reference implements ReferenceInterface
+{
+ /** @psalm-readonly */
+ private string $label;
+
+ /** @psalm-readonly */
+ private string $destination;
+
+ /** @psalm-readonly */
+ private string $title;
+
+ public function __construct(string $label, string $destination, string $title)
+ {
+ $this->label = $label;
+ $this->destination = $destination;
+ $this->title = $title;
+ }
+
+ public function getLabel(): string
+ {
+ return $this->label;
+ }
+
+ public function getDestination(): string
+ {
+ return $this->destination;
+ }
+
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+}
diff --git a/vendor/league/commonmark/src/Reference/ReferenceInterface.php b/vendor/league/commonmark/src/Reference/ReferenceInterface.php
new file mode 100644
index 0000000..244b354
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/ReferenceInterface.php
@@ -0,0 +1,29 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+/**
+ * Link reference
+ */
+interface ReferenceInterface
+{
+ public function getLabel(): string;
+
+ public function getDestination(): string;
+
+ public function getTitle(): string;
+}
diff --git a/vendor/league/commonmark/src/Reference/ReferenceMap.php b/vendor/league/commonmark/src/Reference/ReferenceMap.php
new file mode 100644
index 0000000..97a167d
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/ReferenceMap.php
@@ -0,0 +1,85 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+use League\CommonMark\Normalizer\TextNormalizer;
+
+/**
+ * A collection of references, indexed by label
+ */
+final class ReferenceMap implements ReferenceMapInterface
+{
+ /** @psalm-readonly */
+ private TextNormalizer $normalizer;
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $references = [];
+
+ public function __construct()
+ {
+ $this->normalizer = new TextNormalizer();
+ }
+
+ public function add(ReferenceInterface $reference): void
+ {
+ // Normalize the key
+ $key = $this->normalizer->normalize($reference->getLabel());
+ // Store the reference
+ $this->references[$key] = $reference;
+ }
+
+ public function contains(string $label): bool
+ {
+ if ($this->references === []) {
+ return false;
+ }
+
+ $label = $this->normalizer->normalize($label);
+
+ return isset($this->references[$label]);
+ }
+
+ public function get(string $label): ?ReferenceInterface
+ {
+ if ($this->references === []) {
+ return null;
+ }
+
+ $label = $this->normalizer->normalize($label);
+
+ return $this->references[$label] ?? null;
+ }
+
+ /**
+ * @return \Traversable
+ */
+ public function getIterator(): \Traversable
+ {
+ foreach ($this->references as $normalizedLabel => $reference) {
+ yield $normalizedLabel => $reference;
+ }
+ }
+
+ public function count(): int
+ {
+ return \count($this->references);
+ }
+}
diff --git a/vendor/league/commonmark/src/Reference/ReferenceMapInterface.php b/vendor/league/commonmark/src/Reference/ReferenceMapInterface.php
new file mode 100644
index 0000000..71daa19
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/ReferenceMapInterface.php
@@ -0,0 +1,31 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+/**
+ * A collection of references
+ *
+ * @phpstan-extends \IteratorAggregate
+ */
+interface ReferenceMapInterface extends \IteratorAggregate, \Countable
+{
+ public function add(ReferenceInterface $reference): void;
+
+ public function contains(string $label): bool;
+
+ public function get(string $label): ?ReferenceInterface;
+}
diff --git a/vendor/league/commonmark/src/Reference/ReferenceParser.php b/vendor/league/commonmark/src/Reference/ReferenceParser.php
new file mode 100644
index 0000000..c01dd21
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/ReferenceParser.php
@@ -0,0 +1,324 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Reference;
+
+use League\CommonMark\Parser\Cursor;
+use League\CommonMark\Util\LinkParserHelper;
+
+final class ReferenceParser
+{
+ // Looking for the start of a definition, i.e. `[`
+ private const START_DEFINITION = 0;
+ // Looking for and parsing the label, i.e. `[foo]` within `[foo]`
+ private const LABEL = 1;
+ // Parsing the destination, i.e. `/url` in `[foo]: /url`
+ private const DESTINATION = 2;
+ // Looking for the start of a title, i.e. the first `"` in `[foo]: /url "title"`
+ private const START_TITLE = 3;
+ // Parsing the content of the title, i.e. `title` in `[foo]: /url "title"`
+ private const TITLE = 4;
+ // End state, no matter what kind of lines we add, they won't be references
+ private const PARAGRAPH = 5;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private string $paragraph = '';
+
+ /**
+ * @var array
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private array $references = [];
+
+ /** @psalm-readonly-allow-private-mutation */
+ private int $state = self::START_DEFINITION;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?string $label = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?string $destination = null;
+
+ /**
+ * @var string string
+ *
+ * @psalm-readonly-allow-private-mutation
+ */
+ private string $title = '';
+
+ /** @psalm-readonly-allow-private-mutation */
+ private ?string $titleDelimiter = null;
+
+ /** @psalm-readonly-allow-private-mutation */
+ private bool $referenceValid = false;
+
+ public function getParagraphContent(): string
+ {
+ return $this->paragraph;
+ }
+
+ /**
+ * @return ReferenceInterface[]
+ */
+ public function getReferences(): iterable
+ {
+ $this->finishReference();
+
+ return $this->references;
+ }
+
+ public function hasReferences(): bool
+ {
+ return $this->references !== [];
+ }
+
+ public function parse(string $line): void
+ {
+ if ($this->paragraph !== '') {
+ $this->paragraph .= "\n";
+ }
+
+ $this->paragraph .= $line;
+
+ $cursor = new Cursor($line);
+ while (! $cursor->isAtEnd()) {
+ $result = false;
+ switch ($this->state) {
+ case self::PARAGRAPH:
+ // We're in a paragraph now. Link reference definitions can only appear at the beginning, so once
+ // we're in a paragraph, there's no going back.
+ return;
+ case self::START_DEFINITION:
+ $result = $this->parseStartDefinition($cursor);
+ break;
+ case self::LABEL:
+ $result = $this->parseLabel($cursor);
+ break;
+ case self::DESTINATION:
+ $result = $this->parseDestination($cursor);
+ break;
+ case self::START_TITLE:
+ $result = $this->parseStartTitle($cursor);
+ break;
+ case self::TITLE:
+ $result = $this->parseTitle($cursor);
+ break;
+ default:
+ // this should never happen
+ break;
+ }
+
+ if (! $result) {
+ $this->state = self::PARAGRAPH;
+
+ return;
+ }
+ }
+ }
+
+ private function parseStartDefinition(Cursor $cursor): bool
+ {
+ $cursor->advanceToNextNonSpaceOrTab();
+ if ($cursor->isAtEnd() || $cursor->getCurrentCharacter() !== '[') {
+ return false;
+ }
+
+ $this->state = self::LABEL;
+ $this->label = '';
+
+ $cursor->advance();
+ if ($cursor->isAtEnd()) {
+ $this->label .= "\n";
+ }
+
+ return true;
+ }
+
+ private function parseLabel(Cursor $cursor): bool
+ {
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ $partialLabel = LinkParserHelper::parsePartialLinkLabel($cursor);
+ if ($partialLabel === null) {
+ return false;
+ }
+
+ \assert($this->label !== null);
+ $this->label .= $partialLabel;
+
+ if ($cursor->isAtEnd()) {
+ // label might continue on next line
+ $this->label .= "\n";
+
+ return true;
+ }
+
+ if ($cursor->getCurrentCharacter() !== ']') {
+ return false;
+ }
+
+ $cursor->advance();
+
+ // end of label
+ if ($cursor->getCurrentCharacter() !== ':') {
+ return false;
+ }
+
+ $cursor->advance();
+
+ // spec: A link label can have at most 999 characters inside the square brackets
+ if (\mb_strlen($this->label, 'UTF-8') > 999) {
+ return false;
+ }
+
+ // spec: A link label must contain at least one non-whitespace character
+ if (\trim($this->label) === '') {
+ return false;
+ }
+
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ $this->state = self::DESTINATION;
+
+ return true;
+ }
+
+ private function parseDestination(Cursor $cursor): bool
+ {
+ $cursor->advanceToNextNonSpaceOrTab();
+
+ $destination = LinkParserHelper::parseLinkDestination($cursor);
+ if ($destination === null) {
+ return false;
+ }
+
+ $this->destination = $destination;
+
+ $advanced = $cursor->advanceToNextNonSpaceOrTab();
+ if ($cursor->isAtEnd()) {
+ // Destination was at end of line, so this is a valid reference for sure (and maybe a title).
+ // If not at end of line, wait for title to be valid first.
+ $this->referenceValid = true;
+ $this->paragraph = '';
+ } elseif ($advanced === 0) {
+ // spec: The title must be separated from the link destination by whitespace
+ return false;
+ }
+
+ $this->state = self::START_TITLE;
+
+ return true;
+ }
+
+ private function parseStartTitle(Cursor $cursor): bool
+ {
+ $cursor->advanceToNextNonSpaceOrTab();
+ if ($cursor->isAtEnd()) {
+ $this->state = self::START_DEFINITION;
+
+ return true;
+ }
+
+ $this->titleDelimiter = null;
+ switch ($c = $cursor->getCurrentCharacter()) {
+ case '"':
+ case "'":
+ $this->titleDelimiter = $c;
+ break;
+ case '(':
+ $this->titleDelimiter = ')';
+ break;
+ default:
+ // no title delimter found
+ break;
+ }
+
+ if ($this->titleDelimiter !== null) {
+ $this->state = self::TITLE;
+ $cursor->advance();
+ if ($cursor->isAtEnd()) {
+ $this->title .= "\n";
+ }
+ } else {
+ $this->finishReference();
+ // There might be another reference instead, try that for the same character.
+ $this->state = self::START_DEFINITION;
+ }
+
+ return true;
+ }
+
+ private function parseTitle(Cursor $cursor): bool
+ {
+ \assert($this->titleDelimiter !== null);
+ $title = LinkParserHelper::parsePartialLinkTitle($cursor, $this->titleDelimiter);
+
+ if ($title === null) {
+ // Invalid title, stop
+ return false;
+ }
+
+ // Did we find the end delimiter?
+ $endDelimiterFound = false;
+ if (\substr($title, -1) === $this->titleDelimiter) {
+ $endDelimiterFound = true;
+ // Chop it off
+ $title = \substr($title, 0, -1);
+ }
+
+ $this->title .= $title;
+
+ if (! $endDelimiterFound && $cursor->isAtEnd()) {
+ // Title still going, continue on next line
+ $this->title .= "\n";
+
+ return true;
+ }
+
+ // We either hit the end delimiter or some extra whitespace
+ $cursor->advanceToNextNonSpaceOrTab();
+ if (! $cursor->isAtEnd()) {
+ // spec: No further non-whitespace characters may occur on the line.
+ return false;
+ }
+
+ $this->referenceValid = true;
+ $this->finishReference();
+ $this->paragraph = '';
+
+ // See if there's another definition
+ $this->state = self::START_DEFINITION;
+
+ return true;
+ }
+
+ private function finishReference(): void
+ {
+ if (! $this->referenceValid) {
+ return;
+ }
+
+ /** @psalm-suppress PossiblyNullArgument -- these can't possibly be null if we're in this state */
+ $this->references[] = new Reference($this->label, $this->destination, $this->title);
+
+ $this->label = null;
+ $this->referenceValid = false;
+ $this->destination = null;
+ $this->title = '';
+ $this->titleDelimiter = null;
+ }
+}
diff --git a/vendor/league/commonmark/src/Reference/ReferenceableInterface.php b/vendor/league/commonmark/src/Reference/ReferenceableInterface.php
new file mode 100644
index 0000000..b45f379
--- /dev/null
+++ b/vendor/league/commonmark/src/Reference/ReferenceableInterface.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace League\CommonMark\Reference;
+
+interface ReferenceableInterface
+{
+ public function getReference(): ReferenceInterface;
+}
diff --git a/vendor/league/commonmark/src/Renderer/Block/DocumentRenderer.php b/vendor/league/commonmark/src/Renderer/Block/DocumentRenderer.php
new file mode 100644
index 0000000..3262691
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/Block/DocumentRenderer.php
@@ -0,0 +1,57 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer\Block;
+
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class DocumentRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Document $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ Document::assertInstanceOf($node);
+
+ $wholeDoc = $childRenderer->renderNodes($node->children());
+
+ return $wholeDoc === '' ? '' : $wholeDoc . "\n";
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'document';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [
+ 'xmlns' => 'http://commonmark.org/xml/1.0',
+ ];
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/Block/ParagraphRenderer.php b/vendor/league/commonmark/src/Renderer/Block/ParagraphRenderer.php
new file mode 100644
index 0000000..934eac2
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/Block/ParagraphRenderer.php
@@ -0,0 +1,74 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer\Block;
+
+use League\CommonMark\Node\Block\Paragraph;
+use League\CommonMark\Node\Block\TightBlockInterface;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\HtmlElement;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class ParagraphRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Paragraph $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer)
+ {
+ Paragraph::assertInstanceOf($node);
+
+ if ($this->inTightList($node)) {
+ return $childRenderer->renderNodes($node->children());
+ }
+
+ $attrs = $node->data->get('attributes');
+
+ return new HtmlElement('p', $attrs, $childRenderer->renderNodes($node->children()));
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'paragraph';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+
+ private function inTightList(Paragraph $node): bool
+ {
+ // Only check up to two (2) levels above this for tightness
+ $i = 2;
+ while (($node = $node->parent()) && $i--) {
+ if ($node instanceof TightBlockInterface) {
+ return $node->isTight();
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/ChildNodeRendererInterface.php b/vendor/league/commonmark/src/Renderer/ChildNodeRendererInterface.php
new file mode 100644
index 0000000..8e866b5
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/ChildNodeRendererInterface.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Node\Node;
+
+/**
+ * Renders multiple nodes by delegating to the individual node renderers and adding spacing where needed
+ */
+interface ChildNodeRendererInterface
+{
+ /**
+ * @param Node[] $nodes
+ */
+ public function renderNodes(iterable $nodes): string;
+
+ public function getBlockSeparator(): string;
+
+ public function getInnerSeparator(): string;
+}
diff --git a/vendor/league/commonmark/src/Renderer/DocumentRendererInterface.php b/vendor/league/commonmark/src/Renderer/DocumentRendererInterface.php
new file mode 100644
index 0000000..dd34dd6
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/DocumentRendererInterface.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Output\RenderedContentInterface;
+
+/**
+ * Renders a parsed Document AST
+ */
+interface DocumentRendererInterface extends MarkdownRendererInterface
+{
+ /**
+ * Render the given Document node (and all of its children)
+ */
+ public function renderDocument(Document $document): RenderedContentInterface;
+}
diff --git a/vendor/league/commonmark/src/Renderer/HtmlDecorator.php b/vendor/league/commonmark/src/Renderer/HtmlDecorator.php
new file mode 100644
index 0000000..46a38d9
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/HtmlDecorator.php
@@ -0,0 +1,45 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Node\Node;
+use League\CommonMark\Util\HtmlElement;
+
+final class HtmlDecorator implements NodeRendererInterface
+{
+ private NodeRendererInterface $inner;
+ private string $tag;
+ /** @var array */
+ private array $attributes;
+ private bool $selfClosing;
+
+ /**
+ * @param array $attributes
+ */
+ public function __construct(NodeRendererInterface $inner, string $tag, array $attributes = [], bool $selfClosing = false)
+ {
+ $this->inner = $inner;
+ $this->tag = $tag;
+ $this->attributes = $attributes;
+ $this->selfClosing = $selfClosing;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer)
+ {
+ return new HtmlElement($this->tag, $this->attributes, $this->inner->render($node, $childRenderer), $this->selfClosing);
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/HtmlRenderer.php b/vendor/league/commonmark/src/Renderer/HtmlRenderer.php
new file mode 100644
index 0000000..2e05cfb
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/HtmlRenderer.php
@@ -0,0 +1,100 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Event\DocumentPreRenderEvent;
+use League\CommonMark\Event\DocumentRenderedEvent;
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Output\RenderedContent;
+use League\CommonMark\Output\RenderedContentInterface;
+
+final class HtmlRenderer implements DocumentRendererInterface, ChildNodeRendererInterface
+{
+ /** @psalm-readonly */
+ private EnvironmentInterface $environment;
+
+ public function __construct(EnvironmentInterface $environment)
+ {
+ $this->environment = $environment;
+ }
+
+ public function renderDocument(Document $document): RenderedContentInterface
+ {
+ $this->environment->dispatch(new DocumentPreRenderEvent($document, 'html'));
+
+ $output = new RenderedContent($document, (string) $this->renderNode($document));
+
+ $event = new DocumentRenderedEvent($output);
+ $this->environment->dispatch($event);
+
+ return $event->getOutput();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function renderNodes(iterable $nodes): string
+ {
+ $output = '';
+
+ $isFirstItem = true;
+
+ foreach ($nodes as $node) {
+ if (! $isFirstItem && $node instanceof AbstractBlock) {
+ $output .= $this->getBlockSeparator();
+ }
+
+ $output .= $this->renderNode($node);
+
+ $isFirstItem = false;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @return \Stringable|string
+ *
+ * @throws NoMatchingRendererException
+ */
+ private function renderNode(Node $node)
+ {
+ $renderers = $this->environment->getRenderersForClass(\get_class($node));
+
+ foreach ($renderers as $renderer) {
+ \assert($renderer instanceof NodeRendererInterface);
+ if (($result = $renderer->render($node, $this)) !== null) {
+ return $result;
+ }
+ }
+
+ throw new NoMatchingRendererException('Unable to find corresponding renderer for node type ' . \get_class($node));
+ }
+
+ public function getBlockSeparator(): string
+ {
+ return $this->environment->getConfiguration()->get('renderer/block_separator');
+ }
+
+ public function getInnerSeparator(): string
+ {
+ return $this->environment->getConfiguration()->get('renderer/inner_separator');
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/Inline/NewlineRenderer.php b/vendor/league/commonmark/src/Renderer/Inline/NewlineRenderer.php
new file mode 100644
index 0000000..f64cc58
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/Inline/NewlineRenderer.php
@@ -0,0 +1,76 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Node\Inline\Newline;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+use League\Config\ConfigurationAwareInterface;
+use League\Config\ConfigurationInterface;
+
+final class NewlineRenderer implements NodeRendererInterface, XmlNodeRendererInterface, ConfigurationAwareInterface
+{
+ /** @psalm-readonly-allow-private-mutation */
+ private ConfigurationInterface $config;
+
+ public function setConfiguration(ConfigurationInterface $configuration): void
+ {
+ $this->config = $configuration;
+ }
+
+ /**
+ * @param Newline $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ Newline::assertInstanceOf($node);
+
+ if ($node->getType() === Newline::HARDBREAK) {
+ return " \n";
+ }
+
+ return $this->config->get('renderer/soft_break');
+ }
+
+ /**
+ * @param Newline $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function getXmlTagName(Node $node): string
+ {
+ Newline::assertInstanceOf($node);
+
+ return $node->getType() === Newline::SOFTBREAK ? 'softbreak' : 'linebreak';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/Inline/TextRenderer.php b/vendor/league/commonmark/src/Renderer/Inline/TextRenderer.php
new file mode 100644
index 0000000..40ad02a
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/Inline/TextRenderer.php
@@ -0,0 +1,54 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer\Inline;
+
+use League\CommonMark\Node\Inline\Text;
+use League\CommonMark\Node\Node;
+use League\CommonMark\Renderer\ChildNodeRendererInterface;
+use League\CommonMark\Renderer\NodeRendererInterface;
+use League\CommonMark\Util\Xml;
+use League\CommonMark\Xml\XmlNodeRendererInterface;
+
+final class TextRenderer implements NodeRendererInterface, XmlNodeRendererInterface
+{
+ /**
+ * @param Text $node
+ *
+ * {@inheritDoc}
+ *
+ * @psalm-suppress MoreSpecificImplementedParamType
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer): string
+ {
+ Text::assertInstanceOf($node);
+
+ return Xml::escape($node->getLiteral());
+ }
+
+ public function getXmlTagName(Node $node): string
+ {
+ return 'text';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ return [];
+ }
+}
diff --git a/vendor/league/commonmark/src/Renderer/MarkdownRendererInterface.php b/vendor/league/commonmark/src/Renderer/MarkdownRendererInterface.php
new file mode 100644
index 0000000..83af8cd
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/MarkdownRendererInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Node\Block\Document;
+use League\CommonMark\Output\RenderedContentInterface;
+
+/**
+ * Renders a parsed Document AST
+ *
+ * @deprecated since 2.3; use {@link DocumentRendererInterface} instead
+ */
+interface MarkdownRendererInterface
+{
+ /**
+ * Render the given Document node (and all of its children)
+ */
+ public function renderDocument(Document $document): RenderedContentInterface;
+}
diff --git a/vendor/league/commonmark/src/Renderer/NoMatchingRendererException.php b/vendor/league/commonmark/src/Renderer/NoMatchingRendererException.php
new file mode 100644
index 0000000..14fe493
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/NoMatchingRendererException.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Exception\LogicException;
+
+class NoMatchingRendererException extends LogicException
+{
+}
diff --git a/vendor/league/commonmark/src/Renderer/NodeRendererInterface.php b/vendor/league/commonmark/src/Renderer/NodeRendererInterface.php
new file mode 100644
index 0000000..5d40582
--- /dev/null
+++ b/vendor/league/commonmark/src/Renderer/NodeRendererInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Renderer;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+use League\CommonMark\Node\Node;
+
+interface NodeRendererInterface
+{
+ /**
+ * @return \Stringable|string|null
+ *
+ * @throws InvalidArgumentException if the wrong type of Node is provided
+ */
+ public function render(Node $node, ChildNodeRendererInterface $childRenderer);
+}
diff --git a/vendor/league/commonmark/src/Util/ArrayCollection.php b/vendor/league/commonmark/src/Util/ArrayCollection.php
new file mode 100644
index 0000000..7210770
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/ArrayCollection.php
@@ -0,0 +1,173 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+/**
+ * Array collection
+ *
+ * Provides a wrapper around a standard PHP array.
+ *
+ * @internal
+ *
+ * @phpstan-template T
+ * @phpstan-implements \IteratorAggregate
+ * @phpstan-implements \ArrayAccess
+ */
+final class ArrayCollection implements \IteratorAggregate, \Countable, \ArrayAccess
+{
+ /**
+ * @var array
+ * @phpstan-var array
+ */
+ private array $elements;
+
+ /**
+ * Constructor
+ *
+ * @param array $elements
+ *
+ * @phpstan-param array $elements
+ */
+ public function __construct(array $elements = [])
+ {
+ $this->elements = $elements;
+ }
+
+ /**
+ * @return mixed|false
+ *
+ * @phpstan-return T|false
+ */
+ public function first()
+ {
+ return \reset($this->elements);
+ }
+
+ /**
+ * @return mixed|false
+ *
+ * @phpstan-return T|false
+ */
+ public function last()
+ {
+ return \end($this->elements);
+ }
+
+ /**
+ * Retrieve an external iterator
+ *
+ * @return \ArrayIterator
+ *
+ * @phpstan-return \ArrayIterator
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator(): \ArrayIterator
+ {
+ return new \ArrayIterator($this->elements);
+ }
+
+ /**
+ * Count elements of an object
+ *
+ * @return int The count as an integer.
+ */
+ public function count(): int
+ {
+ return \count($this->elements);
+ }
+
+ /**
+ * Whether an offset exists
+ *
+ * {@inheritDoc}
+ *
+ * @phpstan-param int $offset
+ */
+ public function offsetExists($offset): bool
+ {
+ return \array_key_exists($offset, $this->elements);
+ }
+
+ /**
+ * Offset to retrieve
+ *
+ * {@inheritDoc}
+ *
+ * @phpstan-param int $offset
+ *
+ * @phpstan-return T|null
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($offset)
+ {
+ return $this->elements[$offset] ?? null;
+ }
+
+ /**
+ * Offset to set
+ *
+ * {@inheritDoc}
+ *
+ * @phpstan-param int|null $offset
+ * @phpstan-param T $value
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetSet($offset, $value): void
+ {
+ if ($offset === null) {
+ $this->elements[] = $value;
+ } else {
+ $this->elements[$offset] = $value;
+ }
+ }
+
+ /**
+ * Offset to unset
+ *
+ * {@inheritDoc}
+ *
+ * @phpstan-param int $offset
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetUnset($offset): void
+ {
+ if (! \array_key_exists($offset, $this->elements)) {
+ return;
+ }
+
+ unset($this->elements[$offset]);
+ }
+
+ /**
+ * Returns a subset of the array
+ *
+ * @return array
+ *
+ * @phpstan-return array
+ */
+ public function slice(int $offset, ?int $length = null): array
+ {
+ return \array_slice($this->elements, $offset, $length, true);
+ }
+
+ /**
+ * @return array
+ *
+ * @phpstan-return array
+ */
+ public function toArray(): array
+ {
+ return $this->elements;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/Html5EntityDecoder.php b/vendor/league/commonmark/src/Util/Html5EntityDecoder.php
new file mode 100644
index 0000000..52550a0
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/Html5EntityDecoder.php
@@ -0,0 +1,75 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+/**
+ * @psalm-immutable
+ */
+final class Html5EntityDecoder
+{
+ /**
+ * @psalm-pure
+ */
+ public static function decode(string $entity): string
+ {
+ if (\substr($entity, -1) !== ';') {
+ return $entity;
+ }
+
+ if (\substr($entity, 0, 2) === '') {
+ if (\strtolower(\substr($entity, 2, 1)) === 'x') {
+ return self::fromHex(\substr($entity, 3, -1));
+ }
+
+ return self::fromDecimal(\substr($entity, 2, -1));
+ }
+
+ return \html_entity_decode($entity, \ENT_QUOTES | \ENT_HTML5, 'UTF-8');
+ }
+
+ /**
+ * @param mixed $number
+ *
+ * @psalm-pure
+ */
+ private static function fromDecimal($number): string
+ {
+ // Only convert code points within planes 0-2, excluding NULL
+ // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
+ if (empty($number) || $number > 0x2FFFF) {
+ return self::fromHex('fffd');
+ }
+
+ $entity = '' . $number . ';';
+
+ $converted = \mb_decode_numericentity($entity, [0x0, 0x2FFFF, 0, 0xFFFF], 'UTF-8');
+
+ if ($converted === $entity) {
+ return self::fromHex('fffd');
+ }
+
+ return $converted;
+ }
+
+ /**
+ * @psalm-pure
+ */
+ private static function fromHex(string $hexChars): string
+ {
+ return self::fromDecimal(\hexdec($hexChars));
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/HtmlElement.php b/vendor/league/commonmark/src/Util/HtmlElement.php
new file mode 100644
index 0000000..51fa6de
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/HtmlElement.php
@@ -0,0 +1,160 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+final class HtmlElement implements \Stringable
+{
+ /** @psalm-readonly */
+ private string $tagName;
+
+ /** @var array */
+ private array $attributes = [];
+
+ /** @var \Stringable|\Stringable[]|string */
+ private $contents;
+
+ /** @psalm-readonly */
+ private bool $selfClosing;
+
+ /**
+ * @param string $tagName Name of the HTML tag
+ * @param array $attributes Array of attributes (values should be unescaped)
+ * @param \Stringable|\Stringable[]|string|null $contents Inner contents, pre-escaped if needed
+ * @param bool $selfClosing Whether the tag is self-closing
+ */
+ public function __construct(string $tagName, array $attributes = [], $contents = '', bool $selfClosing = false)
+ {
+ $this->tagName = $tagName;
+ $this->selfClosing = $selfClosing;
+
+ foreach ($attributes as $name => $value) {
+ $this->setAttribute($name, $value);
+ }
+
+ $this->setContents($contents ?? '');
+ }
+
+ /** @psalm-immutable */
+ public function getTagName(): string
+ {
+ return $this->tagName;
+ }
+
+ /**
+ * @return array
+ *
+ * @psalm-immutable
+ */
+ public function getAllAttributes(): array
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * @return string|bool|null
+ *
+ * @psalm-immutable
+ */
+ public function getAttribute(string $key)
+ {
+ return $this->attributes[$key] ?? null;
+ }
+
+ /**
+ * @param string|string[]|bool $value
+ */
+ public function setAttribute(string $key, $value = true): self
+ {
+ if (\is_array($value)) {
+ $this->attributes[$key] = \implode(' ', \array_unique($value));
+ } else {
+ $this->attributes[$key] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return \Stringable|\Stringable[]|string
+ *
+ * @psalm-immutable
+ */
+ public function getContents(bool $asString = true)
+ {
+ if (! $asString) {
+ return $this->contents;
+ }
+
+ return $this->getContentsAsString();
+ }
+
+ /**
+ * Sets the inner contents of the tag (must be pre-escaped if needed)
+ *
+ * @param \Stringable|\Stringable[]|string $contents
+ *
+ * @return $this
+ */
+ public function setContents($contents): self
+ {
+ $this->contents = $contents ?? ''; // @phpstan-ignore-line
+
+ return $this;
+ }
+
+ /** @psalm-immutable */
+ public function __toString(): string
+ {
+ $result = '<' . $this->tagName;
+
+ foreach ($this->attributes as $key => $value) {
+ if ($value === true) {
+ $result .= ' ' . $key;
+ } elseif ($value === false) {
+ continue;
+ } else {
+ $result .= ' ' . $key . '="' . Xml::escape($value) . '"';
+ }
+ }
+
+ if ($this->contents !== '') {
+ $result .= '>' . $this->getContentsAsString() . '' . $this->tagName . '>';
+ } elseif ($this->selfClosing && $this->tagName === 'input') {
+ $result .= '>';
+ } elseif ($this->selfClosing) {
+ $result .= ' />';
+ } else {
+ $result .= '>' . $this->tagName . '>';
+ }
+
+ return $result;
+ }
+
+ /** @psalm-immutable */
+ private function getContentsAsString(): string
+ {
+ if (\is_string($this->contents)) {
+ return $this->contents;
+ }
+
+ if (\is_array($this->contents)) {
+ return \implode('', $this->contents);
+ }
+
+ return (string) $this->contents;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/HtmlFilter.php b/vendor/league/commonmark/src/Util/HtmlFilter.php
new file mode 100644
index 0000000..b1e0555
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/HtmlFilter.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+
+/**
+ * @psalm-immutable
+ */
+final class HtmlFilter
+{
+ // Return the entire string as-is
+ public const ALLOW = 'allow';
+ // Escape the entire string so any HTML/JS won't be interpreted as such
+ public const ESCAPE = 'escape';
+ // Return an empty string
+ public const STRIP = 'strip';
+
+ /**
+ * Runs the given HTML through the given filter
+ *
+ * @param string $html HTML input to be filtered
+ * @param string $filter One of the HtmlFilter constants
+ *
+ * @return string Filtered HTML
+ *
+ * @throws InvalidArgumentException when an invalid $filter is given
+ *
+ * @psalm-pure
+ */
+ public static function filter(string $html, string $filter): string
+ {
+ switch ($filter) {
+ case self::STRIP:
+ return '';
+ case self::ESCAPE:
+ return \htmlspecialchars($html, \ENT_NOQUOTES);
+ case self::ALLOW:
+ return $html;
+ default:
+ throw new InvalidArgumentException(\sprintf('Invalid filter provided: "%s"', $filter));
+ }
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/LinkParserHelper.php b/vendor/league/commonmark/src/Util/LinkParserHelper.php
new file mode 100644
index 0000000..3e76c28
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/LinkParserHelper.php
@@ -0,0 +1,165 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+use League\CommonMark\Parser\Cursor;
+
+/**
+ * @psalm-immutable
+ */
+final class LinkParserHelper
+{
+ /**
+ * Attempt to parse link destination
+ *
+ * @return string|null The string, or null if no match
+ */
+ public static function parseLinkDestination(Cursor $cursor): ?string
+ {
+ if ($cursor->getCurrentCharacter() === '<') {
+ return self::parseDestinationBraces($cursor);
+ }
+
+ $destination = self::manuallyParseLinkDestination($cursor);
+ if ($destination === null) {
+ return null;
+ }
+
+ return UrlEncoder::unescapeAndEncode(
+ RegexHelper::unescape($destination)
+ );
+ }
+
+ public static function parseLinkLabel(Cursor $cursor): int
+ {
+ $match = $cursor->match('/^\[(?:[^\\\\\[\]]|\\\\.){0,1000}\]/');
+ if ($match === null) {
+ return 0;
+ }
+
+ $length = \mb_strlen($match, 'UTF-8');
+
+ if ($length > 1001) {
+ return 0;
+ }
+
+ return $length;
+ }
+
+ public static function parsePartialLinkLabel(Cursor $cursor): ?string
+ {
+ return $cursor->match('/^(?:[^\\\\\[\]]++|\\\\.?)*+/');
+ }
+
+ /**
+ * Attempt to parse link title (sans quotes)
+ *
+ * @return string|null The string, or null if no match
+ */
+ public static function parseLinkTitle(Cursor $cursor): ?string
+ {
+ if ($title = $cursor->match('/' . RegexHelper::PARTIAL_LINK_TITLE . '/')) {
+ // Chop off quotes from title and unescape
+ return RegexHelper::unescape(\substr($title, 1, -1));
+ }
+
+ return null;
+ }
+
+ public static function parsePartialLinkTitle(Cursor $cursor, string $endDelimiter): ?string
+ {
+ $endDelimiter = \preg_quote($endDelimiter, '/');
+ $regex = \sprintf('/(%s|[^%s\x00])*(?:%s)?/', RegexHelper::PARTIAL_ESCAPED_CHAR, $endDelimiter, $endDelimiter);
+ if (($partialTitle = $cursor->match($regex)) === null) {
+ return null;
+ }
+
+ return RegexHelper::unescape($partialTitle);
+ }
+
+ private static function manuallyParseLinkDestination(Cursor $cursor): ?string
+ {
+ $remainder = $cursor->getRemainder();
+ $openParens = 0;
+ $len = \strlen($remainder);
+ for ($i = 0; $i < $len; $i++) {
+ $c = $remainder[$i];
+ if ($c === '\\' && $i + 1 < $len && RegexHelper::isEscapable($remainder[$i + 1])) {
+ $i++;
+ } elseif ($c === '(') {
+ $openParens++;
+ // Limit to 32 nested parens for pathological cases
+ if ($openParens > 32) {
+ return null;
+ }
+ } elseif ($c === ')') {
+ if ($openParens < 1) {
+ break;
+ }
+
+ $openParens--;
+ } elseif (\ord($c) <= 32 && RegexHelper::isWhitespace($c)) {
+ break;
+ }
+ }
+
+ if ($openParens !== 0) {
+ return null;
+ }
+
+ if ($i === 0 && (! isset($c) || $c !== ')')) {
+ return null;
+ }
+
+ $destination = \substr($remainder, 0, $i);
+ $cursor->advanceBy(\mb_strlen($destination, 'UTF-8'));
+
+ return $destination;
+ }
+
+ /** @var \WeakReference|null */
+ private static ?\WeakReference $lastCursor = null;
+ private static bool $lastCursorLacksClosingBrace = false;
+
+ private static function parseDestinationBraces(Cursor $cursor): ?string
+ {
+ // Optimization: If we've previously parsed this cursor and returned `null`, we know
+ // that no closing brace exists, so we can skip the regex entirely. This helps avoid
+ // certain pathological cases where the regex engine can take a very long time to
+ // determine that no match exists.
+ if (self::$lastCursor !== null && self::$lastCursor->get() === $cursor) {
+ if (self::$lastCursorLacksClosingBrace) {
+ return null;
+ }
+ } else {
+ self::$lastCursor = \WeakReference::create($cursor);
+ }
+
+ if ($res = $cursor->match(RegexHelper::REGEX_LINK_DESTINATION_BRACES)) {
+ self::$lastCursorLacksClosingBrace = false;
+
+ // Chop off surrounding <..>:
+ return UrlEncoder::unescapeAndEncode(
+ RegexHelper::unescape(\substr($res, 1, -1))
+ );
+ }
+
+ self::$lastCursorLacksClosingBrace = true;
+
+ return null;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/PrioritizedList.php b/vendor/league/commonmark/src/Util/PrioritizedList.php
new file mode 100644
index 0000000..77ec24a
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/PrioritizedList.php
@@ -0,0 +1,73 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+/**
+ * @internal
+ *
+ * @phpstan-template T
+ * @phpstan-implements \IteratorAggregate
+ */
+final class PrioritizedList implements \IteratorAggregate
+{
+ /**
+ * @var array>
+ * @phpstan-var array>
+ */
+ private array $list = [];
+
+ /**
+ * @var \Traversable|null
+ * @phpstan-var \Traversable|null
+ */
+ private ?\Traversable $optimized = null;
+
+ /**
+ * @param mixed $item
+ *
+ * @phpstan-param T $item
+ */
+ public function add($item, int $priority): void
+ {
+ $this->list[$priority][] = $item;
+ $this->optimized = null;
+ }
+
+ /**
+ * @return \Traversable
+ *
+ * @phpstan-return \Traversable
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator(): \Traversable
+ {
+ if ($this->optimized === null) {
+ \krsort($this->list);
+
+ $sorted = [];
+ foreach ($this->list as $group) {
+ foreach ($group as $item) {
+ $sorted[] = $item;
+ }
+ }
+
+ $this->optimized = new \ArrayIterator($sorted);
+ }
+
+ return $this->optimized;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/RegexHelper.php b/vendor/league/commonmark/src/Util/RegexHelper.php
new file mode 100644
index 0000000..429b2d8
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/RegexHelper.php
@@ -0,0 +1,243 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+use League\CommonMark\Exception\InvalidArgumentException;
+use League\CommonMark\Extension\CommonMark\Node\Block\HtmlBlock;
+
+/**
+ * Provides regular expressions and utilities for parsing Markdown
+ *
+ * All of the PARTIAL_ regex constants assume that they'll be used in case-insensitive searches
+ * All other complete regexes provided by this class (either via constants or methods) will have case-insensitivity enabled.
+ *
+ * @phpcs:disable Generic.Strings.UnnecessaryStringConcat.Found
+ *
+ * @psalm-immutable
+ */
+final class RegexHelper
+{
+ // Partial regular expressions (wrap with `/` on each side and add the case-insensitive `i` flag before use)
+ public const PARTIAL_ENTITY = '&(?>#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});';
+ public const PARTIAL_ESCAPABLE = '[!"#$%&\'()*+,.\/:;<=>?@[\\\\\]^_`{|}~-]';
+ public const PARTIAL_ESCAPED_CHAR = '\\\\' . self::PARTIAL_ESCAPABLE;
+ public const PARTIAL_IN_DOUBLE_QUOTES = '"(' . self::PARTIAL_ESCAPED_CHAR . '|[^"\x00])*"';
+ public const PARTIAL_IN_SINGLE_QUOTES = '\'(' . self::PARTIAL_ESCAPED_CHAR . '|[^\'\x00])*\'';
+ public const PARTIAL_IN_PARENS = '\\((' . self::PARTIAL_ESCAPED_CHAR . '|[^)\x00])*\\)';
+ public const PARTIAL_REG_CHAR = '[^\\\\()\x00-\x20]';
+ public const PARTIAL_IN_PARENS_NOSP = '\((' . self::PARTIAL_REG_CHAR . '|' . self::PARTIAL_ESCAPED_CHAR . '|\\\\)*\)';
+ public const PARTIAL_TAGNAME = '[a-z][a-z0-9-]*';
+ public const PARTIAL_BLOCKTAGNAME = '(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)';
+ public const PARTIAL_ATTRIBUTENAME = '[a-z_:][a-z0-9:._-]*';
+ public const PARTIAL_UNQUOTEDVALUE = '[^"\'=<>`\x00-\x20]+';
+ public const PARTIAL_SINGLEQUOTEDVALUE = '\'[^\']*\'';
+ public const PARTIAL_DOUBLEQUOTEDVALUE = '"[^"]*"';
+ public const PARTIAL_ATTRIBUTEVALUE = '(?:' . self::PARTIAL_UNQUOTEDVALUE . '|' . self::PARTIAL_SINGLEQUOTEDVALUE . '|' . self::PARTIAL_DOUBLEQUOTEDVALUE . ')';
+ public const PARTIAL_ATTRIBUTEVALUESPEC = '(?:' . '\s*=' . '\s*' . self::PARTIAL_ATTRIBUTEVALUE . ')';
+ public const PARTIAL_ATTRIBUTE = '(?:' . '\s+' . self::PARTIAL_ATTRIBUTENAME . self::PARTIAL_ATTRIBUTEVALUESPEC . '?)';
+ public const PARTIAL_OPENTAG = '<' . self::PARTIAL_TAGNAME . self::PARTIAL_ATTRIBUTE . '*+' . '\s*+\/?+>';
+ public const PARTIAL_CLOSETAG = '<\/' . self::PARTIAL_TAGNAME . '\s*+[>]';
+ public const PARTIAL_OPENBLOCKTAG = '<' . self::PARTIAL_BLOCKTAGNAME . self::PARTIAL_ATTRIBUTE . '*+' . '\s*+\/?+>';
+ public const PARTIAL_CLOSEBLOCKTAG = '<\/' . self::PARTIAL_BLOCKTAGNAME . '\s*+[>]';
+ public const PARTIAL_HTMLCOMMENT = '(?:||)';
+ public const PARTIAL_PROCESSINGINSTRUCTION = '[<][?][\s\S]*?[?][>]';
+ public const PARTIAL_DECLARATION = ']*>';
+ public const PARTIAL_CDATA = '';
+ public const PARTIAL_HTMLTAG = '(?:' . self::PARTIAL_OPENTAG . '|' . self::PARTIAL_CLOSETAG . '|' . self::PARTIAL_HTMLCOMMENT . '|' .
+ self::PARTIAL_PROCESSINGINSTRUCTION . '|' . self::PARTIAL_DECLARATION . '|' . self::PARTIAL_CDATA . ')';
+ public const PARTIAL_HTMLBLOCKOPEN = '<(?:' . self::PARTIAL_BLOCKTAGNAME . '(?:[\s\/>]|$)' . '|' .
+ '\/' . self::PARTIAL_BLOCKTAGNAME . '(?:[\s>]|$)' . '|' . '[?!])';
+ public const PARTIAL_LINK_TITLE = '^(?:"(' . self::PARTIAL_ESCAPED_CHAR . '|[^"\x00])*+"' .
+ '|' . '\'(' . self::PARTIAL_ESCAPED_CHAR . '|[^\'\x00])*+\'' .
+ '|' . '\((' . self::PARTIAL_ESCAPED_CHAR . '|[^()\x00])*+\))';
+
+ public const REGEX_PUNCTUATION = '/^[\p{P}\p{S}]/u';
+ public const REGEX_UNSAFE_PROTOCOL = '/^javascript:|vbscript:|file:|data:/i';
+ public const REGEX_SAFE_DATA_PROTOCOL = '/^data:image\/(?:png|gif|jpeg|webp)/i';
+ public const REGEX_NON_SPACE = '/[^ \t\f\v\r\n]/';
+
+ public const REGEX_WHITESPACE_CHAR = '/^[ \t\n\x0b\x0c\x0d]/';
+ public const REGEX_UNICODE_WHITESPACE_CHAR = '/^\pZ|\s/u';
+ public const REGEX_THEMATIC_BREAK = '/^(?:(?:\*[ \t]*){3,}|(?:_[ \t]*){3,}|(?:-[ \t]*){3,})$/';
+ public const REGEX_LINK_DESTINATION_BRACES = '/^(?:<(?:[^<>\\n\\\\\\x00]|\\\\.)*>)/';
+
+ /**
+ * @psalm-pure
+ */
+ public static function isEscapable(string $character): bool
+ {
+ return \preg_match('/' . self::PARTIAL_ESCAPABLE . '/', $character) === 1;
+ }
+
+ public static function isWhitespace(string $character): bool
+ {
+ /** @psalm-suppress InvalidLiteralArgument */
+ return $character !== '' && \strpos(" \t\n\x0b\x0c\x0d", $character) !== false;
+ }
+
+ /**
+ * @psalm-pure
+ */
+ public static function isLetter(?string $character): bool
+ {
+ if ($character === null) {
+ return false;
+ }
+
+ return \preg_match('/[\pL]/u', $character) === 1;
+ }
+
+ /**
+ * Attempt to match a regex in string s at offset offset
+ *
+ * @psalm-param non-empty-string $regex
+ *
+ * @return int|null Index of match, or null
+ *
+ * @psalm-pure
+ */
+ public static function matchAt(string $regex, string $string, int $offset = 0): ?int
+ {
+ $matches = [];
+ $string = \mb_substr($string, $offset, null, 'UTF-8');
+ if (! \preg_match($regex, $string, $matches, \PREG_OFFSET_CAPTURE)) {
+ return null;
+ }
+
+ // PREG_OFFSET_CAPTURE always returns the byte offset, not the char offset, which is annoying
+ $charPos = \mb_strlen(\mb_strcut($string, 0, $matches[0][1], 'UTF-8'), 'UTF-8');
+
+ return $offset + $charPos;
+ }
+
+ /**
+ * Functional wrapper around preg_match_all which only returns the first set of matches
+ *
+ * @psalm-param non-empty-string $pattern
+ *
+ * @return string[]|null
+ *
+ * @psalm-pure
+ */
+ public static function matchFirst(string $pattern, string $subject, int $offset = 0): ?array
+ {
+ if ($offset !== 0) {
+ $subject = \substr($subject, $offset);
+ }
+
+ \preg_match_all($pattern, $subject, $matches, \PREG_SET_ORDER);
+
+ if ($matches === []) {
+ return null;
+ }
+
+ return $matches[0] ?: null;
+ }
+
+ /**
+ * Replace backslash escapes with literal characters
+ *
+ * @psalm-pure
+ */
+ public static function unescape(string $string): string
+ {
+ $allEscapedChar = '/\\\\(' . self::PARTIAL_ESCAPABLE . ')/';
+
+ $escaped = \preg_replace($allEscapedChar, '$1', $string);
+ \assert(\is_string($escaped));
+
+ return \preg_replace_callback('/' . self::PARTIAL_ENTITY . '/i', static fn ($e) => Html5EntityDecoder::decode($e[0]), $escaped);
+ }
+
+ /**
+ * @internal
+ *
+ * @param int $type HTML block type
+ *
+ * @psalm-param HtmlBlock::TYPE_* $type
+ *
+ * @phpstan-param HtmlBlock::TYPE_* $type
+ *
+ * @psalm-return non-empty-string
+ *
+ * @throws InvalidArgumentException if an invalid type is given
+ *
+ * @psalm-pure
+ */
+ public static function getHtmlBlockOpenRegex(int $type): string
+ {
+ switch ($type) {
+ case HtmlBlock::TYPE_1_CODE_CONTAINER:
+ return '/^<(?:script|pre|textarea|style)(?:\s|>|$)/i';
+ case HtmlBlock::TYPE_2_COMMENT:
+ return '/^/';
+ case HtmlBlock::TYPE_3:
+ return '/\?>/';
+ case HtmlBlock::TYPE_4:
+ return '/>/';
+ case HtmlBlock::TYPE_5_CDATA:
+ return '/\]\]>/';
+ default:
+ throw new InvalidArgumentException('Invalid HTML block type');
+ }
+ }
+
+ /**
+ * @psalm-pure
+ */
+ public static function isLinkPotentiallyUnsafe(string $url): bool
+ {
+ return \preg_match(self::REGEX_UNSAFE_PROTOCOL, $url) !== 0 && \preg_match(self::REGEX_SAFE_DATA_PROTOCOL, $url) === 0;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/SpecReader.php b/vendor/league/commonmark/src/Util/SpecReader.php
new file mode 100644
index 0000000..faee204
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/SpecReader.php
@@ -0,0 +1,72 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+use League\CommonMark\Exception\IOException;
+
+/**
+ * Reads in a CommonMark spec document and extracts the input/output examples for testing against them
+ */
+final class SpecReader
+{
+ private function __construct()
+ {
+ }
+
+ /**
+ * @return iterable
+ */
+ public static function read(string $data): iterable
+ {
+ // Normalize newlines for platform independence
+ $data = \preg_replace('/\r\n?/', "\n", $data);
+ \assert($data !== null);
+ $data = \preg_replace('/.*$/', '', $data);
+ \assert($data !== null);
+ \preg_match_all('/^`{32} (example ?\w*)\n([\s\S]*?)^\.\n([\s\S]*?)^`{32}$|^#{1,6} *(.*)$/m', $data, $matches, PREG_SET_ORDER);
+
+ $currentSection = 'Example';
+ $exampleNumber = 0;
+
+ foreach ($matches as $match) {
+ \assert(isset($match[1], $match[2], $match[3]));
+ if (isset($match[4])) {
+ $currentSection = $match[4];
+ continue;
+ }
+
+ yield \trim($currentSection . ' #' . $exampleNumber) => [
+ 'input' => \str_replace('→', "\t", $match[2]),
+ 'output' => \str_replace('→', "\t", $match[3]),
+ 'type' => $match[1],
+ 'section' => $currentSection,
+ 'number' => $exampleNumber++,
+ ];
+ }
+ }
+
+ /**
+ * @return iterable
+ *
+ * @throws IOException if the file cannot be loaded
+ */
+ public static function readFile(string $filename): iterable
+ {
+ if (($data = \file_get_contents($filename)) === false) {
+ throw new IOException(\sprintf('Failed to load spec from %s', $filename));
+ }
+
+ return self::read($data);
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/UrlEncoder.php b/vendor/league/commonmark/src/Util/UrlEncoder.php
new file mode 100644
index 0000000..bba1af3
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/UrlEncoder.php
@@ -0,0 +1,69 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+use League\CommonMark\Exception\UnexpectedEncodingException;
+
+/**
+ * @psalm-immutable
+ */
+final class UrlEncoder
+{
+ private const ENCODE_CACHE = ['%00', '%01', '%02', '%03', '%04', '%05', '%06', '%07', '%08', '%09', '%0A', '%0B', '%0C', '%0D', '%0E', '%0F', '%10', '%11', '%12', '%13', '%14', '%15', '%16', '%17', '%18', '%19', '%1A', '%1B', '%1C', '%1D', '%1E', '%1F', '%20', '!', '%22', '#', '$', '%25', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '%3C', '=', '%3E', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '%5B', '%5C', '%5D', '%5E', '_', '%60', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '%7B', '%7C', '%7D', '~', '%7F'];
+
+ /**
+ * @throws UnexpectedEncodingException if a non-UTF-8-compatible encoding is used
+ *
+ * @psalm-pure
+ */
+ public static function unescapeAndEncode(string $uri): string
+ {
+ // Optimization: if the URL only includes characters we know will be kept as-is, then just return the URL as-is.
+ if (\preg_match('/^[A-Za-z0-9~!@#$&*()\-_=+;:,.\/?]+$/', $uri)) {
+ return $uri;
+ }
+
+ if (! \mb_check_encoding($uri, 'UTF-8')) {
+ throw new UnexpectedEncodingException('Unexpected encoding - UTF-8 or ASCII was expected');
+ }
+
+ $result = '';
+
+ $chars = \mb_str_split($uri, 1, 'UTF-8');
+
+ $l = \count($chars);
+ for ($i = 0; $i < $l; $i++) {
+ $code = $chars[$i];
+ if ($code === '%' && $i + 2 < $l) {
+ if (\preg_match('/^[0-9a-f]{2}$/i', $chars[$i + 1] . $chars[$i + 2]) === 1) {
+ $result .= '%' . $chars[$i + 1] . $chars[$i + 2];
+ $i += 2;
+ continue;
+ }
+ }
+
+ if (\ord($code) < 128) {
+ $result .= self::ENCODE_CACHE[\ord($code)];
+ continue;
+ }
+
+ $result .= \rawurlencode($code);
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/league/commonmark/src/Util/Xml.php b/vendor/league/commonmark/src/Util/Xml.php
new file mode 100644
index 0000000..8f9e84d
--- /dev/null
+++ b/vendor/league/commonmark/src/Util/Xml.php
@@ -0,0 +1,33 @@
+
+ *
+ * Original code based on the CommonMark JS reference parser (https://bitly.com/commonmark-js)
+ * - (c) John MacFarlane
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Util;
+
+/**
+ * Utility class for handling/generating XML and HTML
+ *
+ * @psalm-immutable
+ */
+final class Xml
+{
+ /**
+ * @psalm-pure
+ */
+ public static function escape(string $string): string
+ {
+ return \str_replace(['&', '<', '>', '"'], ['&', '<', '>', '"'], $string);
+ }
+}
diff --git a/vendor/league/commonmark/src/Xml/FallbackNodeXmlRenderer.php b/vendor/league/commonmark/src/Xml/FallbackNodeXmlRenderer.php
new file mode 100644
index 0000000..48aa3c8
--- /dev/null
+++ b/vendor/league/commonmark/src/Xml/FallbackNodeXmlRenderer.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Xml;
+
+use League\CommonMark\Node\Block\AbstractBlock;
+use League\CommonMark\Node\Inline\AbstractInline;
+use League\CommonMark\Node\Node;
+
+/**
+ * @internal
+ */
+final class FallbackNodeXmlRenderer implements XmlNodeRendererInterface
+{
+ /**
+ * @var array
+ *
+ * @psalm-allow-private-mutation
+ */
+ private array $classCache = [];
+
+ /**
+ * @psalm-allow-private-mutation
+ */
+ public function getXmlTagName(Node $node): string
+ {
+ $className = \get_class($node);
+ if (isset($this->classCache[$className])) {
+ return $this->classCache[$className];
+ }
+
+ $type = $node instanceof AbstractBlock ? 'block' : 'inline';
+ $shortName = \strtolower((new \ReflectionClass($node))->getShortName());
+
+ return $this->classCache[$className] = \sprintf('custom_%s_%s', $type, $shortName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getXmlAttributes(Node $node): array
+ {
+ $attrs = [];
+ foreach ($node->data->export() as $k => $v) {
+ if (self::isValueUsable($v)) {
+ $attrs[$k] = $v;
+ }
+ }
+
+ $reflClass = new \ReflectionClass($node);
+ foreach ($reflClass->getProperties() as $property) {
+ if (\in_array($property->getDeclaringClass()->getName(), [Node::class, AbstractBlock::class, AbstractInline::class], true)) {
+ continue;
+ }
+
+ $property->setAccessible(true);
+ $value = $property->getValue($node);
+ if (self::isValueUsable($value)) {
+ $attrs[$property->getName()] = $value;
+ }
+ }
+
+ return $attrs;
+ }
+
+ /**
+ * @param mixed $var
+ *
+ * @psalm-pure
+ */
+ private static function isValueUsable($var): bool
+ {
+ return \is_string($var) || \is_int($var) || \is_float($var) || \is_bool($var);
+ }
+}
diff --git a/vendor/league/commonmark/src/Xml/MarkdownToXmlConverter.php b/vendor/league/commonmark/src/Xml/MarkdownToXmlConverter.php
new file mode 100644
index 0000000..538ad98
--- /dev/null
+++ b/vendor/league/commonmark/src/Xml/MarkdownToXmlConverter.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Xml;
+
+use League\CommonMark\ConverterInterface;
+use League\CommonMark\Environment\EnvironmentInterface;
+use League\CommonMark\Exception\CommonMarkException;
+use League\CommonMark\Output\RenderedContentInterface;
+use League\CommonMark\Parser\MarkdownParser;
+use League\CommonMark\Parser\MarkdownParserInterface;
+use League\CommonMark\Renderer\DocumentRendererInterface;
+
+final class MarkdownToXmlConverter implements ConverterInterface
+{
+ /** @psalm-readonly */
+ private MarkdownParserInterface $parser;
+
+ /** @psalm-readonly */
+ private DocumentRendererInterface $renderer;
+
+ public function __construct(EnvironmentInterface $environment)
+ {
+ $this->parser = new MarkdownParser($environment);
+ $this->renderer = new XmlRenderer($environment);
+ }
+
+ /**
+ * Converts Markdown to XML
+ *
+ * @throws CommonMarkException
+ */
+ public function convert(string $input): RenderedContentInterface
+ {
+ return $this->renderer->renderDocument($this->parser->parse($input));
+ }
+
+ /**
+ * Converts CommonMark to HTML.
+ *
+ * @see MarkdownToXmlConverter::convert()
+ *
+ * @throws CommonMarkException
+ */
+ public function __invoke(string $input): RenderedContentInterface
+ {
+ return $this->convert($input);
+ }
+}
diff --git a/vendor/league/commonmark/src/Xml/XmlNodeRendererInterface.php b/vendor/league/commonmark/src/Xml/XmlNodeRendererInterface.php
new file mode 100644
index 0000000..aafc9f1
--- /dev/null
+++ b/vendor/league/commonmark/src/Xml/XmlNodeRendererInterface.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\CommonMark\Xml;
+
+use League\CommonMark\Node\Node;
+
+interface XmlNodeRendererInterface
+{
+ public function getXmlTagName(Node $node): string;
+
+ /**
+ * @return array
+ *
+ * @psalm-return array
+ */
+ public function getXmlAttributes(Node $node): array;
+}
diff --git a/vendor/league/commonmark/src/Xml/XmlRenderer.php b/vendor/league/commonmark/src/Xml/XmlRenderer.php
new file mode 100644
index 0000000..2973dd7
--- /dev/null
+++ b/vendor/league/commonmark/src/Xml/XmlRenderer.php
@@ -0,0 +1,135 @@
+ */
+ private array $rendererCache = [];
+
+ public function __construct(EnvironmentInterface $environment)
+ {
+ $this->environment = $environment;
+ $this->fallbackRenderer = new FallbackNodeXmlRenderer();
+ }
+
+ public function renderDocument(Document $document): RenderedContentInterface
+ {
+ $this->environment->dispatch(new DocumentPreRenderEvent($document, 'xml'));
+
+ $xml = '';
+
+ $indent = 0;
+ $walker = $document->walker();
+ while ($event = $walker->next()) {
+ $node = $event->getNode();
+
+ $closeImmediately = ! $node->hasChildren();
+ $selfClosing = $closeImmediately && ! $node instanceof StringContainerInterface;
+
+ $renderer = $this->findXmlRenderer($node);
+ $tagName = $renderer->getXmlTagName($node);
+
+ if ($event->isEntering()) {
+ $attrs = $renderer->getXmlAttributes($node);
+
+ $xml .= "\n" . \str_repeat(self::INDENTATION, $indent);
+ $xml .= self::tag($tagName, $attrs, $selfClosing);
+
+ if ($node instanceof StringContainerInterface) {
+ $xml .= Xml::escape($node->getLiteral());
+ }
+
+ if ($closeImmediately && ! $selfClosing) {
+ $xml .= self::tag('/' . $tagName);
+ }
+
+ if (! $closeImmediately) {
+ $indent++;
+ }
+ } elseif (! $closeImmediately) {
+ $indent--;
+ $xml .= "\n" . \str_repeat(self::INDENTATION, $indent);
+ $xml .= self::tag('/' . $tagName);
+ }
+ }
+
+ return new RenderedContent($document, $xml . "\n");
+ }
+
+ /**
+ * @param array $attrs
+ */
+ private static function tag(string $name, array $attrs = [], bool $selfClosing = \false): string
+ {
+ $result = '<' . $name;
+ foreach ($attrs as $key => $value) {
+ $result .= \sprintf(' %s="%s"', $key, self::convertAndEscape($value));
+ }
+
+ if ($selfClosing) {
+ $result .= ' /';
+ }
+
+ $result .= '>';
+
+ return $result;
+ }
+
+ /**
+ * @param string|int|float|bool $value
+ */
+ private static function convertAndEscape($value): string
+ {
+ if (\is_string($value)) {
+ return Xml::escape($value);
+ }
+
+ if (\is_int($value) || \is_float($value)) {
+ return (string) $value;
+ }
+
+ if (\is_bool($value)) {
+ return $value ? 'true' : 'false';
+ }
+
+ // @phpstan-ignore-next-line
+ throw new InvalidArgumentException('$value must be a string, int, float, or bool');
+ }
+
+ private function findXmlRenderer(Node $node): XmlNodeRendererInterface
+ {
+ $class = \get_class($node);
+
+ if (\array_key_exists($class, $this->rendererCache)) {
+ return $this->rendererCache[$class];
+ }
+
+ foreach ($this->environment->getRenderersForClass($class) as $renderer) {
+ if ($renderer instanceof XmlNodeRendererInterface) {
+ return $this->rendererCache[$class] = $renderer;
+ }
+ }
+
+ return $this->rendererCache[$class] = $this->fallbackRenderer;
+ }
+}
diff --git a/vendor/league/config/CHANGELOG.md b/vendor/league/config/CHANGELOG.md
new file mode 100644
index 0000000..9a7813a
--- /dev/null
+++ b/vendor/league/config/CHANGELOG.md
@@ -0,0 +1,42 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) principles.
+
+## [Unreleased][unreleased]
+
+## [1.2.0] - 2022-12-11
+
+### Changed
+
+- Values can now be set prior to the corresponding schema being registered.
+- `exists()` and `get()` now only trigger validation for the relevant schema, not the entire config at once.
+
+## [1.1.1] - 2021-08-14
+
+### Changed
+
+ - Bumped the minimum version of dflydev/dot-access-data for PHP 8.1 support
+
+## [1.1.0] - 2021-06-19
+
+### Changed
+
+- Bumped the minimum PHP version to 7.4+
+- Bumped the minimum version of nette/schema to 1.2.0
+
+## [1.0.1] - 2021-05-31
+
+### Fixed
+
+- Fixed the `ConfigurationExceptionInterface` marker interface not extending `Throwable` (#2)
+
+## [1.0.0] - 2021-05-31
+
+Initial release! 🎉
+
+[unreleased]: https://github.com/thephpleague/config/compare/v1.2.0...main
+[1.2.0]: https://github.com/thephpleague/config/compare/v1.1.1...v.1.2.0
+[1.1.1]: https://github.com/thephpleague/config/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/thephpleague/config/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/thephpleague/config/compare/v1.0.0...v1.0.1
+[1.0.0]: https://github.com/thephpleague/config/releases/tag/v1.0.0
diff --git a/vendor/league/config/LICENSE.md b/vendor/league/config/LICENSE.md
new file mode 100644
index 0000000..1a444a1
--- /dev/null
+++ b/vendor/league/config/LICENSE.md
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2022, Colin O'Dell. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/league/config/README.md b/vendor/league/config/README.md
new file mode 100644
index 0000000..2304746
--- /dev/null
+++ b/vendor/league/config/README.md
@@ -0,0 +1,153 @@
+# league/config
+
+[](https://packagist.org/packages/league/config)
+[](https://packagist.org/packages/league/config)
+[](LICENSE)
+[](https://github.com/thephpleague/config/actions?query=workflow%3ATests+branch%3Amain)
+[](https://scrutinizer-ci.com/g/thephpleague/config/code-structure)
+[](https://scrutinizer-ci.com/g/thephpleague/config)
+[](https://www.colinodell.com/sponsor)
+
+**league/config** helps you define nested configuration arrays with strict schemas and access configuration values with dot notation. It was created by [Colin O'Dell][@colinodell].
+
+## 📦 Installation
+
+This project requires PHP 7.4 or higher. To install it via [Composer] simply run:
+
+```bash
+composer require league/config
+```
+
+## 🧰️ Basic Usage
+
+The `Configuration` class provides everything you need to define the configuration structure and fetch values:
+
+```php
+use League\Config\Configuration;
+use Nette\Schema\Expect;
+
+// Define your configuration schema
+$config = new Configuration([
+ 'database' => Expect::structure([
+ 'driver' => Expect::anyOf('mysql', 'postgresql', 'sqlite')->required(),
+ 'host' => Expect::string()->default('localhost'),
+ 'port' => Expect::int()->min(1)->max(65535),
+ 'ssl' => Expect::bool(),
+ 'database' => Expect::string()->required(),
+ 'username' => Expect::string()->required(),
+ 'password' => Expect::string()->nullable(),
+ ]),
+ 'logging' => Expect::structure([
+ 'enabled' => Expect::bool()->default($_ENV['DEBUG'] == true),
+ 'file' => Expect::string()->deprecated("use logging.path instead"),
+ 'path' => Expect::string()->assert(function ($path) { return \is_writeable($path); })->required(),
+ ]),
+]);
+
+// Set the values, either all at once with `merge()`:
+$config->merge([
+ 'database' => [
+ 'driver' => 'mysql',
+ 'port' => 3306,
+ 'database' => 'mydb',
+ 'username' => 'user',
+ 'password' => 'secret',
+ ],
+]);
+
+// Or one-at-a-time with `set()`:
+$config->set('logging.path', '/var/log/myapp.log');
+
+// You can now retrieve those values with `get()`.
+// Validation and defaults will be applied for you automatically
+$config->get('database'); // Fetches the entire "database" section as an array
+$config->get('database.driver'); // Fetch a specific nested value with dot notation
+$config->get('database/driver'); // Fetch a specific nested value with slash notation
+$config->get('database.host'); // Returns the default value "localhost"
+$config->get('logging.path'); // Guaranteed to be writeable thanks to the assertion in the schema
+
+// If validation fails an `InvalidConfigurationException` will be thrown:
+$config->set('database.driver', 'mongodb');
+$config->get('database.driver'); // InvalidConfigurationException
+
+// Attempting to fetch a non-existent key will result in an `InvalidConfigurationException`
+$config->get('foo.bar');
+
+// You could avoid this by checking whether that item exists:
+$config->exists('foo.bar'); // Returns `false`
+```
+
+## 📓 Documentation
+
+Full documentation can be found at [config.thephpleague.com][docs].
+
+## 💭 Philosophy
+
+This library aims to provide a **simple yet opinionated** approach to configuration with the following goals:
+
+- The configuration should operate on **arrays with nested values** which are easily accessible
+- The configuration structure should be **defined with strict schemas** defining the overall structure, allowed types, and allowed values
+- Schemas should be defined using a **simple, fluent interface**
+- You should be able to **add and combine schemas but never modify existing ones**
+- Both the configuration values and the schema should be **defined and managed with PHP code**
+- Schemas should be **immutable**; they should never change once they are set
+- Configuration values should never define or influence the schemas
+
+As a result, this library will likely **never** support features like:
+
+- Loading and/or exporting configuration values or schemas using YAML, XML, or other files
+- Parsing configuration values from a command line or other user interface
+- Dynamically changing the schema, allowed values, or default values based on other configuration values
+
+If you need that functionality you should check out other libraries like:
+
+- [symfony/config]
+- [symfony/options-resolver]
+- [hassankhan/config]
+- [consolidation/config]
+- [laminas/laminas-config]
+
+## 🏷️ Versioning
+
+[SemVer](http://semver.org/) is followed closely. Minor and patch releases should not introduce breaking changes to the codebase.
+
+Any classes or methods marked `@internal` are not intended for use outside this library and are subject to breaking changes at any time, so please avoid using them.
+
+## 🛠️ Maintenance & Support
+
+When a new **minor** version (e.g. `1.0` -> `1.1`) is released, the previous one (`1.0`) will continue to receive security and critical bug fixes for *at least* 3 months.
+
+When a new **major** version is released (e.g. `1.1` -> `2.0`), the previous one (`1.1`) will receive critical bug fixes for *at least* 3 months and security updates for 6 months after that new release comes out.
+
+(This policy may change in the future and exceptions may be made on a case-by-case basis.)
+
+## 👷️ Contributing
+
+Contributions to this library are **welcome**! We only ask that you adhere to our [contributor guidelines] and avoid making changes that conflict with our Philosophy above.
+
+## 🧪 Testing
+
+```bash
+composer test
+```
+
+## 📄 License
+
+**league/config** is licensed under the BSD-3 license. See the [`LICENSE.md`][license] file for more details.
+
+## 🗺️ Who Uses It?
+
+This project is used by [league/commonmark][league-commonmark].
+
+[docs]: https://config.thephpleague.com/
+[@colinodell]: https://www.twitter.com/colinodell
+[Composer]: https://getcomposer.org/
+[PHP League]: https://thephpleague.com
+[symfony/config]: https://symfony.com/doc/current/components/config.html
+[symfony/options-resolver]: https://symfony.com/doc/current/components/options_resolver.html
+[hassankhan/config]: https://github.com/hassankhan/config
+[consolidation/config]: https://github.com/consolidation/config
+[laminas/laminas-config]: https://docs.laminas.dev/laminas-config/
+[contributor guidelines]: https://github.com/thephpleague/config/blob/main/.github/CONTRIBUTING.md
+[license]: https://github.com/thephpleague/config/blob/main/LICENSE.md
+[league-commonmark]: https://commonmark.thephpleague.com
diff --git a/vendor/league/config/composer.json b/vendor/league/config/composer.json
new file mode 100644
index 0000000..3cd8d87
--- /dev/null
+++ b/vendor/league/config/composer.json
@@ -0,0 +1,69 @@
+{
+ "name": "league/config",
+ "type": "library",
+ "description": "Define configuration arrays with strict schemas and access values with dot notation",
+ "keywords": ["configuration","config","schema","array","nested","dot","dot-access"],
+ "homepage": "https://config.thephpleague.com",
+ "license": "BSD-3-Clause",
+ "authors": [
+ {
+ "name": "Colin O'Dell",
+ "email": "colinodell@gmail.com",
+ "homepage": "https://www.colinodell.com",
+ "role": "Lead Developer"
+ }
+ ],
+ "support": {
+ "docs": "https://config.thephpleague.com/",
+ "issues": "https://github.com/thephpleague/config/issues",
+ "rss": "https://github.com/thephpleague/config/releases.atom",
+ "source": "https://github.com/thephpleague/config"
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "dflydev/dot-access-data": "^3.0.1",
+ "nette/schema": "^1.2"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.8.2",
+ "phpunit/phpunit": "^9.5.5",
+ "scrutinizer/ocular": "^1.8.1",
+ "unleashedtech/php-coding-standard": "^3.1",
+ "vimeo/psalm": "^4.7.3"
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "autoload": {
+ "psr-4": {
+ "League\\Config\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "League\\Config\\Tests\\": "tests"
+ }
+ },
+ "scripts": {
+ "phpcs": "phpcs",
+ "phpstan": "phpstan analyse",
+ "phpunit": "phpunit --no-coverage",
+ "psalm": "psalm",
+ "test": [
+ "@phpcs",
+ "@phpstan",
+ "@psalm",
+ "@phpunit"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.2-dev"
+ }
+ },
+ "config": {
+ "sort-packages": true,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
+ }
+}
diff --git a/vendor/league/config/src/Configuration.php b/vendor/league/config/src/Configuration.php
new file mode 100644
index 0000000..6294367
--- /dev/null
+++ b/vendor/league/config/src/Configuration.php
@@ -0,0 +1,255 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+use Dflydev\DotAccessData\Data;
+use Dflydev\DotAccessData\DataInterface;
+use Dflydev\DotAccessData\Exception\DataException;
+use Dflydev\DotAccessData\Exception\InvalidPathException;
+use Dflydev\DotAccessData\Exception\MissingPathException;
+use League\Config\Exception\UnknownOptionException;
+use League\Config\Exception\ValidationException;
+use Nette\Schema\Expect;
+use Nette\Schema\Processor;
+use Nette\Schema\Schema;
+use Nette\Schema\ValidationException as NetteValidationException;
+
+final class Configuration implements ConfigurationBuilderInterface, ConfigurationInterface
+{
+ /** @psalm-readonly */
+ private Data $userConfig;
+
+ /**
+ * @var array
+ *
+ * @psalm-allow-private-mutation
+ */
+ private array $configSchemas = [];
+
+ /** @psalm-allow-private-mutation */
+ private Data $finalConfig;
+
+ /**
+ * @var array
+ *
+ * @psalm-allow-private-mutation
+ */
+ private array $cache = [];
+
+ /** @psalm-readonly */
+ private ConfigurationInterface $reader;
+
+ /**
+ * @param array $baseSchemas
+ */
+ public function __construct(array $baseSchemas = [])
+ {
+ $this->configSchemas = $baseSchemas;
+ $this->userConfig = new Data();
+ $this->finalConfig = new Data();
+
+ $this->reader = new ReadOnlyConfiguration($this);
+ }
+
+ /**
+ * Registers a new configuration schema at the given top-level key
+ *
+ * @psalm-allow-private-mutation
+ */
+ public function addSchema(string $key, Schema $schema): void
+ {
+ $this->invalidate();
+
+ $this->configSchemas[$key] = $schema;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-allow-private-mutation
+ */
+ public function merge(array $config = []): void
+ {
+ $this->invalidate();
+
+ $this->userConfig->import($config, DataInterface::REPLACE);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-allow-private-mutation
+ */
+ public function set(string $key, $value): void
+ {
+ $this->invalidate();
+
+ try {
+ $this->userConfig->set($key, $value);
+ } catch (DataException $ex) {
+ throw new UnknownOptionException($ex->getMessage(), $key, (int) $ex->getCode(), $ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-external-mutation-free
+ */
+ public function get(string $key)
+ {
+ if (\array_key_exists($key, $this->cache)) {
+ return $this->cache[$key];
+ }
+
+ try {
+ $this->build(self::getTopLevelKey($key));
+
+ return $this->cache[$key] = $this->finalConfig->get($key);
+ } catch (InvalidPathException | MissingPathException $ex) {
+ throw new UnknownOptionException($ex->getMessage(), $key, (int) $ex->getCode(), $ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @psalm-external-mutation-free
+ */
+ public function exists(string $key): bool
+ {
+ if (\array_key_exists($key, $this->cache)) {
+ return true;
+ }
+
+ try {
+ $this->build(self::getTopLevelKey($key));
+
+ return $this->finalConfig->has($key);
+ } catch (InvalidPathException | UnknownOptionException $ex) {
+ return false;
+ }
+ }
+
+ /**
+ * @psalm-mutation-free
+ */
+ public function reader(): ConfigurationInterface
+ {
+ return $this->reader;
+ }
+
+ /**
+ * @psalm-external-mutation-free
+ */
+ private function invalidate(): void
+ {
+ $this->cache = [];
+ $this->finalConfig = new Data();
+ }
+
+ /**
+ * Applies the schema against the configuration to return the final configuration
+ *
+ * @throws ValidationException|UnknownOptionException|InvalidPathException
+ *
+ * @psalm-allow-private-mutation
+ */
+ private function build(string $topLevelKey): void
+ {
+ if ($this->finalConfig->has($topLevelKey)) {
+ return;
+ }
+
+ if (! isset($this->configSchemas[$topLevelKey])) {
+ throw new UnknownOptionException(\sprintf('Missing config schema for "%s"', $topLevelKey), $topLevelKey);
+ }
+
+ try {
+ $userData = [$topLevelKey => $this->userConfig->get($topLevelKey)];
+ } catch (DataException $ex) {
+ $userData = [];
+ }
+
+ try {
+ $schema = $this->configSchemas[$topLevelKey];
+ $processor = new Processor();
+
+ $processed = $processor->process(Expect::structure([$topLevelKey => $schema]), $userData);
+
+ $this->raiseAnyDeprecationNotices($processor->getWarnings());
+
+ $this->finalConfig->import((array) self::convertStdClassesToArrays($processed));
+ } catch (NetteValidationException $ex) {
+ throw new ValidationException($ex);
+ }
+ }
+
+ /**
+ * Recursively converts stdClass instances to arrays
+ *
+ * @phpstan-template T
+ *
+ * @param T $data
+ *
+ * @return mixed
+ *
+ * @phpstan-return ($data is \stdClass ? array : T)
+ *
+ * @psalm-pure
+ */
+ private static function convertStdClassesToArrays($data)
+ {
+ if ($data instanceof \stdClass) {
+ $data = (array) $data;
+ }
+
+ if (\is_array($data)) {
+ foreach ($data as $k => $v) {
+ $data[$k] = self::convertStdClassesToArrays($v);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * @param string[] $warnings
+ */
+ private function raiseAnyDeprecationNotices(array $warnings): void
+ {
+ foreach ($warnings as $warning) {
+ @\trigger_error($warning, \E_USER_DEPRECATED);
+ }
+ }
+
+ /**
+ * @throws InvalidPathException
+ */
+ private static function getTopLevelKey(string $path): string
+ {
+ if (\strlen($path) === 0) {
+ throw new InvalidPathException('Path cannot be an empty string');
+ }
+
+ $path = \str_replace(['.', '/'], '.', $path);
+
+ $firstDelimiter = \strpos($path, '.');
+ if ($firstDelimiter === false) {
+ return $path;
+ }
+
+ return \substr($path, 0, $firstDelimiter);
+ }
+}
diff --git a/vendor/league/config/src/ConfigurationAwareInterface.php b/vendor/league/config/src/ConfigurationAwareInterface.php
new file mode 100644
index 0000000..ec5d7b3
--- /dev/null
+++ b/vendor/league/config/src/ConfigurationAwareInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+/**
+ * Implement this class to facilitate setter injection of the configuration where needed
+ */
+interface ConfigurationAwareInterface
+{
+ public function setConfiguration(ConfigurationInterface $configuration): void;
+}
diff --git a/vendor/league/config/src/ConfigurationBuilderInterface.php b/vendor/league/config/src/ConfigurationBuilderInterface.php
new file mode 100644
index 0000000..e9c5ed6
--- /dev/null
+++ b/vendor/league/config/src/ConfigurationBuilderInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+/**
+ * An interface that provides the ability to set both the schema and configuration values
+ */
+interface ConfigurationBuilderInterface extends MutableConfigurationInterface, SchemaBuilderInterface
+{
+}
diff --git a/vendor/league/config/src/ConfigurationInterface.php b/vendor/league/config/src/ConfigurationInterface.php
new file mode 100644
index 0000000..534bd9f
--- /dev/null
+++ b/vendor/league/config/src/ConfigurationInterface.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+use League\Config\Exception\UnknownOptionException;
+use League\Config\Exception\ValidationException;
+
+/**
+ * Interface for reading configuration values
+ */
+interface ConfigurationInterface
+{
+ /**
+ * @param string $key Configuration option path/key
+ *
+ * @psalm-param non-empty-string $key
+ *
+ * @return mixed
+ *
+ * @throws ValidationException if the schema failed to validate the given input
+ * @throws UnknownOptionException if the requested key does not exist or is malformed
+ */
+ public function get(string $key);
+
+ /**
+ * @param string $key Configuration option path/key
+ *
+ * @psalm-param non-empty-string $key
+ *
+ * @return bool Whether the given option exists
+ *
+ * @throws ValidationException if the schema failed to validate the given input
+ */
+ public function exists(string $key): bool;
+}
diff --git a/vendor/league/config/src/ConfigurationProviderInterface.php b/vendor/league/config/src/ConfigurationProviderInterface.php
new file mode 100644
index 0000000..7af6148
--- /dev/null
+++ b/vendor/league/config/src/ConfigurationProviderInterface.php
@@ -0,0 +1,22 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+/**
+ * Interface for a service which provides a readable configuration object
+ */
+interface ConfigurationProviderInterface
+{
+ public function getConfiguration(): ConfigurationInterface;
+}
diff --git a/vendor/league/config/src/Exception/ConfigurationExceptionInterface.php b/vendor/league/config/src/Exception/ConfigurationExceptionInterface.php
new file mode 100644
index 0000000..db9ee78
--- /dev/null
+++ b/vendor/league/config/src/Exception/ConfigurationExceptionInterface.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config\Exception;
+
+/**
+ * Marker interface for any/all exceptions thrown by this library
+ */
+interface ConfigurationExceptionInterface extends \Throwable
+{
+}
diff --git a/vendor/league/config/src/Exception/InvalidConfigurationException.php b/vendor/league/config/src/Exception/InvalidConfigurationException.php
new file mode 100644
index 0000000..f2a6b69
--- /dev/null
+++ b/vendor/league/config/src/Exception/InvalidConfigurationException.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config\Exception;
+
+class InvalidConfigurationException extends \UnexpectedValueException implements ConfigurationExceptionInterface
+{
+ /**
+ * @param string $option Name/path of the option
+ * @param mixed $valueGiven The invalid option that was provided
+ * @param ?string $description Additional text describing the issue (optional)
+ */
+ public static function forConfigOption(string $option, $valueGiven, ?string $description = null): self
+ {
+ $message = \sprintf('Invalid config option for "%s": %s', $option, self::getDebugValue($valueGiven));
+ if ($description !== null) {
+ $message .= \sprintf(' (%s)', $description);
+ }
+
+ return new self($message);
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @psalm-pure
+ */
+ private static function getDebugValue($value): string
+ {
+ if (\is_object($value)) {
+ return \get_class($value);
+ }
+
+ return \print_r($value, true);
+ }
+}
diff --git a/vendor/league/config/src/Exception/UnknownOptionException.php b/vendor/league/config/src/Exception/UnknownOptionException.php
new file mode 100644
index 0000000..5afba12
--- /dev/null
+++ b/vendor/league/config/src/Exception/UnknownOptionException.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config\Exception;
+
+use Throwable;
+
+final class UnknownOptionException extends \InvalidArgumentException implements ConfigurationExceptionInterface
+{
+ private string $path;
+
+ public function __construct(string $message, string $path, int $code = 0, ?Throwable $previous = null)
+ {
+ parent::__construct($message, $code, $previous);
+
+ $this->path = $path;
+ }
+
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+}
diff --git a/vendor/league/config/src/Exception/ValidationException.php b/vendor/league/config/src/Exception/ValidationException.php
new file mode 100644
index 0000000..b43e2f5
--- /dev/null
+++ b/vendor/league/config/src/Exception/ValidationException.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config\Exception;
+
+use Nette\Schema\ValidationException as NetteException;
+
+final class ValidationException extends InvalidConfigurationException
+{
+ /** @var string[] */
+ private array $messages;
+
+ public function __construct(NetteException $innerException)
+ {
+ parent::__construct($innerException->getMessage(), (int) $innerException->getCode(), $innerException);
+
+ $this->messages = $innerException->getMessages();
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getMessages(): array
+ {
+ return $this->messages;
+ }
+}
diff --git a/vendor/league/config/src/MutableConfigurationInterface.php b/vendor/league/config/src/MutableConfigurationInterface.php
new file mode 100644
index 0000000..2d4b2ee
--- /dev/null
+++ b/vendor/league/config/src/MutableConfigurationInterface.php
@@ -0,0 +1,34 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+use League\Config\Exception\UnknownOptionException;
+
+/**
+ * Interface for setting/merging user-defined configuration values into the configuration object
+ */
+interface MutableConfigurationInterface
+{
+ /**
+ * @param mixed $value
+ *
+ * @throws UnknownOptionException if $key contains a nested path which doesn't point to an array value
+ */
+ public function set(string $key, $value): void;
+
+ /**
+ * @param array $config
+ */
+ public function merge(array $config = []): void;
+}
diff --git a/vendor/league/config/src/ReadOnlyConfiguration.php b/vendor/league/config/src/ReadOnlyConfiguration.php
new file mode 100644
index 0000000..58e6171
--- /dev/null
+++ b/vendor/league/config/src/ReadOnlyConfiguration.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+/**
+ * Provides read-only access to a given Configuration object
+ */
+final class ReadOnlyConfiguration implements ConfigurationInterface
+{
+ private Configuration $config;
+
+ public function __construct(Configuration $config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function get(string $key)
+ {
+ return $this->config->get($key);
+ }
+
+ public function exists(string $key): bool
+ {
+ return $this->config->exists($key);
+ }
+}
diff --git a/vendor/league/config/src/SchemaBuilderInterface.php b/vendor/league/config/src/SchemaBuilderInterface.php
new file mode 100644
index 0000000..3a19807
--- /dev/null
+++ b/vendor/league/config/src/SchemaBuilderInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace League\Config;
+
+use Nette\Schema\Schema;
+
+/**
+ * Interface that allows new schemas to be added to a configuration
+ */
+interface SchemaBuilderInterface
+{
+ /**
+ * Registers a new configuration schema at the given top-level key
+ */
+ public function addSchema(string $key, Schema $schema): void;
+}
diff --git a/vendor/mustache/mustache/.github/workflows/tests.yml b/vendor/mustache/mustache/.github/workflows/tests.yml
new file mode 100644
index 0000000..7ef087c
--- /dev/null
+++ b/vendor/mustache/mustache/.github/workflows/tests.yml
@@ -0,0 +1,47 @@
+name: Tests
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 0 * * *'
+
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php:
+ - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+ - 7.3
+ - 7.4
+ - 8.0
+ - 8.1
+ - 8.2
+ - 8.3
+ - 8.4
+
+ name: PHP ${{ matrix.php }}
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-interaction --no-progress
+
+ - name: Run tests
+ run: vendor/bin/phpunit
diff --git a/vendor/mustache/mustache/.php-cs-fixer.php b/vendor/mustache/mustache/.php-cs-fixer.php
new file mode 100644
index 0000000..e4f15b4
--- /dev/null
+++ b/vendor/mustache/mustache/.php-cs-fixer.php
@@ -0,0 +1,20 @@
+setRules([
+ '@Symfony' => true,
+ 'binary_operator_spaces' => false,
+ 'concat_space' => ['spacing' => 'one'],
+ 'increment_style' => false,
+ 'single_line_throw' => false,
+ 'yoda_style' => false,
+]);
+
+$finder = $config->getFinder()
+ ->in('src')
+ ->in('test');
+
+return $config;
diff --git a/vendor/mustache/mustache/LICENSE b/vendor/mustache/mustache/LICENSE
new file mode 100644
index 0000000..2e686ed
--- /dev/null
+++ b/vendor/mustache/mustache/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2010-2025 Justin Hileman
+
+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.
diff --git a/vendor/mustache/mustache/README.md b/vendor/mustache/mustache/README.md
new file mode 100644
index 0000000..902c113
--- /dev/null
+++ b/vendor/mustache/mustache/README.md
@@ -0,0 +1,94 @@
+# Mustache.php
+
+A [Mustache][mustache] implementation in PHP.
+
+[][packagist]
+[][packagist]
+
+
+## Installation
+
+```
+composer require mustache/mustache
+```
+
+## Usage
+
+A quick example:
+
+```php
+ ENT_QUOTES]);
+echo $m->render('Hello {{planet}}', ['planet' => 'World!']); // "Hello World!"
+```
+
+
+And a more in-depth example -- this is the canonical Mustache template:
+
+```html+jinja
+Hello {{name}}
+You have just won {{value}} dollars!
+{{#in_ca}}
+Well, {{taxed_value}} dollars, after taxes.
+{{/in_ca}}
+```
+
+
+Create a view "context" object -- which could also be an associative array, but those don't do functions quite as well:
+
+```php
+value - ($this->value * 0.4);
+ }
+
+ public $in_ca = true;
+}
+```
+
+
+And render it:
+
+```php
+ ENT_QUOTES]);
+$chris = new \Chris;
+echo $m->render($template, $chris);
+```
+
+*Note:* we recommend using `ENT_QUOTES` as a default of [entity_flags][entity_flags] to decrease the chance of Cross-site scripting vulnerability.
+
+
+## And That's Not All!
+
+Read [the Mustache.php documentation][docs] for more information.
+
+
+## Upgrading from v2.x
+_Mustache.php v3.x drops support for PHP 5.2–5.5_, but is otherwise backwards compatible with v2.x.
+
+To ease the transition, previous behavior can be preserved via configuration:
+
+ - The `strict_callables` config option now defaults to `true`. Lambda sections should use closures or callable objects. To continue supporting array-style callables for lambda sections (e.g. `[$this, 'foo']`), set `strict_callables` to `false`.
+ - [A context shadowing bug from v2.x has been fixed](https://github.com/bobthecow/mustache.php/commit/66ecb327ce15b9efa0cfcb7026fdc62c6659b27f), but if you depend on the previous buggy behavior you can preserve it via the `buggy_property_shadowing` config option.
+ - By default the return value of higher-order sections that are rendered via the lambda helper will no longer be double-rendered. To preserve the previous behavior, set `double_render_lambdas` to `true`. _This is not recommended._
+
+In order to maintain a wide PHP version support range, there are minor changes to a few interfaces, which you might need to handle if you extend Mustache (see [c0453be](https://github.com/bobthecow/mustache.php/commit/c0453be5c09e7d988b396982e29218fcb25b7304)).
+
+
+## See Also
+
+ - [mustache(5)][manpage] man page.
+ - [Readme for the Ruby Mustache implementation][ruby].
+
+
+[mustache]: https://mustache.github.io/
+[packagist]: https://packagist.org/packages/mustache/mustache
+[entity_flags]: https://github.com/bobthecow/mustache.php/wiki#entity_flags
+[docs]: https://github.com/bobthecow/mustache.php/wiki/Home
+[manpage]: https://mustache.github.io/mustache.5.html
+[ruby]: https://github.com/mustache/mustache/blob/master/README.md
diff --git a/vendor/mustache/mustache/composer.json b/vendor/mustache/mustache/composer.json
new file mode 100644
index 0000000..b034a10
--- /dev/null
+++ b/vendor/mustache/mustache/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "mustache/mustache",
+ "description": "A Mustache implementation in PHP.",
+ "keywords": [
+ "templating",
+ "mustache"
+ ],
+ "homepage": "https://github.com/bobthecow/mustache.php",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Justin Hileman",
+ "email": "justin@justinhileman.info",
+ "homepage": "http://justinhileman.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "~2.19.3",
+ "yoast/phpunit-polyfills": "^2.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Mustache\\": "src/"
+ },
+ "classmap": [
+ "src/compat.php"
+ ]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Mustache\\Test\\": "test/"
+ }
+ }
+}
diff --git a/vendor/mustache/mustache/src/Cache.php b/vendor/mustache/mustache/src/Cache.php
new file mode 100644
index 0000000..8f1e36b
--- /dev/null
+++ b/vendor/mustache/mustache/src/Cache.php
@@ -0,0 +1,46 @@
+logger;
+ }
+
+ /**
+ * Set a logger instance.
+ *
+ * @param Logger|LoggerInterface $logger
+ */
+ public function setLogger($logger = null)
+ {
+ // n.b. this uses `is_a` to prevent a dependency on Psr\Log
+ if ($logger !== null && !$logger instanceof Logger && !is_a($logger, 'Psr\\Log\\LoggerInterface')) {
+ throw new InvalidArgumentException('Expected an instance of Mustache\\Logger or Psr\\Log\\LoggerInterface.');
+ }
+
+ $this->logger = $logger;
+ }
+
+ /**
+ * Add a log record if logging is enabled.
+ *
+ * @param string $level The logging level
+ * @param string $message The log message
+ * @param array $context The log context
+ */
+ protected function log($level, $message, array $context = [])
+ {
+ if (isset($this->logger)) {
+ $this->logger->log($level, $message, $context);
+ }
+ }
+}
diff --git a/vendor/mustache/mustache/src/Cache/FilesystemCache.php b/vendor/mustache/mustache/src/Cache/FilesystemCache.php
new file mode 100644
index 0000000..94d9cd6
--- /dev/null
+++ b/vendor/mustache/mustache/src/Cache/FilesystemCache.php
@@ -0,0 +1,166 @@
+cache($className, $compiledSource);
+ *
+ * The FilesystemCache benefits from any opcode caching that may be setup in your environment. So do that, k?
+ */
+class FilesystemCache extends AbstractCache
+{
+ private $baseDir;
+ private $fileMode;
+
+ /**
+ * Filesystem cache constructor.
+ *
+ * @param string $baseDir Directory for compiled templates
+ * @param int $fileMode Override default permissions for cache files. Defaults to using the system-defined umask
+ */
+ public function __construct($baseDir, $fileMode = null)
+ {
+ $this->baseDir = $baseDir;
+ $this->fileMode = $fileMode;
+ }
+
+ /**
+ * Load the class from cache using `require_once`.
+ *
+ * @param string $key
+ *
+ * @return bool
+ */
+ public function load($key)
+ {
+ $fileName = $this->getCacheFilename($key);
+ if (!is_file($fileName)) {
+ return false;
+ }
+
+ require_once $fileName;
+
+ return true;
+ }
+
+ /**
+ * Cache and load the compiled class.
+ *
+ * @param string $key
+ * @param string $value
+ */
+ public function cache($key, $value)
+ {
+ $fileName = $this->getCacheFilename($key);
+
+ $this->log(
+ Logger::DEBUG,
+ 'Writing to template cache: "{fileName}"',
+ ['fileName' => $fileName]
+ );
+
+ $this->writeFile($fileName, $value);
+ $this->load($key);
+ }
+
+ /**
+ * Build the cache filename.
+ * Subclasses should override for custom cache directory structures.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ protected function getCacheFilename($name)
+ {
+ return sprintf('%s/%s.php', $this->baseDir, $name);
+ }
+
+ /**
+ * Create cache directory.
+ *
+ * @throws RuntimeException If unable to create directory
+ *
+ * @param string $fileName
+ *
+ * @return string
+ */
+ private function buildDirectoryForFilename($fileName)
+ {
+ $dirName = dirname($fileName);
+ if (!is_dir($dirName)) {
+ $this->log(
+ Logger::INFO,
+ 'Creating Mustache template cache directory: "{dirName}"',
+ ['dirName' => $dirName]
+ );
+
+ @mkdir($dirName, 0777, true);
+ // @codeCoverageIgnoreStart
+ if (!is_dir($dirName)) {
+ throw new RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName));
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ return $dirName;
+ }
+
+ /**
+ * Write cache file.
+ *
+ * @throws RuntimeException If unable to write file
+ *
+ * @param string $fileName
+ * @param string $value
+ */
+ private function writeFile($fileName, $value)
+ {
+ $dirName = $this->buildDirectoryForFilename($fileName);
+
+ $this->log(
+ Logger::DEBUG,
+ 'Caching compiled template to "{fileName}"',
+ ['fileName' => $fileName]
+ );
+
+ $tempFile = tempnam($dirName, basename($fileName));
+ if (false !== @file_put_contents($tempFile, $value)) {
+ if (@rename($tempFile, $fileName)) {
+ $mode = isset($this->fileMode) ? $this->fileMode : (0666 & ~umask());
+ @chmod($fileName, $mode);
+
+ return;
+ }
+
+ // @codeCoverageIgnoreStart
+ $this->log(
+ Logger::ERROR,
+ 'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"',
+ ['tempName' => $tempFile, 'fileName' => $fileName]
+ );
+ // @codeCoverageIgnoreEnd
+ }
+
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException(sprintf('Failed to write cache file "%s".', $fileName));
+ // @codeCoverageIgnoreEnd
+ }
+}
diff --git a/vendor/mustache/mustache/src/Cache/NoopCache.php b/vendor/mustache/mustache/src/Cache/NoopCache.php
new file mode 100644
index 0000000..35e6822
--- /dev/null
+++ b/vendor/mustache/mustache/src/Cache/NoopCache.php
@@ -0,0 +1,51 @@
+log(
+ Logger::WARNING,
+ 'Template cache disabled, evaluating "{className}" class at runtime',
+ ['className' => $key]
+ );
+ eval('?>' . $value);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Compiler.php b/vendor/mustache/mustache/src/Compiler.php
new file mode 100644
index 0000000..a78c89f
--- /dev/null
+++ b/vendor/mustache/mustache/src/Compiler.php
@@ -0,0 +1,807 @@
+pragmas = $this->defaultPragmas;
+ $this->sections = [];
+ $this->blocks = [];
+ $this->source = $source;
+ $this->indentNextLine = true;
+ $this->customEscape = $customEscape;
+ $this->entityFlags = $entityFlags;
+ $this->charset = $charset;
+ $this->strictCallables = $strictCallables;
+
+ $code = $this->writeCode($tree, $name);
+
+ if (isset($this->pragmas[Engine::PRAGMA_FILTERS]) && !$this->lambdas) {
+ throw new InvalidArgumentException('The FILTERS pragma requires lambda support');
+ }
+
+ return $code;
+ }
+
+ /**
+ * Disable optional Mustache specs.
+ *
+ * @internal Users should set options in Mustache\Engine, not here :)
+ *
+ * @param bool[] $options
+ */
+ public function setOptions(array $options)
+ {
+ if (isset($options['lambdas'])) {
+ $this->lambdas = $options['lambdas'] !== false;
+ }
+ }
+
+ /**
+ * Enable pragmas across all templates, regardless of the presence of pragma
+ * tags in the individual templates.
+ *
+ * @internal Users should set global pragmas in \Mustache\Engine, not here :)
+ *
+ * @param string[] $pragmas
+ */
+ public function setPragmas(array $pragmas)
+ {
+ $this->pragmas = [];
+ foreach ($pragmas as $pragma) {
+ $this->pragmas[$pragma] = true;
+ }
+ $this->defaultPragmas = $this->pragmas;
+ }
+
+ /**
+ * Helper function for walking the Mustache token parse tree.
+ *
+ * @throws SyntaxException upon encountering unknown token types
+ *
+ * @param array $tree Parse tree of Mustache tokens
+ * @param int $level (default: 0)
+ *
+ * @return string Generated PHP source code
+ */
+ private function walk(array $tree, $level = 0)
+ {
+ $code = '';
+ $level++;
+ foreach ($tree as $node) {
+ switch ($node[Tokenizer::TYPE]) {
+ case Tokenizer::T_PRAGMA:
+ $this->pragmas[$node[Tokenizer::NAME]] = true;
+ break;
+
+ case Tokenizer::T_SECTION:
+ $code .= $this->section(
+ $node[Tokenizer::NODES],
+ $node[Tokenizer::NAME],
+ isset($node[Tokenizer::FILTERS]) ? $node[Tokenizer::FILTERS] : [],
+ $node[Tokenizer::INDEX],
+ $node[Tokenizer::END],
+ $node[Tokenizer::OTAG],
+ $node[Tokenizer::CTAG],
+ $level
+ );
+ break;
+
+ case Tokenizer::T_INVERTED:
+ $code .= $this->invertedSection(
+ $node[Tokenizer::NODES],
+ $node[Tokenizer::NAME],
+ isset($node[Tokenizer::FILTERS]) ? $node[Tokenizer::FILTERS] : [],
+ $level
+ );
+ break;
+
+ case Tokenizer::T_PARTIAL:
+ $code .= $this->partial(
+ $node[Tokenizer::NAME],
+ isset($node[Tokenizer::DYNAMIC]) ? $node[Tokenizer::DYNAMIC] : false,
+ isset($node[Tokenizer::INDENT]) ? $node[Tokenizer::INDENT] : '',
+ $level
+ );
+ break;
+
+ case Tokenizer::T_PARENT:
+ $code .= $this->parent(
+ $node[Tokenizer::NAME],
+ isset($node[Tokenizer::DYNAMIC]) ? $node[Tokenizer::DYNAMIC] : false,
+ isset($node[Tokenizer::INDENT]) ? $node[Tokenizer::INDENT] : '',
+ $node[Tokenizer::NODES],
+ $level
+ );
+ break;
+
+ case Tokenizer::T_BLOCK_ARG:
+ $code .= $this->blockArg(
+ $node[Tokenizer::NODES],
+ $node[Tokenizer::NAME],
+ $node[Tokenizer::INDEX],
+ $node[Tokenizer::END],
+ $node[Tokenizer::OTAG],
+ $node[Tokenizer::CTAG],
+ $level
+ );
+ break;
+
+ case Tokenizer::T_BLOCK_VAR:
+ $code .= $this->blockVar(
+ $node[Tokenizer::NODES],
+ $node[Tokenizer::NAME],
+ $node[Tokenizer::INDEX],
+ $node[Tokenizer::END],
+ $node[Tokenizer::OTAG],
+ $node[Tokenizer::CTAG],
+ $level
+ );
+ break;
+
+ case Tokenizer::T_COMMENT:
+ break;
+
+ case Tokenizer::T_ESCAPED:
+ case Tokenizer::T_UNESCAPED:
+ case Tokenizer::T_UNESCAPED_2:
+ $code .= $this->variable(
+ $node[Tokenizer::NAME],
+ isset($node[Tokenizer::FILTERS]) ? $node[Tokenizer::FILTERS] : [],
+ $node[Tokenizer::TYPE] === Tokenizer::T_ESCAPED,
+ $level
+ );
+ break;
+
+ case Tokenizer::T_TEXT:
+ $code .= $this->text($node[Tokenizer::VALUE], $level);
+ break;
+
+ default:
+ throw new SyntaxException(sprintf('Unknown token type: %s', $node[Tokenizer::TYPE]), $node);
+ }
+ }
+
+ return $code;
+ }
+
+ const KLASS = 'lambdaHelper = new \\Mustache\\LambdaHelper($this->mustache, $context);
+ $buffer = \'\';
+ %s
+
+ return $buffer;
+ }
+ %s
+ %s
+ }';
+
+ const KLASS_NO_LAMBDAS = 'walk($tree);
+ $sections = implode("\n", $this->sections);
+ $blocks = implode("\n", $this->blocks);
+ $klass = empty($this->sections) && empty($this->blocks) ? self::KLASS_NO_LAMBDAS : self::KLASS;
+
+ $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : '';
+ $lambda = $this->lambdas ? '' : $this->prepare(self::NO_LAMBDAS);
+
+ return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $lambda, $code, $sections, $blocks);
+ }
+
+ const BLOCK_VAR = '
+ $blockFunction = $context->findInBlock(%s);
+ if (is_callable($blockFunction)) {
+ $buffer .= call_user_func($blockFunction, $context);
+ %s}
+ ';
+
+ const BLOCK_VAR_ELSE = '} else {%s';
+
+ /**
+ * Generate Mustache Template inheritance block variable PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ * @param string $id Section name
+ * @param int $start Section start offset
+ * @param int $end Section end offset
+ * @param string $otag Current Mustache opening tag
+ * @param string $ctag Current Mustache closing tag
+ * @param int $level
+ *
+ * @return string Generated PHP source code
+ */
+ private function blockVar(array $nodes, $id, $start, $end, $otag, $ctag, $level)
+ {
+ $id = var_export($id, true);
+
+ $else = $this->walk($nodes, $level);
+ if ($else !== '') {
+ $else = sprintf($this->prepare(self::BLOCK_VAR_ELSE, $level + 1, false, true), $else);
+ }
+
+ return sprintf($this->prepare(self::BLOCK_VAR, $level), $id, $else);
+ }
+
+ const BLOCK_ARG = '%s => [$this, \'block%s\'],';
+
+ /**
+ * Generate Mustache Template inheritance block argument PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ * @param string $id Section name
+ * @param int $start Section start offset
+ * @param int $end Section end offset
+ * @param string $otag Current Mustache opening tag
+ * @param string $ctag Current Mustache closing tag
+ * @param int $level
+ *
+ * @return string Generated PHP source code
+ */
+ private function blockArg($nodes, $id, $start, $end, $otag, $ctag, $level)
+ {
+ $key = $this->block($nodes);
+ $id = var_export($id, true);
+
+ return sprintf($this->prepare(self::BLOCK_ARG, $level), $id, $key);
+ }
+
+ const BLOCK_FUNCTION = '
+ public function block%s($context)
+ {
+ $indent = $buffer = \'\';%s
+
+ return $buffer;
+ }
+ ';
+
+ /**
+ * Generate Mustache Template inheritance block function PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ *
+ * @return string key of new block function
+ */
+ private function block(array $nodes)
+ {
+ $code = $this->walk($nodes, 0);
+ $key = ucfirst(md5($code));
+
+ if (!isset($this->blocks[$key])) {
+ $this->blocks[$key] = sprintf($this->prepare(self::BLOCK_FUNCTION, 0), $key, $code);
+ }
+
+ return $key;
+ }
+
+ const SECTION_CALL = '
+ $value = $context->%s(%s%s);%s
+ $buffer .= $this->section%s($context, $indent, $value);
+ ';
+
+ const SECTION = '
+ private function section%s(\\Mustache\\Context $context, $indent, $value)
+ {
+ $buffer = \'\';
+
+ if (%s) {
+ $source = %s;
+ $value = call_user_func($value, $source, %s);
+
+ if ($value instanceof \\Mustache\\RenderedString) {
+ return $value->getValue();
+ }
+
+ if (is_string($value)) {
+ if (strpos($value, \'{{\') === false) {
+ return $value;
+ }
+
+ return $this->mustache
+ ->loadLambda($value%s)
+ ->renderInternal($context);
+ }
+ }
+
+ if (!empty($value)) {
+ $values = $this->isIterable($value) ? $value : [$value];
+ foreach ($values as $value) {
+ $context->push($value);
+ %s
+ $context->pop();
+ }
+ }
+
+ return $buffer;
+ }
+ ';
+
+ const SECTION_NO_LAMBDAS = '
+ private function section%s(\\Mustache\\Context $context, $indent, $value)
+ {
+ $buffer = \'\';
+
+ if (!empty($value)) {
+ $values = $this->isIterable($value) ? $value : [$value];
+ foreach ($values as $value) {
+ $context->push($value);
+ %s
+ $context->pop();
+ }
+ }
+
+ return $buffer;
+ }
+ ';
+
+ /**
+ * Generate Mustache Template section PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ * @param string $id Section name
+ * @param string[] $filters Array of filters
+ * @param int $start Section start offset
+ * @param int $end Section end offset
+ * @param string $otag Current Mustache opening tag
+ * @param string $ctag Current Mustache closing tag
+ * @param int $level
+ *
+ * @return string Generated section PHP source code
+ */
+ private function section(array $nodes, $id, $filters, $start, $end, $otag, $ctag, $level)
+ {
+ $source = var_export(substr($this->source, $start, $end - $start), true);
+ $callable = $this->getCallable();
+
+ if ($otag !== '{{' || $ctag !== '}}') {
+ $delimTag = var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true);
+ $helper = sprintf('$this->lambdaHelper->withDelimiters(%s)', $delimTag);
+ $delims = ', ' . $delimTag;
+ } else {
+ $helper = '$this->lambdaHelper';
+ $delims = '';
+ }
+
+ $key = ucfirst(md5($delims . "\n" . $source));
+
+ if (!isset($this->sections[$key])) {
+ if ($this->lambdas) {
+ $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $helper, $delims, $this->walk($nodes, 2));
+ } else {
+ $this->sections[$key] = sprintf($this->prepare(self::SECTION_NO_LAMBDAS), $key, $this->walk($nodes, 2));
+ }
+ }
+
+ $method = $this->getFindMethod($id);
+ $id = var_export($id, true);
+ $findArg = $this->getFindMethodArgs($method);
+ $filters = $this->getFilters($filters, $level);
+
+ return sprintf($this->prepare(self::SECTION_CALL, $level), $method, $id, $findArg, $filters, $key);
+ }
+
+ const INVERTED_SECTION = '
+ $value = $context->%s(%s%s);%s
+ if (empty($value)) {
+ %s
+ }
+ ';
+
+ /**
+ * Generate Mustache Template inverted section PHP source.
+ *
+ * @param array $nodes Array of child tokens
+ * @param string $id Section name
+ * @param string[] $filters Array of filters
+ * @param int $level
+ *
+ * @return string Generated inverted section PHP source code
+ */
+ private function invertedSection(array $nodes, $id, $filters, $level)
+ {
+ $method = $this->getFindMethod($id);
+ $id = var_export($id, true);
+ $findArg = $this->getFindMethodArgs($method);
+ $filters = $this->getFilters($filters, $level);
+
+ return sprintf($this->prepare(self::INVERTED_SECTION, $level), $method, $id, $findArg, $filters, $this->walk($nodes, $level));
+ }
+
+ const DYNAMIC_NAME = '$this->resolveValue($context->%s(%s%s), $context)';
+
+ /**
+ * Generate Mustache Template dynamic name resolution PHP source.
+ *
+ * @param string $id Tag name
+ * @param bool $dynamic True if the name is dynamic
+ *
+ * @return string Dynamic name resolution PHP source code
+ */
+ private function resolveDynamicName($id, $dynamic)
+ {
+ if (!$dynamic) {
+ return var_export($id, true);
+ }
+
+ $method = $this->getFindMethod($id);
+ $id = ($method !== 'last') ? var_export($id, true) : '';
+ $findArg = $this->getFindMethodArgs($method);
+
+ // TODO: filters?
+
+ return sprintf(self::DYNAMIC_NAME, $method, $id, $findArg);
+ }
+
+ const PARTIAL_INDENT = ', $indent . %s';
+ const PARTIAL = '
+ if ($partial = $this->mustache->loadPartial(%s)) {
+ $buffer .= $partial->renderInternal($context%s);
+ }
+ ';
+
+ /**
+ * Generate Mustache Template partial call PHP source.
+ *
+ * @param string $id Partial name
+ * @param bool $dynamic Partial name is dynamic
+ * @param string $indent Whitespace indent to apply to partial
+ * @param int $level
+ *
+ * @return string Generated partial call PHP source code
+ */
+ private function partial($id, $dynamic, $indent, $level)
+ {
+ if ($indent !== '') {
+ $indentParam = sprintf(self::PARTIAL_INDENT, var_export($indent, true));
+ } else {
+ $indentParam = '';
+ }
+
+ return sprintf(
+ $this->prepare(self::PARTIAL, $level),
+ $this->resolveDynamicName($id, $dynamic),
+ $indentParam
+ );
+ }
+
+ const PARENT = '
+ if ($parent = $this->mustache->loadPartial(%s)) {
+ $context->pushBlockContext([%s
+ ]);
+ $buffer .= $parent->renderInternal($context, $indent);
+ $context->popBlockContext();
+ }
+ ';
+
+ const PARENT_NO_CONTEXT = '
+ if ($parent = $this->mustache->loadPartial(%s)) {
+ $buffer .= $parent->renderInternal($context, $indent);
+ }
+ ';
+
+ /**
+ * Generate Mustache Template inheritance parent call PHP source.
+ *
+ * @param string $id Parent tag name
+ * @param bool $dynamic Tag name is dynamic
+ * @param string $indent Whitespace indent to apply to parent
+ * @param array $children Child nodes
+ * @param int $level
+ *
+ * @return string Generated PHP source code
+ */
+ private function parent($id, $dynamic, $indent, array $children, $level)
+ {
+ $realChildren = array_filter($children, [self::class, 'onlyBlockArgs']);
+ $partialName = $this->resolveDynamicName($id, $dynamic);
+
+ if (empty($realChildren)) {
+ return sprintf($this->prepare(self::PARENT_NO_CONTEXT, $level), $partialName);
+ }
+
+ return sprintf(
+ $this->prepare(self::PARENT, $level),
+ $partialName,
+ $this->walk($realChildren, $level + 1)
+ );
+ }
+
+ /**
+ * Helper method for filtering out non-block-arg tokens.
+ *
+ * @return bool True if $node is a block arg token
+ */
+ private static function onlyBlockArgs(array $node)
+ {
+ return $node[Tokenizer::TYPE] === Tokenizer::T_BLOCK_ARG;
+ }
+
+ const VARIABLE = '
+ $value = $this->resolveValue($context->%s(%s%s), $context);%s
+ $buffer .= %s($value === null ? \'\' : %s);
+ ';
+
+ /**
+ * Generate Mustache Template variable interpolation PHP source.
+ *
+ * @param string $id Variable name
+ * @param string[] $filters Array of filters
+ * @param bool $escape Escape the variable value for output?
+ * @param int $level
+ *
+ * @return string Generated variable interpolation PHP source
+ */
+ private function variable($id, $filters, $escape, $level)
+ {
+ $method = $this->getFindMethod($id);
+ $id = ($method !== 'last') ? var_export($id, true) : '';
+ $findArg = $this->getFindMethodArgs($method);
+ $filters = $this->getFilters($filters, $level);
+ $value = $escape ? $this->getEscape() : '$value';
+
+ return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $findArg, $filters, $this->flushIndent(), $value);
+ }
+
+ const FILTER = '
+ $filter = $context->%s(%s%s);
+ if (!(%s)) {
+ throw new \\Mustache\\Exception\\UnknownFilterException(%s);
+ }
+ $value = call_user_func($filter, %s);%s
+ ';
+ const FILTER_FIRST_VALUE = '$this->resolveValue($value, $context)';
+ const FILTER_VALUE = '$value';
+
+ /**
+ * Generate Mustache Template variable filtering PHP source.
+ *
+ * If the initial $value is a lambda it will be resolved before starting the filter chain.
+ *
+ * @param string[] $filters Array of filters
+ * @param int $level
+ * @param bool $first (default: false)
+ *
+ * @return string Generated filter PHP source
+ */
+ private function getFilters(array $filters, $level, $first = true)
+ {
+ if (empty($filters)) {
+ return '';
+ }
+
+ $name = array_shift($filters);
+ $method = $this->getFindMethod($name);
+ $filter = ($method !== 'last') ? var_export($name, true) : '';
+ $findArg = $this->getFindMethodArgs($method);
+ $callable = $this->getCallable('$filter');
+ $msg = var_export($name, true);
+ $value = $first ? self::FILTER_FIRST_VALUE : self::FILTER_VALUE;
+
+ return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $findArg, $callable, $msg, $value, $this->getFilters($filters, $level, false));
+ }
+
+ const LINE = '$buffer .= "\n";';
+ const TEXT = '$buffer .= %s%s;';
+
+ /**
+ * Generate Mustache Template output Buffer call PHP source.
+ *
+ * @param string $text
+ * @param int $level
+ *
+ * @return string Generated output Buffer call PHP source
+ */
+ private function text($text, $level)
+ {
+ $indentNextLine = (substr($text, -1) === "\n");
+ $code = sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true));
+ $this->indentNextLine = $indentNextLine;
+
+ return $code;
+ }
+
+ /**
+ * Prepare PHP source code snippet for output.
+ *
+ * @param string $text
+ * @param int $bonus Additional indent level (default: 0)
+ * @param bool $prependNewline Prepend a newline to the snippet? (default: true)
+ * @param bool $appendNewline Append a newline to the snippet? (default: false)
+ *
+ * @return string PHP source code snippet
+ */
+ private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false)
+ {
+ $text = ($prependNewline ? "\n" : '') . trim($text);
+ if ($prependNewline) {
+ $bonus++;
+ }
+ if ($appendNewline) {
+ $text .= "\n";
+ }
+
+ return preg_replace("/\n( {8})?/", "\n" . str_repeat(' ', $bonus * 4), $text);
+ }
+
+ const DEFAULT_ESCAPE = 'htmlspecialchars(%s, %s, %s)';
+ const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)';
+
+ /**
+ * Get the current escaper.
+ *
+ * @param string $value (default: '$value')
+ *
+ * @return string Either a custom callback, or an inline call to `htmlspecialchars`
+ */
+ private function getEscape($value = '$value')
+ {
+ if ($this->customEscape) {
+ return sprintf(self::CUSTOM_ESCAPE, $value);
+ }
+
+ return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->entityFlags, true), var_export($this->charset, true));
+ }
+
+ /**
+ * Select the appropriate Context `find` method for a given $id.
+ *
+ * The return value will be one of `find`, `findDot`, `findAnchoredDot` or `last`.
+ *
+ * @see \Mustache\Context::find
+ * @see \Mustache\Context::findDot
+ * @see \Mustache\Context::last
+ *
+ * @param string $id Variable name
+ *
+ * @return string `find` method name
+ */
+ private function getFindMethod($id)
+ {
+ if ($id === '.') {
+ return 'last';
+ }
+
+ if (isset($this->pragmas[Engine::PRAGMA_ANCHORED_DOT]) && $this->pragmas[Engine::PRAGMA_ANCHORED_DOT]) {
+ if (substr($id, 0, 1) === '.') {
+ return 'findAnchoredDot';
+ }
+ }
+
+ if (strpos($id, '.') === false) {
+ return 'find';
+ }
+
+ return 'findDot';
+ }
+
+ /**
+ * Get the args needed for a given find method.
+ *
+ * In this case, it's "true" iff it's a "find dot" method and strict callables is enabled.
+ *
+ * @param string $method Find method name
+ */
+ private function getFindMethodArgs($method)
+ {
+ if (($method === 'findDot' || $method === 'findAnchoredDot') && $this->strictCallables) {
+ return ', true';
+ }
+
+ return '';
+ }
+
+ const IS_CALLABLE = '!is_string(%s) && is_callable(%s)';
+ const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)';
+
+ /**
+ * Helper function to compile strict vs lax "is callable" logic.
+ *
+ * @param string $variable (default: '$value')
+ *
+ * @return string "is callable" logic
+ */
+ private function getCallable($variable = '$value')
+ {
+ $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE;
+
+ return sprintf($tpl, $variable, $variable);
+ }
+
+ const LINE_INDENT = '$indent . ';
+
+ /**
+ * Get the current $indent prefix to write to the buffer.
+ *
+ * @return string "$indent . " or ""
+ */
+ private function flushIndent()
+ {
+ if (!$this->indentNextLine) {
+ return '';
+ }
+
+ $this->indentNextLine = false;
+
+ return self::LINE_INDENT;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Context.php b/vendor/mustache/mustache/src/Context.php
new file mode 100644
index 0000000..9f60845
--- /dev/null
+++ b/vendor/mustache/mustache/src/Context.php
@@ -0,0 +1,277 @@
+stack = [$context];
+ }
+
+ $this->buggyPropertyShadowing = $buggyPropertyShadowing;
+ }
+
+ /**
+ * Push a new Context frame onto the stack.
+ *
+ * @param mixed $value Object or array to use for context
+ */
+ public function push($value)
+ {
+ array_push($this->stack, $value);
+ }
+
+ /**
+ * Push a new Context frame onto the block context stack.
+ *
+ * @param mixed $value Object or array to use for block context
+ */
+ public function pushBlockContext($value)
+ {
+ array_push($this->blockStack, $value);
+ }
+
+ /**
+ * Pop the last Context frame from the stack.
+ *
+ * @return mixed Last Context frame (object or array)
+ */
+ public function pop()
+ {
+ return array_pop($this->stack);
+ }
+
+ /**
+ * Pop the last block Context frame from the stack.
+ *
+ * @return mixed Last block Context frame (object or array)
+ */
+ public function popBlockContext()
+ {
+ return array_pop($this->blockStack);
+ }
+
+ /**
+ * Get the last Context frame.
+ *
+ * @return mixed Last Context frame (object or array)
+ */
+ public function last()
+ {
+ return end($this->stack);
+ }
+
+ /**
+ * Find a variable in the Context stack.
+ *
+ * Starting with the last Context frame (the context of the innermost section), and working back to the top-level
+ * rendering context, look for a variable with the given name:
+ *
+ * * If the Context frame is an associative array which contains the key $id, returns the value of that element.
+ * * If the Context frame is an object, this will check first for a public method, then a public property named
+ * $id. Failing both of these, it will try `__isset` and `__get` magic methods.
+ * * If a value named $id is not found in any Context frame, returns an empty string.
+ *
+ * @param string $id Variable name
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function find($id)
+ {
+ return $this->findVariableInStack($id, $this->stack);
+ }
+
+ /**
+ * Find a 'dot notation' variable in the Context stack.
+ *
+ * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding
+ * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous
+ * result. For example, given the following context stack:
+ *
+ * $data = [
+ * 'name' => 'Fred',
+ * 'child' => [
+ * 'name' => 'Bob'
+ * ],
+ * ];
+ *
+ * ... and the Mustache following template:
+ *
+ * {{ child.name }}
+ *
+ * ... the `name` value is only searched for within the `child` value of the global Context, not within parent
+ * Context frames.
+ *
+ * @param string $id Dotted variable selector
+ * @param bool $strictCallables (default: false)
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function findDot($id, $strictCallables = false)
+ {
+ $chunks = explode('.', $id);
+ $first = array_shift($chunks);
+ $value = $this->findVariableInStack($first, $this->stack);
+
+ // This wasn't really a dotted name, so we can just return the value.
+ if (empty($chunks)) {
+ return $value;
+ }
+
+ foreach ($chunks as $chunk) {
+ $isCallable = $strictCallables ? (is_object($value) && is_callable($value)) : (!is_string($value) && is_callable($value));
+
+ if ($isCallable) {
+ $value = $value();
+ } elseif ($value === '') {
+ return $value;
+ }
+
+ $value = $this->findVariableInStack($chunk, [$value]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Find an 'anchored dot notation' variable in the Context stack.
+ *
+ * This is the same as findDot(), except it looks in the top of the context
+ * stack for the first value, rather than searching the whole context stack
+ * and starting from there.
+ *
+ * @see Mustache\Context::findDot
+ *
+ * @throws InvalidArgumentException if given an invalid anchored dot $id
+ *
+ * @param string $id Dotted variable selector
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function findAnchoredDot($id)
+ {
+ $chunks = explode('.', $id);
+ $first = array_shift($chunks);
+ if ($first !== '') {
+ throw new InvalidArgumentException(sprintf('Unexpected id for findAnchoredDot: %s', $id));
+ }
+
+ $value = $this->last();
+
+ foreach ($chunks as $chunk) {
+ if ($value === '') {
+ return $value;
+ }
+
+ $value = $this->findVariableInStack($chunk, [$value]);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Find an argument in the block context stack.
+ *
+ * @param string $id
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ public function findInBlock($id)
+ {
+ foreach ($this->blockStack as $context) {
+ if (array_key_exists($id, $context)) {
+ return $context[$id];
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * Helper function to find a variable in the Context stack.
+ *
+ * @see Mustache\Context::find
+ *
+ * @param string $id Variable name
+ * @param array $stack Context stack
+ *
+ * @return mixed Variable value, or '' if not found
+ */
+ private function findVariableInStack($id, array $stack)
+ {
+ for ($i = count($stack) - 1; $i >= 0; $i--) {
+ $frame = &$stack[$i];
+
+ switch (gettype($frame)) {
+ case 'object':
+ if (!($frame instanceof \Closure)) {
+ // Note that is_callable() *will not work here*
+ // See https://github.com/bobthecow/mustache.php/wiki/Magic-Methods
+ if (method_exists($frame, $id)) {
+ return $frame->$id();
+ }
+
+ if (isset($frame->$id)) {
+ return $frame->$id;
+ }
+
+ // Preserve backwards compatibility with a property shadowing bug in
+ // Mustache.php <= 2.14.2
+ // See https://github.com/bobthecow/mustache.php/pull/410
+ if ($this->buggyPropertyShadowing) {
+ if ($frame instanceof \ArrayAccess && isset($frame[$id])) {
+ return $frame[$id];
+ }
+ } else {
+ if (property_exists($frame, $id)) {
+ $rp = new \ReflectionProperty($frame, $id);
+ if ($rp->isPublic()) {
+ return $frame->$id;
+ }
+ }
+
+ if ($frame instanceof \ArrayAccess && $frame->offsetExists($id)) {
+ return $frame[$id];
+ }
+ }
+ }
+ break;
+
+ case 'array':
+ if (array_key_exists($id, $frame)) {
+ return $frame[$id];
+ }
+ break;
+ }
+ }
+
+ return '';
+ }
+}
diff --git a/vendor/mustache/mustache/src/Engine.php b/vendor/mustache/mustache/src/Engine.php
new file mode 100644
index 0000000..732a29a
--- /dev/null
+++ b/vendor/mustache/mustache/src/Engine.php
@@ -0,0 +1,963 @@
+ true,
+ self::PRAGMA_ANCHORED_DOT => true,
+ self::PRAGMA_BLOCKS => true,
+ ];
+
+ // Template cache
+ private $templates = [];
+
+ // Environment
+ private $templateClassPrefix = '__Mustache_';
+ private $cache;
+ private $lambdaCache;
+ private $cacheLambdaTemplates = false;
+ private $doubleRenderLambdas = false;
+ private $loader;
+ private $partialsLoader;
+ private $helpers;
+ private $escape;
+ private $entityFlags = ENT_COMPAT;
+ private $charset = 'UTF-8';
+ private $logger;
+ private $strictCallables = true;
+ private $pragmas = [];
+ private $delimiters;
+ private $buggyPropertyShadowing = false;
+
+ // Optional Mustache specs
+ private $dynamicNames = true;
+ private $inheritance = true;
+ private $lambdas = true;
+
+ // Services
+ private $tokenizer;
+ private $parser;
+ private $compiler;
+
+ /**
+ * Mustache class constructor.
+ *
+ * Passing an $options array allows overriding certain Mustache options during instantiation:
+ *
+ * $options = [
+ * // The class prefix for compiled templates. Defaults to '__Mustache_'.
+ * 'template_class_prefix' => '__MyTemplates_',
+ *
+ * // A Mustache cache instance or a cache directory string for compiled templates.
+ * // Mustache will not cache templates unless this is set.
+ * 'cache' => __DIR__.'/tmp/cache/mustache',
+ *
+ * // Override default permissions for cache files. Defaults to using the system-defined umask. It is
+ * // *strongly* recommended that you configure your umask properly rather than overriding permissions here.
+ * 'cache_file_mode' => 0666,
+ *
+ * // Optionally, enable caching for lambda section templates. This is generally not recommended, as lambda
+ * // sections are often too dynamic to benefit from caching.
+ * 'cache_lambda_templates' => true,
+ *
+ * // Customize the tag delimiters used by this engine instance. Note that overriding here changes the
+ * // delimiters used to parse all templates and partials loaded by this instance. To override just for a
+ * // single template, use an inline "change delimiters" tag at the start of the template file:
+ * //
+ * // {{=<% %>=}}
+ * //
+ * 'delimiters' => '<% %>',
+ *
+ * // A Mustache template loader instance. Uses a StringLoader if not specified.
+ * 'loader' => new \Mustache\Loader\FilesystemLoader(__DIR__.'/views'),
+ *
+ * // A Mustache loader instance for partials.
+ * 'partials_loader' => new \Mustache\Loader\FilesystemLoader(__DIR__.'/views/partials'),
+ *
+ * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as
+ * // efficient or lazy as a Filesystem (or database) loader.
+ * 'partials' => ['foo' => file_get_contents(__DIR__.'/views/partials/foo.mustache')],
+ *
+ * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order
+ * // sections), or any other valid Mustache context value. They will be prepended to the context stack,
+ * // so they will be available in any template loaded by this Mustache instance.
+ * 'helpers' => ['i18n' => function ($text) {
+ * // do something translatey here...
+ * }],
+ *
+ * // An 'escape' callback, responsible for escaping double-mustache variables.
+ * 'escape' => function ($value) {
+ * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8');
+ * },
+ *
+ * // Type argument for `htmlspecialchars`. Defaults to ENT_COMPAT. You may prefer ENT_QUOTES.
+ * 'entity_flags' => ENT_QUOTES,
+ *
+ * // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'.
+ * 'charset' => 'ISO-8859-1',
+ *
+ * // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible
+ * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is
+ * // available as well:
+ * 'logger' => new \Mustache\Logger\StreamLogger('php://stderr'),
+ *
+ *
+ * // OPTIONAL MUSTACHE FEATURES:
+ *
+ * // Enable dynamic names. By default, variables and sections like `{{*name}}` will be resolved dynamically.
+ * //
+ * // To disable dynamic name resolution, set this to false.
+ * 'dynamic_names' => true,
+ *
+ * // Enable template inheritance. By default, templates can extend other templates using the `{{< name}}` and
+ * // `{{$ block}}` tags.
+ * //
+ * // To disable inheritance, set this to false.
+ * 'inheritance' => true,
+ *
+ * // Enable lambda sections and values. By default, "lambdas" are enabled; if a variable resolves to a
+ * // callable value, that callable is called before interpolation. If a section name resolves to a callable
+ * // value, it is treated as a "higher order section", and the section content is passed to the callable
+ * // for processing prior to rendering.
+ * //
+ * // Note that the FILTERS pragma requires lambdas to function, so using FILTERS without lambdas enabled
+ * // will throw an invalid argument exception.
+ * //
+ * // To disable lambdas and higher order sections entirely, set this to false.
+ * 'lambdas' => true,
+ *
+ * // Enable pragmas across all templates, regardless of the presence of pragma tags in the individual
+ * // templates.
+ * 'pragmas' => [\Mustache\Engine::PRAGMA_FILTERS],
+ *
+ *
+ * // BACKWARDS COMPATIBILITY:
+ *
+ * // Only treat \Closure instances and invokable classes as callable. If true, values like
+ * // `['ClassName', 'methodName']` and `[$classInstance, 'methodName']`, which are traditionally
+ * // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This
+ * // helps protect against arbitrary code execution when user input is passed directly into the template.
+ * //
+ * // Defaults to true, but can be set to false to preserve Mustache.php v2.x behavior.
+ * //
+ * // THIS IS NOT RECOMMENDED.
+ * 'strict_callables' => true,
+ *
+ * // Enable buggy property shadowing. Per the Mustache spec, keys of a value higher in the context stack
+ * // shadow similarly named keys lower in the stack. For example, in the template
+ * // `{{# foo }}{{ bar }}{{/ foo }}` if the value for `foo` has a method, property, or key named `bar`, it
+ * // will prevent looking lower in the context stack for a another value named `bar`.
+ * //
+ * // Setting the value of an array key to null prevents lookups higher in the context stack. The behavior
+ * // should have been identical for object properties (and ArrayAccess) as well, but a bug in the context
+ * // lookup logic meant that a property which exists but is set to null would not prevent further context
+ * // lookup.
+ * //
+ * // This bug was fixed in Mustache.php v3.x, but the previous buggy behavior can be preserved by setting this
+ * // option to true.
+ * //
+ * // THIS IS NOT RECOMMENDED.
+ * 'buggy_property_shadowing' => false,
+ *
+ * // Double-render lambda return values. By default, the return value of higher order sections that are
+ * // rendered via the lambda helper will *not* be re-rendered.
+ * //
+ * // To preserve the behavior of Mustache.php v2.x, set this to true.
+ * //
+ * // THIS IS NOT RECOMMENDED.
+ * 'double_render_lambdas' => false,
+ * ];
+ *
+ * @throws InvalidArgumentException If `escape` option is not callable
+ * @throws InvalidArgumentException If `lambdas` is disabled but the `FILTERS` pragma is enabled
+ */
+ public function __construct(array $options = [])
+ {
+ if (isset($options['template_class_prefix'])) {
+ if ((string) $options['template_class_prefix'] === '') {
+ throw new InvalidArgumentException('Mustache Constructor "template_class_prefix" must not be empty');
+ }
+
+ $this->templateClassPrefix = $options['template_class_prefix'];
+ }
+
+ if (isset($options['cache'])) {
+ $cache = $options['cache'];
+
+ if (is_string($cache)) {
+ $mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null;
+ $cache = new FilesystemCache($cache, $mode);
+ }
+
+ $this->setCache($cache);
+ }
+
+ if (isset($options['cache_lambda_templates'])) {
+ $this->cacheLambdaTemplates = (bool) $options['cache_lambda_templates'];
+ }
+
+ if (isset($options['loader'])) {
+ $this->setLoader($options['loader']);
+ }
+
+ if (isset($options['partials_loader'])) {
+ $this->setPartialsLoader($options['partials_loader']);
+ }
+
+ if (isset($options['partials'])) {
+ $this->setPartials($options['partials']);
+ }
+
+ if (isset($options['helpers'])) {
+ $this->setHelpers($options['helpers']);
+ }
+
+ if (isset($options['escape'])) {
+ if (!is_callable($options['escape'])) {
+ throw new InvalidArgumentException('Mustache Constructor "escape" option must be callable');
+ }
+
+ $this->escape = $options['escape'];
+ }
+
+ if (isset($options['entity_flags'])) {
+ $this->entityFlags = $options['entity_flags'];
+ }
+
+ if (isset($options['charset'])) {
+ $this->charset = $options['charset'];
+ }
+
+ if (isset($options['logger'])) {
+ $this->setLogger($options['logger']);
+ }
+
+ if (isset($options['delimiters'])) {
+ $this->delimiters = $options['delimiters'];
+ }
+
+ // Optional Mustache features
+
+ if (isset($options['dynamic_names'])) {
+ $this->dynamicNames = $options['dynamic_names'] !== false;
+ }
+
+ if (isset($options['inheritance'])) {
+ $this->inheritance = $options['inheritance'] !== false;
+ }
+
+ if (isset($options['lambdas'])) {
+ $this->lambdas = $options['lambdas'] !== false;
+ }
+
+ if (isset($options['pragmas'])) {
+ foreach ($options['pragmas'] as $pragma) {
+ if (!isset(self::$knownPragmas[$pragma])) {
+ throw new InvalidArgumentException(sprintf('Unknown pragma: "%s"', $pragma));
+ }
+ $this->pragmas[$pragma] = true;
+ }
+ }
+
+ if (!$this->lambdas && isset($this->pragmas[self::PRAGMA_FILTERS])) {
+ throw new InvalidArgumentException('The FILTERS pragma requires lambda support');
+ }
+
+ // Backwards compatibility
+
+ if (isset($options['strict_callables'])) {
+ $this->strictCallables = (bool) $options['strict_callables'];
+ }
+
+ if (isset($options['buggy_property_shadowing'])) {
+ $this->buggyPropertyShadowing = (bool) $options['buggy_property_shadowing'];
+ }
+
+ if (isset($options['double_render_lambdas'])) {
+ $this->doubleRenderLambdas = (bool) $options['double_render_lambdas'];
+ }
+ }
+
+ /**
+ * Shortcut 'render' invocation.
+ *
+ * Equivalent to calling `$mustache->loadTemplate($template)->render($context);`
+ *
+ * @see Mustache\Engine::loadTemplate
+ * @see Mustache\Template::render
+ *
+ * @param string $template
+ *
+ * @return string Rendered template
+ */
+ public function render($template, $context = [])
+ {
+ return $this->loadTemplate($template)->render($context);
+ }
+
+ /**
+ * Get the current Mustache escape callback.
+ *
+ * @return callable|null
+ */
+ public function getEscape()
+ {
+ return $this->escape;
+ }
+
+ /**
+ * Get the current Mustache entity type to escape.
+ *
+ * @return int
+ */
+ public function getEntityFlags()
+ {
+ return $this->entityFlags;
+ }
+
+ /**
+ * Get the current Mustache character set.
+ *
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Check whether to double-render higher-order sections.
+ *
+ * By default, the return value of higher order sections that are rendered
+ * via the lambda helper will *not* be re-rendered. To preserve the
+ * behavior of Mustache.php v2.x, set this to true.
+ *
+ * THIS IS NOT RECOMMENDED.
+ */
+ public function getDoubleRenderLambdas()
+ {
+ return $this->doubleRenderLambdas;
+ }
+
+ /**
+ * Check whether to use buggy property shadowing.
+ *
+ * THIS IS NOT RECOMMENDED.
+ *
+ * See https://github.com/bobthecow/mustache.php/pull/410
+ */
+ public function getBuggyPropertyShadowing()
+ {
+ return $this->buggyPropertyShadowing;
+ }
+
+ /**
+ * Get currently enabled optional features.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return [
+ 'dynamic_names' => $this->dynamicNames,
+ 'inheritance' => $this->inheritance,
+ 'lambdas' => $this->lambdas,
+ ];
+ }
+
+ /**
+ * Get the current globally enabled pragmas.
+ *
+ * @return array
+ */
+ public function getPragmas()
+ {
+ return array_keys($this->pragmas);
+ }
+
+ /**
+ * Set the Mustache template Loader instance.
+ */
+ public function setLoader(Loader $loader)
+ {
+ $this->loader = $loader;
+ }
+
+ /**
+ * Get the current Mustache template Loader instance.
+ *
+ * If no Loader instance has been explicitly specified, this method will instantiate and return
+ * a StringLoader instance.
+ *
+ * @return Loader
+ */
+ public function getLoader()
+ {
+ if (!isset($this->loader)) {
+ $this->loader = new StringLoader();
+ }
+
+ return $this->loader;
+ }
+
+ /**
+ * Set the Mustache partials Loader instance.
+ */
+ public function setPartialsLoader(Loader $partialsLoader)
+ {
+ $this->partialsLoader = $partialsLoader;
+ }
+
+ /**
+ * Get the current Mustache partials Loader instance.
+ *
+ * If no Loader instance has been explicitly specified, this method will instantiate and return
+ * an ArrayLoader instance.
+ *
+ * @return Loader
+ */
+ public function getPartialsLoader()
+ {
+ if (!isset($this->partialsLoader)) {
+ $this->partialsLoader = new ArrayLoader();
+ }
+
+ return $this->partialsLoader;
+ }
+
+ /**
+ * Set partials for the current partials Loader instance.
+ *
+ * @throws RuntimeException If the current Loader instance is immutable
+ */
+ public function setPartials(array $partials = [])
+ {
+ if (!isset($this->partialsLoader)) {
+ $this->partialsLoader = new ArrayLoader();
+ }
+
+ if (!$this->partialsLoader instanceof MutableLoader) {
+ throw new RuntimeException('Unable to set partials on an immutable Mustache Loader instance');
+ }
+
+ $this->partialsLoader->setTemplates($partials);
+ }
+
+ /**
+ * Set an array of Mustache helpers.
+ *
+ * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or
+ * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in
+ * any template loaded by this Mustache instance.
+ *
+ * @throws InvalidArgumentException if $helpers is not an array or \Traversable
+ *
+ * @param array|\Traversable $helpers
+ */
+ public function setHelpers($helpers)
+ {
+ if (!is_array($helpers) && !$helpers instanceof \Traversable) {
+ throw new InvalidArgumentException('setHelpers expects an array of helpers');
+ }
+
+ $this->getHelpers()->clear();
+
+ foreach ($helpers as $name => $helper) {
+ $this->addHelper($name, $helper);
+ }
+ }
+
+ /**
+ * Get the current set of Mustache helpers.
+ *
+ * @see Mustache\Engine::setHelpers
+ *
+ * @return HelperCollection
+ */
+ public function getHelpers()
+ {
+ if (!isset($this->helpers)) {
+ $this->helpers = new HelperCollection();
+ }
+
+ return $this->helpers;
+ }
+
+ /**
+ * Add a new Mustache helper.
+ *
+ * @see Mustache\Engine::setHelpers
+ *
+ * @param string $name
+ * @param mixed $helper
+ */
+ public function addHelper($name, $helper)
+ {
+ $this->getHelpers()->add($name, $helper);
+ }
+
+ /**
+ * Get a Mustache helper by name.
+ *
+ * @see Mustache\Engine::setHelpers
+ *
+ * @param string $name
+ *
+ * @return mixed Helper
+ */
+ public function getHelper($name)
+ {
+ return $this->getHelpers()->get($name);
+ }
+
+ /**
+ * Check whether this Mustache instance has a helper.
+ *
+ * @see Mustache\Engine::setHelpers
+ *
+ * @param string $name
+ *
+ * @return bool True if the helper is present
+ */
+ public function hasHelper($name)
+ {
+ return $this->getHelpers()->has($name);
+ }
+
+ /**
+ * Remove a helper by name.
+ *
+ * @see Mustache\Engine::setHelpers
+ *
+ * @param string $name
+ */
+ public function removeHelper($name)
+ {
+ $this->getHelpers()->remove($name);
+ }
+
+ /**
+ * Set the Mustache Logger instance.
+ *
+ * @throws InvalidArgumentException If logger is not an instance of Mustache\Logger or Psr\Log\LoggerInterface
+ *
+ * @param Logger|LoggerInterface $logger
+ */
+ public function setLogger($logger = null)
+ {
+ // n.b. this uses `is_a` to prevent a dependency on Psr\Log
+ if ($logger !== null && !$logger instanceof Logger && !is_a($logger, 'Psr\\Log\\LoggerInterface')) {
+ throw new InvalidArgumentException('Expected an instance of Mustache\\Logger or Psr\\Log\\LoggerInterface.');
+ }
+
+ if ($this->getCache()->getLogger() === null) {
+ $this->getCache()->setLogger($logger);
+ }
+
+ $this->logger = $logger;
+ }
+
+ /**
+ * Get the current Mustache Logger instance.
+ *
+ * @return Logger|LoggerInterface
+ */
+ public function getLogger()
+ {
+ return $this->logger;
+ }
+
+ /**
+ * Set the Mustache Tokenizer instance.
+ */
+ public function setTokenizer(Tokenizer $tokenizer)
+ {
+ $this->tokenizer = $tokenizer;
+ }
+
+ /**
+ * Get the current Mustache Tokenizer instance.
+ *
+ * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Tokenizer
+ */
+ public function getTokenizer()
+ {
+ if (!isset($this->tokenizer)) {
+ $this->tokenizer = new Tokenizer();
+ }
+
+ return $this->tokenizer;
+ }
+
+ /**
+ * Set the Mustache Parser instance.
+ */
+ public function setParser(Parser $parser)
+ {
+ $this->parser = $parser;
+ }
+
+ /**
+ * Get the current Mustache Parser instance.
+ *
+ * If no Parser instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Parser
+ */
+ public function getParser()
+ {
+ if (!isset($this->parser)) {
+ $this->parser = new Parser();
+ }
+
+ return $this->parser;
+ }
+
+ /**
+ * Set the Mustache Compiler instance.
+ */
+ public function setCompiler(Compiler $compiler)
+ {
+ $this->compiler = $compiler;
+ }
+
+ /**
+ * Get the current Mustache Compiler instance.
+ *
+ * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Compiler
+ */
+ public function getCompiler()
+ {
+ if (!isset($this->compiler)) {
+ $this->compiler = new Compiler();
+ }
+
+ return $this->compiler;
+ }
+
+ /**
+ * Set the Mustache Cache instance.
+ */
+ public function setCache(Cache $cache)
+ {
+ if (isset($this->logger) && $cache->getLogger() === null) {
+ $cache->setLogger($this->getLogger());
+ }
+
+ $this->cache = $cache;
+ }
+
+ /**
+ * Get the current Mustache Cache instance.
+ *
+ * If no Cache instance has been explicitly specified, this method will instantiate and return a new one.
+ *
+ * @return Cache
+ */
+ public function getCache()
+ {
+ if (!isset($this->cache)) {
+ $this->setCache(new NoopCache());
+ }
+
+ return $this->cache;
+ }
+
+ /**
+ * Get the current Lambda Cache instance.
+ *
+ * If 'cache_lambda_templates' is enabled, this is the default cache instance. Otherwise, it is a NoopCache.
+ *
+ * @see Mustache\Engine::getCache
+ *
+ * @return Cache
+ */
+ protected function getLambdaCache()
+ {
+ if ($this->cacheLambdaTemplates) {
+ return $this->getCache();
+ }
+
+ if (!isset($this->lambdaCache)) {
+ $this->lambdaCache = new NoopCache();
+ }
+
+ return $this->lambdaCache;
+ }
+
+ /**
+ * Helper method to generate a Mustache template class.
+ *
+ * This method must be updated any time options are added which make it so
+ * the same template could be parsed and compiled multiple different ways.
+ *
+ * @param string|Source $source
+ *
+ * @return string Mustache Template class name
+ */
+ public function getTemplateClassName($source)
+ {
+ // For the most part, adding a new option here should do the trick.
+ //
+ // Pick a value here which is unique for each possible way the template
+ // could be compiled... but not necessarily unique per option value. See
+ // escape below, which only needs to differentiate between 'custom' and
+ // 'default' escapes.
+ //
+ // Keep this list in alphabetical order :)
+ $chunks = [
+ 'charset' => $this->charset,
+ 'delimiters' => $this->delimiters ?: '{{ }}',
+ 'entityFlags' => $this->entityFlags,
+ 'escape' => isset($this->escape) ? 'custom' : 'default',
+ 'key' => ($source instanceof Source) ? $source->getKey() : 'source',
+ 'options' => $this->getOptions(),
+ 'pragmas' => $this->getPragmas(),
+ 'strictCallables' => $this->strictCallables,
+ 'version' => self::VERSION,
+ ];
+
+ $key = json_encode($chunks);
+
+ // Template Source instances have already provided their own source key. For strings, just include the whole
+ // source string in the md5 hash.
+ if (!$source instanceof Source) {
+ $key .= "\n" . $source;
+ }
+
+ return $this->templateClassPrefix . md5($key);
+ }
+
+ /**
+ * Load a Mustache Template by name.
+ *
+ * @param string $name
+ *
+ * @return Template
+ */
+ public function loadTemplate($name)
+ {
+ return $this->loadSource($this->getLoader()->load($name));
+ }
+
+ /**
+ * Load a Mustache partial Template by name.
+ *
+ * This is a helper method used internally by Template instances for loading partial templates. You can most likely
+ * ignore it completely.
+ *
+ * @param string $name
+ *
+ * @return Template
+ */
+ public function loadPartial($name)
+ {
+ try {
+ if (isset($this->partialsLoader)) {
+ $loader = $this->partialsLoader;
+ } elseif (isset($this->loader) && !$this->loader instanceof StringLoader) {
+ $loader = $this->loader;
+ } else {
+ throw new UnknownTemplateException($name);
+ }
+
+ return $this->loadSource($loader->load($name));
+ } catch (UnknownTemplateException $e) {
+ // If the named partial cannot be found, log then return null.
+ $this->log(
+ Logger::WARNING,
+ 'Partial not found: "{name}"',
+ ['name' => $e->getTemplateName()]
+ );
+ }
+ }
+
+ /**
+ * Load a Mustache lambda Template by source.
+ *
+ * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most
+ * likely ignore it completely.
+ *
+ * @param string $source
+ * @param string $delims (default: null)
+ *
+ * @return Template
+ */
+ public function loadLambda($source, $delims = null)
+ {
+ if ($delims !== null) {
+ $source = $delims . "\n" . $source;
+ }
+
+ return $this->loadSource($source, $this->getLambdaCache());
+ }
+
+ /**
+ * Instantiate and return a Mustache Template instance by source.
+ *
+ * Optionally provide a Mustache\Cache instance. This is used internally by Mustache\Engine::loadLambda to respect
+ * the 'cache_lambda_templates' configuration option.
+ *
+ * @see Mustache\Engine::loadTemplate
+ * @see Mustache\Engine::loadPartial
+ * @see Mustache\Engine::loadLambda
+ *
+ * @param string|Source $source
+ * @param Cache $cache (default: null)
+ *
+ * @return Template
+ */
+ private function loadSource($source, $cache = null)
+ {
+ $className = $this->getTemplateClassName($source);
+
+ if (!isset($this->templates[$className])) {
+ if ($cache === null || !$cache instanceof Cache) {
+ $cache = $this->getCache();
+ }
+
+ if (!class_exists($className, false)) {
+ if (!$cache->load($className)) {
+ $compiled = $this->compile($source);
+ $cache->cache($className, $compiled);
+ }
+ }
+
+ $this->log(
+ Logger::DEBUG,
+ 'Instantiating template: "{className}"',
+ ['className' => $className]
+ );
+
+ $this->templates[$className] = new $className($this);
+ }
+
+ return $this->templates[$className];
+ }
+
+ /**
+ * Helper method to tokenize a Mustache template.
+ *
+ * @see Mustache\Tokenizer::scan
+ *
+ * @param string $source
+ *
+ * @return array Tokens
+ */
+ private function tokenize($source)
+ {
+ return $this->getTokenizer()->scan($source, $this->delimiters);
+ }
+
+ /**
+ * Helper method to parse a Mustache template.
+ *
+ * @see Mustache\Parser::parse
+ *
+ * @param string $source
+ *
+ * @return array Token tree
+ */
+ private function parse($source)
+ {
+ $parser = $this->getParser();
+ $parser->setOptions($this->getOptions());
+ $parser->setPragmas($this->getPragmas());
+
+ return $parser->parse($this->tokenize($source));
+ }
+
+ /**
+ * Helper method to compile a Mustache template.
+ *
+ * @see Mustache\Compiler::compile
+ *
+ * @param string|Source $source
+ *
+ * @return string generated Mustache template class code
+ */
+ private function compile($source)
+ {
+ $name = $this->getTemplateClassName($source);
+
+ $this->log(
+ Logger::INFO,
+ 'Compiling template to "{className}" class',
+ ['className' => $name]
+ );
+
+ if ($source instanceof Source) {
+ $source = $source->getSource();
+ }
+ $tree = $this->parse($source);
+
+ $compiler = $this->getCompiler();
+ $compiler->setOptions($this->getOptions());
+ $compiler->setPragmas($this->getPragmas());
+
+ return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
+ }
+
+ /**
+ * Add a log record if logging is enabled.
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ * @param array $context The log context
+ */
+ private function log($level, $message, array $context = [])
+ {
+ if (isset($this->logger)) {
+ $this->logger->log($level, $message, $context);
+ }
+ }
+}
diff --git a/vendor/mustache/mustache/src/Exception.php b/vendor/mustache/mustache/src/Exception.php
new file mode 100644
index 0000000..c72be7b
--- /dev/null
+++ b/vendor/mustache/mustache/src/Exception.php
@@ -0,0 +1,17 @@
+token = $token;
+ parent::__construct($msg, 0, $previous);
+ }
+
+ /**
+ * @return array
+ */
+ public function getToken()
+ {
+ return $this->token;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Exception/UnknownFilterException.php b/vendor/mustache/mustache/src/Exception/UnknownFilterException.php
new file mode 100644
index 0000000..4645c03
--- /dev/null
+++ b/vendor/mustache/mustache/src/Exception/UnknownFilterException.php
@@ -0,0 +1,38 @@
+filterName = $filterName;
+ $message = sprintf('Unknown filter: %s', $filterName);
+ parent::__construct($message, 0, $previous);
+ }
+
+ public function getFilterName()
+ {
+ return $this->filterName;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Exception/UnknownHelperException.php b/vendor/mustache/mustache/src/Exception/UnknownHelperException.php
new file mode 100644
index 0000000..1c8e475
--- /dev/null
+++ b/vendor/mustache/mustache/src/Exception/UnknownHelperException.php
@@ -0,0 +1,38 @@
+helperName = $helperName;
+ $message = sprintf('Unknown helper: %s', $helperName);
+ parent::__construct($message, 0, $previous);
+ }
+
+ public function getHelperName()
+ {
+ return $this->helperName;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Exception/UnknownTemplateException.php b/vendor/mustache/mustache/src/Exception/UnknownTemplateException.php
new file mode 100644
index 0000000..20432cf
--- /dev/null
+++ b/vendor/mustache/mustache/src/Exception/UnknownTemplateException.php
@@ -0,0 +1,38 @@
+templateName = $templateName;
+ $message = sprintf('Unknown template: %s', $templateName);
+ parent::__construct($message, 0, $previous);
+ }
+
+ public function getTemplateName()
+ {
+ return $this->templateName;
+ }
+}
diff --git a/vendor/mustache/mustache/src/HelperCollection.php b/vendor/mustache/mustache/src/HelperCollection.php
new file mode 100644
index 0000000..9b46b9c
--- /dev/null
+++ b/vendor/mustache/mustache/src/HelperCollection.php
@@ -0,0 +1,177 @@
+ $helper` pairs.
+ *
+ * @throws InvalidArgumentException if the $helpers argument isn't an array or \Traversable
+ *
+ * @param array|\Traversable $helpers (default: null)
+ */
+ public function __construct($helpers = null)
+ {
+ if ($helpers === null) {
+ return;
+ }
+
+ if (!is_array($helpers) && !$helpers instanceof \Traversable) {
+ throw new InvalidArgumentException('HelperCollection constructor expects an array of helpers');
+ }
+
+ foreach ($helpers as $name => $helper) {
+ $this->add($name, $helper);
+ }
+ }
+
+ /**
+ * Magic mutator.
+ *
+ * @see Mustache\HelperCollection::add
+ *
+ * @param string $name
+ * @param mixed $helper
+ */
+ public function __set($name, $helper)
+ {
+ $this->add($name, $helper);
+ }
+
+ /**
+ * Add a helper to this collection.
+ *
+ * @param string $name
+ * @param mixed $helper
+ */
+ public function add($name, $helper)
+ {
+ $this->helpers[$name] = $helper;
+ }
+
+ /**
+ * Magic accessor.
+ *
+ * @see Mustache\HelperCollection::get
+ *
+ * @param string $name
+ *
+ * @return mixed Helper
+ */
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ /**
+ * Get a helper by name.
+ *
+ * @throws UnknownHelperException If helper does not exist
+ *
+ * @param string $name
+ *
+ * @return mixed Helper
+ */
+ public function get($name)
+ {
+ if (!$this->has($name)) {
+ throw new UnknownHelperException($name);
+ }
+
+ return $this->helpers[$name];
+ }
+
+ /**
+ * Magic isset().
+ *
+ * @see Mustache\HelperCollection::has
+ *
+ * @param string $name
+ *
+ * @return bool True if helper is present
+ */
+ public function __isset($name)
+ {
+ return $this->has($name);
+ }
+
+ /**
+ * Check whether a given helper is present in the collection.
+ *
+ * @param string $name
+ *
+ * @return bool True if helper is present
+ */
+ public function has($name)
+ {
+ return array_key_exists($name, $this->helpers);
+ }
+
+ /**
+ * Magic unset().
+ *
+ * @see Mustache\HelperCollection::remove
+ *
+ * @param string $name
+ */
+ public function __unset($name)
+ {
+ $this->remove($name);
+ }
+
+ /**
+ * Check whether a given helper is present in the collection.
+ *
+ * @throws UnknownHelperException if the requested helper is not present
+ *
+ * @param string $name
+ */
+ public function remove($name)
+ {
+ if (!$this->has($name)) {
+ throw new UnknownHelperException($name);
+ }
+
+ unset($this->helpers[$name]);
+ }
+
+ /**
+ * Clear the helper collection.
+ *
+ * Removes all helpers from this collection
+ */
+ public function clear()
+ {
+ $this->helpers = [];
+ }
+
+ /**
+ * Check whether the helper collection is empty.
+ *
+ * @return bool True if the collection is empty
+ */
+ public function isEmpty()
+ {
+ return empty($this->helpers);
+ }
+}
diff --git a/vendor/mustache/mustache/src/LambdaHelper.php b/vendor/mustache/mustache/src/LambdaHelper.php
new file mode 100644
index 0000000..9ac58cc
--- /dev/null
+++ b/vendor/mustache/mustache/src/LambdaHelper.php
@@ -0,0 +1,96 @@
+ =}}`. (default: null)
+ */
+ public function __construct(Engine $mustache, Context $context, $delims = null)
+ {
+ $this->mustache = $mustache;
+ $this->context = $context;
+ $this->delims = $delims;
+ }
+
+ /**
+ * Render a string as a Mustache template with the current rendering context.
+ *
+ * @param string $string
+ *
+ * @return string Rendered template
+ */
+ public function render($string)
+ {
+ $value = $this->mustache
+ ->loadLambda((string) $string, $this->delims)
+ ->renderInternal($this->context);
+
+ return $this->mustache->getDoubleRenderLambdas() ? $value : $this->preventRender($value);
+ }
+
+ /**
+ * Prevent rendering of a string as a Mustache template.
+ *
+ * This is useful for returning a raw string from a lambda without processing it as a Mustache template.
+ *
+ * @see RenderedString
+ *
+ * @param string $value The raw string value to return
+ *
+ * @return RenderedString A RenderedString instance containing the raw value
+ */
+ public function preventRender($value)
+ {
+ return new RenderedString($value);
+ }
+
+ /**
+ * Render a string as a Mustache template with the current rendering context.
+ *
+ * @param string $string
+ *
+ * @return string Rendered template
+ */
+ public function __invoke($string)
+ {
+ return $this->render($string);
+ }
+
+ /**
+ * Get a Lambda Helper with custom delimiters.
+ *
+ * @param string $delims Custom delimiters, in the format `{{= <% %> =}}`
+ *
+ * @return LambdaHelper
+ */
+ public function withDelimiters($delims)
+ {
+ return new self($this->mustache, $this->context, $delims);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader.php b/vendor/mustache/mustache/src/Loader.php
new file mode 100644
index 0000000..ef3c780
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader.php
@@ -0,0 +1,28 @@
+ '{{ bar }}',
+ * 'baz' => 'Hey {{ qux }}!'
+ * );
+ *
+ * $tpl = $loader->load('foo'); // '{{ bar }}'
+ *
+ * The ArrayLoader is used internally as a partials loader by Mustache\Engine instance when an array of partials
+ * is set. It can also be used as a quick-and-dirty Template loader.
+ */
+class ArrayLoader implements Loader, MutableLoader
+{
+ private $templates;
+
+ /**
+ * ArrayLoader constructor.
+ *
+ * @param array $templates Associative array of Template source (default: [])
+ */
+ public function __construct(array $templates = [])
+ {
+ $this->templates = $templates;
+ }
+
+ /**
+ * Load a Template.
+ *
+ * @throws UnknownTemplateException If a template file is not found
+ *
+ * @param string $name
+ *
+ * @return string Mustache Template source
+ */
+ public function load($name)
+ {
+ if (!isset($this->templates[$name])) {
+ throw new UnknownTemplateException($name);
+ }
+
+ return $this->templates[$name];
+ }
+
+ /**
+ * Set an associative array of Template sources for this loader.
+ */
+ public function setTemplates(array $templates)
+ {
+ $this->templates = $templates;
+ }
+
+ /**
+ * Set a Template source by name.
+ *
+ * @param string $name
+ * @param string $template Mustache Template source
+ */
+ public function setTemplate($name, $template)
+ {
+ $this->templates[$name] = $template;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader/CascadingLoader.php b/vendor/mustache/mustache/src/Loader/CascadingLoader.php
new file mode 100644
index 0000000..cecb2d8
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader/CascadingLoader.php
@@ -0,0 +1,72 @@
+loaders = [];
+ foreach ($loaders as $loader) {
+ $this->addLoader($loader);
+ }
+ }
+
+ /**
+ * Add a Loader instance.
+ */
+ public function addLoader(Loader $loader)
+ {
+ $this->loaders[] = $loader;
+ }
+
+ /**
+ * Load a Template by name.
+ *
+ * @throws UnknownTemplateException If a template file is not found
+ *
+ * @param string $name
+ *
+ * @return string Mustache Template source
+ */
+ public function load($name)
+ {
+ foreach ($this->loaders as $loader) {
+ try {
+ return $loader->load($name);
+ } catch (UnknownTemplateException $e) {
+ // do nothing, check the next loader.
+ }
+ }
+
+ throw new UnknownTemplateException($name);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader/FilesystemLoader.php b/vendor/mustache/mustache/src/Loader/FilesystemLoader.php
new file mode 100644
index 0000000..f30f720
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader/FilesystemLoader.php
@@ -0,0 +1,141 @@
+load('foo'); // equivalent to `file_get_contents(__DIR__.'/views/foo.mustache');
+ *
+ * This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates:
+ *
+ * $m = new \Mustache\Engine([
+ * 'loader' => new FilesystemLoader(__DIR__.'/views'),
+ * 'partials_loader' => new FilesystemLoader(__DIR__.'/views/partials'),
+ * ]);
+ */
+class FilesystemLoader implements Loader
+{
+ private $baseDir;
+ private $extension = '.mustache';
+ private $templates = [];
+
+ /**
+ * Mustache filesystem Loader constructor.
+ *
+ * Passing an $options array allows overriding certain Loader options during instantiation:
+ *
+ * $options = [
+ * // The filename extension used for Mustache templates. Defaults to '.mustache'
+ * 'extension' => '.ms',
+ * ];
+ *
+ * @throws RuntimeException if $baseDir does not exist
+ *
+ * @param string $baseDir Base directory containing Mustache template files
+ * @param array $options Loader options (default: [])
+ */
+ public function __construct($baseDir, array $options = [])
+ {
+ $this->baseDir = $baseDir;
+
+ if (strpos($this->baseDir, '://') === false) {
+ $this->baseDir = realpath($this->baseDir);
+ }
+
+ if ($this->shouldCheckPath() && !is_dir($this->baseDir)) {
+ throw new RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir));
+ }
+
+ if (array_key_exists('extension', $options)) {
+ if (empty($options['extension'])) {
+ $this->extension = '';
+ } else {
+ $this->extension = '.' . ltrim($options['extension'], '.');
+ }
+ }
+ }
+
+ /**
+ * Load a Template by name.
+ *
+ * $loader = new FilesystemLoader(__DIR__.'/views');
+ * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache";
+ *
+ * @param string $name
+ *
+ * @return string Mustache Template source
+ */
+ public function load($name)
+ {
+ if (!isset($this->templates[$name])) {
+ $this->templates[$name] = $this->loadFile($name);
+ }
+
+ return $this->templates[$name];
+ }
+
+ /**
+ * Helper function for loading a Mustache file by name.
+ *
+ * @throws UnknownTemplateException If a template file is not found
+ *
+ * @param string $name
+ *
+ * @return string Mustache Template source
+ */
+ protected function loadFile($name)
+ {
+ $fileName = $this->getFileName($name);
+
+ if ($this->shouldCheckPath() && !file_exists($fileName)) {
+ throw new UnknownTemplateException($name);
+ }
+
+ return file_get_contents($fileName);
+ }
+
+ /**
+ * Helper function for getting a Mustache template file name.
+ *
+ * @param string $name
+ *
+ * @return string Template file name
+ */
+ protected function getFileName($name)
+ {
+ $fileName = $this->baseDir . '/' . $name;
+ if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) {
+ $fileName .= $this->extension;
+ }
+
+ return $fileName;
+ }
+
+ /**
+ * Only check if baseDir is a directory and requested templates are files if
+ * baseDir is using the filesystem stream wrapper.
+ *
+ * @return bool Whether to check `is_dir` and `file_exists`
+ */
+ protected function shouldCheckPath()
+ {
+ return strpos($this->baseDir, '://') === false || strpos($this->baseDir, 'file://') === 0;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader/InlineLoader.php b/vendor/mustache/mustache/src/Loader/InlineLoader.php
new file mode 100644
index 0000000..97e12d0
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader/InlineLoader.php
@@ -0,0 +1,129 @@
+load('hello');
+ * $goodbye = $loader->load('goodbye');
+ *
+ * __halt_compiler();
+ *
+ * @@ hello
+ * Hello, {{ planet }}!
+ *
+ * @@ goodbye
+ * Goodbye, cruel {{ planet }}
+ *
+ * Templates are deliniated by lines containing only `@@ name`.
+ *
+ * The InlineLoader is well-suited to micro-frameworks such as Silex:
+ *
+ * $app->register(new MustacheServiceProvider, [
+ * 'mustache.loader' => new InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__)
+ * ]);
+ *
+ * $app->get('/{name}', function ($name) use ($app) {
+ * return $app['mustache']->render('hello', compact('name'));
+ * })
+ * ->value('name', 'world');
+ *
+ * // ...
+ *
+ * __halt_compiler();
+ *
+ * @@ hello
+ * Hello, {{ name }}!
+ */
+class InlineLoader implements Loader
+{
+ protected $fileName;
+ protected $offset;
+ protected $templates;
+
+ /**
+ * The InlineLoader requires a filename and offset to process templates.
+ *
+ * The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually
+ * perfectly suited to the job:
+ *
+ * $loader = new InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__);
+ *
+ * Note that this only works if the loader is instantiated inside the same
+ * file as the inline templates. If the templates are located in another
+ * file, it would be necessary to manually specify the filename and offset.
+ *
+ * @param string $fileName The file to parse for inline templates
+ * @param int $offset A string offset for the start of the templates.
+ * This usually coincides with the `__halt_compiler`
+ * call, and the `__COMPILER_HALT_OFFSET__`
+ */
+ public function __construct($fileName, $offset)
+ {
+ if (!is_file($fileName)) {
+ throw new InvalidArgumentException('InlineLoader expects a valid filename.');
+ }
+
+ if (!is_int($offset) || $offset < 0) {
+ throw new InvalidArgumentException('InlineLoader expects a valid file offset.');
+ }
+
+ $this->fileName = $fileName;
+ $this->offset = $offset;
+ }
+
+ /**
+ * Load a Template by name.
+ *
+ * @throws UnknownTemplateException If a template file is not found
+ *
+ * @param string $name
+ *
+ * @return string Mustache Template source
+ */
+ public function load($name)
+ {
+ $this->loadTemplates();
+
+ if (!array_key_exists($name, $this->templates)) {
+ throw new UnknownTemplateException($name);
+ }
+
+ return $this->templates[$name];
+ }
+
+ /**
+ * Parse and load templates from the end of a source file.
+ */
+ protected function loadTemplates()
+ {
+ if ($this->templates === null) {
+ $this->templates = [];
+ $data = file_get_contents($this->fileName, false, null, $this->offset);
+ foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) {
+ if (trim($chunk) !== '') {
+ list($name, $content) = explode("\n", $chunk, 2);
+ $this->templates[trim($name)] = trim($content);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader/MutableLoader.php b/vendor/mustache/mustache/src/Loader/MutableLoader.php
new file mode 100644
index 0000000..218b570
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader/MutableLoader.php
@@ -0,0 +1,28 @@
+ '.ms',
+ * 'stat_props' => ['size', 'mtime'],
+ * ];
+ *
+ * Specifying 'stat_props' overrides the stat properties used to invalidate the template cache. By default, this
+ * uses 'mtime' and 'size', but this can be set to any of the properties supported by stat():
+ *
+ * http://php.net/manual/en/function.stat.php
+ *
+ * You can also disable filesystem stat entirely:
+ *
+ * $options = ['stat_props' => null];
+ *
+ * But with great power comes great responsibility. Namely, if you disable stat-based cache invalidation,
+ * YOU MUST CLEAR THE TEMPLATE CACHE YOURSELF when your templates change. Make it part of your build or deploy
+ * process so you don't forget!
+ *
+ * @throws RuntimeException if $baseDir does not exist
+ *
+ * @param string $baseDir base directory containing Mustache template files
+ * @param array $options Loader options (default: [])
+ */
+ public function __construct($baseDir, array $options = [])
+ {
+ parent::__construct($baseDir, $options);
+
+ if (array_key_exists('stat_props', $options)) {
+ if (empty($options['stat_props'])) {
+ $this->statProps = [];
+ } else {
+ $this->statProps = $options['stat_props'];
+ }
+ } else {
+ $this->statProps = ['size', 'mtime'];
+ }
+ }
+
+ /**
+ * Helper function for loading a Mustache file by name.
+ *
+ * @throws UnknownTemplateException if a template file is not found
+ *
+ * @param string $name
+ *
+ * @return Source Mustache Template source
+ */
+ protected function loadFile($name)
+ {
+ $fileName = $this->getFileName($name);
+
+ if (!file_exists($fileName)) {
+ throw new UnknownTemplateException($name);
+ }
+
+ return new FilesystemSource($fileName, $this->statProps);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Loader/StringLoader.php b/vendor/mustache/mustache/src/Loader/StringLoader.php
new file mode 100644
index 0000000..4d82639
--- /dev/null
+++ b/vendor/mustache/mustache/src/Loader/StringLoader.php
@@ -0,0 +1,43 @@
+load('{{ foo }}'); // '{{ foo }}'
+ *
+ * This is the default Template Loader instance used by Mustache:
+ *
+ * $m = new \Mustache\Engine;
+ * $tpl = $m->loadTemplate('{{ foo }}');
+ * echo $tpl->render(['foo' => 'bar']); // "bar"
+ */
+class StringLoader implements Loader
+{
+ /**
+ * Load a Template by source.
+ *
+ * @param string $name Mustache Template source
+ *
+ * @return string Mustache Template source
+ */
+ public function load($name)
+ {
+ return $name;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Logger.php b/vendor/mustache/mustache/src/Logger.php
new file mode 100644
index 0000000..c36eb78
--- /dev/null
+++ b/vendor/mustache/mustache/src/Logger.php
@@ -0,0 +1,102 @@
+log(Logger::EMERGENCY, $message, $context);
+ }
+
+ /**
+ * Action must be taken immediately.
+ *
+ * Example: Entire website down, database unavailable, etc. This should
+ * trigger the SMS alerts and wake you up.
+ *
+ * @param string $message
+ */
+ public function alert($message, array $context = [])
+ {
+ $this->log(Logger::ALERT, $message, $context);
+ }
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ */
+ public function critical($message, array $context = [])
+ {
+ $this->log(Logger::CRITICAL, $message, $context);
+ }
+
+ /**
+ * Runtime errors that do not require immediate action but should typically
+ * be logged and monitored.
+ *
+ * @param string $message
+ */
+ public function error($message, array $context = [])
+ {
+ $this->log(Logger::ERROR, $message, $context);
+ }
+
+ /**
+ * Exceptional occurrences that are not errors.
+ *
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
+ * that are not necessarily wrong.
+ *
+ * @param string $message
+ */
+ public function warning($message, array $context = [])
+ {
+ $this->log(Logger::WARNING, $message, $context);
+ }
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ */
+ public function notice($message, array $context = [])
+ {
+ $this->log(Logger::NOTICE, $message, $context);
+ }
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ */
+ public function info($message, array $context = [])
+ {
+ $this->log(Logger::INFO, $message, $context);
+ }
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ */
+ public function debug($message, array $context = [])
+ {
+ $this->log(Logger::DEBUG, $message, $context);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Logger/StreamLogger.php b/vendor/mustache/mustache/src/Logger/StreamLogger.php
new file mode 100644
index 0000000..1dec1ca
--- /dev/null
+++ b/vendor/mustache/mustache/src/Logger/StreamLogger.php
@@ -0,0 +1,199 @@
+ 100,
+ self::INFO => 200,
+ self::NOTICE => 250,
+ self::WARNING => 300,
+ self::ERROR => 400,
+ self::CRITICAL => 500,
+ self::ALERT => 550,
+ self::EMERGENCY => 600,
+ ];
+
+ protected $level;
+ protected $stream = null;
+ protected $url = null;
+
+ /**
+ * @throws InvalidArgumentException if the logging level is unknown
+ *
+ * @param resource|string $stream Resource instance or URL
+ * @param int $level The minimum logging level at which this handler will be triggered
+ */
+ public function __construct($stream, $level = Logger::ERROR)
+ {
+ $this->setLevel($level);
+
+ if (is_resource($stream)) {
+ $this->stream = $stream;
+ } else {
+ $this->url = $stream;
+ }
+ }
+
+ /**
+ * Close stream resources.
+ */
+ public function __destruct()
+ {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ }
+
+ /**
+ * Set the minimum logging level.
+ *
+ * @throws InvalidArgumentException if the logging level is unknown
+ *
+ * @param int $level The minimum logging level which will be written
+ */
+ public function setLevel($level)
+ {
+ if (!array_key_exists($level, self::$levels)) {
+ throw new InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
+ }
+
+ $this->level = $level;
+ }
+
+ /**
+ * Get the current minimum logging level.
+ *
+ * @return int
+ */
+ public function getLevel()
+ {
+ return $this->level;
+ }
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @throws InvalidArgumentException if the logging level is unknown
+ *
+ * @param mixed $level
+ * @param string $message
+ */
+ public function log($level, $message, array $context = [])
+ {
+ if (!array_key_exists($level, self::$levels)) {
+ throw new InvalidArgumentException(sprintf('Unexpected logging level: %s', $level));
+ }
+
+ if (self::$levels[$level] >= self::$levels[$this->level]) {
+ $this->writeLog($level, $message, $context);
+ }
+ }
+
+ /**
+ * Write a record to the log.
+ *
+ * @throws LogicException If neither a stream resource nor url is present
+ * @throws RuntimeException If the stream url cannot be opened
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ * @param array $context The log context
+ */
+ protected function writeLog($level, $message, array $context = [])
+ {
+ if (!is_resource($this->stream)) {
+ if (!isset($this->url)) {
+ throw new LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
+ }
+
+ $this->stream = fopen($this->url, 'a');
+ if (!is_resource($this->stream)) {
+ // @codeCoverageIgnoreStart
+ throw new RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url));
+ // @codeCoverageIgnoreEnd
+ }
+ }
+
+ fwrite($this->stream, self::formatLine($level, $message, $context));
+ }
+
+ /**
+ * Gets the name of the logging level.
+ *
+ * @throws InvalidArgumentException if the logging level is unknown
+ *
+ * @param int $level
+ *
+ * @return string
+ */
+ protected static function getLevelName($level)
+ {
+ return strtoupper($level);
+ }
+
+ /**
+ * Format a log line for output.
+ *
+ * @param int $level The logging level
+ * @param string $message The log message
+ * @param array $context The log context
+ *
+ * @return string
+ */
+ protected static function formatLine($level, $message, array $context = [])
+ {
+ return sprintf(
+ "%s: %s\n",
+ self::getLevelName($level),
+ self::interpolateMessage($message, $context)
+ );
+ }
+
+ /**
+ * Interpolate context values into the message placeholders.
+ *
+ * @param string $message
+ *
+ * @return string
+ */
+ protected static function interpolateMessage($message, array $context = [])
+ {
+ if (strpos($message, '{') === false) {
+ return $message;
+ }
+
+ // build a replacement array with braces around the context keys
+ $replace = [];
+ foreach ($context as $key => $val) {
+ $replace['{' . $key . '}'] = $val;
+ }
+
+ // interpolate replacement values into the the message and return
+ return strtr($message, $replace);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Parser.php b/vendor/mustache/mustache/src/Parser.php
new file mode 100644
index 0000000..c08dc10
--- /dev/null
+++ b/vendor/mustache/mustache/src/Parser.php
@@ -0,0 +1,392 @@
+lineNum = -1;
+ $this->lineTokens = 0;
+ $this->pragmas = $this->defaultPragmas;
+
+ $this->pragmaFilters = isset($this->pragmas[Engine::PRAGMA_FILTERS]);
+
+ return $this->buildTree($tokens);
+ }
+
+ /**
+ * Disable optional Mustache specs.
+ *
+ * @internal Users should set options in Mustache\Engine, not here :)
+ *
+ * @param bool[] $options
+ */
+ public function setOptions(array $options)
+ {
+ if (isset($options['dynamic_names'])) {
+ $this->dynamicNames = $options['dynamic_names'] !== false;
+ }
+
+ if (isset($options['inheritance'])) {
+ $this->inheritance = $options['inheritance'] !== false;
+ }
+ }
+
+ /**
+ * Enable pragmas across all templates, regardless of the presence of pragma
+ * tags in the individual templates.
+ *
+ * @internal Users should set global pragmas in Mustache\Engine, not here :)
+ *
+ * @param string[] $pragmas
+ */
+ public function setPragmas(array $pragmas)
+ {
+ $this->pragmas = [];
+ foreach ($pragmas as $pragma) {
+ $this->enablePragma($pragma);
+ }
+ $this->defaultPragmas = $this->pragmas;
+ }
+
+ /**
+ * Helper method for recursively building a parse tree.
+ *
+ * @throws SyntaxException when nesting errors or mismatched section tags are encountered
+ *
+ * @param array &$tokens Set of Mustache tokens
+ * @param array $parent Parent token (default: null)
+ *
+ * @return array Mustache Token parse tree
+ */
+ private function buildTree(array &$tokens, $parent = null)
+ {
+ $nodes = [];
+
+ while (!empty($tokens)) {
+ $token = array_shift($tokens);
+
+ if ($token[Tokenizer::LINE] === $this->lineNum) {
+ $this->lineTokens++;
+ } else {
+ $this->lineNum = $token[Tokenizer::LINE];
+ $this->lineTokens = 0;
+ }
+
+ if ($token[Tokenizer::TYPE] !== Tokenizer::T_COMMENT) {
+ if (isset($token[Tokenizer::NAME])) {
+ list($name, $isDynamic) = $this->getDynamicName($token);
+ if ($isDynamic) {
+ $token[Tokenizer::NAME] = $name;
+ $token[Tokenizer::DYNAMIC] = true;
+ }
+ }
+
+ if ($this->pragmaFilters && isset($token[Tokenizer::NAME])) {
+ list($name, $filters) = $this->getNameAndFilters($token[Tokenizer::NAME]);
+ if (!empty($filters)) {
+ $token[Tokenizer::NAME] = $name;
+ $token[Tokenizer::FILTERS] = $filters;
+ }
+ }
+ }
+
+ switch ($token[Tokenizer::TYPE]) {
+ case Tokenizer::T_DELIM_CHANGE:
+ $this->checkIfTokenIsAllowedInParent($parent, $token);
+ $this->clearStandaloneLines($nodes, $tokens);
+ break;
+
+ case Tokenizer::T_SECTION:
+ case Tokenizer::T_INVERTED:
+ $this->checkIfTokenIsAllowedInParent($parent, $token);
+ $this->clearStandaloneLines($nodes, $tokens);
+ $nodes[] = $this->buildTree($tokens, $token);
+ break;
+
+ case Tokenizer::T_END_SECTION:
+ if (!isset($parent)) {
+ $msg = sprintf(
+ 'Unexpected closing tag: /%s on line %d',
+ $token[Tokenizer::NAME],
+ $token[Tokenizer::LINE]
+ );
+ throw new SyntaxException($msg, $token);
+ }
+
+ $sameName = $token[Tokenizer::NAME] !== $parent[Tokenizer::NAME];
+ $tokenDynamic = isset($token[Tokenizer::DYNAMIC]) && $token[Tokenizer::DYNAMIC];
+ $parentDynamic = isset($parent[Tokenizer::DYNAMIC]) && $parent[Tokenizer::DYNAMIC];
+
+ if ($sameName || ($tokenDynamic !== $parentDynamic)) {
+ $msg = sprintf(
+ 'Nesting error: %s%s (on line %d) vs. %s%s (on line %d)',
+ $parentDynamic ? '*' : '',
+ $parent[Tokenizer::NAME],
+ $parent[Tokenizer::LINE],
+ $tokenDynamic ? '*' : '',
+ $token[Tokenizer::NAME],
+ $token[Tokenizer::LINE]
+ );
+ throw new SyntaxException($msg, $token);
+ }
+
+ $this->clearStandaloneLines($nodes, $tokens);
+ $parent[Tokenizer::END] = $token[Tokenizer::INDEX];
+ $parent[Tokenizer::NODES] = $nodes;
+
+ return $parent;
+
+ case Tokenizer::T_PARTIAL:
+ $this->checkIfTokenIsAllowedInParent($parent, $token);
+ //store the whitespace prefix for laters!
+ if ($indent = $this->clearStandaloneLines($nodes, $tokens)) {
+ $token[Tokenizer::INDENT] = $indent[Tokenizer::VALUE];
+ }
+ $nodes[] = $token;
+ break;
+
+ case Tokenizer::T_PARENT:
+ $this->checkIfTokenIsAllowedInParent($parent, $token);
+ $nodes[] = $this->buildTree($tokens, $token);
+ break;
+
+ case Tokenizer::T_BLOCK_VAR:
+ if ($this->inheritance) {
+ if (isset($parent) && $parent[Tokenizer::TYPE] === Tokenizer::T_PARENT) {
+ $token[Tokenizer::TYPE] = Tokenizer::T_BLOCK_ARG;
+ }
+ $this->clearStandaloneLines($nodes, $tokens);
+ $nodes[] = $this->buildTree($tokens, $token);
+ } else {
+ // pretend this was just a normal "escaped" token...
+ $token[Tokenizer::TYPE] = Tokenizer::T_ESCAPED;
+ // TODO: figure out how to figure out if there was a space after this dollar:
+ $token[Tokenizer::NAME] = '$' . $token[Tokenizer::NAME];
+ $nodes[] = $token;
+ }
+ break;
+
+ case Tokenizer::T_PRAGMA:
+ $this->enablePragma($token[Tokenizer::NAME]);
+ // no break
+
+ case Tokenizer::T_COMMENT:
+ $this->clearStandaloneLines($nodes, $tokens);
+ $nodes[] = $token;
+ break;
+
+ default:
+ $nodes[] = $token;
+ break;
+ }
+ }
+
+ if (isset($parent)) {
+ $msg = sprintf(
+ 'Missing closing tag: %s opened on line %d',
+ $parent[Tokenizer::NAME],
+ $parent[Tokenizer::LINE]
+ );
+ throw new SyntaxException($msg, $parent);
+ }
+
+ return $nodes;
+ }
+
+ /**
+ * Clear standalone line tokens.
+ *
+ * Returns a whitespace token for indenting partials, if applicable.
+ *
+ * @param array $nodes Parsed nodes
+ * @param array $tokens Tokens to be parsed
+ *
+ * @return array|null Resulting indent token, if any
+ */
+ private function clearStandaloneLines(array &$nodes, array &$tokens)
+ {
+ if ($this->lineTokens > 1) {
+ // this is the third or later node on this line, so it can't be standalone
+ return;
+ }
+
+ $prev = null;
+ if ($this->lineTokens === 1) {
+ // this is the second node on this line, so it can't be standalone
+ // unless the previous node is whitespace.
+ if ($prev = end($nodes)) {
+ if (!$this->tokenIsWhitespace($prev)) {
+ return;
+ }
+ }
+ }
+
+ if ($next = reset($tokens)) {
+ // If we're on a new line, bail.
+ if ($next[Tokenizer::LINE] !== $this->lineNum) {
+ return;
+ }
+
+ // If the next token isn't whitespace, bail.
+ if (!$this->tokenIsWhitespace($next)) {
+ return;
+ }
+
+ if (count($tokens) !== 1) {
+ // Unless it's the last token in the template, the next token
+ // must end in newline for this to be standalone.
+ if (substr($next[Tokenizer::VALUE], -1) !== "\n") {
+ return;
+ }
+ }
+
+ // Discard the whitespace suffix
+ array_shift($tokens);
+ }
+
+ if ($prev) {
+ // Return the whitespace prefix, if any
+ return array_pop($nodes);
+ }
+ }
+
+ /**
+ * Check whether token is a whitespace token.
+ *
+ * True if token type is T_TEXT and value is all whitespace characters.
+ *
+ * @return bool True if token is a whitespace token
+ */
+ private function tokenIsWhitespace(array $token)
+ {
+ if ($token[Tokenizer::TYPE] === Tokenizer::T_TEXT) {
+ return preg_match('/^\s*$/', $token[Tokenizer::VALUE]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Check whether a token is allowed inside a parent tag.
+ *
+ * @throws SyntaxException if an invalid token is found inside a parent tag
+ *
+ * @param array|null $parent
+ */
+ private function checkIfTokenIsAllowedInParent($parent, array $token)
+ {
+ if (isset($parent) && $parent[Tokenizer::TYPE] === Tokenizer::T_PARENT) {
+ throw new SyntaxException('Illegal content in < parent tag', $token);
+ }
+ }
+
+ /**
+ * Parse dynamic names.
+ *
+ * @throws SyntaxException when a tag does not allow *
+ * @throws SyntaxException on multiple *s, or dots or filters with *
+ */
+ private function getDynamicName(array $token)
+ {
+ $name = $token[Tokenizer::NAME];
+ $isDynamic = false;
+
+ if ($this->dynamicNames && preg_match('/^\s*\*\s*/', $name)) {
+ $this->ensureTagAllowsDynamicNames($token);
+ $name = preg_replace('/^\s*\*\s*/', '', $name);
+ $isDynamic = true;
+ }
+
+ return [$name, $isDynamic];
+ }
+
+ /**
+ * Check whether the given token supports dynamic tag names.
+ *
+ * @throws SyntaxException when a tag does not allow *
+ */
+ private function ensureTagAllowsDynamicNames(array $token)
+ {
+ switch ($token[Tokenizer::TYPE]) {
+ case Tokenizer::T_PARTIAL:
+ case Tokenizer::T_PARENT:
+ case Tokenizer::T_END_SECTION:
+ return;
+ }
+
+ $msg = sprintf(
+ 'Invalid dynamic name: %s in %s tag',
+ $token[Tokenizer::NAME],
+ Tokenizer::getTagName($token[Tokenizer::TYPE])
+ );
+
+ throw new SyntaxException($msg, $token);
+ }
+
+ /**
+ * Split a tag name into name and filters.
+ *
+ * @param string $name
+ *
+ * @return array [Tag name, Array of filters]
+ */
+ private function getNameAndFilters($name)
+ {
+ $filters = array_map('trim', explode('|', $name));
+ $name = array_shift($filters);
+
+ return [$name, $filters];
+ }
+
+ /**
+ * Enable a pragma.
+ *
+ * @param string $name
+ */
+ private function enablePragma($name)
+ {
+ $this->pragmas[$name] = true;
+
+ switch ($name) {
+ case Engine::PRAGMA_FILTERS:
+ $this->pragmaFilters = true;
+ break;
+ }
+ }
+}
diff --git a/vendor/mustache/mustache/src/RenderedString.php b/vendor/mustache/mustache/src/RenderedString.php
new file mode 100644
index 0000000..3c98911
--- /dev/null
+++ b/vendor/mustache/mustache/src/RenderedString.php
@@ -0,0 +1,51 @@
+value = (string) $value;
+ }
+
+ public function __toString()
+ {
+ return $this->value;
+ }
+
+ /**
+ * Get the rendered string value.
+ *
+ * @return string The rendered string value
+ */
+ public function getValue()
+ {
+ return $this->value;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Source.php b/vendor/mustache/mustache/src/Source.php
new file mode 100644
index 0000000..205d1cd
--- /dev/null
+++ b/vendor/mustache/mustache/src/Source.php
@@ -0,0 +1,39 @@
+fileName = $fileName;
+ $this->statProps = $statProps;
+ }
+
+ /**
+ * Get the Source key (used to generate the compiled class name).
+ *
+ * @throws RuntimeException when a source file cannot be read
+ *
+ * @return string
+ */
+ public function getKey()
+ {
+ $chunks = [
+ 'fileName' => $this->fileName,
+ ];
+
+ if (!empty($this->statProps)) {
+ if (!isset($this->stat)) {
+ $this->stat = @stat($this->fileName);
+ }
+
+ if ($this->stat === false) {
+ throw new RuntimeException(sprintf('Failed to read source file "%s".', $this->fileName));
+ }
+
+ foreach ($this->statProps as $prop) {
+ $chunks[$prop] = $this->stat[$prop];
+ }
+ }
+
+ return json_encode($chunks);
+ }
+
+ /**
+ * Get the template Source.
+ *
+ * @return string
+ */
+ public function getSource()
+ {
+ return file_get_contents($this->fileName);
+ }
+}
diff --git a/vendor/mustache/mustache/src/Template.php b/vendor/mustache/mustache/src/Template.php
new file mode 100644
index 0000000..2d9219f
--- /dev/null
+++ b/vendor/mustache/mustache/src/Template.php
@@ -0,0 +1,193 @@
+mustache = $mustache;
+ }
+
+ /**
+ * Mustache Template instances can be treated as a function and rendered by simply calling them.
+ *
+ * $m = new \Mustache\Engine;
+ * $tpl = $m->loadTemplate('Hello, {{ name }}!');
+ * echo $tpl(['name' => 'World']); // "Hello, World!"
+ *
+ * @see \Mustache\Template::render
+ *
+ * @param mixed $context Array or object rendering context (default: [])
+ *
+ * @return string Rendered template
+ */
+ public function __invoke($context = [])
+ {
+ return $this->render($context);
+ }
+
+ /**
+ * Render this template given the rendering context.
+ *
+ * @param mixed $context Array or object rendering context (default: [])
+ *
+ * @return string Rendered template
+ */
+ public function render($context = [])
+ {
+ return $this->renderInternal(
+ $this->prepareContextStack($context)
+ );
+ }
+
+ /**
+ * Internal rendering method implemented by Mustache Template concrete subclasses.
+ *
+ * This is where the magic happens :)
+ *
+ * NOTE: This method is not part of the Mustache.php public API.
+ *
+ * @param string $indent (default: '')
+ *
+ * @return string Rendered template
+ */
+ abstract public function renderInternal(Context $context, $indent = '');
+
+ /**
+ * Tests whether a value should be iterated over (e.g. in a section context).
+ *
+ * In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists
+ * should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript,
+ * Java, Python, etc.
+ *
+ * PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish
+ * between between a list of things (numeric, normalized array) and a set of variables to be used as section context
+ * (associative array). In other words, this will be iterated over:
+ *
+ * $items = [
+ * ['name' => 'foo'],
+ * ['name' => 'bar'],
+ * ['name' => 'baz'],
+ * ];
+ *
+ * ... but this will be used as a section context block:
+ *
+ * $items = [
+ * 1 => ['name' => 'foo'],
+ * 'banana' => ['name' => 'bar'],
+ * 42 => ['name' => 'baz'],
+ * ];
+ *
+ * @param mixed $value
+ *
+ * @return bool True if the value is 'iterable'
+ */
+ protected function isIterable($value)
+ {
+ switch (gettype($value)) {
+ case 'object':
+ return $value instanceof \Traversable;
+
+ case 'array':
+ $i = 0;
+ foreach ($value as $k => $v) {
+ if ($k !== $i++) {
+ return false;
+ }
+ }
+
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Helper method to prepare the Context stack.
+ *
+ * Adds the Mustache HelperCollection to the stack's top context frame if helpers are present.
+ *
+ * @param mixed $context Optional first context frame (default: null)
+ *
+ * @return Context
+ */
+ protected function prepareContextStack($context = null)
+ {
+ $stack = new Context(null, $this->mustache->getBuggyPropertyShadowing());
+
+ $helpers = $this->mustache->getHelpers();
+ if (!$helpers->isEmpty()) {
+ $stack->push($helpers);
+ }
+
+ if (!empty($context)) {
+ $stack->push($context);
+ }
+
+ return $stack;
+ }
+
+ /**
+ * Resolve a context value.
+ *
+ * Invoke the value if it is callable, otherwise return the value.
+ *
+ * @param mixed $value
+ *
+ * @return string
+ */
+ protected function resolveValue($value, Context $context)
+ {
+ if (!$this->lambdas) {
+ return $value;
+ }
+
+ if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) {
+ $result = call_user_func($value);
+
+ if (is_string($result)) {
+ return $this->mustache
+ ->loadLambda($result)
+ ->renderInternal($context);
+ }
+
+ return $result;
+ }
+
+ return $value;
+ }
+}
diff --git a/vendor/mustache/mustache/src/Tokenizer.php b/vendor/mustache/mustache/src/Tokenizer.php
new file mode 100644
index 0000000..4b4821c
--- /dev/null
+++ b/vendor/mustache/mustache/src/Tokenizer.php
@@ -0,0 +1,412 @@
+';
+ const T_PARENT = '<';
+ const T_DELIM_CHANGE = '=';
+ const T_ESCAPED = '_v';
+ const T_UNESCAPED = '{';
+ const T_UNESCAPED_2 = '&';
+ const T_TEXT = '_t';
+ const T_PRAGMA = '%';
+ const T_BLOCK_VAR = '$';
+ const T_BLOCK_ARG = '$arg';
+
+ // Valid token types
+ private static $tagTypes = [
+ self::T_SECTION => true,
+ self::T_INVERTED => true,
+ self::T_END_SECTION => true,
+ self::T_COMMENT => true,
+ self::T_PARTIAL => true,
+ self::T_PARENT => true,
+ self::T_DELIM_CHANGE => true,
+ self::T_ESCAPED => true,
+ self::T_UNESCAPED => true,
+ self::T_UNESCAPED_2 => true,
+ self::T_PRAGMA => true,
+ self::T_BLOCK_VAR => true,
+ ];
+
+ private static $tagNames = [
+ self::T_SECTION => 'section',
+ self::T_INVERTED => 'inverted section',
+ self::T_END_SECTION => 'section end',
+ self::T_COMMENT => 'comment',
+ self::T_PARTIAL => 'partial',
+ self::T_PARENT => 'parent',
+ self::T_DELIM_CHANGE => 'set delimiter',
+ self::T_ESCAPED => 'variable',
+ self::T_UNESCAPED => 'unescaped variable',
+ self::T_UNESCAPED_2 => 'unescaped variable',
+ self::T_PRAGMA => 'pragma',
+ self::T_BLOCK_VAR => 'block variable',
+ self::T_BLOCK_ARG => 'block variable',
+ ];
+
+ // Token properties
+ const TYPE = 'type';
+ const NAME = 'name';
+ const DYNAMIC = 'dynamic';
+ const OTAG = 'otag';
+ const CTAG = 'ctag';
+ const LINE = 'line';
+ const INDEX = 'index';
+ const END = 'end';
+ const INDENT = 'indent';
+ const NODES = 'nodes';
+ const VALUE = 'value';
+ const FILTERS = 'filters';
+
+ private $state;
+ private $tagType;
+ private $buffer;
+ private $tokens;
+ private $seenTag;
+ private $line;
+
+ private $otag;
+ private $otagChar;
+ private $otagLen;
+
+ private $ctag;
+ private $ctagChar;
+ private $ctagLen;
+
+ /**
+ * Scan and tokenize template source.
+ *
+ * @throws SyntaxException when mismatched section tags are encountered
+ * @throws InvalidArgumentException when $delimiters string is invalid
+ *
+ * @param string $text Mustache template source to tokenize
+ * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: empty string)
+ *
+ * @return array Set of Mustache tokens
+ */
+ public function scan($text, $delimiters = '')
+ {
+ // Setting mbstring.func_overload makes things *really* slow.
+ // Let's do everyone a favor and scan this string as ASCII instead.
+ //
+ // The INI directive was removed in PHP 8.0 so we don't need to check there (and can drop it
+ // when we remove support for older versions of PHP).
+ //
+ // @codeCoverageIgnoreStart
+ $encoding = null;
+ if (version_compare(PHP_VERSION, '8.0.0', '<')) {
+ if (function_exists('mb_internal_encoding') && ini_get('mbstring.func_overload') & 2) {
+ $encoding = mb_internal_encoding();
+ mb_internal_encoding('ASCII');
+ }
+ }
+ // @codeCoverageIgnoreEnd
+
+ $this->reset();
+
+ if (is_string($delimiters) && ($delimiters = trim($delimiters)) !== '') {
+ $this->setDelimiters($delimiters);
+ }
+
+ $len = strlen($text);
+ for ($i = 0; $i < $len; $i++) {
+ switch ($this->state) {
+ case self::IN_TEXT:
+ $char = $text[$i];
+ // Test whether it's time to change tags.
+ if ($char === $this->otagChar && substr($text, $i, $this->otagLen) === $this->otag) {
+ $i--;
+ $this->flushBuffer();
+ $this->state = self::IN_TAG_TYPE;
+ } else {
+ $this->buffer .= $char;
+ if ($char === "\n") {
+ $this->flushBuffer();
+ $this->line++;
+ }
+ }
+ break;
+
+ case self::IN_TAG_TYPE:
+ $i += $this->otagLen - 1;
+ $char = $text[$i + 1];
+ if (isset(self::$tagTypes[$char])) {
+ $tag = $char;
+ $this->tagType = $tag;
+ } else {
+ $tag = null;
+ $this->tagType = self::T_ESCAPED;
+ }
+
+ if ($this->tagType === self::T_DELIM_CHANGE) {
+ $i = $this->changeDelimiters($text, $i);
+ $this->state = self::IN_TEXT;
+ } elseif ($this->tagType === self::T_PRAGMA) {
+ $i = $this->addPragma($text, $i);
+ $this->state = self::IN_TEXT;
+ } else {
+ if ($tag !== null) {
+ $i++;
+ }
+ $this->state = self::IN_TAG;
+ }
+ $this->seenTag = $i;
+ break;
+
+ default:
+ $char = $text[$i];
+ // Test whether it's time to change tags.
+ if ($char === $this->ctagChar && substr($text, $i, $this->ctagLen) === $this->ctag) {
+ $token = [
+ self::TYPE => $this->tagType,
+ self::NAME => trim($this->buffer),
+ self::OTAG => $this->otag,
+ self::CTAG => $this->ctag,
+ self::LINE => $this->line,
+ self::INDEX => ($this->tagType === self::T_END_SECTION) ? $this->seenTag - $this->otagLen : $i + $this->ctagLen,
+ ];
+
+ if ($this->tagType === self::T_UNESCAPED) {
+ // Clean up `{{{ tripleStache }}}` style tokens.
+ if ($this->ctag === '}}') {
+ if (($i + 2 < $len) && $text[$i + 2] === '}') {
+ $i++;
+ } else {
+ $msg = sprintf(
+ 'Mismatched tag delimiters: %s on line %d',
+ $token[self::NAME],
+ $token[self::LINE]
+ );
+
+ throw new SyntaxException($msg, $token);
+ }
+ } else {
+ $lastName = $token[self::NAME];
+ if (substr($lastName, -1) === '}') {
+ $token[self::NAME] = trim(substr($lastName, 0, -1));
+ } else {
+ $msg = sprintf(
+ 'Mismatched tag delimiters: %s on line %d',
+ $token[self::NAME],
+ $token[self::LINE]
+ );
+
+ throw new SyntaxException($msg, $token);
+ }
+ }
+ }
+
+ $this->buffer = '';
+ $i += $this->ctagLen - 1;
+ $this->state = self::IN_TEXT;
+ $this->tokens[] = $token;
+ } else {
+ $this->buffer .= $char;
+ }
+ break;
+ }
+ }
+
+ if ($this->state !== self::IN_TEXT) {
+ $this->throwUnclosedTagException();
+ }
+
+ $this->flushBuffer();
+
+ // Restore the user's encoding...
+ // @codeCoverageIgnoreStart
+ if ($encoding) {
+ mb_internal_encoding($encoding);
+ }
+ // @codeCoverageIgnoreEnd
+
+ return $this->tokens;
+ }
+
+ /**
+ * Helper function to reset tokenizer internal state.
+ */
+ private function reset()
+ {
+ $this->state = self::IN_TEXT;
+ $this->tagType = null;
+ $this->buffer = '';
+ $this->tokens = [];
+ $this->seenTag = false;
+ $this->line = 0;
+
+ $this->otag = '{{';
+ $this->otagChar = '{';
+ $this->otagLen = 2;
+
+ $this->ctag = '}}';
+ $this->ctagChar = '}';
+ $this->ctagLen = 2;
+ }
+
+ /**
+ * Flush the current buffer to a token.
+ */
+ private function flushBuffer()
+ {
+ if (strlen($this->buffer) > 0) {
+ $this->tokens[] = [
+ self::TYPE => self::T_TEXT,
+ self::LINE => $this->line,
+ self::VALUE => $this->buffer,
+ ];
+ $this->buffer = '';
+ }
+ }
+
+ /**
+ * Change the current Mustache delimiters. Set new `otag` and `ctag` values.
+ *
+ * @throws SyntaxException when delimiter string is invalid
+ *
+ * @param string $text Mustache template source
+ * @param int $index Current tokenizer index
+ *
+ * @return int New index value
+ */
+ private function changeDelimiters($text, $index)
+ {
+ $startIndex = strpos($text, '=', $index) + 1;
+ $close = '=' . $this->ctag;
+ $closeIndex = strpos($text, $close, $index);
+
+ if ($closeIndex === false) {
+ $this->throwUnclosedTagException();
+ }
+
+ $token = [
+ self::TYPE => self::T_DELIM_CHANGE,
+ self::LINE => $this->line,
+ ];
+
+ try {
+ $this->setDelimiters(trim(substr($text, $startIndex, $closeIndex - $startIndex)));
+ } catch (InvalidArgumentException $e) {
+ throw new SyntaxException($e->getMessage(), $token);
+ }
+
+ $this->tokens[] = $token;
+
+ return $closeIndex + strlen($close) - 1;
+ }
+
+ /**
+ * Set the current Mustache `otag` and `ctag` delimiters.
+ *
+ * @throws InvalidArgumentException when delimiter string is invalid
+ *
+ * @param string $delimiters
+ */
+ private function setDelimiters($delimiters)
+ {
+ if (!preg_match('/^\s*(\S+)\s+(\S+)\s*$/', $delimiters, $matches)) {
+ throw new InvalidArgumentException(sprintf('Invalid delimiters: %s', $delimiters));
+ }
+
+ list($_, $otag, $ctag) = $matches;
+
+ $this->otag = $otag;
+ $this->otagChar = $otag[0];
+ $this->otagLen = strlen($otag);
+
+ $this->ctag = $ctag;
+ $this->ctagChar = $ctag[0];
+ $this->ctagLen = strlen($ctag);
+ }
+
+ /**
+ * Add pragma token.
+ *
+ * Pragmas are hoisted to the front of the template, so all pragma tokens
+ * will appear at the front of the token list.
+ *
+ * @param string $text
+ * @param int $index
+ *
+ * @return int New index value
+ */
+ private function addPragma($text, $index)
+ {
+ $end = strpos($text, $this->ctag, $index);
+ if ($end === false) {
+ $this->throwUnclosedTagException();
+ }
+
+ $pragma = trim(substr($text, $index + 2, $end - $index - 2));
+
+ // Pragmas are hoisted to the front of the template.
+ array_unshift($this->tokens, [
+ self::TYPE => self::T_PRAGMA,
+ self::NAME => $pragma,
+ self::LINE => 0,
+ ]);
+
+ return $end + $this->ctagLen - 1;
+ }
+
+ private function throwUnclosedTagException()
+ {
+ $name = trim($this->buffer);
+ if ($name !== '') {
+ $msg = sprintf('Unclosed tag: %s on line %d', $name, $this->line);
+ } else {
+ $msg = sprintf('Unclosed tag on line %d', $this->line);
+ }
+
+ throw new SyntaxException($msg, [
+ self::TYPE => $this->tagType,
+ self::NAME => $name,
+ self::OTAG => $this->otag,
+ self::CTAG => $this->ctag,
+ self::LINE => $this->line,
+ self::INDEX => $this->seenTag - $this->otagLen,
+ ]);
+ }
+
+ /**
+ * Get the human readable name for a tag type.
+ *
+ * @param string $tagType One of the tokenizer T_* constants
+ *
+ * @return string
+ */
+ public static function getTagName($tagType)
+ {
+ return isset(self::$tagNames[$tagType]) ? self::$tagNames[$tagType] : 'unknown';
+ }
+}
diff --git a/vendor/mustache/mustache/src/compat.php b/vendor/mustache/mustache/src/compat.php
new file mode 100644
index 0000000..99a2b24
--- /dev/null
+++ b/vendor/mustache/mustache/src/compat.php
@@ -0,0 +1,282 @@
+process($schema, $data);
+} catch (Nette\Schema\ValidationException $e) {
+ echo 'Data is invalid: ' . $e->getMessage();
+}
+```
+
+Method `$e->getMessages()` returns array of all message strings and `$e->getMessageObjects()` return all messages as [Nette\Schema\Message](https://api.nette.org/schema/master/Nette/Schema/Message.html) objects.
+
+
+Defining Schema
+---------------
+
+And now let's create a schema. The class [Nette\Schema\Expect](https://api.nette.org/schema/master/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int.
+
+```php
+use Nette\Schema\Expect;
+
+$schema = Expect::structure([
+ 'processRefund' => Expect::bool(),
+ 'refundAmount' => Expect::int(),
+]);
+```
+
+We believe that the schema definition looks clear, even if you see it for the very first time.
+
+Lets send the following data for validation:
+
+```php
+$data = [
+ 'processRefund' => true,
+ 'refundAmount' => 17,
+];
+
+$normalized = $processor->process($schema, $data); // OK, it passes
+```
+
+The output, i.e. the value `$normalized`, is the object `stdClass`. If we want the output to be an array, we add a cast to schema `Expect::structure([...])->castTo('array')`.
+
+All elements of the structure are optional and have a default value `null`. Example:
+
+```php
+$data = [
+ 'refundAmount' => 17,
+];
+
+$normalized = $processor->process($schema, $data); // OK, it passes
+// $normalized = {'processRefund' => null, 'refundAmount' => 17}
+```
+
+The fact that the default value is `null` does not mean that it would be accepted in the input data `'processRefund' => null`. No, the input must be boolean, i.e. only `true` or `false`. We would have to explicitly allow `null` via `Expect::bool()->nullable()`.
+
+An item can be made mandatory using `Expect::bool()->required()`. We change the default value to `false` using `Expect::bool()->default(false)` or shortly using `Expect::bool(false)`.
+
+And what if we wanted to accept `1` and `0` besides booleans? Then we list the allowed values, which we will also normalize to boolean:
+
+```php
+$schema = Expect::structure([
+ 'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
+ 'refundAmount' => Expect::int(),
+]);
+
+$normalized = $processor->process($schema, $data);
+is_bool($normalized->processRefund); // true
+```
+
+Now you know the basics of how the schema is defined and how the individual elements of the structure behave. We will now show what all the other elements can be used in defining a schema.
+
+
+Data Types: type()
+------------------
+
+All standard PHP data types can be listed in the schema:
+
+```php
+Expect::string($default = null)
+Expect::int($default = null)
+Expect::float($default = null)
+Expect::bool($default = null)
+Expect::null()
+Expect::array($default = [])
+```
+
+And then all types [supported by the Validators](https://doc.nette.org/validators#toc-validation-rules) via `Expect::type('scalar')` or abbreviated `Expect::scalar()`. Also class or interface names are accepted, e.g. `Expect::type('AddressEntity')`.
+
+You can also use union notation:
+
+```php
+Expect::type('bool|string|array')
+```
+
+The default value is always `null` except for `array` and `list`, where it is an empty array. (A list is an array indexed in ascending order of numeric keys from zero, that is, a non-associative array).
+
+
+Array of Values: arrayOf() listOf()
+-----------------------------------
+
+The array is too general structure, it is more useful to specify exactly what elements it can contain. For example, an array whose elements can only be strings:
+
+```php
+$schema = Expect::arrayOf('string');
+
+$processor->process($schema, ['hello', 'world']); // OK
+$processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
+$processor->process($schema, ['key' => 123]); // ERROR: 123 is not a string
+```
+
+The second parameter can be used to specify keys (since version 1.2):
+
+```php
+$schema = Expect::arrayOf('string', 'int');
+
+$processor->process($schema, ['hello', 'world']); // OK
+$processor->process($schema, ['a' => 'hello']); // ERROR: 'a' is not int
+```
+
+The list is an indexed array:
+
+```php
+$schema = Expect::listOf('string');
+
+$processor->process($schema, ['a', 'b']); // OK
+$processor->process($schema, ['a', 123]); // ERROR: 123 is not a string
+$processor->process($schema, ['key' => 'a']); // ERROR: is not a list
+$processor->process($schema, [1 => 'a', 0 => 'b']); // ERROR: is not a list
+```
+
+The parameter can also be a schema, so we can write:
+
+```php
+Expect::arrayOf(Expect::bool())
+```
+
+The default value is an empty array. If you specify a default value, it will be merged with the passed data. This can be disabled using `mergeDefaults(false)`.
+
+
+Enumeration: anyOf()
+--------------------
+
+`anyOf()` is a set of values or schemas that a value can be. Here's how to write an array of elements that can be either `'a'`, `true`, or `null`:
+
+```php
+$schema = Expect::listOf(
+ Expect::anyOf('a', true, null),
+);
+
+$processor->process($schema, ['a', true, null, 'a']); // OK
+$processor->process($schema, ['a', false]); // ERROR: false does not belong there
+```
+
+The enumeration elements can also be schemas:
+
+```php
+$schema = Expect::listOf(
+ Expect::anyOf(Expect::string(), true, null),
+);
+
+$processor->process($schema, ['foo', true, null, 'bar']); // OK
+$processor->process($schema, [123]); // ERROR
+```
+
+The `anyOf()` method accepts variants as individual parameters, not as array. To pass it an array of values, use the unpacking operator `anyOf(...$variants)`.
+
+The default value is `null`. Use the `firstIsDefault()` method to make the first element the default:
+
+```php
+// default is 'hello'
+Expect::anyOf(Expect::string('hello'), true, null)->firstIsDefault();
+```
+
+
+Structures
+----------
+
+Structures are objects with defined keys. Each of these key => value pairs is referred to as a "property":
+
+Structures accept arrays and objects and return objects `stdClass` (unless you change it with `castTo('array')`, etc.).
+
+By default, all properties are optional and have a default value of `null`. You can define mandatory properties using `required()`:
+
+```php
+$schema = Expect::structure([
+ 'required' => Expect::string()->required(),
+ 'optional' => Expect::string(), // the default value is null
+]);
+
+$processor->process($schema, ['optional' => '']);
+// ERROR: option 'required' is missing
+
+$processor->process($schema, ['required' => 'foo']);
+// OK, returns {'required' => 'foo', 'optional' => null}
+```
+
+If you do not want to output properties with only a default value, use `skipDefaults()`:
+
+```php
+$schema = Expect::structure([
+ 'required' => Expect::string()->required(),
+ 'optional' => Expect::string(),
+])->skipDefaults();
+
+$processor->process($schema, ['required' => 'foo']);
+// OK, returns {'required' => 'foo'}
+```
+
+Although `null` is the default value of the `optional` property, it is not allowed in the input data (the value must be a string). Properties accepting `null` are defined using `nullable()`:
+
+```php
+$schema = Expect::structure([
+ 'optional' => Expect::string(),
+ 'nullable' => Expect::string()->nullable(),
+]);
+
+$processor->process($schema, ['optional' => null]);
+// ERROR: 'optional' expects to be string, null given.
+
+$processor->process($schema, ['nullable' => null]);
+// OK, returns {'optional' => null, 'nullable' => null}
+```
+
+By default, there can be no extra items in the input data:
+
+```php
+$schema = Expect::structure([
+ 'key' => Expect::string(),
+]);
+
+$processor->process($schema, ['additional' => 1]);
+// ERROR: Unexpected item 'additional'
+```
+
+Which we can change with `otherItems()`. As a parameter, we will specify the schema for each extra element:
+
+```php
+$schema = Expect::structure([
+ 'key' => Expect::string(),
+])->otherItems(Expect::int());
+
+$processor->process($schema, ['additional' => 1]); // OK
+$processor->process($schema, ['additional' => true]); // ERROR
+```
+
+
+Deprecations
+------------
+
+You can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor->getWarnings()`:
+
+```php
+$schema = Expect::structure([
+ 'old' => Expect::int()->deprecated('The item %path% is deprecated'),
+]);
+
+$processor->process($schema, ['old' => 1]); // OK
+$processor->getWarnings(); // ["The item 'old' is deprecated"]
+```
+
+
+Ranges: min() max()
+-------------------
+
+Use `min()` and `max()` to limit the number of elements for arrays:
+
+```php
+// array, at least 10 items, maximum 20 items
+Expect::array()->min(10)->max(20);
+```
+
+For strings, limit their length:
+
+```php
+// string, at least 10 characters long, maximum 20 characters
+Expect::string()->min(10)->max(20);
+```
+
+For numbers, limit their value:
+
+```php
+// integer, between 10 and 20 inclusive
+Expect::int()->min(10)->max(20);
+```
+
+Of course, it is possible to mention only `min()`, or only `max()`:
+
+```php
+// string, maximum 20 characters
+Expect::string()->max(20);
+```
+
+
+Regular Expressions: pattern()
+------------------------------
+
+Using `pattern()`, you can specify a regular expression which the **whole** input string must match (i.e. as if it were wrapped in characters `^` a `$`):
+
+```php
+// just 9 digits
+Expect::string()->pattern('\d{9}');
+```
+
+
+Custom Assertions: assert()
+---------------------------
+
+You can add any other restrictions using `assert(callable $fn)`.
+
+```php
+$countIsEven = fn($v) => count($v) % 2 === 0;
+
+$schema = Expect::arrayOf('string')
+ ->assert($countIsEven); // the count must be even
+
+$processor->process($schema, ['a', 'b']); // OK
+$processor->process($schema, ['a', 'b', 'c']); // ERROR: 3 is not even
+```
+
+Or
+
+```php
+Expect::string()->assert('is_file'); // the file must exist
+```
+
+You can add your own description for each assertion. It will be part of the error message.
+
+```php
+$schema = Expect::arrayOf('string')
+ ->assert($countIsEven, 'Even items in array');
+
+$processor->process($schema, ['a', 'b', 'c']);
+// Failed assertion "Even items in array" for item with value array.
+```
+
+The method can be called repeatedly to add multiple constraints. It can be intermixed with calls to `transform()` and `castTo()`.
+
+
+Transformation: transform()
+---------------------------
+
+Successfully validated data can be modified using a custom function:
+
+```php
+// conversion to uppercase:
+Expect::string()->transform(fn(string $s) => strtoupper($s));
+```
+
+The method can be called repeatedly to add multiple transformations. It can be intermixed with calls to `assert()` and `castTo()`. The operations will be executed in the order in which they are declared:
+
+```php
+Expect::type('string|int')
+ ->castTo('string')
+ ->assert('ctype_lower', 'All characters must be lowercased')
+ ->transform(fn(string $s) => strtoupper($s)); // conversion to uppercase
+```
+
+The `transform()` method can both transform and validate the value simultaneously. This is often simpler and less redundant than chaining `transform()` and `assert()`. For this purpose, the function receives a [Nette\Schema\Context](https://api.nette.org/schema/master/Nette/Schema/Context.html) object with an `addError()` method, which can be used to add information about validation issues:
+
+```php
+Expect::string()
+ ->transform(function (string $s, Nette\Schema\Context $context) {
+ if (!ctype_lower($s)) {
+ $context->addError('All characters must be lowercased', 'my.case.error');
+ return null;
+ }
+
+ return strtoupper($s);
+ });
+```
+
+
+Casting: castTo()
+-----------------
+
+Successfully validated data can be cast:
+
+```php
+Expect::scalar()->castTo('string');
+```
+
+In addition to native PHP types, you can also cast to classes. It distinguishes whether it is a simple class without a constructor or a class with a constructor. If the class has no constructor, an instance of it is created and all elements of the structure are written to its properties:
+
+```php
+class Info
+{
+ public bool $processRefund;
+ public int $refundAmount;
+}
+
+Expect::structure([
+ 'processRefund' => Expect::bool(),
+ 'refundAmount' => Expect::int(),
+])->castTo(Info::class);
+
+// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount
+```
+
+If the class has a constructor, the elements of the structure are passed as named parameters to the constructor:
+
+```php
+class Info
+{
+ public function __construct(
+ public bool $processRefund,
+ public int $refundAmount,
+ ) {
+ }
+}
+
+// creates $obj = new Info(processRefund: ..., refundAmount: ...)
+```
+
+Casting combined with a scalar parameter creates an object and passes the value as the sole parameter to the constructor:
+
+```php
+Expect::string()->castTo(DateTime::class);
+// creates new DateTime(...)
+```
+
+
+Normalization: before()
+-----------------------
+
+Prior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`:
+
+```php
+$explode = fn($v) => explode(' ', $v);
+
+$schema = Expect::arrayOf('string')
+ ->before($explode);
+
+$normalized = $processor->process($schema, 'a b c');
+// OK, returns ['a', 'b', 'c']
+```
+
+
+Mapping to Objects: from()
+--------------------------
+
+You can generate structure schema from the class. Example:
+
+```php
+class Config
+{
+ /** @var string */
+ public $name;
+ /** @var string|null */
+ public $password;
+ /** @var bool */
+ public $admin = false;
+}
+
+$schema = Expect::from(new Config);
+
+$data = [
+ 'name' => 'jeff',
+];
+
+$normalized = $processor->process($schema, $data);
+// $normalized instanceof Config
+// $normalized = {'name' => 'jeff', 'password' => null, 'admin' => false}
+```
+
+If you are using PHP 7.4 or higher, you can use native types:
+
+```php
+class Config
+{
+ public string $name;
+ public ?string $password;
+ public bool $admin = false;
+}
+
+$schema = Expect::from(new Config);
+```
+
+Anonymous classes are also supported:
+
+```php
+$schema = Expect::from(new class {
+ public string $name;
+ public ?string $password;
+ public bool $admin = false;
+});
+```
+
+Because the information obtained from the class definition may not be sufficient, you can add a custom schema for the elements with the second parameter:
+
+```php
+$schema = Expect::from(new Config, [
+ 'name' => Expect::string()->pattern('\w:.*'),
+]);
+```
diff --git a/vendor/nette/schema/src/Schema/Context.php b/vendor/nette/schema/src/Schema/Context.php
new file mode 100644
index 0000000..2c64cde
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Context.php
@@ -0,0 +1,53 @@
+isKey;
+ return $this->errors[] = new Message($message, $code, $this->path, $variables);
+ }
+
+
+ public function addWarning(string $message, string $code, array $variables = []): Message
+ {
+ return $this->warnings[] = new Message($message, $code, $this->path, $variables);
+ }
+
+
+ /** @return \Closure(): bool */
+ public function createChecker(): \Closure
+ {
+ $count = count($this->errors);
+ return fn(): bool => $count === count($this->errors);
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/DynamicParameter.php b/vendor/nette/schema/src/Schema/DynamicParameter.php
new file mode 100644
index 0000000..8dd6105
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/DynamicParameter.php
@@ -0,0 +1,15 @@
+set = $set;
+ }
+
+
+ public function firstIsDefault(): self
+ {
+ $this->default = $this->set[0];
+ return $this;
+ }
+
+
+ public function nullable(): self
+ {
+ $this->set[] = null;
+ return $this;
+ }
+
+
+ public function dynamic(): self
+ {
+ $this->set[] = new Type(Nette\Schema\DynamicParameter::class);
+ return $this;
+ }
+
+
+ /********************* processing ****************d*g**/
+
+
+ public function normalize(mixed $value, Context $context): mixed
+ {
+ return $this->doNormalize($value, $context);
+ }
+
+
+ public function merge(mixed $value, mixed $base): mixed
+ {
+ if (is_array($value) && isset($value[Helpers::PreventMerging])) {
+ unset($value[Helpers::PreventMerging]);
+ return $value;
+ }
+
+ return Helpers::merge($value, $base);
+ }
+
+
+ public function complete(mixed $value, Context $context): mixed
+ {
+ $isOk = $context->createChecker();
+ $value = $this->findAlternative($value, $context);
+ $isOk() && $value = $this->doTransform($value, $context);
+ return $isOk() ? $value : null;
+ }
+
+
+ private function findAlternative(mixed $value, Context $context): mixed
+ {
+ $expecteds = $innerErrors = [];
+ foreach ($this->set as $item) {
+ if ($item instanceof Schema) {
+ $dolly = new Context;
+ $dolly->path = $context->path;
+ $res = $item->complete($item->normalize($value, $dolly), $dolly);
+ if (!$dolly->errors) {
+ $context->warnings = array_merge($context->warnings, $dolly->warnings);
+ return $res;
+ }
+
+ foreach ($dolly->errors as $error) {
+ if ($error->path !== $context->path || empty($error->variables['expected'])) {
+ $innerErrors[] = $error;
+ } else {
+ $expecteds[] = $error->variables['expected'];
+ }
+ }
+ } else {
+ if ($item === $value) {
+ return $value;
+ }
+
+ $expecteds[] = Nette\Schema\Helpers::formatValue($item);
+ }
+ }
+
+ if ($innerErrors) {
+ $context->errors = array_merge($context->errors, $innerErrors);
+ } else {
+ $context->addError(
+ 'The %label% %path% expects to be %expected%, %value% given.',
+ Nette\Schema\Message::TypeMismatch,
+ [
+ 'value' => $value,
+ 'expected' => implode('|', array_unique($expecteds)),
+ ],
+ );
+ }
+
+ return null;
+ }
+
+
+ public function completeDefault(Context $context): mixed
+ {
+ if ($this->required) {
+ $context->addError(
+ 'The mandatory item %path% is missing.',
+ Nette\Schema\Message::MissingItem,
+ );
+ return null;
+ }
+
+ if ($this->default instanceof Schema) {
+ return $this->default->completeDefault($context);
+ }
+
+ return $this->default;
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Elements/Base.php b/vendor/nette/schema/src/Schema/Elements/Base.php
new file mode 100644
index 0000000..93694a6
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Elements/Base.php
@@ -0,0 +1,163 @@
+default = $value;
+ return $this;
+ }
+
+
+ public function required(bool $state = true): self
+ {
+ $this->required = $state;
+ return $this;
+ }
+
+
+ public function before(callable $handler): self
+ {
+ $this->before = $handler;
+ return $this;
+ }
+
+
+ public function castTo(string $type): self
+ {
+ return $this->transform(Helpers::getCastStrategy($type));
+ }
+
+
+ public function transform(callable $handler): self
+ {
+ $this->transforms[] = $handler;
+ return $this;
+ }
+
+
+ public function assert(callable $handler, ?string $description = null): self
+ {
+ $expected = $description ?: (is_string($handler) ? "$handler()" : '#' . count($this->transforms));
+ return $this->transform(function ($value, Context $context) use ($handler, $description, $expected) {
+ if ($handler($value)) {
+ return $value;
+ }
+ $context->addError(
+ 'Failed assertion ' . ($description ? "'%assertion%'" : '%assertion%') . ' for %label% %path% with value %value%.',
+ Nette\Schema\Message::FailedAssertion,
+ ['value' => $value, 'assertion' => $expected],
+ );
+ });
+ }
+
+
+ /** Marks as deprecated */
+ public function deprecated(string $message = 'The item %path% is deprecated.'): self
+ {
+ $this->deprecated = $message;
+ return $this;
+ }
+
+
+ public function completeDefault(Context $context): mixed
+ {
+ if ($this->required) {
+ $context->addError(
+ 'The mandatory item %path% is missing.',
+ Nette\Schema\Message::MissingItem,
+ );
+ return null;
+ }
+
+ return $this->default;
+ }
+
+
+ public function doNormalize(mixed $value, Context $context): mixed
+ {
+ if ($this->before) {
+ $value = ($this->before)($value);
+ }
+
+ return $value;
+ }
+
+
+ private function doDeprecation(Context $context): void
+ {
+ if ($this->deprecated !== null) {
+ $context->addWarning(
+ $this->deprecated,
+ Nette\Schema\Message::Deprecated,
+ );
+ }
+ }
+
+
+ private function doTransform(mixed $value, Context $context): mixed
+ {
+ $isOk = $context->createChecker();
+ foreach ($this->transforms as $handler) {
+ $value = $handler($value, $context);
+ if (!$isOk()) {
+ return null;
+ }
+ }
+ return $value;
+ }
+
+
+ /** @deprecated use Nette\Schema\Validators::validateType() */
+ private function doValidate(mixed $value, string $expected, Context $context): bool
+ {
+ $isOk = $context->createChecker();
+ Helpers::validateType($value, $expected, $context);
+ return $isOk();
+ }
+
+
+ /** @deprecated use Nette\Schema\Validators::validateRange() */
+ private static function doValidateRange(mixed $value, array $range, Context $context, string $types = ''): bool
+ {
+ $isOk = $context->createChecker();
+ Helpers::validateRange($value, $range, $context, $types);
+ return $isOk();
+ }
+
+
+ /** @deprecated use doTransform() */
+ private function doFinalize(mixed $value, Context $context): mixed
+ {
+ return $this->doTransform($value, $context);
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Elements/Structure.php b/vendor/nette/schema/src/Schema/Elements/Structure.php
new file mode 100644
index 0000000..0862d33
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Elements/Structure.php
@@ -0,0 +1,211 @@
+items = $shape;
+ $this->castTo('object');
+ $this->required = true;
+ }
+
+
+ public function default(mixed $value): self
+ {
+ throw new Nette\InvalidStateException('Structure cannot have default value.');
+ }
+
+
+ public function min(?int $min): self
+ {
+ $this->range[0] = $min;
+ return $this;
+ }
+
+
+ public function max(?int $max): self
+ {
+ $this->range[1] = $max;
+ return $this;
+ }
+
+
+ public function otherItems(string|Schema $type = 'mixed'): self
+ {
+ $this->otherItems = $type instanceof Schema ? $type : new Type($type);
+ return $this;
+ }
+
+
+ public function skipDefaults(bool $state = true): self
+ {
+ $this->skipDefaults = $state;
+ return $this;
+ }
+
+
+ public function extend(array|self $shape): self
+ {
+ $shape = $shape instanceof self ? $shape->items : $shape;
+ return new self(array_merge($this->items, $shape));
+ }
+
+
+ public function getShape(): array
+ {
+ return $this->items;
+ }
+
+
+ /********************* processing ****************d*g**/
+
+
+ public function normalize(mixed $value, Context $context): mixed
+ {
+ if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
+ unset($value[Helpers::PreventMerging]);
+ }
+
+ $value = $this->doNormalize($value, $context);
+ if (is_object($value)) {
+ $value = (array) $value;
+ }
+
+ if (is_array($value)) {
+ foreach ($value as $key => $val) {
+ $itemSchema = $this->items[$key] ?? $this->otherItems;
+ if ($itemSchema) {
+ $context->path[] = $key;
+ $value[$key] = $itemSchema->normalize($val, $context);
+ array_pop($context->path);
+ }
+ }
+
+ if ($prevent) {
+ $value[Helpers::PreventMerging] = true;
+ }
+ }
+
+ return $value;
+ }
+
+
+ public function merge(mixed $value, mixed $base): mixed
+ {
+ if (is_array($value) && isset($value[Helpers::PreventMerging])) {
+ unset($value[Helpers::PreventMerging]);
+ $base = null;
+ }
+
+ if (is_array($value) && is_array($base)) {
+ $index = $this->otherItems === null ? null : 0;
+ foreach ($value as $key => $val) {
+ if ($key === $index) {
+ $base[] = $val;
+ $index++;
+ } else {
+ $base[$key] = array_key_exists($key, $base) && ($itemSchema = $this->items[$key] ?? $this->otherItems)
+ ? $itemSchema->merge($val, $base[$key])
+ : $val;
+ }
+ }
+
+ return $base;
+ }
+
+ return $value ?? $base;
+ }
+
+
+ public function complete(mixed $value, Context $context): mixed
+ {
+ if ($value === null) {
+ $value = []; // is unable to distinguish null from array in NEON
+ }
+
+ $this->doDeprecation($context);
+
+ $isOk = $context->createChecker();
+ Helpers::validateType($value, 'array', $context);
+ $isOk() && Helpers::validateRange($value, $this->range, $context);
+ $isOk() && $this->validateItems($value, $context);
+ $isOk() && $value = $this->doTransform($value, $context);
+ return $isOk() ? $value : null;
+ }
+
+
+ private function validateItems(array &$value, Context $context): void
+ {
+ $items = $this->items;
+ if ($extraKeys = array_keys(array_diff_key($value, $items))) {
+ if ($this->otherItems) {
+ $items += array_fill_keys($extraKeys, $this->otherItems);
+ } else {
+ $keys = array_map('strval', array_keys($items));
+ foreach ($extraKeys as $key) {
+ $hint = Nette\Utils\Helpers::getSuggestion($keys, (string) $key);
+ $context->addError(
+ 'Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'),
+ Nette\Schema\Message::UnexpectedItem,
+ ['hint' => $hint],
+ )->path[] = $key;
+ }
+ }
+ }
+
+ foreach ($items as $itemKey => $itemVal) {
+ $context->path[] = $itemKey;
+ if (array_key_exists($itemKey, $value)) {
+ $value[$itemKey] = $itemVal->complete($value[$itemKey], $context);
+ } else {
+ $default = $itemVal->completeDefault($context); // checks required item
+ if (!$context->skipDefaults && !$this->skipDefaults) {
+ $value[$itemKey] = $default;
+ }
+ }
+
+ array_pop($context->path);
+ }
+ }
+
+
+ public function completeDefault(Context $context): mixed
+ {
+ return $this->required
+ ? $this->complete([], $context)
+ : null;
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Elements/Type.php b/vendor/nette/schema/src/Schema/Elements/Type.php
new file mode 100644
index 0000000..cd02c75
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Elements/Type.php
@@ -0,0 +1,209 @@
+ [], 'array' => []];
+ $this->type = $type;
+ $this->default = strpos($type, '[]') ? [] : $defaults[$type] ?? null;
+ }
+
+
+ public function nullable(): self
+ {
+ $this->type = 'null|' . $this->type;
+ return $this;
+ }
+
+
+ public function mergeDefaults(bool $state = true): self
+ {
+ $this->merge = $state;
+ return $this;
+ }
+
+
+ public function dynamic(): self
+ {
+ $this->type = DynamicParameter::class . '|' . $this->type;
+ return $this;
+ }
+
+
+ public function min(?float $min): self
+ {
+ $this->range[0] = $min;
+ return $this;
+ }
+
+
+ public function max(?float $max): self
+ {
+ $this->range[1] = $max;
+ return $this;
+ }
+
+
+ /**
+ * @internal use arrayOf() or listOf()
+ */
+ public function items(string|Schema $valueType = 'mixed', string|Schema|null $keyType = null): self
+ {
+ $this->itemsValue = $valueType instanceof Schema
+ ? $valueType
+ : new self($valueType);
+ $this->itemsKey = $keyType instanceof Schema || $keyType === null
+ ? $keyType
+ : new self($keyType);
+ return $this;
+ }
+
+
+ public function pattern(?string $pattern): self
+ {
+ $this->pattern = $pattern;
+ return $this;
+ }
+
+
+ /********************* processing ****************d*g**/
+
+
+ public function normalize(mixed $value, Context $context): mixed
+ {
+ if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
+ unset($value[Helpers::PreventMerging]);
+ }
+
+ $value = $this->doNormalize($value, $context);
+ if (is_array($value) && $this->itemsValue) {
+ $res = [];
+ foreach ($value as $key => $val) {
+ $context->path[] = $key;
+ $context->isKey = true;
+ $key = $this->itemsKey
+ ? $this->itemsKey->normalize($key, $context)
+ : $key;
+ $context->isKey = false;
+ $res[$key] = $this->itemsValue->normalize($val, $context);
+ array_pop($context->path);
+ }
+
+ $value = $res;
+ }
+
+ if ($prevent && is_array($value)) {
+ $value[Helpers::PreventMerging] = true;
+ }
+
+ return $value;
+ }
+
+
+ public function merge(mixed $value, mixed $base): mixed
+ {
+ if (is_array($value) && isset($value[Helpers::PreventMerging])) {
+ unset($value[Helpers::PreventMerging]);
+ return $value;
+ }
+
+ if (is_array($value) && is_array($base) && $this->itemsValue) {
+ $index = 0;
+ foreach ($value as $key => $val) {
+ if ($key === $index) {
+ $base[] = $val;
+ $index++;
+ } else {
+ $base[$key] = array_key_exists($key, $base)
+ ? $this->itemsValue->merge($val, $base[$key])
+ : $val;
+ }
+ }
+
+ return $base;
+ }
+
+ return Helpers::merge($value, $base);
+ }
+
+
+ public function complete(mixed $value, Context $context): mixed
+ {
+ $merge = $this->merge;
+ if (is_array($value) && isset($value[Helpers::PreventMerging])) {
+ unset($value[Helpers::PreventMerging]);
+ $merge = false;
+ }
+
+ if ($value === null && is_array($this->default)) {
+ $value = []; // is unable to distinguish null from array in NEON
+ }
+
+ $this->doDeprecation($context);
+
+ $isOk = $context->createChecker();
+ Helpers::validateType($value, $this->type, $context);
+ $isOk() && Helpers::validateRange($value, $this->range, $context, $this->type);
+ $isOk() && $value !== null && $this->pattern !== null && Helpers::validatePattern($value, $this->pattern, $context);
+ $isOk() && is_array($value) && $this->validateItems($value, $context);
+ $isOk() && $merge && $value = Helpers::merge($value, $this->default);
+ $isOk() && $value = $this->doTransform($value, $context);
+ if (!$isOk()) {
+ return null;
+ }
+
+ if ($value instanceof DynamicParameter) {
+ $expected = $this->type . ($this->range === [null, null] ? '' : ':' . implode('..', $this->range));
+ $context->dynamics[] = [$value, str_replace(DynamicParameter::class . '|', '', $expected), $context->path];
+ }
+ return $value;
+ }
+
+
+ private function validateItems(array &$value, Context $context): void
+ {
+ if (!$this->itemsValue) {
+ return;
+ }
+
+ $res = [];
+ foreach ($value as $key => $val) {
+ $context->path[] = $key;
+ $context->isKey = true;
+ $key = $this->itemsKey ? $this->itemsKey->complete($key, $context) : $key;
+ $context->isKey = false;
+ $res[$key ?? ''] = $this->itemsValue->complete($val, $context);
+ array_pop($context->path);
+ }
+ $value = $res;
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Expect.php b/vendor/nette/schema/src/Schema/Expect.php
new file mode 100644
index 0000000..82345b4
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Expect.php
@@ -0,0 +1,119 @@
+default($args[0]);
+ }
+
+ return $type;
+ }
+
+
+ public static function type(string $type): Type
+ {
+ return new Type($type);
+ }
+
+
+ public static function anyOf(mixed ...$set): AnyOf
+ {
+ return new AnyOf(...$set);
+ }
+
+
+ /**
+ * @param Schema[] $shape
+ */
+ public static function structure(array $shape): Structure
+ {
+ return new Structure($shape);
+ }
+
+
+ public static function from(object $object, array $items = []): Structure
+ {
+ $ro = new \ReflectionObject($object);
+ $props = $ro->hasMethod('__construct')
+ ? $ro->getMethod('__construct')->getParameters()
+ : $ro->getProperties();
+
+ foreach ($props as $prop) {
+ $item = &$items[$prop->getName()];
+ if (!$item) {
+ $type = Helpers::getPropertyType($prop) ?? 'mixed';
+ $item = new Type($type);
+ if ($prop instanceof \ReflectionProperty ? $prop->isInitialized($object) : $prop->isOptional()) {
+ $def = ($prop instanceof \ReflectionProperty ? $prop->getValue($object) : $prop->getDefaultValue());
+ if (is_object($def)) {
+ $item = static::from($def);
+ } elseif ($def === null && !Nette\Utils\Validators::is(null, $type)) {
+ $item->required();
+ } else {
+ $item->default($def);
+ }
+ } else {
+ $item->required();
+ }
+ }
+ }
+
+ return (new Structure($items))->castTo($ro->getName());
+ }
+
+
+ /**
+ * @param mixed[] $shape
+ */
+ public static function array(?array $shape = []): Structure|Type
+ {
+ return Nette\Utils\Arrays::first($shape ?? []) instanceof Schema
+ ? (new Structure($shape))->castTo('array')
+ : (new Type('array'))->default($shape);
+ }
+
+
+ public static function arrayOf(string|Schema $valueType, string|Schema|null $keyType = null): Type
+ {
+ return (new Type('array'))->items($valueType, $keyType);
+ }
+
+
+ public static function listOf(string|Schema $type): Type
+ {
+ return (new Type('list'))->items($type);
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Helpers.php b/vendor/nette/schema/src/Schema/Helpers.php
new file mode 100644
index 0000000..170d3d4
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Helpers.php
@@ -0,0 +1,184 @@
+ $val) {
+ if ($key === $index) {
+ $base[] = $val;
+ $index++;
+ } else {
+ $base[$key] = static::merge($val, $base[$key] ?? null);
+ }
+ }
+
+ return $base;
+
+ } elseif ($value === null && is_array($base)) {
+ return $base;
+
+ } else {
+ return $value;
+ }
+ }
+
+
+ public static function getPropertyType(\ReflectionProperty|\ReflectionParameter $prop): ?string
+ {
+ if ($type = Nette\Utils\Type::fromReflection($prop)) {
+ return (string) $type;
+ } elseif (
+ ($prop instanceof \ReflectionProperty)
+ && ($type = preg_replace('#\s.*#', '', (string) self::parseAnnotation($prop, 'var')))
+ ) {
+ $class = Reflection::getPropertyDeclaringClass($prop);
+ return preg_replace_callback('#[\w\\\]+#', fn($m) => Reflection::expandClassName($m[0], $class), $type);
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Returns an annotation value.
+ * @param \ReflectionProperty $ref
+ */
+ public static function parseAnnotation(\Reflector $ref, string $name): ?string
+ {
+ if (!Reflection::areCommentsAvailable()) {
+ throw new Nette\InvalidStateException('You have to enable phpDoc comments in opcode cache.');
+ }
+
+ $re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#';
+ if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) {
+ return $m[1] ?? '';
+ }
+
+ return null;
+ }
+
+
+ public static function formatValue(mixed $value): string
+ {
+ if ($value instanceof DynamicParameter) {
+ return 'dynamic';
+ } elseif (is_object($value)) {
+ return 'object ' . $value::class;
+ } elseif (is_string($value)) {
+ return "'" . Nette\Utils\Strings::truncate($value, 15, '...') . "'";
+ } elseif (is_scalar($value)) {
+ return var_export($value, return: true);
+ } else {
+ return get_debug_type($value);
+ }
+ }
+
+
+ public static function validateType(mixed $value, string $expected, Context $context): void
+ {
+ if (!Nette\Utils\Validators::is($value, $expected)) {
+ $expected = str_replace(DynamicParameter::class . '|', '', $expected);
+ $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
+ $context->addError(
+ 'The %label% %path% expects to be %expected%, %value% given.',
+ Message::TypeMismatch,
+ ['value' => $value, 'expected' => $expected],
+ );
+ }
+ }
+
+
+ public static function validateRange(mixed $value, array $range, Context $context, string $types = ''): void
+ {
+ if (is_array($value) || is_string($value)) {
+ [$length, $label] = is_array($value)
+ ? [count($value), 'items']
+ : (in_array('unicode', explode('|', $types), true)
+ ? [Nette\Utils\Strings::length($value), 'characters']
+ : [strlen($value), 'bytes']);
+
+ if (!self::isInRange($length, $range)) {
+ $context->addError(
+ "The length of %label% %path% expects to be in range %expected%, %length% $label given.",
+ Message::LengthOutOfRange,
+ ['value' => $value, 'length' => $length, 'expected' => implode('..', $range)],
+ );
+ }
+ } elseif ((is_int($value) || is_float($value)) && !self::isInRange($value, $range)) {
+ $context->addError(
+ 'The %label% %path% expects to be in range %expected%, %value% given.',
+ Message::ValueOutOfRange,
+ ['value' => $value, 'expected' => implode('..', $range)],
+ );
+ }
+ }
+
+
+ public static function isInRange(mixed $value, array $range): bool
+ {
+ return ($range[0] === null || $value >= $range[0])
+ && ($range[1] === null || $value <= $range[1]);
+ }
+
+
+ public static function validatePattern(string $value, string $pattern, Context $context): void
+ {
+ if (!preg_match("\x01^(?:$pattern)$\x01Du", $value)) {
+ $context->addError(
+ "The %label% %path% expects to match pattern '%pattern%', %value% given.",
+ Message::PatternMismatch,
+ ['value' => $value, 'pattern' => $pattern],
+ );
+ }
+ }
+
+
+ public static function getCastStrategy(string $type): \Closure
+ {
+ if (Nette\Utils\Validators::isBuiltinType($type)) {
+ return static function ($value) use ($type) {
+ settype($value, $type);
+ return $value;
+ };
+ } elseif (method_exists($type, '__construct')) {
+ return static fn($value) => is_array($value) || $value instanceof \stdClass
+ ? new $type(...(array) $value)
+ : new $type($value);
+ } else {
+ return static fn($value) => Nette\Utils\Arrays::toObject((array) $value, new $type);
+ }
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Message.php b/vendor/nette/schema/src/Schema/Message.php
new file mode 100644
index 0000000..a9a7277
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Message.php
@@ -0,0 +1,99 @@
+message = $message;
+ $this->code = $code;
+ $this->path = $path;
+ $this->variables = $variables;
+ }
+
+
+ public function toString(): string
+ {
+ $vars = $this->variables;
+ $vars['label'] = empty($vars['isKey']) ? 'item' : 'key of item';
+ $vars['path'] = $this->path
+ ? "'" . implode("\u{a0}›\u{a0}", $this->path) . "'"
+ : null;
+ $vars['value'] = Helpers::formatValue($vars['value'] ?? null);
+
+ return preg_replace_callback('~( ?)%(\w+)%~', function ($m) use ($vars) {
+ [, $space, $key] = $m;
+ return $vars[$key] === null ? '' : $space . $vars[$key];
+ }, $this->message) ?? throw new Nette\InvalidStateException(preg_last_error_msg());
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Processor.php b/vendor/nette/schema/src/Schema/Processor.php
new file mode 100644
index 0000000..3290ba6
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Processor.php
@@ -0,0 +1,96 @@
+skipDefaults = $value;
+ }
+
+
+ /**
+ * Normalizes and validates data. Result is a clean completed data.
+ * @throws ValidationException
+ */
+ public function process(Schema $schema, mixed $data): mixed
+ {
+ $this->createContext();
+ $data = $schema->normalize($data, $this->context);
+ $this->throwsErrors();
+ $data = $schema->complete($data, $this->context);
+ $this->throwsErrors();
+ return $data;
+ }
+
+
+ /**
+ * Normalizes and validates and merges multiple data. Result is a clean completed data.
+ * @throws ValidationException
+ */
+ public function processMultiple(Schema $schema, array $dataset): mixed
+ {
+ $this->createContext();
+ $flatten = null;
+ $first = true;
+ foreach ($dataset as $data) {
+ $data = $schema->normalize($data, $this->context);
+ $this->throwsErrors();
+ $flatten = $first ? $data : $schema->merge($data, $flatten);
+ $first = false;
+ }
+
+ $data = $schema->complete($flatten, $this->context);
+ $this->throwsErrors();
+ return $data;
+ }
+
+
+ /**
+ * @return string[]
+ */
+ public function getWarnings(): array
+ {
+ $res = [];
+ foreach ($this->context->warnings as $message) {
+ $res[] = $message->toString();
+ }
+
+ return $res;
+ }
+
+
+ private function throwsErrors(): void
+ {
+ if ($this->context->errors) {
+ throw new ValidationException(null, $this->context->errors);
+ }
+ }
+
+
+ private function createContext(): void
+ {
+ $this->context = new Context;
+ $this->context->skipDefaults = $this->skipDefaults;
+ Nette\Utils\Arrays::invoke($this->onNewContext, $this->context);
+ }
+}
diff --git a/vendor/nette/schema/src/Schema/Schema.php b/vendor/nette/schema/src/Schema/Schema.php
new file mode 100644
index 0000000..3ded769
--- /dev/null
+++ b/vendor/nette/schema/src/Schema/Schema.php
@@ -0,0 +1,37 @@
+toString());
+ $this->messages = $messages;
+ }
+
+
+ /**
+ * @return string[]
+ */
+ public function getMessages(): array
+ {
+ $res = [];
+ foreach ($this->messages as $message) {
+ $res[] = $message->toString();
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * @return Message[]
+ */
+ public function getMessageObjects(): array
+ {
+ return $this->messages;
+ }
+}
diff --git a/vendor/nette/utils/.phpstorm.meta.php b/vendor/nette/utils/.phpstorm.meta.php
new file mode 100644
index 0000000..25851af
--- /dev/null
+++ b/vendor/nette/utils/.phpstorm.meta.php
@@ -0,0 +1,13 @@
+
+✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks
+✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …
+✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories
+✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers
+✅ [Helper Functions](https://doc.nette.org/utils/helpers)
+✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML
+✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images
+✅ [Iterables](https://doc.nette.org/utils/iterables)
+✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding
+✅ [Generating Random Strings](https://doc.nette.org/utils/random)
+✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math
+✅ [PHP Reflection](https://doc.nette.org/utils/reflection)
+✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions
+✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements
+✅ [Type](https://doc.nette.org/utils/type) - PHP data type
+✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs
+
+
+
+Installation
+------------
+
+The recommended way to install is via Composer:
+
+```
+composer require nette/utils
+```
+
+Nette Utils 4.0 is compatible with PHP 8.0 to 8.5.
+
+
+
+[Support Me](https://github.com/sponsors/dg)
+--------------------------------------------
+
+Do you like Nette Utils? Are you looking forward to the new features?
+
+[](https://github.com/sponsors/dg)
+
+Thank you!
diff --git a/vendor/nette/utils/src/HtmlStringable.php b/vendor/nette/utils/src/HtmlStringable.php
new file mode 100644
index 0000000..d749d4e
--- /dev/null
+++ b/vendor/nette/utils/src/HtmlStringable.php
@@ -0,0 +1,22 @@
+counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0);
+ }
+
+
+ /**
+ * Is the current element the last one?
+ */
+ public function isLast(?int $gridWidth = null): bool
+ {
+ return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0);
+ }
+
+
+ /**
+ * Is the iterator empty?
+ */
+ public function isEmpty(): bool
+ {
+ return $this->counter === 0;
+ }
+
+
+ /**
+ * Is the counter odd?
+ */
+ public function isOdd(): bool
+ {
+ return $this->counter % 2 === 1;
+ }
+
+
+ /**
+ * Is the counter even?
+ */
+ public function isEven(): bool
+ {
+ return $this->counter % 2 === 0;
+ }
+
+
+ /**
+ * Returns the counter.
+ */
+ public function getCounter(): int
+ {
+ return $this->counter;
+ }
+
+
+ /**
+ * Returns the count of elements.
+ */
+ public function count(): int
+ {
+ $inner = $this->getInnerIterator();
+ if ($inner instanceof \Countable) {
+ return $inner->count();
+
+ } else {
+ throw new Nette\NotSupportedException('Iterator is not countable.');
+ }
+ }
+
+
+ /**
+ * Forwards to the next element.
+ */
+ public function next(): void
+ {
+ parent::next();
+ if (parent::valid()) {
+ $this->counter++;
+ }
+ }
+
+
+ /**
+ * Rewinds the Iterator.
+ */
+ public function rewind(): void
+ {
+ parent::rewind();
+ $this->counter = parent::valid() ? 1 : 0;
+ }
+
+
+ /**
+ * Returns the next key.
+ */
+ public function getNextKey(): mixed
+ {
+ return $this->getInnerIterator()->key();
+ }
+
+
+ /**
+ * Returns the next element.
+ */
+ public function getNextValue(): mixed
+ {
+ return $this->getInnerIterator()->current();
+ }
+}
diff --git a/vendor/nette/utils/src/Iterators/Mapper.php b/vendor/nette/utils/src/Iterators/Mapper.php
new file mode 100644
index 0000000..284da29
--- /dev/null
+++ b/vendor/nette/utils/src/Iterators/Mapper.php
@@ -0,0 +1,33 @@
+callback = $callback;
+ }
+
+
+ public function current(): mixed
+ {
+ return ($this->callback)(parent::current(), parent::key());
+ }
+}
diff --git a/vendor/nette/utils/src/SmartObject.php b/vendor/nette/utils/src/SmartObject.php
new file mode 100644
index 0000000..3b2203f
--- /dev/null
+++ b/vendor/nette/utils/src/SmartObject.php
@@ -0,0 +1,140 @@
+$name ?? null;
+ if (is_iterable($handlers)) {
+ foreach ($handlers as $handler) {
+ $handler(...$args);
+ }
+ } elseif ($handlers !== null) {
+ throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.');
+ }
+
+ return null;
+ }
+
+ ObjectHelpers::strictCall($class, $name);
+ }
+
+
+ /**
+ * @throws MemberAccessException
+ */
+ public static function __callStatic(string $name, array $args)
+ {
+ ObjectHelpers::strictStaticCall(static::class, $name);
+ }
+
+
+ /**
+ * @return mixed
+ * @throws MemberAccessException if the property is not defined.
+ */
+ public function &__get(string $name)
+ {
+ $class = static::class;
+
+ if ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property getter
+ if (!($prop & 0b0001)) {
+ throw new MemberAccessException("Cannot read a write-only property $class::\$$name.");
+ }
+
+ $m = ($prop & 0b0010 ? 'get' : 'is') . ucfirst($name);
+ if ($prop & 0b10000) {
+ $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call()
+ $loc = isset($trace['file'], $trace['line'])
+ ? " in $trace[file] on line $trace[line]"
+ : '';
+ trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED);
+ }
+
+ if ($prop & 0b0100) { // return by reference
+ return $this->$m();
+ } else {
+ $val = $this->$m();
+ return $val;
+ }
+ } else {
+ ObjectHelpers::strictGet($class, $name);
+ }
+ }
+
+
+ /**
+ * @throws MemberAccessException if the property is not defined or is read-only
+ */
+ public function __set(string $name, mixed $value): void
+ {
+ $class = static::class;
+
+ if (ObjectHelpers::hasProperty($class, $name)) { // unsetted property
+ $this->$name = $value;
+
+ } elseif ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property setter
+ if (!($prop & 0b1000)) {
+ throw new MemberAccessException("Cannot write to a read-only property $class::\$$name.");
+ }
+
+ $m = 'set' . ucfirst($name);
+ if ($prop & 0b10000) {
+ $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call()
+ $loc = isset($trace['file'], $trace['line'])
+ ? " in $trace[file] on line $trace[line]"
+ : '';
+ trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED);
+ }
+
+ $this->$m($value);
+
+ } else {
+ ObjectHelpers::strictSet($class, $name);
+ }
+ }
+
+
+ /**
+ * @throws MemberAccessException
+ */
+ public function __unset(string $name): void
+ {
+ $class = static::class;
+ if (!ObjectHelpers::hasProperty($class, $name)) {
+ throw new MemberAccessException("Cannot unset the property $class::\$$name.");
+ }
+ }
+
+
+ public function __isset(string $name): bool
+ {
+ return isset(ObjectHelpers::getMagicProperties(static::class)[$name]);
+ }
+}
diff --git a/vendor/nette/utils/src/StaticClass.php b/vendor/nette/utils/src/StaticClass.php
new file mode 100644
index 0000000..b1d8486
--- /dev/null
+++ b/vendor/nette/utils/src/StaticClass.php
@@ -0,0 +1,34 @@
+
+ * @implements \ArrayAccess
+ */
+class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate
+{
+ /**
+ * Transforms array to ArrayHash.
+ * @param array $array
+ */
+ public static function from(array $array, bool $recursive = true): static
+ {
+ $obj = new static;
+ foreach ($array as $key => $value) {
+ $obj->$key = $recursive && is_array($value)
+ ? static::from($value)
+ : $value;
+ }
+
+ return $obj;
+ }
+
+
+ /**
+ * Returns an iterator over all items.
+ * @return \Iterator
+ */
+ public function &getIterator(): \Iterator
+ {
+ foreach ((array) $this as $key => $foo) {
+ yield $key => $this->$key;
+ }
+ }
+
+
+ /**
+ * Returns items count.
+ */
+ public function count(): int
+ {
+ return count((array) $this);
+ }
+
+
+ /**
+ * Replaces or appends an item.
+ * @param array-key $key
+ * @param T $value
+ */
+ public function offsetSet($key, $value): void
+ {
+ if (!is_scalar($key)) { // prevents null
+ throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key)));
+ }
+
+ $this->$key = $value;
+ }
+
+
+ /**
+ * Returns an item.
+ * @param array-key $key
+ * @return T
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->$key;
+ }
+
+
+ /**
+ * Determines whether an item exists.
+ * @param array-key $key
+ */
+ public function offsetExists($key): bool
+ {
+ return isset($this->$key);
+ }
+
+
+ /**
+ * Removes the element from this list.
+ * @param array-key $key
+ */
+ public function offsetUnset($key): void
+ {
+ unset($this->$key);
+ }
+}
diff --git a/vendor/nette/utils/src/Utils/ArrayList.php b/vendor/nette/utils/src/Utils/ArrayList.php
new file mode 100644
index 0000000..c9fe538
--- /dev/null
+++ b/vendor/nette/utils/src/Utils/ArrayList.php
@@ -0,0 +1,137 @@
+
+ * @implements \ArrayAccess
+ */
+class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
+{
+ use Nette\SmartObject;
+
+ private array $list = [];
+
+
+ /**
+ * Transforms array to ArrayList.
+ * @param list $array
+ */
+ public static function from(array $array): static
+ {
+ if (!Arrays::isList($array)) {
+ throw new Nette\InvalidArgumentException('Array is not valid list.');
+ }
+
+ $obj = new static;
+ $obj->list = $array;
+ return $obj;
+ }
+
+
+ /**
+ * Returns an iterator over all items.
+ * @return \Iterator
+ */
+ public function &getIterator(): \Iterator
+ {
+ foreach ($this->list as &$item) {
+ yield $item;
+ }
+ }
+
+
+ /**
+ * Returns items count.
+ */
+ public function count(): int
+ {
+ return count($this->list);
+ }
+
+
+ /**
+ * Replaces or appends an item.
+ * @param int|null $index
+ * @param T $value
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetSet($index, $value): void
+ {
+ if ($index === null) {
+ $this->list[] = $value;
+
+ } elseif (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+
+ } else {
+ $this->list[$index] = $value;
+ }
+ }
+
+
+ /**
+ * Returns an item.
+ * @param int $index
+ * @return T
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetGet($index): mixed
+ {
+ if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+ }
+
+ return $this->list[$index];
+ }
+
+
+ /**
+ * Determines whether an item exists.
+ * @param int $index
+ */
+ public function offsetExists($index): bool
+ {
+ return is_int($index) && $index >= 0 && $index < count($this->list);
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * @param int $index
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetUnset($index): void
+ {
+ if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+ }
+
+ array_splice($this->list, $index, 1);
+ }
+
+
+ /**
+ * Prepends an item.
+ * @param T $value
+ */
+ public function prepend(mixed $value): void
+ {
+ $first = array_slice($this->list, 0, 1);
+ $this->offsetSet(0, $value);
+ array_splice($this->list, 1, 0, $first);
+ }
+}
diff --git a/vendor/nette/utils/src/Utils/Arrays.php b/vendor/nette/utils/src/Utils/Arrays.php
new file mode 100644
index 0000000..8985a70
--- /dev/null
+++ b/vendor/nette/utils/src/Utils/Arrays.php
@@ -0,0 +1,555 @@
+ $array
+ * @param array-key|array-key[] $key
+ * @param ?T $default
+ * @return ?T
+ * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
+ */
+ public static function get(array $array, string|int|array $key, mixed $default = null): mixed
+ {
+ foreach (is_array($key) ? $key : [$key] as $k) {
+ if (is_array($array) && array_key_exists($k, $array)) {
+ $array = $array[$k];
+ } else {
+ if (func_num_args() < 3) {
+ throw new Nette\InvalidArgumentException("Missing item '$k'.");
+ }
+
+ return $default;
+ }
+ }
+
+ return $array;
+ }
+
+
+ /**
+ * Returns reference to array item. If the index does not exist, new one is created with value null.
+ * @template T
+ * @param array $array
+ * @param array-key|array-key[] $key
+ * @return ?T
+ * @throws Nette\InvalidArgumentException if traversed item is not an array
+ */
+ public static function &getRef(array &$array, string|int|array $key): mixed
+ {
+ foreach (is_array($key) ? $key : [$key] as $k) {
+ if (is_array($array) || $array === null) {
+ $array = &$array[$k];
+ } else {
+ throw new Nette\InvalidArgumentException('Traversed item is not an array.');
+ }
+ }
+
+ return $array;
+ }
+
+
+ /**
+ * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as
+ * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains
+ * the value from the first array in the case of a key collision.
+ * @template T1
+ * @template T2
+ * @param array $array1
+ * @param array $array2
+ * @return array
+ */
+ public static function mergeTree(array $array1, array $array2): array
+ {
+ $res = $array1 + $array2;
+ foreach (array_intersect_key($array1, $array2) as $k => $v) {
+ if (is_array($v) && is_array($array2[$k])) {
+ $res[$k] = self::mergeTree($v, $array2[$k]);
+ }
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Returns zero-indexed position of given array key. Returns null if key is not found.
+ */
+ public static function getKeyOffset(array $array, string|int $key): ?int
+ {
+ return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true));
+ }
+
+
+ /**
+ * @deprecated use getKeyOffset()
+ */
+ public static function searchKey(array $array, $key): ?int
+ {
+ return self::getKeyOffset($array, $key);
+ }
+
+
+ /**
+ * Tests an array for the presence of value.
+ */
+ public static function contains(array $array, mixed $value): bool
+ {
+ return in_array($value, $array, true);
+ }
+
+
+ /**
+ * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?V
+ */
+ public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed
+ {
+ $key = self::firstKey($array, $predicate);
+ return $key === null
+ ? ($else ? $else() : null)
+ : $array[$key];
+ }
+
+
+ /**
+ * Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?V
+ */
+ public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed
+ {
+ $key = self::lastKey($array, $predicate);
+ return $key === null
+ ? ($else ? $else() : null)
+ : $array[$key];
+ }
+
+
+ /**
+ * Returns the key of first item (matching the specified predicate if given) or null if there is no such item.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array