PageSourceSearch

https://www.ticketfairy.com/resources/js/home_new/header.js?v=1790211420

js ticketfairy.com collected 2026-09-24 09:09:05 UTC 9,473 bytes, 269 lines download raw bytes

1/**
2 * Header nav and burger interactions for new home
3 */
4(function() {
5    var headerBurger = document.querySelector('.header-links-wrap .burger');
6    var menuBurger = document.querySelector('header nav .mobile-menu-header .burger');
7    var nav = document.querySelector('header nav');
8    var body = document.body;
9
10    if (!nav) return;
11
12    function toggleMenu() {
13        nav.classList.toggle('active');
14        body.classList.toggle('menu-open');
15
16        if (headerBurger) {
17            headerBurger.classList.toggle('active');
18        }
19
20        body.style.overflow = nav.classList.contains('active') ? 'hidden' : '';
21
22        if (nav.classList.contains('active') && window.innerWidth <= 768) {
23            setTimeout(function() {
24                updateVisualViewportHeight();
25            }, 100);
26        }
27    }
28
29    if (headerBurger) {
30        headerBurger.addEventListener('click', function(e) {
31            e.preventDefault();
32            e.stopPropagation();
33            toggleMenu();
34        });
35    }
36
37    if (menuBurger) {
38        menuBurger.addEventListener('click', function(e) {
39            e.preventDefault();
40            e.stopPropagation();
41            toggleMenu();
42        });
43    }
44
45    function isTouchDevice() {
46        return ('ontouchstart' in window) ||
47            (navigator.maxTouchPoints > 0) ||
48            (navigator.msMaxTouchPoints > 0);
49    }
50
51    function handleSubmenuClick(e) {
52        var link = e.target.closest('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li > a');
53        if (!link) {
54            link = e.target.closest('header nav > ul > li > a');
55        }
56        if (!link) return;
57
58        var parentLi = link.parentElement;
59        var submenu = parentLi.querySelector('ul');
60
61        if (!submenu) return;
62
63        if (window.innerWidth >= 1024 && !isTouchDevice()) {
64            e.preventDefault();
65            e.stopPropagation();
66
67            var isOpen = parentLi.classList.contains('sub-menu-active');
68
69            document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) {
70                if (item !== parentLi) {
71                    item.classList.remove('sub-menu-active');
72                }
73            });
74
75            if (isOpen) {
76                parentLi.classList.remove('sub-menu-active');
77            } else {
78                parentLi.classList.add('sub-menu-active');
79            }
80            return;
81        }
82
83        e.preventDefault();
84        e.stopPropagation();
85
86        var isOpen = parentLi.classList.contains('sub-menu-active');
87
88        document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) {
89            if (item !== parentLi) {
90                item.classList.remove('sub-menu-active');
91            }
92        });
93
94        if (isOpen) {
95            parentLi.classList.remove('sub-menu-active');
96        } else {
97            parentLi.classList.add('sub-menu-active');
98        }
99    }
100
101    nav.addEventListener('click', handleSubmenuClick);
102
103    document.addEventListener('click', function(e) {
104        if (!e.target.closest('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li') &&
105            !e.target.closest('header nav > ul > li') &&
106            !e.target.closest('header nav')) {
107            document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul > li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) {
108                item.classList.remove('sub-menu-active');
109            });
110        }
111    });
112
113    nav.addEventListener('click', function(e) {
114        if (e.target === nav) {
115            toggleMenu();
116        }
117    });
118
119    var previousWidth = window.innerWidth;
120    window.addEventListener('resize', function() {
121        var currentWidth = window.innerWidth;
122
123        if (previousWidth < 1024 && currentWidth >= 1024) {
124            if (nav.classList.contains('active')) {
125                toggleMenu();
126            }
127            document.querySelectorAll('header nav .mobile-menu-scrollable .mobile-menu-content > ul >
127 li.sub-menu-active, header nav > ul > li.sub-menu-active').forEach(function(item) {
128                item.classList.remove('sub-menu-active');
129            });
130        }
131
132        previousWidth = currentWidth;
133    });
134
135    var header = document.querySelector('header');
136    if (header) {
137        function updateHeaderOnScroll() {
138            var scrollY = window.scrollY || window.pageYOffset;
139
140            if (scrollY === 0) {
141				header.classList.remove('scrolling');
142            } else {
143				header.classList.add('scrolling');
144            }
145        }
146
147        updateHeaderOnScroll();
148
149        window.addEventListener('scroll', updateHeaderOnScroll, { passive: true });
150    }
151
152    // VisualViewport-based height calculation for mobile menu (normal behavior)
153    // Ensures menu and button are always within visible viewport
154    function isMobileDevice() {
155        return window.innerWidth <= 768;
156    }
157
158    function updateVisualViewportHeight() {
159        if (!nav || !isMobileDevice() || !nav.classList.contains('active')) {
160            return;
161        }
162
163        var visualViewport = window.visualViewport;
164        var innerHeight = window.innerHeight;
165
166        if (visualViewport) {
167            // Set nav height to visible viewport height (normal behavior)
168            nav.style.setProperty('--vvh', visualViewport.height + 'px');
169        } else {
170            // Fallback: use window innerHeight
171            nav.style.setProperty('--vvh', innerHeight + 'px');
172        }
173    }
174
175    // Keyboard detection - prevents layout shift when keyboard opens
176    var KEYBOARD_THRESHOLD = 120; // Keyboard is considered open if viewport shrinks by more than this
177
178    function detectKeyboardState() {
179        if (!window.visualViewport || !nav || !nav.classList.contains('active') || !isMobileDevice()) {
180            return;
181        }
182
183        var innerHeight = window.innerHeight;
184        var visualHeight = window.visualViewport.height;
185        var difference = innerHeight - visualHeight;
186        var keyboardOpen = difference > KEYBOARD_THRESHOLD;
187
188        // Add/remove .kb-open class on html element
189        if (keyboardOpen) {
190            document.documentElement.classList.add('kb-open');
191        } else {
192            document.documentElement.classList.remove('kb-open');
193        }
194    }
195
196    // Initialize and set up listeners for VisualViewport changes (normal behavior)
197    if (nav) {
198        // Listen for visual viewport changes (preferred)
199        if (window.visualViewport) {
200            window.visualViewport.addEventListener('resize', function() {
201                if (nav.classList.contains('active') && isMobileDevice()) {
202                    updateVisualViewportHeight(); // Normal behavior
203                    detectKeyboardState(); // Also detect keyboard state
204                }
205            });
206            window.visualViewport.addEventListener('scroll', function() {
207                if (nav.classList.contains('active') && isMobileDevice()) {
208                    updateVisualViewportHeight();
209                }
210            });
211        }
212
213        // Fallback: listen for window resize (less accurate but works on older iOS)
214        var resizeTimeout;
215        window.addEventListener('resize', function() {
216            if (nav.classList.contains('active') && isMobileDevice()) {
217                clearTimeout(resizeTimeout);
218                resizeTimeout = setTimeout(function() {
219                    updateVisualViewportHeight();
220                    detectKeyboardState();
221                }, 100);
222            }
223        }, { passive: true });
224
225        // Also adjust on orientation change
226        window.addEventListener('orientationchange', function() {
227            if (nav.classList.contains('active') && isMobileDevice()) {
228                setTimeout(function() {
229                    updateVisualViewportHeight();
230                    detectKeyboardState();
231                }, 200);
232            }
233        });
234    }
235
236    // Language Dropdown
237    var languageDropdownWrapper = document.querySelector('.language-dropdown-wrapper');
238    var languageToggle = document.querySelector('.language-toggle');
239
240    if (languageToggle && languageDropdownWrapper) {
241        languageToggle.addEventListener('click', function(e) {
242            e.preventDefault();
243            e.stopPropagation();
244            languageDropdownWrapper.classList.toggle('active');
245        });
246
247        // Close dropdown when clicking outside
248        document.addEventListener('click', function(e) {
249            if (!e.target.closest('.language-dropdown-wrapper')) {
250                languageDropdownWrapper.classList.remove('active');
251            }
252        });
253
254        // Handle language option clicks
255        var languageOptions = document.querySelectorAll('.language-option');
256        languageOptions.forEach(function(option) {
257            option.addEventListener('click', function(e) {
258                e.preventDefault();
259                var locale = this.getAttribute('data-lang');
260                if (locale) {
261                    var url = new URL(window.location.href);
262                    url.searchParams.set('locale', locale);
263                    window.location.href = url.toString();
264                }
265            });
266        });
267    }
268})();
269

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.