{# This file is partially duplicated in src/Symfony/Component/ErrorHandler/Resources/assets/js/exception.js. If you make any change in this file, verify the same change is needed in the other file. #} {# CAUTION: the contents of this file are processed by Twig before loading them as JavaScript source code. Always use '/*' comments instead of '//' comments to avoid impossible-to-debug side-effects #} window.addEventListener('DOMContentLoaded', () => { new SymfonyProfiler(); }); class SymfonyProfiler { constructor() { this.#createTabs(); this.#createToggles(); this.#createCopyToClipboard(); this.#convertDateTimesToUserTimezone(); } #createTabs() { /* the accessibility options of this component have been defined according to: */ /* www.w3.org/WAI/ARIA/apg/example-index/tabs/tabs-manual.html */ const tabGroups = document.querySelectorAll('.sf-tabs:not([data-processed=true])'); /* create the tab navigation for each group of tabs */ tabGroups.forEach((tabGroup, i) => { const tabs = tabGroup.querySelectorAll(':scope > .tab'); const tabNavigation = document.createElement('div'); tabNavigation.classList.add('tab-navigation'); tabNavigation.setAttribute('role', 'tablist'); let selectedTabId = `tab-${i}-0`; /* select the first tab by default */ tabs.forEach((tab, j) => { const tabId = `tab-${i}-${j}`; const tabTitle = tab.querySelector('.tab-title').innerHTML; const tabNavigationItem = document.createElement('button'); tabNavigationItem.classList.add('tab-control'); tabNavigationItem.setAttribute('data-tab-id', tabId); tabNavigationItem.setAttribute('role', 'tab'); tabNavigationItem.setAttribute('aria-controls', tabId); if (tab.classList.contains('active')) { selectedTabId = tabId; } if (tab.classList.contains('disabled')) { tabNavigationItem.classList.add('disabled'); } tabNavigationItem.innerHTML = tabTitle; tabNavigation.appendChild(tabNavigationItem); const tabContent = tab.querySelector('.tab-content'); tabContent.parentElement.setAttribute('id', tabId); }); tabGroup.insertBefore(tabNavigation, tabGroup.firstChild); document.querySelector('[data-tab-id="' + selectedTabId + '"]').classList.add('active'); }); /* display the active tab and add the 'click' event listeners */ tabGroups.forEach((tabGroup) => { const tabs = tabGroup.querySelectorAll(':scope > .tab-navigation .tab-control'); tabs.forEach((tab) => { const tabId = tab.getAttribute('data-tab-id'); const tabPanel = document.getElementById(tabId); tabPanel.setAttribute('role', 'tabpanel'); tabPanel.setAttribute('aria-labelledby', tabId); tabPanel.querySelector('.tab-title').className = 'hidden'; if (tab.classList.contains('active')) { tabPanel.className = 'block'; tab.setAttribute('aria-selected', 'true'); tab.removeAttribute('tabindex'); } else { tabPanel.className = 'hidden'; tab.removeAttribute('aria-selected'); tab.setAttribute('tabindex', '-1'); } tab.addEventListener('click', function(e) { let activeTab = e.target || e.srcElement; /* needed because when the tab contains HTML contents, user can click */ /* on any of those elements instead of their parent '