1"use strict"; 2 3const secondaryNav = document.getElementById('top-nav-secondary'); 4const primaryNav = document.getElementById('top-nav-primary'); 5 6if (primaryNav) { 7 const topNavs = document.getElementById('top-navs'); 8 const primaryNavBlock = primaryNav.querySelectorAll('.block-colossal-menu > ul > li'); 9 10 const mobile = window.matchMedia("(max-width: 1023px)"); 11 const toggleNavBtns = document.querySelectorAll('[data-js="toggleNavBtn"]'); 12 const eventListeners = []; 13 const createdElements = []; 14 let resizeTimeout; 15 16 let isMobile = mobile.matches; 17 18 let windowWidth = window.innerWidth; 19 window.addEventListener('resize', function(e) { 20 // Only fire event if the width changes, this is needed due to search on mobile 21 // when clicking the search box the keyboard opens 22 // could use interactive-widget=resizes-content but not working in all browsers 23 if (window.innerWidth !== windowWidth) { 24 clearTimeout(resizeTimeout); 25 resizeTimeout = setTimeout(onResize, 200); 26 } 27 }); 28 29 function onResize() { 30 isMobile = mobile.matches; 31 closeMenu(); 32 removeAllTrackedEventListeners(); 33 removeCreatedElements(); 34 setupMenu(); 35 } 36 37 // Function to add an event listener and store its reference 38 function addEventListenerWithTracking(element, event, handler) { 39 element.addEventListener(event, handler); 40 eventListeners.push({ element, event, handler }); 41 } 42 43 // Function to remove all tracked event listeners 44 function removeAllTrackedEventListeners() { 45 eventListeners.forEach(({ element, event, handler }) => { 46 element.removeEventListener(event, handler); 47 }); 48 eventListeners.length = 0; // Clear the array 49 } 50 51 function createElementWithTracking(el) { 52 let created = document.createElement(el); 53 createdElements.push({created, el}); 54 return created; 55 } 56 57 function removeCreatedElements() { 58 createdElements.forEach(({created, el}) => { 59 created.remove(); 60 }); 61 createdElements.length = 0; // Clear the array 62 } 63 64 function closeMenu() { 65 document.querySelectorAll('.clicked').forEach((clicked) => { 66 clicked.classList.remove('clicked'); 67 }); 68 document.querySelectorAll('.selected').forEach((selected) => { 69 selected.classList.remove('selected'); 70 }); 71 document.querySelectorAll('.grid-show').forEach((gridShow) => { 72 gridShow.classList.remove('grid-show'); 73 }); 74 document.querySelectorAll('.mobile-show').forEach((mobileShow) => { 75 mobileShow.classList.remove('mobile-show'); 76 }); 77 document.querySelectorAll('.primary-show').forEach((primaryShow) => { 78 primaryShow.classList.remove('primary-show'); 79 }); 80 81 // Mobile Menu 82 document.querySelector('html').classList.remove('overflow-hidden'); 83 document.querySelectorAll('.cloned').forEach((cloned) => { 84 cloned.remove(); 85 }); 86 87 const searchToggle = document.querySelector('[data-js="toggleSearch"]'); 88 if (searchToggle) { 89 searchToggle.classList.remove('search--mobile-open'); 90 } 91 92 // Reset the open/close buttons to their default state when the menu closes 93 const closeBtn = document.querySelector('.button-close'); 94 const hamburgerBtn = document.querySelector('.nav-mobile-icon'); 95 if (closeBtn) { 96 closeBtn.classList.add('hidden'); 97 } 98 if (hamburgerBtn) { 99 hamburgerBtn.classList.remove('hidden'); 100 } 101 } 102 103 setupMenu(); 104 105 // Primary Menu 106 // ------------------ 107 function setupMenu() { 108 if (!mobile.matches && primaryNavBlock) { 109 primaryNavBlock.forEach((item) => { 110 addEventListenerWithTracking(item.querySelector('span'), 'click', function (e) { 111 secondaryNav.querySelectorAll('nav > .nav__secondary > li.dropdown .clicked').forEach((clicked) => { 112 clicked.classList.remove('clicked'); 113 }); 114 primaryNav.querySelectorAll('.block-colossal-menu > ul > li span.clicked').forEach((clicked) => { 115 if (clicked !== e.target) { 116 clicked.classList.remove('clicked'); 117 } 118 }); 119 120 e.target.classList.toggle('clicked'); 121 item.querySelectorAll('.menu-item-link.selected').forEach((selectedItem) => { 122 selectedItem.classList.remove('selected'); 123 }); 124 125 if (item.querySelector('.field--name-field-blocks') != null) { 126 let menuBlock = item.querySelector('.field--name-field-blocks'); 127 close(menuBlock); 128 menuBlock.classList.toggle('grid-show'); 129 130 // When we first select the menu item show the first submenu in the middle column
131 let clonedMenu = item.querySelector('.sub-menu-level-1').cloneNode(true); 132 clonedMenu.classList.add('grid'); 133 let appendTo = item.querySelector('.mega-menu-submenu'); 134 appendTo.innerHTML = ''; 135 appendTo.appendChild(clonedMenu); 136 item.querySelector('.link-media').classList.add('selected'); 137 138 let $items = item.querySelectorAll('.menu-level-0 > li > .menu-item-link'); 139 140 // On click display the relevent submenu 141 $items.forEach((subItem) => { 142 if (subItem.querySelector('a.card-2030--clickable') != null) { 143 // Disable link so we can use to tab through items 144 addEventListenerWithTracking(subItem.querySelector('a.card-2030--clickable'), 'click', function (e) { 145 e.preventDefault(); 146 }); 147 148 // If there is only one link remove styles 149 if ($items.length === 1) { 150 subItem.querySelector('a.card-2030--clickable').className = ""; 151 } 152 else { 153 addEventListenerWithTracking(subItem, 'click', function (e) { 154 let itemCloned = subItem.parentElement.querySelector('.sub-menu-level-1').cloneNode(true); 155 itemCloned.classList.add('grid'); 156 appendTo.innerHTML = ''; 157 appendTo.appendChild(itemCloned); 158 // Go over them again and remove selecdted 159 primaryNav.querySelectorAll('.selected').forEach((selected) => { 160 selected.classList.remove('selected'); 161 }); 162 subItem.classList.add('selected'); 163 }); 164 } 165 } 166 }); 167 } 168 }); 169 }); 170 171 // if the mouse leaves from the bottom close the menu 172 document.querySelectorAll('.block-colossal-menu > ul > li .field--name-field-blocks').forEach((item) => { 173 addEventListenerWithTracking(item, 'mouseleave', function (e) { 174 primaryNav.querySelectorAll('.block-colossal-menu > ul > li span.clicked').forEach((clicked) => { 175 if (clicked !== e.target) { 176 clicked.classList.remove('clicked'); 177 } 178 }); 179 if (e.target.classList.contains('grid-show')) { 180 const rect = item.getBoundingClientRect(); 181 const mouseY = e.clientY; 182 if (Math.round(mouseY) >= Math.round(rect.bottom)) { 183 e.target.classList.remove('grid-show'); 184 } 185 } 186 }); 187 }); 188 } 189 // Mobile JS 190 else { 191 192 // Loop through both buttons so clicking either triggers the logic, and toggle visibility 193 toggleNavBtns.forEach((btn) => { 194 addEventListenerWithTracking(btn, 'click', function(e) { 195 e.preventDefault(); 196 primaryNav.classList.toggle('primary-show'); 197 topNavs.classList.toggle('mobile-show'); 198 document.querySelector('html').classList.toggle('overflow-hidden'); 199 200 const searchToggle = document.querySelector('[data-js="toggleSearch"]'); 201 if (searchToggle) { 202 searchToggle.classList.add('search--mobile-open'); 203 } 204 205 // Swap the visibility of the hamburger and the cross 206 document.querySelector('.nav-mobile-icon').classList.toggle('hidden'); 207 document.querySelector('.button-close').classList.toggle('hidden'); 208 }); 209 }); 210 211 primaryNav.querySelectorAll('.block-colossal-menu > ul > li').forEach((item) => { 212 if (item.querySelector('.field--name-field-blocks') != null) { 213 214 let menu = item.querySelector('.field--name-field-blocks'); 215 let back = createElementWithTracking('div'); 216 back.classList.add('back-link'); 217 back.innerHTML = '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" transform="scale(-1 1)" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" fill="none" xml:space="preserve"><path d="M9,3L8.3,3.7l3.8,3.8H2v1h10.1l-3.8,3.8L9,13l5-5L9,3z" fill="currentColor"></path></svg> ' + item.querySelector('span').innerHTML; 218 menu.prepend(back); 219 addEventListenerWithTracking(back, 'click', function() { 220 menu.classList.toggle('mobile-show'); 221 }); 222 223 // subItems is level 0 224 // eg sections for services such as corp, fund, private client solutions 225 let subItems = item.querySelectorAll('.menu-level-0 > li > .menu-item-link'); 226 227 // If there is only one link here eg about us we want to select the level 1 menu straight away 228 if (subItems.length > 1) { 229 addEventListenerWithTracking(item.querySelector('span'), 'click', function (e) { 230 menu.classList.toggle('mobile-show'); 231 }); 232
233 subItems.forEach((subItem) => { 234 235 // Disable link so we can use to tab through items 236 addEventListenerWithTracking(subItem.querySelector('a.card-2030--clickable'), 'click', function (e) { 237 e.preventDefault(); 238 }); 239 240 let subMenu = subItem.parentElement.querySelector('.sub-menu-level-1'); 241 let back = createElementWithTracking('div'); 242 back.classList.add('back-link'); 243 back.innerHTML = '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" transform="scale(-1 1)" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" fill="none" xml:space="preserve"><path d="M9,3L8.3,3.7l3.8,3.8H2v1h10.1l-3.8,3.8L9,13l5-5L9,3z" fill="currentColor"></path></svg> ' + subItem.querySelector('.card__heading').innerHTML; 244 subMenu.prepend(back); 245 // Clone featured content to each pane 246 let nodetoclone; 247 if (nodetoclone = item.querySelector('.paragraph--type--mega-menu-ds-col1')) { 248 let featured = nodetoclone.cloneNode(true); 249 // make it easy to tidy up on resize 250 featured.classList.add('cloned'); 251 subMenu.appendChild(featured); 252 } 253 254 addEventListenerWithTracking(back, 'click', function() { 255 subMenu.classList.toggle('mobile-show'); 256 }); 257 258 addEventListenerWithTracking(subItem, 'click', function (e) { 259 subMenu.classList.toggle('mobile-show'); 260 }); 261 }); 262 } 263 else { 264 // Should only be 1 item but we have to loop due to queryselectorAll 265 subItems.forEach((subItem) => { 266 267 let subMenu = subItem.parentElement.querySelector('.sub-menu-level-1'); 268 let back = createElementWithTracking('div'); 269 back.classList.add('back-link'); 270 back.innerHTML = '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" transform="scale(-1 1)" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" fill="none" xml:space="preserve"><path d="M9,3L8.3,3.7l3.8,3.8H2v1h10.1l-3.8,3.8L9,13l5-5L9,3z" fill="currentColor"></path></svg> ' + subMenu.querySelector('.card__heading').innerHTML; 271 subMenu.prepend(back); 272 // Clone featued content to each pane 273 let nodetoclone; 274 if (nodetoclone = item.querySelector('.paragraph--type--mega-menu-ds-col1')) { 275 let featured = nodetoclone.cloneNode(true); 276 // make it easy to tidy up on resize 277 featured.classList.add('cloned'); 278 subMenu.appendChild(featured); 279 } 280 281 addEventListenerWithTracking(back, 'click', function() { 282 menu.classList.toggle('mobile-show'); 283 subMenu.classList.toggle('mobile-show'); 284 }); 285 addEventListenerWithTracking(item.querySelector('span'), 'click', function (e) { 286 menu.classList.toggle('mobile-show'); 287 subMenu.classList.toggle('mobile-show'); 288 }); 289 }); 290 } 291 } 292 }); 293 } 294 295 // Secondary Menu 296 // --------------------------------- 297 298 // Normal Dropdown 299 if (!mobile.matches) { 300 secondaryNav.querySelectorAll('nav > .nav__secondary > li.dropdown').forEach((item) => { 301 addEventListenerWithTracking(item, 'click', function (e) { 302 primaryNav.querySelectorAll('.block-colossal-menu > ul > li span.clicked').forEach((clicked) => { 303 clicked.classList.remove('clicked'); 304 }); 305 secondaryNav.querySelectorAll('nav > .nav__secondary > li.dropdown .clicked').forEach((clicked) => { 306 if (clicked !== e.target) { 307 clicked.classList.remove('clicked'); 308 } 309 }); 310 e.target.classList.toggle('clicked'); 311 312 close(item.querySelector('.site-menu')); 313 item.querySelector('.site-menu').classList.toggle('grid-show'); 314 }); 315 addEventListenerWithTracking(item.querySelector('.nav__secondary-link'), 'click', function (e) { 316 e.preventDefault(); 317 }); 318 }); 319 320 // Show the search box 321 secondaryNav.querySelectorAll('nav > .nav__secondary > li.link-search').forEach((item) => { 322 addEventListenerWithTracking(item, 'click', function (e) { 323 primaryNav.querySelectorAll('.block-colossal-menu > ul > li span.clicked').forEach((clicked) => { 324 clicked.classList.remove('clicked'); 325 }); 326 secondaryNav.querySelectorAll('nav > .nav__secondary > li.dropdown .clicked').forEach((clicked) => { 327 clicked.classList.remove('clicked'); 328 }); 329 close(document.querySelector('[data-js="toggleSearch"]')); 330 document.querySelector('[data-js="toggleSearch"]').classList.toggle('grid-show'); 331 }); 332 addEventListenerWithTracking(item.querySelector('.nav__secondary-link'), 'click', function (e) { 333 e.preventDefault(); 334 }); 335 }); 336 } 337 } 338 339 // Close everything else on click 340 function close(item) { 341 if (!item) { 342 return; 343 } 344 item.classList.add('clicked'); 345 document.querySelectorAll('.grid-show').forEach((all) => { 346 if (all.classList.contains('clicked') === false) { 347 all.classList.remove('grid-show'); 348 } 349 }); 350 item.classList.remove('clicked'); 351 } 352}
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.