1var te_dropdown = { 2 ClearAllDropdowns: function () { 3 const visibleDropdowns = $('.dropdown-toggle.show'); 4 if(visibleDropdowns.length > 0) { 5 bootstrap.Dropdown.getOrCreateInstance(visibleDropdowns).hide(); 6 } 7 } 8} 9 10{ 11 const DropdownTimeoutMS = 500; 12 let DropdownTimeoutHandle; 13 14 let NavbarClickMode = false; 15 let DeviceIsHoverclicker = undefined; 16 17 /** 18 * Here is the general behavior of dropdowns: 19 * - The default is to open when the user hovers and close when the user exits after a timeout 20 * - If the user clicks any of the dropdowns, they will switch to being click-only 21 * - We detect if the user clicks immediately after hovering since that implies they are mobile and should be click-only 22 * - If you click or hover to open a dropdown that contains a nation page link, click again on the nation page words to navigate to the nation page 23 * - clicking on the dropdown arrows will always toggle the dropdown and not navigate you to the nation page 24 */ 25 26 $(document).ready(function() { 27 window.addEventListener("scroll", te_dropdown.ClearAllDropdowns); 28 29 function HoverClicker_TemporaryClickEventHandler() { 30 DeviceIsHoverclicker = DeviceIsHoverclicker ?? true; //null coalesce just in case 31 } 32 33 function ExecuteIfNotHoverClickerDevice(action, elementWithHoverAction) { 34 if(DeviceIsHoverclicker === false) { 35 action(); 36 return; 37 } 38 if(DeviceIsHoverclicker === undefined) { 39 elementWithHoverAction.addEventListener("click", HoverClicker_TemporaryClickEventHandler); 40 setTimeout(() => { 41 if(!DeviceIsHoverclicker) { 42 DeviceIsHoverclicker = false; 43 action(); 44 } 45 elementWithHoverAction.removeEventListener("click", HoverClicker_TemporaryClickEventHandler); 46 }, 1); 47 } 48 } 49 50 //For auto-closing the dropdowns when your mouse leaves all dropdown UI 51 $(".te-navbar .dropdown, .te-navbar .dropdown-toggle, .te-navbar .nav-item").hover( 52 function (e) { 53 ExecuteIfNotHoverClickerDevice(() => { 54 if(DropdownTimeoutHandle) { 55 clearTimeout(DropdownTimeoutHandle); 56 } 57 }, e.target); 58 }, 59 function (e) { 60 ExecuteIfNotHoverClickerDevice(() => { 61 if(DropdownTimeoutHandle) { 62 clearTimeout(DropdownTimeoutHandle); 63 } 64 if(!NavbarClickMode) { 65 DropdownTimeoutHandle = setTimeout(() => { 66 te_dropdown.ClearAllDropdowns(); 67 }, DropdownTimeoutMS); 68 } 69 }, e.target); 70 } 71 ); 72 73 /* 74 The story of this additional click handler: 75 1. The dropdowns rely on padding-left to allow for 76 unclipped overflow content on subsequent dropdowns 77 2. This padding-left prevents clicks from triggering a 78 "hover out" event, thus not closing the dropdowns 79 3. Originally, this was solved with a simple 80 "pointer-events: none" style, with a reset on child 81 <li> elements. 82 4. However, this does not work in Safari 83 as the dropdowns were not scrollable, even with the 84 reset style in-place. 85 5. Tweaks were made the <li> elements such that it is 86 no longer possible for a user to click on the <ol> 87 element while attempting to access a dropdown value 88 6. Thus, the <ol> element itself can be made to close 89 the dropdowns for when a user clicks on the padding 90 */ 91 $(".te-navbar .dropdown-menu").on("click", function (e) { 92 te_dropdown.ClearAllDropdowns(); 93 }); 94 95 //Split from the rest of the hover events to prevent closing the dropdown when you leave the nav-link but not the nav-item 96 $(".te-navbar .nav-link").on("mouseenter", function (e) { 97 ExecuteIfNotHoverClickerDevice(() => { 98 if(DropdownTimeoutHandle) { 99 clearTimeout(DropdownTimeoutHandle); 100 } 101 }, e.target); 102 }); 103 104 //For actually openning the dropdowns via mouse hover. Note: this is for both the top-level and the subdropdowns 105 $(".te-navbar .dropdown .dropdown-toggle").on("mouseenter", function (e) { 106 ExecuteIfNotHoverClickerDevice(() => { 107 //Hover actions should never close a dropdown 108 if(!NavbarClickMode && !$(e.target).hasClass("show")) { 109 e.target.click(); 110 e.target.blur(); 111 NavbarClickMode = false;
112 } 113 }, e.target); 114 }); 115 116 // close sibling dropdowns when hovered on an item with no children 117 $(".te-navbar .nav-item:not(.has-children)").on("mouseenter", function (e) { 118 ExecuteIfNotHoverClickerDevice(() => { 119 if(!NavbarClickMode) { 120 //target could be the nav-link or nav-item for some reason... 121 let openDropdownSibling 122 if($(e.target).hasClass("nav-link")) { 123 openDropdownSibling = $(e.target).parent().parent(".dropdown-menu.show").find(".dropdown-toggle.show"); 124 } else { 125 openDropdownSibling = $(e.target).parent(".dropdown-menu.show").find(".dropdown-toggle.show"); 126 } 127 if(openDropdownSibling.length > 0) bootstrap.Dropdown.getOrCreateInstance(openDropdownSibling).hide(); 128 } 129 }, e.target); 130 }); 131 132 //remove the drop-up styling so we can recalculate if that is even necessary 133 $(".te-navbar .dropdown .dropdown .dropdown-toggle").on("hidden.bs.dropdown", function (e) { 134 $(e.target).siblings(".dropdown-menu").toArray().forEach(s => { 135 s.style.maxHeight = "unset"; 136 s.style.top = 0; 137 }) 138 }); 139 140 $(".te-navbar .dropdown .dropdown-toggle").on("click", function (e) { 141 NavbarClickMode = true; //this will trigger for both hovers and real clicks, but it gets reset to false during a hover 142 }); 143 144 // Prevents closing the dropdown when we click between items 145 $(".te-navbar .dropdown .dropdown").on("click", function (e) { 146 // do not prevent default here, that removes the clicks on actual links 147 e.stopPropagation(); 148 }); 149 150 //Generic dropdown click behavior for desktop and mobile 151 $(".te-navbar .dropdown .dropdown .dropdown-toggle").on("click", function (e) { 152 e.preventDefault(); 153 e.stopPropagation(); 154 const ParentsNeedingShown = 155 $(e.target) 156 .closest(".dropdown") //skip this element, we need to toggle instead of show 157 .parents(".dropdown") //all parent dropdowns excluding the dropdown we clicked 158 .children(".dropdown-toggle"); //the element that can actually toggle (the toggles are not direct descendants) 159 let previouslyOpen = $('.dropdown .dropdown .dropdown-toggle.show').not(ParentsNeedingShown).not($(e.target)); 160 161 // we found that after navigating to deeper dropdown levels, lower level dropdowns would not immediately get hidden 162 // I believe this is because of a compatibility issue between jQuery's implicit looping system and Bootstrap 163 // This should only loop as many times as the number of dropdown levels present on the site, at max. 164 while(previouslyOpen.length > 0) { 165 bootstrap.Dropdown.getOrCreateInstance(previouslyOpen).hide(); 166 previouslyOpen = $('.dropdown .dropdown .dropdown-toggle.show').not(ParentsNeedingShown).not($(e.target)); 167 } 168 169 bootstrap.Dropdown.getOrCreateInstance(ParentsNeedingShown).show(); 170 //we used to have a call to toggle here on e.target. Some kind of event timing makes this unnecessary 171 172 HandleDropdownOverflowStyling(); 173 }); 174 175 $(window).on("resize", function (e) { 176 HandleDropdownOverflowStyling(); 177 HandleDropdownOverflowStylingLevelOne(); 178 }); 179 180 $("#navbar-menus > li > button.dropdown-toggle").on("shown.bs.dropdown", function (e) { 181 HandleDropdownOverflowStylingLevelOne(); 182 }); 183 184 $("#navbar-menus > li > a.nav-link:not(*[href^='#'])").on("click", function (e) { 185 e.stopPropagation(); 186 }) 187 188 function HandleDropdownOverflowStylingLevelOne() { 189 $("#navbar-menus:not(.collapse) > li > ol.dropdown-menu.show").toArray().forEach((el) => { 190 let bounds = el.getBoundingClientRect(); 191 let yPos = bounds.y; 192 let height = bounds.height; 193 let totalH = window.innerHeight; 194 let maxH = totalH - yPos; 195 //1/2 pixel because browsers aren't super precise, especially when zooming in or out 196 if(height + 0.5 >= maxH) { 197 //proceed as dropdown with scroll (no dropup) 198 el.style.maxHeight = `${maxH}px`; 199 200 $(el).addClass("scrollable"); 201 } else { 202 el.style.maxHeight = "unset"; //reset so when you zoom in and out the bounding height updates 203 $(el).removeClass("scrollable"); 204 } 205 }); 206 } 207 208 function HandleDropdownOverflowStyling() { 209 //#navbar-menus.collapse would indicate that the hamburgur dropdowns are being used 210 $("#navbar-menus:not(.collapse) .nav-item .nav-item ol.dropdown-menu.show").toArray().forEac
210h((el) => { 211 let bounds = el.getBoundingClientRect(); 212 let yPos = bounds.y; 213 let height = bounds.height; 214 let totalH = window.innerHeight; 215 let maxH = totalH - yPos; 216 let dropUpDiff = height - yPos; 217 let dropDownDiff = totalH - yPos - height; 218 //1/2 pixel because browsers aren't super precise, especially when zooming in or out 219 if(height + 0.5 >= maxH) { 220 //should we drop up instead of adding the scroll bar? 221 if(dropUpDiff < dropDownDiff) { 222 //attempt to dropup 223 el.style.maxHeight = `${yPos}}px`; 224 el.style.top = `calc(100% - ${height}px - 1px)`; //1px for borders 225 if(dropUpDiff > 0.5) { //1/2 pixel because browsers aren't super precise, especially when zooming in or out 226 $(el).addClass("scrollable"); 227 } 228 } else { 229 //proceed as dropdown with scroll 230 el.style.maxHeight = `${maxH}px`; 231 el.style.top = 0; 232 233 $(el).addClass("scrollable"); 234 } 235 } else { 236 $(el).removeClass("scrollable"); 237 } 238 }); 239 } 240 }); 241}
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.