1document.addEventListener('DOMContentLoaded', function () { 2 document.querySelectorAll('.umbc-horizontal-tabs').forEach(function (tabsBlock) { 3 const tabPanels = tabsBlock.querySelectorAll('.umbc-tab-panel'); 4 const tabList = document.createElement('ul'); 5 tabList.classList.add('umbc-tab-list'); 6 tabList.setAttribute('role', 'tablist'); 7 tabList.setAttribute('aria-label', 'Tabs'); 8 tabsBlock.prepend(tabList); 9 10 const isMobile = window.matchMedia('(max-width: 767px)').matches; 11 12 const mobilePanelsMap = []; 13 14 tabPanels.forEach(function (panel, index) { 15 const titleEl = panel.querySelector('h3'); 16 const title = titleEl ? titleEl.textContent.trim() : `Tab ${index + 1}`; 17 const tabId = `tab-${index}`; 18 const panelId = `panel-${index}`; 19 panel.id = panelId; 20 21 const li = document.createElement('li'); 22 li.setAttribute('role', 'presentation'); 23 24 const button = document.createElement('button'); 25 button.classList.add('umbc-tab'); 26 button.setAttribute('role', 'tab'); 27 button.setAttribute('id', tabId); 28 button.setAttribute('aria-controls', panelId); 29 button.setAttribute('tabindex', index === 0 ? '0' : '-1'); 30 button.setAttribute('aria-selected', index === 0 ? 'true' : 'false'); 31 button.innerText = title; 32 33 if (index === 0) { 34 button.classList.add('active'); 35 panel.classList.add('active'); 36 panel.removeAttribute('hidden'); 37 } else { 38 panel.setAttribute('hidden', ''); 39 } 40 41 li.appendChild(button); 42 tabList.appendChild(li); 43 44 if (isMobile) { 45 const mobileWrapper = document.createElement('div'); 46 mobileWrapper.classList.add('mobile-tab-panel'); 47 mobileWrapper.hidden = index !== 0; 48 49 // Move the actual content instead of copying HTML 50 while (panel.firstChild) { 51 mobileWrapper.appendChild(panel.firstChild); 52 } 53 54 li.appendChild(mobileWrapper); 55 mobilePanelsMap.push({ li, mobilePanel: mobileWrapper, button, originalPanel: panel }); 56 } 57 58 button.addEventListener('click', function () { 59 const isActive = button.classList.contains('active'); 60 61 // Collapse on mobile if already active 62 if (isMobile && isActive) { 63 button.classList.remove('active'); 64 button.setAttribute('aria-selected', 'false'); 65 button.setAttribute('tabindex', '-1'); 66 panel.classList.remove('active'); 67 panel.setAttribute('hidden', ''); 68 69 const activeMobilePanel = li.querySelector('.mobile-tab-panel'); 70 if (activeMobilePanel) activeMobilePanel.hidden = true; 71 return; 72 } 73 74 // Activate tab 75 tabList.querySelectorAll('.umbc-tab').forEach(btn => { 76 btn.classList.remove('active'); 77 btn.setAttribute('aria-selected', 'false'); 78 btn.setAttribute('tabindex', '-1'); 79 }); 80 81 tabPanels.forEach(p => { 82 p.classList.remove('active'); 83 p.setAttribute('hidden', ''); 84 }); 85 86 if (isMobile) { 87 mobilePanelsMap.forEach(pair => pair.mobilePanel.hidden = true); 88 } 89 90 button.classList.add('active'); 91 button.setAttribute('aria-selected', 'true'); 92 button.setAttribute('tabindex', '0'); 93 panel.classList.add('active'); 94 panel.removeAttribute('hidden'); 95 96 if (isMobile) { 97 const activeMobilePanel = li.querySelector('.mobile-tab-panel'); 98 if (activeMobilePanel) { 99 activeMobilePanel.hidden = false; 100 101 // Trigger chart reinitialization after making visible 102 setTimeout(() => { 103 const charts = activeMobilePanel.querySelectorAll('canvas[data-chart-type]'); 104 charts.forEach(chart => { 105 // Dispatch a custom event to trigger chart re-render 106 chart.dispatchEvent(new Event('chartVisible')); 107 108 // Or if you're using Chart.js, you might need to call resize 109 if (chart.chart) { 110 chart.chart.resize(); 111 } 112 }); 113 }, 50); 114 } 115 } 116 117 button.focus(); 118 }); 119 }); 120 121 if (isMobile) { 122 const mainPanelsContainer = tabsBlock.querySelector('.umbc-tab-panels'); 123 if (mainPanelsContainer) { 124 mainPanelsContainer.style.display = 'none'; 125 } 126 127 mobilePanelsMap.forEach((pair, idx) => { 128 pair.mobilePanel.classList.remove('active'); 129 pair.mobilePanel.hidden = idx !== 0; 130 }); 131 } 132 }); 133});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.