1(function () { 2 'use strict'; 3 4 /** 5 * Lightweight polyfills to support older browsers that still appear in some campus environments. 6 * WordPress core ships similar fallbacks, but we add them locally to avoid ordering issues. 7 */ 8 if (!Element.prototype.matches) { 9 Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; 10 } 11 12 if (!Element.prototype.closest) { 13 Element.prototype.closest = function (selector) { 14 var element = this; 15 while (element && element.nodeType === 1) { 16 if (element.matches(selector)) { 17 return element; 18 } 19 element = element.parentElement || element.parentNode; 20 } 21 return null; 22 }; 23 } 24 25 document.addEventListener('DOMContentLoaded', function () { 26 var navigation = document.getElementById('site-navigation'); 27 var sideNav = document.getElementById('sidenav'); 28 var sideNavToggle = document.getElementById('sidenav_toggle_btn'); 29 var pageWrapper = document.getElementById('page'); 30 var htmlElement = document.documentElement; 31 var searchToggle = document.querySelector('.search-btn'); 32 var mainSearch = document.getElementById('main_search'); 33 34 /** 35 * Primary navigation focus handling (desktop mega menu). 36 * Adds/removes a `.focus` class so CSS can present sub-menus while keyboard tabbing. 37 */ 38 if (navigation) { 39 var menu = navigation.querySelector('ul'); 40 41 if (menu) { 42 setupPrimaryMenu(navigation); 43 } 44 } 45 46 /** 47 * Side navigation focus handling (hamburger / drawer menu). 48 * Opens the drawer when focus enters and closes it once focus leaves or Esc is pressed. 49 */ 50 if (sideNav && pageWrapper && htmlElement) { 51 setupSideNavAccessibility(); 52 } 53 54 /** 55 * Initialise keyboard support for the primary navigation. 56 * 57 * @param {HTMLElement} navElement 58 */ 59 function setupPrimaryMenu(navElement) { 60 var FOCUS_CLASS = 'focus'; 61 62 initialiseAriaStates(); 63 64 navElement.addEventListener('focusin', function (event) { 65 var link = event.target.closest('a'); 66 67 if (!link || !navElement.contains(link)) { 68 return; 69 } 70 71 var menuItem = link.closest('li'); 72 73 if (!menuItem) { 74 return; 75 } 76 77 setFocusState(menuItem); 78 }); 79 80 navElement.addEventListener('focusout', function (event) { 81 var nextElement = event.relatedTarget; 82 83 if (!nextElement || !navElement.contains(nextElement)) { 84 clearAllFocus(); 85 } 86 }); 87 88 navElement.addEventListener('keydown', function (event) { 89 if (event.key === 'Escape' || event.key === 'Esc') { 90 clearAllFocus(); 91 } 92 }); 93 94 function setFocusState(menuItem) { 95 removeSiblingFocus(menuItem); 96 addFocusToAncestors(menuItem); 97 syncAriaExpanded(); 98 } 99 100 function removeSiblingFocus(menuItem) { 101 var parentList = menuItem.parentElement; 102 103 if (!parentList) { 104 return; 105 } 106 107 Array.prototype.forEach.call(parentList.children, function (sibling) { 108 if (sibling !== menuItem) { 109 removeFocusRecursive(sibling); 110 } 111 }); 112 } 113 114 function addFocusToAncestors(menuItem) { 115 var current = menuItem; 116 117 while (current && current !== navElement) { 118 if (current.tagName && current.tagName.toLowerCase() === 'li') { 119 current.classList.add(FOCUS_CLASS); 120 } 121 122 current = current.parentElement; 123 124 if (current && current.tagName && current.tagName.toLowerCase() === 'ul') { 125 current = current.parentElement; 126 } 127 } 128 } 129 130 function removeFocusRecursive(element) { 131 element.classList.remove(FOCUS_CLASS); 132 133 var descendants = element.querySelectorAll('.' + FOCUS_CLASS); 134 135 Array.prototype.forEach.call(descendants, function (descendant) { 136 descendant.classList.remove(FOCUS_CLASS); 137 }); 138 } 139 140 function clearAllFocus() { 141 var focusedItems = navElement.querySelectorAll('.' + FOCUS_CLASS); 142 143 Array.prototype.forEach.call(focusedItems, function (item) { 144 item.classList.remove(FOCUS_CLASS); 145 }); 146 147 syncAriaExpanded(); 148 } 149 150 function initialiseAriaStates() { 151 var toggleLinks = navElement.querySelectorAll('.menu-item-has-children > a, .page_item_has_children > a'); 152 153 Array.prototype.forEach.call(toggleLinks, function (link) { 154 if (!link.hasAttribute('aria-expanded')) { 155 link.setAttribute('aria-expanded', 'false'); 156 } 157 }); 158 } 159 160 function syncAriaExpanded() { 161 var parentItems = navElement.querySelectorAll('.menu-item-has-children, .page_item_has_children'); 162 163 Array.prototype.forEach.call(parentItems, function (item) { 164 var trigger = item.querySelector('a'); 165 166 if (!trigger) { 167 return; 168 } 169 170 if (item.classList.contains(FOCUS_CLASS)) { 171 trigger.setAttribute('aria-expanded', 'true'); 172 } else { 173 trigger.setAttribute('aria-expanded', 'false'); 174 } 175 }); 176 } 177 } 178 179 /**
180 * Attach focus management to the sidenav drawer. 181 */ 182 function setupSideNavAccessibility() { 183 sideNav.addEventListener('focusin', function () { 184 openSideNav(); 185 }); 186 187 sideNav.addEventListener('focusout', function (event) { 188 var next = event.relatedTarget; 189 190 if (!next || !sideNav.contains(next)) { 191 closeSideNav(); 192 } 193 }); 194 195 sideNav.addEventListener('keydown', function (event) { 196 if (event.key === 'Escape' || event.key === 'Esc') { 197 closeSideNav(); 198 199 if (sideNavToggle) { 200 sideNavToggle.focus(); 201 } 202 } 203 }); 204 } 205 206 function openSideNav() { 207 if (pageWrapper.classList.contains('sidenav-open')) { 208 return; 209 } 210 211 if (searchToggle) { 212 searchToggle.classList.remove('search-open'); 213 } 214 215 if (mainSearch) { 216 mainSearch.classList.remove('search-open'); 217 } 218 219 pageWrapper.classList.remove('search-open'); 220 htmlElement.classList.remove('search-open'); 221 222 pageWrapper.classList.add('sidenav-open'); 223 htmlElement.classList.add('sidenav-open'); 224 sideNav.classList.add('sidenav-open'); 225 226 if (sideNavToggle) { 227 sideNavToggle.classList.add('sidenav-open'); 228 } 229 } 230 231 function closeSideNav() { 232 pageWrapper.classList.remove('sidenav-open'); 233 htmlElement.classList.remove('sidenav-open'); 234 235 if (sideNav) { 236 sideNav.classList.remove('sidenav-open'); 237 } 238 239 if (sideNavToggle) { 240 sideNavToggle.classList.remove('sidenav-open'); 241 } 242 } 243 }); 244}()); 245
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.