1// sf-alpha.js (robust, scoped, mit Auto-Open & sanfter Animation) 2(function () { 3 function norm(v) { return (v || '').toString().trim().toUpperCase(); } 4 5 function letterFromChip(chip) { 6 return norm(chip && (chip.dataset.letter || chip.textContent)); 7 } 8 function letterFromPanel(panel) { 9 if (panel && panel.dataset.letter) return norm(panel.dataset.letter); 10 var id = (panel && panel.id) || ''; 11 var m = id.match(/([A-Za-zÃ-ÃÃ-öø-ÿÃÃÃÃ])$/); 12 return m ? norm(m[1]) : ''; 13 } 14 15 function focusChip(chips, idx) { 16 if (!chips.length) return; 17 var len = chips.length; 18 var i = ((idx % len) + len) % len; // wrap 19 var chip = chips[i]; 20 chips.forEach(function (c, j) { c.tabIndex = (j === i ? 0 : -1); }); 21 chip.focus(); 22 return chip; 23 } 24 25 function initAlpha(tablist) { 26 var section = tablist.closest('section.sf-alpha') || tablist.closest('.sf-alpha'); 27 var panelsRoot = section ? section.querySelector('.sf-alpha-panels') : null; 28 if (!panelsRoot) return; 29 30 var chips = Array.prototype.slice.call(tablist.querySelectorAll('.sf-chip[role="tab"]')); 31 var panels = Array.prototype.slice.call(panelsRoot.querySelectorAll('.sf-alpha-panel[role="tabpanel"]')); 32 33 if (!chips.length || !panels.length) return; 34 35 var chipBy = Object.create(null); 36 chips.forEach(function (c) { chipBy[letterFromChip(c)] = c; }); 37 38 var panelBy = Object.create(null); 39 panels.forEach(function (p) { panelBy[letterFromPanel(p)] = p; }); 40 41 function updateChipStates(activeLetter) { 42 chips.forEach(function (c) { 43 var isActive = letterFromChip(c) === activeLetter; 44 c.classList.toggle('is-active', isActive); 45 c.setAttribute('aria-selected', isActive ? 'true' : 'false'); 46 c.tabIndex = isActive ? 0 : -1; 47 }); 48 } 49 50 function hidePanel(p) { 51 if (!p || p.hasAttribute('hidden')) return; 52 p.style.transition = 'none'; 53 p.style.opacity = '0'; 54 p.style.transform = 'translateY(6px)'; 55 p.setAttribute('hidden', 'hidden'); 56 p.style.removeProperty('transition'); 57 p.style.removeProperty('opacity'); 58 p.style.removeProperty('transform'); 59 } 60 61 function showPanel(p) { 62 if (!p) return; 63 p.removeAttribute('hidden'); // sichtbar für Layout 64 p.style.opacity = '0'; // sanfte Animation 65 p.style.transform = 'translateY(6px)'; 66 requestAnimationFrame(function () { 67 p.style.transition = 'opacity 180ms ease, transform 180ms ease'; 68 p.style.opacity = '1'; 69 p.style.transform = 'none'; 70 setTimeout(function () { 71 p.style.removeProperty('transition'); 72 p.style.removeProperty('opacity'); 73 p.style.removeProperty('transform'); 74 }, 220); 75 }); 76 } 77 78 function setActiveLetter(letter) { 79 letter = norm(letter); 80 var target = panelBy[letter]; 81 if (!target) return; 82 83 updateChipStates(letter); 84 85 panels.forEach(function (p) { 86 if (p === target) { 87 p.classList.add('is-active'); 88 showPanel(p); 89 } else { 90 p.classList.remove('is-active'); 91 hidePanel(p); 92 } 93 }); 94 } 95 96 tablist.addEventListener('click', function (e) { 97 var chip = e.target.closest('.sf-chip[role="tab"]'); 98 if (!chip || !tablist.contains(chip)) return; 99 var letter = letterFromChip(chip); 100 if (panelBy[letter]) { 101 e.preventDefault();
102 setActiveLetter(letter); 103 } 104 }, true); 105 106 tablist.addEventListener('keydown', function (e) { 107 var key = e.key; 108 var current = document.activeElement && document.activeElement.closest('.sf-chip[role="tab"]'); 109 if (!current || !tablist.contains(current)) return; 110 111 var idx = chips.indexOf(current); 112 var handled = false; 113 114 if (key === 'ArrowLeft') { focusChip(chips, idx - 1); handled = true; } 115 else if (key === 'ArrowRight') { focusChip(chips, idx + 1); handled = true; } 116 else if (key === 'Home') { focusChip(chips, 0); handled = true; } 117 else if (key === 'End') { focusChip(chips, chips.length - 1); handled = true; } 118 else if (key === 'Enter' || key === ' ') { setActiveLetter(letterFromChip(current)); handled = true; } 119 120 else if (key === 'Tab' && !e.shiftKey) { 121 var activePanel = panels.find(function (p) { return p.classList.contains('is-active') && !p.hasAttribute('hidden'); }); 122 if (activePanel) { 123 var firstLink = activePanel.querySelector('.sf-alpha__link, a[href], button, [tabindex]:not([tabindex="-1"])'); 124 if (firstLink) { 125 e.preventDefault(); 126 firstLink.focus(); 127 return; 128 } 129 } 130 } 131 132 if (handled) { 133 e.preventDefault(); 134 if (key === 'ArrowLeft' || key === 'ArrowRight' || key === 'Home' || key === 'End') { 135 var focused = document.activeElement && document.activeElement.closest('.sf-chip[role="tab"]'); 136 if (focused) setActiveLetter(letterFromChip(focused)); 137 } 138 } 139 }); 140 141 var initial = ''; 142 if (section) { 143 initial = norm(section.getAttribute('data-selected-letter')); 144 } 145 146 if (!initial || initial === 'ALL' || !panelBy[initial]) { 147 chips.forEach(function (c, i) { c.tabIndex = (i === 0 ? 0 : -1); c.setAttribute('aria-selected', 'false'); }); 148 } else {
149 setActiveLetter(initial); 150 } 151 } 152 153 function boot() { 154 var tablists = document.querySelectorAll('.sf-chip-group[role="tablist"]'); 155 if (!tablists.length) return; 156 tablists.forEach(initAlpha); 157 } 158 159 if (document.readyState === 'loading') { 160 document.addEventListener('DOMContentLoaded', boot); 161 } else { 162 boot(); 163 } 164})();
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.