PageSourceSearch

https://mijn.warmtestad.nl/_content/Frontend.Shared.Library/DropDownLegacy.vj77h9c2uh.js

js warmtestad.nl collected 2026-10-07 11:41:34 UTC 18,340 bytes, 420 lines download raw bytes

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.