1//############################################################################// 2// // 3// SUBNAV // 4// // 5//############################################################################// 6 7/** 8 * Subnav Constructor. 9 * 10 * This function will populate a Subnav with a series of HTML code blocks 11 * to display headings and context menus. 12 * 13 * @param object tiles associative array of tpopiles to be displayed. 14 */ 15function populateSubnav(subpages) { 16 $.each(subpages, function(key, value) { 17 var height= document.getElementById("subnavList").offsetTop+54; 18 var heightMobile = document.getElementById("subnavListMobile").offsetTop+54; 19 var idName = key.replace(/\s/g, '').replace(/'/g, ''); 20 21 var list = ""; 22 var listMobile = ""; 23 if (typeof value.children !== "undefined") { 24 list = `<div id="${idName}List" class="contextnav top-[${height}px] z-30 absolute left-0 w-full gap-2 bg-union-teal drop-shadow-xl text-black py-3 justify-center items-center flex-nowrap hidden"> </div>` 25 listMobile = `<div id="${idName}ListMobile" class=" contextnavMobile left-0 absolute w-full gap-2 rounded-b mt-3 bg-union-teal drop-shadow-lg text-black py-2 z-10 justify-center items-center flex-nowrap hidden">` 26 } 27 28 $("#subnavList").append(` 29 <div id="${idName}HeadingContainer" class="subnavHeading z-30 mx-auto text-neutral-100 px-0 rounded-t-lg mt-2 pb-3 pt-1 text-center"> 30 <a href="${value.link}" id="${idName}Heading" class="z-30 grow pt-2 font-union-bold pb-2 whitespace-nowrap px-6 text-2xl items-center w-32 lg:w-full">${key}</a> 31 ${list} 32 </div> 33 `); 34 35 $("#subnavListMobile").append(` 36 <div id="${idName}HeadingContainerMobile" class="subnavHeadingMobile mx-auto text-neutral-100 px-3 rounded-full my-3 text-center snap-center snap-mandatory"> 37 <a id="${idName}Heading" ${typeof value.children === "undefined" ? `href="${value.link}"` : ""} class="grow text-lg pt-2 font-union-bold pb-2 whitespace-nowrap items-center w-32">${key}</a> 38 ${listMobile} 39 </div> 40 </div> 41 `); 42 43 $("#"+idName+"ListMobile").append( `<a id="${idName}ContainerMobile" href="${value.link}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white hover:no-underline hover:bg-neutral-900 shrink rounded-full">${key}</a>`); 44 45 if (typeof value.children !== "undefined") { 46 $.each(value.children, function(subsection, subsectionLink){ 47 var desktopText = 48 `<span class="flex"><a id="${subsection.replace(/\s/g, '')}Container" href="${subsectionLink}" class="z-30 flex text-2xl items-center font-union-bold justify-center hover:text-white/90 hover:no-underline px-4 w-full hover:bg-neutral-800 rounded-full">${subsection} 49 </a></span>`; 50 $("#"+idName+"List").append(desktopText); 51 52 var mobileText = 53 `<a id="${subsection.replace(/\s/g, '')}ContainerMobile" href="${subsectionLink}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white/90 hover:no-underline hover:bg-neutral-800 shrink rounded-full">${subsection} 54 </a>`; 55 $("#"+idName+"ListMobile").append(mobileText); 56 }) 57 } 58 }) 59} 60 61/** 62 * Single Subnav Constructor. 63 * 64 * This function will populate a Subnav without a series of dropdowns. For example, 65 * this is present on the Events Page 66 * 67 * @param object tiles associative array of tiles to be displayed. 68 */ 69function populateSingleSubnav(subpages) { 70 $.each(subpages, function(key, value){ 71 var subnavList = document.getElementById("subnavList"); 72 var subnavListMobile = document.getElementById("subnavListMobile"); 73 var contextnavList = document.getElementById("subnav"); 74 var contextnavListMobile = document.getElementById("subnavMobile"); 75 var height= subnavList.offsetTop+56; 76 var heightMobile = subnavList.offsetTop+56; 77 var desktopHeadings = 78 (` 79 <div id="${key.replace(/\s/g, '')}HeadingContainer" class="subnavHeading mx-auto text-neutral-100 px-3 rounded-full my-3 text-center"> 80 <a href="${value.link}" id="${key.replace(/\s/g, '')}Heading" class="grow pt-2 font-union-bold pb-2 whitespace-nowrap px-6 text-2xl items-center w-32 lg:w-full">${key}</a> 81 <div id="${key.replace(/\s/g, '')}List" class="top-[${height}px] z-30 absolute left-0 w-full gap-2 bg-union-teal drop-shadow-xl text-black py-3 justify-center items-center flex-nowrap hidden"> 82 83 </div> 84 </div> 85 86 `); 87 var mobileHeadings = 88 (` 89 <div id="${key.replace(/\s/g, '')}HeadingContainerMobile" class="subnavHeadingMobile mx-auto text-neutral-100 px-3 rounded-full my-3 text-center snap-center snap-mandatory"> 90 <a href="${value.link}" id="${key.replace(/\s/g, '')}Heading" class="grow text-lg pt-2 font-union-bold pb-2 whitespace-nowrap items-center w-32">${key}</a> 91 <div id="${key.replace(/\s/g, '')}ListMobile" class="left-0 absolute w-full gap-2 rounded-b mt-3 bg-union-teal drop-shadow-lg text-black py-2 z-10 justify-center items-center flex-nowrap hidden"> 92 93 </div> 94 </div> 95 96 `); 97 98 subnavList.innerHTML += desktopHeadings;
99 subnavListMobile.innerHTML += mobileHeadings; 100 101 var target = document.getElementById(key.replace(/\s/g, '')+"List"); 102 var targetMobile = document.getElementById(key.replace(/\s/g, '')+"ListMobile"); 103 var mobileExtraText = 104 `<a id="${key.replace(/\s/g, '')}ContainerMobile" href="${value.link}" class="flex text-lg my-2 items-center font-union-bold justify-center text-white hover:no-underline hover:bg-neutral-900 shrink rounded-full">${key}</a>`; 105 targetMobile.innerHTML += mobileExtraText; 106 }) 107} 108 109/** 110 * Set Active Page. 111 * 112 * This function will set a page as "Active" within the subnav menu. 113 * 114 * @param object tiles associative array of tiles to be displayed. 115 */ 116function setActivePage(subpages) { 117 var pagePath = window.location.pathname.split("/").filter(Boolean); // Remove empty strings 118 119 $.each(subpages, function(key, value) { 120 let menuPath = value.link.split("/").filter(Boolean); // Remove empty strings 121 122 // Check if the menu item has children 123 if (value.children && Object.keys(value.children).length > 0) { 124 // Original logic for parent items with children 125 if (pagePath.includes(menuPath[menuPath.length - 1])) { 126 $('#subnavHeaderText').text(key);
127 setActive(key.replace(/\s/g, '')); 128 setActiveSubpage(value); 129 } 130 } else { 131 // New logic: only highlight the deepest segment for single links 132 let lastSegment = pagePath[pagePath.length - 1]; 133 if (menuPath[menuPath.length - 1] === lastSegment) { 134 $('#subnavHeaderText').text(key); 135 setActive(key.replace(/\s/g, '')); 136 setActiveSubpage(value); 137 } 138 } 139 }); 140} 141 142 143 144/** 145 * Set Active Subpage. 146 * 147 * This function will set a subpage as "Active" within the subnav menu. 148 * (To remember, when adding subpages, will need to prefix a slash to the end 149 * of the URL as we check 2 layers back) 150 * 151 * @param object tiles associative array of tiles to be displayed. 152 */ 153function setActiveSubpage(subpages) { 154 var pagePath = window.location.pathname.split("/"); 155 if (typeof subpages['children'] !== "undefined") { 156 for (const [key, value] of Object.entries(subpages['children'])) { 157 currentPagePath = value.split("/"); 158 if (pagePath.includes(currentPagePath[currentPagePath.length-2])) { 159 $('#subnavHeaderText').text(key); 160 161 var heading = document.getElementById(key.replace(/\s/g, '')+"Container"); 162 addActiveCSS(heading); 163 164 heading = document.getElementById(key.replace(/\s/g, '')+"ContainerMobile"); 165 addActiveCSS(heading); 166 heading.classList.add("mx-5"); 167 } 168 } 169 } 170} 171 172function addActiveCSS(heading) { 173 heading.classList.remove("hover:bg-neutral-800", "hover:text-white/90"); 174 heading.classList.add("bg-neutral-900", "text-white"); 175} 176 177/** 178 * Set Active. 179 * 180 * get the current page on the navbar. Add active class to current page to the 181 * navbar and also the heading container. 182 * 183 * @param string page page name. 184 * 185 * @return 186 */ 187function setActive(page) { 188 page = page.replace(/'/g, ''); 189 var nav = document.getElementById(page+"List"); 190 if (nav !== null) { 191 nav.classList.add("lg:flex"); 192 nav.classList.add("subnavListActive"); 193 nav.classList.add("subnavListActive"); 194 } 195 196 var heading = document.getElementById(page+"HeadingContainer"); 197 heading.classList.remove("subnavHeading"); 198 heading.classList.add("bg-union-teal"); 199 heading.classList.add("subnavActive"); 200 201 heading = document.getElementById(page+"HeadingContainerMobile"); 202 heading.classList.remove("subnavHeading"); 203 heading.classList.toggle("subnavActive"); 204} 205 206function removeActiveMobileHeading() { 207 $('#subnavListMobile').children().each(function () { 208 if ($(this).is(':hover')) { 209 } 210 }); 211} 212 213/** 214 * Add Hyperlinks to Subnav. 215 * 216 * Loops through all subnav tags and validates if a hyperlink can be 217 * added. If valid and sanitised; update the href with the text value of its 218 * tag. 219 * 220 * @return 221 */ 222function addLinksToSubnav() { 223 $('#subnavList').children().each(function () { 224 225 let validated = validateSubnavTag($(this)); 226 227 if (!validated) { 228 return; 229 } 230 231 let subnavTitle = sanitiseSubnavString($(this).children('a').text()); 232 233 $(this).children('a').attr('href', window.location.pathname + subnavTitle); 234 }); 235} 236 237/** 238 * Add Hyperlinks to Sub subnav. 239 * 240 * Loops through all sub subnav tags and validates if a hyperlink can be 241 * added. If valid and sanitised; update the href with the text value of it 242 * tag. 243 * 244 * @return 245 */ 246function addLinksToSubSubnav() { 247 $('#subnavList').find('span').each(function () { 248 249 let validated = validateSubnavTag($(this)); 250 251 if (!validated) { 252 return; 253 } 254 255 let subnavChildTitle = sanitiseSubnavString($(this).children('a').text()); 256 257 let correctPath = getCorrectPathname(window.location.pathname); 258 259 $(this).children('a').attr('href', window.location.pathname + correctPath + subnavChildTitle); 260 });
261} 262 263/** 264 * Validate Subnav Tag. 265 * 266 * Return if tag is undefined, if text of tag is empty or whitespaces, and 267 * if the hyperlink of tag is empty or returns to home. 268 * 269 * @param Object tag Object to be validated. 270 * 271 * @return boolean 272 */ 273function validateSubnavTag(tag) { 274 if (typeof $(tag) === 'undefined') { 275 return false; 276 } 277 278 if (isEmptyOrSpaces($(tag).text())) { 279 return false; 280 } 281 282 if ($(tag).children('a').attr('href') !== '/' && $(tag).children('a').attr('href') !== '') { 283 return false; 284 } 285 286 return true; 287} 288 289/** 290 * Sanitise subnav string. 291 * 292 * Trim the string of whitespace, replace all inner whitespace with a 293 * hypthen, and convert to lower case. 294 * 295 * @param string subnavText Inner text of subnav. 296 * 297 * @return string 298 */ 299function sanitiseSubnavString(subnavText) { 300 return subnavText.trim().replace(/ /g, '-').toLowerCase(); 301} 302 303/** 304 * Get Correct Pathname. 305 * 306 * Split the pathname by forward slashses. If we are one level deep than the 307 * parent path then go up one level. Otherwise don't go up a level. 308 * 309 * @param string pathname URL folder string. 310 * 311 * @return string 312 */ 313function getCorrectPathname(pathname) { 314 pathname = pathname.split('/'); 315 316 let previousPathLevel = '../'; 317 let upLevel = ''; 318 let prevPathLength = recursiveUpPath(pathname.length); 319 320 pathLength = pathname.length - prevPathLength; 321 322 for (let i = 0; i < pathLength; i++) { 323 upLevel = upLevel + previousPathLevel; 324 } 325 326 return upLevel; 327} 328 329/** 330 * Recursive Up Path. 331 * 332 * Recursively go up one level of the pathname. Minus one from the path 333 * length, and check if it 334 */ 335function recursiveUpPath(pathnameLength) { 336 // pathnameLength = pathnameLength - 1; 337 338 if (pathnameLength > 5) { 339 return recursiveUpPath(pathnameLength - 1); 340 } 341 342 return pathnameLength; 343} 344 345/** 346 * Check if string is empty or has whitespace. 347 * 348 * Check if string is empty or if the string is just whitespace. 349 * 350 * @param string str Subnav text. 351 * 352 * @return boolean 353 */ 354function isEmptyOrSpaces(str) { 355 return str === null || str.match(/^ *$/) !== null; 356} 357 358addLinksToSubnav(); 359addLinksToSubSubnav();
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.