1(function (Drupal, once) { 2 3 Drupal.behaviors.moreTabsDropdown = { 4 attach(context) { 5 6 /* =============================== 7 Inject CSS (only once) 8 =============================== */ 9 if (!document.getElementById('mygov-more-tabs-style')) { 10 const style = document.createElement('style'); 11 style.id = 'mygov-more-tabs-style'; 12 style.innerHTML = ` 13 .search-tabs .more-tab { position: relative; } 14 15 .search-tabs .more-toggle { 16 background: none; 17 border: none; 18 cursor: pointer; 19 } 20 21 .search-tabs .more-toggle::after { 22 content: " â¼"; 23 font-size: 12px; 24 } 25 26 .search-tabs .more-dropdown { 27 position: absolute; 28 top: 100%; 29 left: 0; 30 display: none; 31 min-width: 220px; 32 background: #fff; 33 border: 1px solid #e0e0e0; 34 list-style: none; 35 margin: 0; 36 padding: 6px 0; 37 z-index: 1000; 38 border-radius: 6px; 39 box-shadow: 0 8px 20px rgba(0,0,0,0.08); 40 } 41 42 .search-tabs .more-tab.is-open .more-dropdown { 43 display: block; 44 } 45 46 .search-tabs .more-dropdown li a { 47 display: block; 48 padding: 8px 16px; 49 text-decoration: none; 50 } 51 52 .search-tabs .more-dropdown li a:hover { 53 background: #f5f5f5; 54 } 55 `; 56 document.head.appendChild(style); 57 } 58 59 /* =============================== 60 Main Logic 61 =============================== */ 62 63 once('more-tabs-dropdown', '.search-tabs .tabs-list', context) 64 .forEach(function (tabsList) { 65 66 const allItems = Array.from(tabsList.querySelectorAll(':scope > li')); 67 if (allItems.length <= 6) return; 68 69 let searchByItem = null; 70 const categoryItems = []; 71 72 allItems.forEach((item) => { 73 if (item.querySelector('.date-filter')) { 74 searchByItem = item; 75 } else { 76 categoryItems.push(item); 77 } 78 }); 79 80 if (categoryItems.length <= 6) return; 81 82 const activeItem = tabsList.querySelector('li.active'); 83 84 // First 6 visible 85 const visibleItems = categoryItems.slice(0, 6); 86 const dropdownItems = categoryItems.slice(6); 87 88 /* =============================== 89 Create More dropdown 90 =============================== */ 91 92 const moreLi = document.createElement('li'); 93 moreLi.classList.add('more-tab'); 94 95 const moreBtn = document.createElement('button'); 96 moreBtn.type = 'button'; 97 moreBtn.classList.add('more-toggle'); 98 moreBtn.setAttribute('aria-expanded', 'false'); 99 moreBtn.textContent = 'More'; 100 101 const dropdown = document.createElement('ul'); 102 dropdown.classList.add('more-dropdown'); 103 104 dropdownItems.forEach(item => dropdown.appendChild(item)); 105 106 moreLi.appendChild(moreBtn); 107 moreLi.appendChild(dropdown); 108 109 if (searchByItem) { 110 tabsList.insertBefore(moreLi, searchByItem); 111 } else { 112 tabsList.appendChild(moreLi); 113 } 114 115 /* =============================== 116 SWAP LOGIC (Prime â Media) 117 =============================== */ 118 119 if (activeItem && dropdown.contains(activeItem)) { 120 121 const currentVisible = Array.from( 122 tabsList.querySelectorAll(':scope > li') 123 ).filter(li => 124 !li.classList.contains('more-tab') && 125 !li.querySelector('.date-filter') 126 ); 127 128 const lastVisible = currentVisible[currentVisible.length - 1]; 129 130 // Move active outside before More 131 tabsList.insertBefore(activeItem, moreLi); 132 133 // Move last visible into dropdown 134 dropdown.insertBefore(lastVisible, dropdown.firstChild); 135 } 136 137 /* =============================== 138 Toggle 139 =============================== */ 140 141 moreBtn.addEventListener('click', function (e) { 142 e.preventDefault(); 143 e.stopPropagation(); 144 145 const isOpen = moreLi.classList.toggle('is-open'); 146 moreBtn.setAttribute('aria-expanded', isOpen); 147 }); 148 149 document.addEventListener('click', function () { 150 moreLi.classList.remove('is-open'); 151 moreBtn.setAttribute('aria-expanded', 'false'); 152 }); 153 154 }); 155 } 156 }; 157 158})(Drupal, once); 159 160 161// (function (Drupal, once) { 162 163// Drupal.behaviors.mygovSearchHighlight = { 164// attach: function (context) { 165 166// // Get search term (second placeholder inside results count) 167// const placeholders = context.querySelectorAll('.results-count em.placeholder'); 168 169// if (placeholders.length < 2) { 170// return; 171// } 172 173// const term = placeholders[1].textContent.trim(); 174 175// if (!term) { 176// return; 177// } 178 179// // Whole word match (prevents culture match for future) 180// const regex = new RegExp("\\b(" + term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ")\\b", "gi"); 181 182// once('mygovSearchHighlight', context.querySelectorAll('.search-result-item')).forEac
182h(function (item) { 183 184// const targets = item.querySelectorAll('.title, .body'); 185 186// targets.forEach(function (element) { 187// element.innerHTML = element.innerHTML.replace( 188// regex, 189// "<span class='search-highlight'>$1</span>" 190// ); 191// }); 192 193// }); 194 195// } 196// }; 197 198// })(Drupal, once); 199 200(function (Drupal, once) { 201 202 Drupal.behaviors.mygovSearchHighlight = { 203 attach: function (context) { 204 205 const placeholders = context.querySelectorAll('.results-count em.placeholder'); 206 207 if (placeholders.length < 2) { 208 return; 209 } 210 211 const term = placeholders[1].textContent.trim(); 212 if (!term) { 213 return; 214 } 215 216 const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 217 const regex = new RegExp("\\b(" + escaped + ")\\b", "gi"); 218 219 once('mygovSearchHighlight', context.querySelectorAll('.search-result-item')) 220 .forEach(function (item) { 221 222 item.querySelectorAll('.title, .body').forEach(function (element) { 223 highlightText(element); 224 }); 225 226 }); 227 228 function highlightText(node) { 229 230 node.childNodes.forEach(function (child) { 231 232 // If text node â replace safely 233 if (child.nodeType === Node.TEXT_NODE) { 234 235 if (regex.test(child.textContent)) { 236 237 const span = document.createElement('span'); 238 span.innerHTML = child.textContent.replace( 239 regex, 240 "<span class='search-highlight'>$1</span>" 241 ); 242 243 child.replaceWith(span); 244 } 245 246 } 247 248 // If element node â recurse (INCLUDING <a>) 249 else if ( 250 child.nodeType === Node.ELEMENT_NODE && 251 !['SCRIPT', 'STYLE'].includes(child.tagName) 252 ) { 253 highlightText(child); 254 } 255 256 }); 257 258 } 259 260 } 261 }; 262 263})(Drupal, once);
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.