1//V1 2 3let dropdownHandlerIsRegistered = false; 4window.dropdownHandler = { 5 initializeClickHandler: function () { 6 if(dropdownHandlerIsRegistered){ 7 return; 8 } 9 10 dropdownHandlerIsRegistered = true; 11 12 document.addEventListener('click', function (e) { 13 const allMenus = document.querySelectorAll('.d-menu'); 14 const clickedButton = e.target.closest('button[data-target]'); 15 const clickedMenu = e.target.closest('.d-menu'); 16 17 if (clickedButton) { 18 // Prevent event propagation to avoid conflicts with parent dropdowns 19 e.stopPropagation(); 20 21 const targetSelector = clickedButton.getAttribute('data-target'); 22 const targetMenu = document.getElementById(targetSelector); 23 24 if (!targetMenu) { 25 return; 26 } 27 28 const isAlreadyVisible = targetMenu.classList.contains('unfolded'); 29 30 if (isAlreadyVisible) { 31 // If already open, close it 32 closeLegacyDropdownMenu(targetMenu); 33 return; 34 } 35 36 // Check if this is a nested dropdown (inside another dropdown) 37 const isNestedDropdown = clickedButton.closest('.d-menu'); 38 39 // Close other dropdowns, but handle nested dropdowns differently 40 allMenus.forEach(menu => { 41 if (menu !== targetMenu) { 42 // For nested dropdowns, only close sibling dropdowns, not parent dropdowns 43 if (isNestedDropdown) { 44 const parentDropdown = clickedButton.closest('.d-menu'); 45 const isParentOrAncestor = menu.contains(parentDropdown) || menu === parentDropdown; 46 if (!isParentOrAncestor) { 47 closeLegacyDropdownMenu(menu); 48 } 49 } else { 50 // For top-level dropdowns, close all others 51 closeLegacyDropdownMenu(menu); 52 } 53 } 54 }); 55 56 // Position the dropdown BEFORE making it visible 57 const rect = clickedButton.getBoundingClientRect(); 58 const viewportWidth = window.innerWidth; 59 const viewportHeight = window.innerHeight; 60 61 // Get the preferred position from CSS classes 62 let preferredPosition = 'zf-BottomRight'; // default 63 if (targetMenu.classList.contains("zf-TopLeft")) preferredPosition = 'zf-TopLeft'; 64 else if (targetMenu.classList.contains("zf-TopRight")) preferredPosition = 'zf-TopRight'; 65 else if (targetMenu.classList.contains("zf-BottomLeft")) preferredPosition = 'zf-BottomLeft'; 66 else if (targetMenu.classList.contains("zf-BottomRight")) preferredPosition = 'zf-BottomRight'; 67 68 // 1. Get the position of the trigger 69 const triggerRect = clickedButton.getBoundingClientRect(); 70 71 // 2. Set up dropdown as hidden 72 targetMenu.style.position = 'fixed'; 73 targetMenu.style.display = 'block'; 74 targetMenu.style.visibility = 'hidden'; 75 targetMenu.style.opacity = '0'; 76 77 // Set appropriate z-index based on nesting level 78 if (isNestedDropdown) { 79 targetMenu.style.zIndex = '100000'; 80 } else { 81 targetMenu.style.zIndex = '99999'; 82 } 83 84 // Match form dropdown menus to their trigger width before measuring. 85 applyTriggerWidth(targetMenu, triggerRect); 86 87 // 4. Get accurate dropdown dimensions 88 const menuRect = targetMenu.getBoundingClientRect(); 89 const optimalPosition = calculateOptimalPosition(triggerRect, menuRect, viewportWidth, viewportHeight, preferredPosition); 90 91 // 5. Apply the calculated position 92 applyDropdownPosition(targetMenu, optimalPosition, triggerRect, viewportWidth, viewportHe
92ight); 93 94 // 6. Make the dropdown visible 95 targetMenu.style.visibility = 'visible'; 96 targetMenu.style.opacity = '1'; 97 window.listKeyboardNavigation.resetContainer(targetMenu); 98 targetMenu.classList.add('unfolded'); 99 100 window.listKeyboardNavigation.focusDropdownTarget(targetMenu); 101 102 return; 103 } 104 105 // If clicked outside of any dropdown or button, close all 106 if (!clickedMenu) { 107 allMenus.forEach(menu => closeLegacyDropdownMenu(menu)); 108 } 109 }, false); 110 111 // Handle window resize to reposition open dropdowns - keep anchored to trigger 112 window.addEventListener('resize', function () { 113 requestAnimationFrame(() => 114 { 115 const openMenus = document.querySelectorAll('.d-menu.unfolded'); 116 openMenus.forEach(menu => { 117 const button = document.querySelector(`button[data-target="${menu.id}"]`); 118 if (button) { 119 // Get current trigger position 120 const triggerRect = button.getBoundingClientRect(); 121 const viewportWidth = window.innerWidth; 122 const viewportHeight = window.innerHeight; 123 124 // Get the preferred position from CSS classes 125 let preferredPosition = 'zf-BottomRight'; // default 126 if (menu.classList.contains("zf-TopLeft")) preferredPosition = 'zf-TopLeft'; 127 else if (menu.classList.contains("zf-TopRight")) preferredPosition = 'zf-TopRight'; 128 else if (menu.classList.contains("zf-BottomLeft")) preferredPosition = 'zf-BottomLeft'; 129 else if (menu.classList.contains("zf-BottomRight")) preferredPosition = 'zf-BottomRight'; 130 131 // Calculate and apply position to keep anchored to trigger 132 applyTriggerWidth(menu, triggerRect); 133 const menuRect = menu.getBoundingClientRect(); 134 const optimalPosition = calculateOptimalPosition(triggerRect, menuRect, viewportWidth, viewportHeight, preferredPosition); 135 applyDropdownPosition(menu, optimalPosition, triggerRect, viewportWidth, viewportHeight); 136 } 137 }); 138 }); 139 }); 140 141 // Handle scroll to reposition dropdowns when scrolling - keep anchored to trigger 142 window.addEventListener('scroll', function () { 143 requestAnimationFrame(() => 144 { 145 const openMenus = document.querySelectorAll('.d-menu.unfolded'); 146 openMenus.forEach(menu => { 147 const button = document.querySelector(`button[data-target="${menu.id}"]`); 148 if (button) { 149 // Get current trigger position 150 const triggerRect = button.getBoundingClientRect(); 151 const viewportWidth = window.innerWidth; 152 const viewportHeight = window.innerHeight; 153 154 // Get the preferred position from CSS classes 155 let preferredPosition = 'zf-BottomRight'; // default 156 if (menu.classList.contains("zf-TopLeft")) preferredPosition = 'zf-TopLeft'; 157 else if (menu.classList.contains("zf-TopRight")) preferredPosition = 'zf-TopRight'; 158 else if (menu.classList.contains("zf-BottomLeft")) preferredPosition = 'zf-BottomLeft'; 159 else if (menu.classList.contains("zf-BottomRight")) preferredPosition = 'zf-BottomRight'; 160 161 // Calculate and apply position to keep anchored to trigger 162 applyTriggerWidth(menu, triggerRect); 163 const menuRect = menu.getBoundingClientRect(); 164 const optimalPosition = calculateOptimalPosition(triggerRect, menuRect, viewportWidth, viewportHe
164ight, preferredPosition); 165 applyDropdownPosition(menu, optimalPosition, triggerRect, viewportWidth, viewportHeight); 166 } 167 }); 168 }); 169 }, true); // Use capture phase to catch scroll events early 170 171 // Handle keyboard navigation (ESC to close; arrows/enter for plain action menus) 172 document.addEventListener('keydown', function (e) { 173 const openMenus = [...document.querySelectorAll('.d-menu.unfolded')]; 174 if (openMenus.length === 0) { 175 return; 176 } 177 178 if (e.key === 'Escape') { 179 openMenus.forEach(closeLegacyDropdownMenu); 180 return; 181 } 182 183 // The topmost open menu owns keyboard navigation. 184 window.listKeyboardNavigation.handleDocumentKeyDown(e, () => openMenus[openMenus.length - 1]); 185 }); 186 } 187}; 188 189function closeLegacyDropdownMenu(menu) { 190 if (!menu) { 191 return; 192 } 193 194 window.listKeyboardNavigation.resetContainer(menu); 195 menu.classList.remove('unfolded'); 196 menu.style.display = 'none'; 197 menu.style.visibility = 'hidden'; 198 menu.style.opacity = '0'; 199} 200 201window.hideLegacyPopover = function (id) { 202 closeLegacyDropdownMenu(document?.getElementById(id)); 203} 204 205// Shared positioning function used by both initial positioning and scroll/resize handlers 206function positionDropdown(menu, buttonRect, viewportWidth, viewportHeight, preferredPosition) { 207 // Temporarily make dropdown visible to get accurate dimensions 208 const wasHidden = menu.style.visibility === 'hidden'; 209 if (wasHidden) { 210 menu.style.visibility = 'visible'; 211 menu.style.opacity = '0'; // Keep it invisible but get dimensions 212 } 213 214 applyTriggerWidth(menu, buttonRect); 215 const menuRect = menu.getBoundingClientRect(); 216 const optimalPosition = calculateOptimalPosition(buttonRect, menuRect, viewportWidth, viewportHeight, preferredPosition); 217 applyDropdownPosition(menu, optimalPosition, buttonRect, viewportWidth, viewportHeight); 218 219 // Restore hidden state if it was hidden 220 if (wasHidden) { 221 menu.style.visibility = 'hidden'; 222 } 223} 224 225function applyTriggerWidth(menu, buttonRect) { 226 if (buttonRect?.width) { 227 menu.style.minWidth = `${buttonRect.width}px`; 228 } 229} 230 231// Edge detection and positioning logic 232function calculateOptimalPosition(buttonRect, menuRect, viewportWidth, viewportHeight, preferredPosition) { 233 // Calculate available space in each direction 234 const spaceAbove = buttonRect.top; 235 const spaceBelow = viewportHeight - buttonRect.bottom; 236 const spaceLeft = buttonRect.left; 237 const spaceRight = viewportWidth - buttonRect.right; 238 239 // Add padding to ensure dropdown doesn't touch edges 240 const padding = 15; 241 242 // Determine vertical position 243 let verticalPosition; 244 if (preferredPosition.includes('Top')) { 245 // Prefer top, but check if there's enough space 246 if (spaceAbove >= (menuRect.height + padding)) { 247 verticalPosition = 'top'; 248 } else if (spaceBelow >= (menuRect.height + padding)) { 249 verticalPosition = 'bottom'; 250 } else { 251 // Not enough space in either direction, choose the one with more space 252 verticalPosition = spaceAbove > spaceBelow ? 'top' : 'bottom'; 253 } 254 } else { 255 // Prefer bottom, but check if there's enough space 256 if (spaceBelow >= (menuRect.height + padding)) { 257 verticalPosition = 'bottom'; 258 } else if (spaceAbove >= (menuRect.height + padding)) { 259 verticalPosition = 'top'; 260 } else { 261 // Not enough space in either direction, choose the one with more space 262 verticalPosition = spaceBelow > spaceAbove ? 'bottom' : 'top'; 263 } 264 } 265 266 // Determine horizontal position 267 let horizontalPosition; 268 if (preferredPosition.includes('Left')) { 269 // Prefer left, but check if there's enough space 270 if (spaceLeft >= (menuRect.width + padding)) { 271 horizontalPosition = 'left'; 272 }
272 else if (spaceRight >= (menuRect.width + padding)) { 273 horizontalPosition = 'right'; 274 } else { 275 // Not enough space in either direction, choose the one with more space 276 horizontalPosition = spaceLeft > spaceRight ? 'left' : 'right'; 277 } 278 } else { 279 // Prefer right, but check if there's enough space 280 if (spaceRight >= (menuRect.width + padding)) { 281 horizontalPosition = 'right'; 282 } else if (spaceLeft >= (menuRect.width + padding)) { 283 horizontalPosition = 'left'; 284 } else { 285 // Not enough space in either direction, choose the one with more space 286 horizontalPosition = spaceRight > spaceLeft ? 'right' : 'left'; 287 } 288 } 289 290 return { 291 vertical: verticalPosition, 292 horizontal: horizontalPosition 293 }; 294} 295 296function applyDropdownPosition(menu, position, buttonRect, viewportWidth, viewportHeight) { 297 // Reset all positioning styles 298 menu.style.top = ''; 299 menu.style.bottom = ''; 300 menu.style.left = ''; 301 menu.style.right = ''; 302 303 // Apply vertical positioning (using viewport coordinates for fixed positioning) 304 if (position.vertical === 'top') { 305 menu.style.bottom = `${viewportHeight - buttonRect.top}px`; 306 } else { 307 menu.style.top = `${buttonRect.bottom}px`; 308 } 309 310 // Apply horizontal positioning (using viewport coordinates for fixed positioning) 311 if (position.horizontal === 'left') { 312 menu.style.left = `${buttonRect.left}px`; 313 } else { 314 menu.style.right = `${viewportWidth - buttonRect.right}px`; 315 } 316 317 // Immediate safety check - ensure dropdown is within viewport bounds 318 const menuRect = menu.getBoundingClientRect(); 319 const padding = 15; 320 321 // Check if dropdown is off-screen and adjust if necessary 322 if (menuRect.left < padding) { 323 menu.style.left = `${padding}px`; 324 menu.style.right = ''; 325 } 326 if (menuRect.right > viewportWidth - padding) { 327 menu.style.right = `${padding}px`; 328 menu.style.left = ''; 329 } 330 if (menuRect.top < padding) { 331 menu.style.top = `${padding}px`; 332 menu.style.bottom = ''; 333 } 334 if (menuRect.bottom > viewportHeight - padding) { 335 menu.style.bottom = `${padding}px`; 336 menu.style.top = ''; 337 } 338 339 // Additional check for nested dropdowns - ensure they don't go outside parent bounds 340 const parentDropdown = menu.closest('.d-menu'); 341 if (parentDropdown && parentDropdown !== menu) { 342 const parentRect = parentDropdown.getBoundingClientRect(); 343 const currentRect = menu.getBoundingClientRect(); 344 345 // If nested dropdown would go outside parent, adjust position 346 if (currentRect.right > parentRect.right) { 347 menu.style.right = `${viewportWidth - parentRect.right}px`; 348 menu.style.left = ''; 349 } 350 if (currentRect.left < parentRect.left) { 351 menu.style.left = `${parentRect.left}px`; 352 menu.style.right = ''; 353 } 354 } 355} 356 357// Utility function to reposition all open dropdowns 358window.repositionAllDropdowns = function() { 359 const openMenus = document.querySelectorAll('.d-menu.unfolded'); 360 openMenus.forEach(menu => { 361 const button = document.querySelector(`button[data-target="${menu.id}"]`); 362 if (button) { 363 const rect = button.getBoundingClientRect(); 364 const viewportWidth = window.innerWidth; 365 const viewportHeight = window.innerHeight; 366 367 // Get the preferred position from CSS classes 368 let preferredPosition = 'zf-BottomRight'; // default 369 if (menu.classList.contains("zf-TopLeft")) preferredPosition = 'zf-TopLeft'; 370 else if (menu.classList.contains("zf-TopRight")) preferredPosition = 'zf-TopRight'; 371 else if (menu.classList.contains("zf-BottomLeft")) preferredPosition = 'zf-BottomLeft'; 372 else if (menu.classList.contains("zf-BottomRight")) preferredPosition = 'zf-BottomRight'; 373 374 // Use the same positioning logic as all other handlers 375 positionDropdown(menu, rect, viewportWidth, viewportHe
375ight, preferredPosition); 376 } 377 }); 378}; 379 380// Self-initialize as soon as the script loads so dropdowns work even if a Blazor 381// circuit renders before this script registers window.dropdownHandler. 382// initializeClickHandler is idempotent, so any later interop call is a harmless no-op. 383(function registerDropdownHandlerOnLoad() { 384 if (document.readyState === 'loading') { 385 document.addEventListener('DOMContentLoaded', () => window.dropdownHandler.initializeClickHandler()); 386 } else { 387 window.dropdownHandler.initializeClickHandler(); 388 } 389})(); 390 391// Opens a legacy DropDown by its trigger button id (btn-dd-{PopoverId}). 392// Defer the click so an originating UI click (e.g. banner CTA) can finish bubbling 393// through the document listener before we open â otherwise the same gesture closes it. 394// Returns a Promise resolving to whether the click was actually dispatched, so callers 395// awaiting this via Blazor JS interop can tell a real open from a silent no-op. 396window.openLegacyDropdownByButtonId = function (buttonId) { 397 if (!buttonId) { 398 return false; 399 } 400 401 const button = document.getElementById(buttonId); 402 if (!button || !button.getAttribute('data-target')) { 403 return false; 404 } 405 406 button.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); 407 button.focus({ preventScroll: true }); 408 return new Promise(function (resolve) { 409 window.setTimeout(function () { 410 const liveButton = document.getElementById(buttonId); 411 if (liveButton && liveButton.getAttribute('data-target')) { 412 liveButton.click(); 413 resolve(true); 414 } else { 415 resolve(false); 416 } 417 }, 0); 418 }); 419}; 420
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.