PageSourceSearch

https://www.qldairports.com.au/.resources/gca/webresources/js/menu~2026-09-28-13-00-35-000~cache.js

js qldairports.com.au collected 2026-10-04 15:20:33 UTC 15,475 bytes, 373 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2  const langToggleBtn = document.getElementById('langToggleBtn');
3  const langMenu = document.getElementById('langMenu');
4  const hamburgerBtn = document.getElementById('HamburgerBtn');
5  const navFirstLayer = document.getElementById('nav-first-layer');
6  const navMenu = document.getElementById('mobile-menu');
7  const body = document.body;
8
9  // Store original scroll position before locking
10  let scrollPosition = 0;
11
12  // Function to lock body scroll but allow scrolling within the mobile menu
13  function lockBodyScroll() {
14    // Store current scroll position
15    scrollPosition = window.pageYOffset;
16
17    // Add styles to lock body scroll
18    body.style.overflow = 'hidden';
19    body.style.position = 'fixed';
20    body.style.top = `0`;
21    body.style.width = '100%';
22
23    // Ensure the mobile menu itself is scrollable
24    navMenu.style.overflow = 'auto';
25
26    // Make all second-layer menus scrollable as well
27    document.querySelectorAll('.second-layer').forEach((layer) => {
28      layer.style.overflow = 'auto';
29    });
30  }
31
32  // Function to unlock body scroll
33  function unlockBodyScroll() {
34    body.style.removeProperty('overflow');
35    body.style.removeProperty('position');
36    body.style.removeProperty('top');
37    body.style.removeProperty('width');
38    window.scrollTo(0, scrollPosition);
39  }
40
41  // 1) Language toggle
42  langToggleBtn.addEventListener('click', () => {
43    langMenu.classList.toggle('hidden');
44    const isOpen = !langMenu.classList.contains('hidden');
45    langToggleBtn.setAttribute('aria-expanded', isOpen.toString());
46    
47    // Add/remove active state class using BEM modifier
48    if (isOpen) {
49      langToggleBtn.classList.add('language-toggle--active');
50    } else {
51      langToggleBtn.classList.remove('language-toggle--active');
52    }
53  });
54
55  // Close language dropdown if clicking outside
56  document.addEventListener('click', (event) => {
57    const isInside = langMenu.contains(event.target);
58    const isToggle = langToggleBtn.contains(event.target);
59    if (!isInside && !isToggle && !langMenu.classList.contains('hidden')) {
60      langMenu.classList.add('hidden');
61      langToggleBtn.setAttribute('aria-expanded', 'false');
62      // Remove active state class when closing
63      langToggleBtn.classList.remove('language-toggle--active');
64    }
65  });
66
67  // 2) Hamburger icons
68  // -- The default "hamburger" icon:
69  const hamburgerIconHTML = `
70    <span class="mobile-menu__icon">
71      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
72        <path d="M17.5 10C17.5 10.1658 17.4342 10.3247 17.3169 10.4419C17.1997 10.5592 17.0408 10.625 16.875 10.625H3.125C2.95924 10.625 2.80027 10.5592 2.68306 10.4419C2.56585 10.3247 2.5 10.1658 2.5 10C2.5 9.83424 2.56585 9.67527 2.68306 9.55806C2.80027 9.44085 2.95924 9.375 3.125 9.375H16.875C17.0408 9.375 17.1997 9.44085 17.3169 9.55806C17.4342 9.67527 17.5 9.83424 17.5 10ZM3.125 5.625H16.875C17.0408 5.625 17.1997 5.55915 17.3169 5.44194C17.4342 5.32473 17.5 5.16576 17.5 5C17.5 4.83424 17.4342 4.67527 17.3169 4.55806C17.1997 4.44085 17.0408 4.375 16.875 4.375H3.125C2.95924 4.375 2.80027 4.44085 2.68306 4.55806C2.56585 4.67527 2.5 4.83424 2.5 5C2.5 5.16576 2.56585 5.32473 2.68306 5.44194C2.80027 5.55915 2.95924 5.625 3.125 5.625ZM16.875 14.375H3.125C2.95924 14.375 2.80027 14.4408 2.68306 14.5581C2.56585 14.6753 2.5 14.8342 2.5 15C2.5 15.1658 2.56585 15.3247 2.68306 15.4419C2.80027 15.5592 2.95924 15.625 3.125 15.625H16.875C17.0408 15.625 17.1997 15.5592 17.3169 15.4419C17.4342 15.3247 17.5 15.1658 17.5 15C17.5 14.8342 17.4342 14.6753 17.3169 14.5581C17.1997 14.4408 17.0408 14.375 16.875 14.375Z" fill="currentColor"/>
73      </svg>
74    </span>
75  `;
76
77  // -- The "close" icon:
78  const closeIconHTML = `
79    <span class="mobile-menu__icon">
80      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
81        <path d="M16.067 15.1828C16.1251 15.2409 16.1712 15.3098 16.2026 15.3857C16.234 15.4616 16.2502 15.5429 16.2502 15.625C16.2502 15.7071 16.234 15.7885 16.2026 15.8643C16.1712 15.9402 16.1251 16.0091 16.067 16.0672C16.009 16.1253 15.94 16.1713 15.8642 16.2028C15.7883 16.2342 15.707 16.2504 15.6249 16.2504C15.5427 16.2504 15.4614 16.2342 15.3855 16.2028C15.3097 16.1713 15.2407 16.1253 15.1827 16.0672L9.99986 10.8836L4.81705 16.0672C4.69977 16.1845 4.54071 16.2504 4.37486 16.2504C4.20901 16.2504 4.04995 16.1845 3.93267 16.0672C3.8154 15.9499 3.74951 15.7909 3.74951 15.625C3.74951 15.4592 3.8154 15.3001 3.93267 15.1828L9.11627 10L3.93267 4.8172C3.8154 4.69992 3.74951 4.54086 3.74951 4.37501C3.74951 4.20916 3.8154 4.0501 3.93267 3.93282C4.04995 3.81555 4.20901 3.74966 4.37486 3.74966C4.54071 3.74966 4.69977 3.81555 4.81705 3.93282L9.99986 9.11642L15.1827 3.93282C15.2999 3.81555 15.459 3.74966 15.6249 3.74966C15.7907 3.74966 15.9498 3.81555 16.067 3.93282C16.1843 4.0501 16.2502 4.20916 16.2502 4.37501C16.2502 4.54086 16.1843 4.69992 16.067 4.8172L10.8835 10L16.067 15.1828Z" fill="currentColor"/>
82      </svg>
83    </span>
84  `;
85
86  // Initially show the hamburger icon
87  hamburgerBtn.innerHTML = hamburgerIconHTML;
88
89  // Get references to utility elements
90  const utilityButtonsContainer = document.getElementById('utility-buttons');
91  const langToggleContainer = document.querySelector('.language-selector');
92  const accessibilityButton = document.getElementById('enableRecite');
93  const searchButton = document.getElementById('search-button');
94
95  // On click, toggle menu visibility + replace icon
96  hamburgerBtn.addEventListener('click', () => {
97    navMenu.classList.toggle('hidden');
98    hamburgerBtn.classList.toggle('animate-fade');
99
100    // If navMenu is NOT hidden => menu is open
101    if (!navMenu.classList.contains('hidden')) {
102      hamburgerBtn.innerHTML = closeIconHTML;
103      lockBodyScroll(); // Lock body scroll when menu opens
104
105      // Hide specific utility elements individually
106      if (langToggleContainer) langToggleContainer.classList.add('hidden!');
107      if (accessibilityButton) accessibilityButton.classList.add('hidden!');
108      if (searchButton) searchButton.classList.add('hidden!');
109
110      // Reset to first layer if menu was previously closed in second layer
111      navFirstLayer.classList.remove('hidden');
112      document.querySelectorAll('.second-layer').forEach((secondLayer) => {
113        secondLayer.classList.add('hidden');
114      });
115      if (document.getElementById('BackButtonContainer')) {
116        document.getElementById('BackButtonContainer').classList.add('hidden!');
117      }
118    } else {
119      // If navMenu is hidden => menu is closed
120      hamburgerBtn.innerHTML = hamburgerIconHTML;
121      unlockBodyScroll(); // Unlock body scroll when menu closes
122
123      // Show all utility elements again
124      if (langToggleContainer) langToggleContainer.classList.remove('hidden!');
125      if (accessibilityButton) accessibilityButton.classList.remove('hidden!');
126      if (searchButton) searchButton.classList.remove('hidden!');
127    }
128  });
129
130  // Search functionality
131  const searchBtn = document.getElementById('search-button');
132  const searchForm = document.querySelector('#header form');
133  const searchBar = document.getElementById('search-bar');
134  const desktopNav = document.getElementById('desktop-nav');
135  const searchCancelButton = document.getElementById('search-cancel-button');
136  const searchSubmitButton = document.getElementById('search-submit-button');
137  const logoContainer = document.querySelector('#header > div > a'); // Logo container
138  const clearSearchButton = document.querySelector(
139    'button[aria-label="Clear search"]'
140  );
141  const mobileMenu = document.getElementById('mobile-menu');
142
143  // Initially hide the search form completely
144  if (searchForm) {
145    searchForm.style.display = 'none';
146  }
147
148  // Check if we're on mobile
149  function isMobile() {
150    return window.innerWidth < 768; // Standard md breakpoint
151  }
152
153  // Get the flight info message element from the DOM
154  const flightInfoMessage = document.getElementById('flight-info-message');
155
156  // Append flight info to mobile menu if it doesn't already exist
157  if (
158    mobileMenu &&
159    flightInfoMessage &&
160    !mobileMenu.contains(flightInfoMessage)
161  ) {
162    mobileMenu.appendChild(flightInfoMessage);
163  }
164
165  // Function to show search form
166  function showSearchForm() {
167    if (isMobile()) {
168      // On mobile: show the mobile menu container with flight info only
169      if (mobileMenu) {
170        // Show mobile menu container
171        mobileMenu.classList.remove('hidden');
172        // Add search header gradient class
173        mobileMenu.classList.add('search-header-gradient');
174
175        // Hide regular nav content
176        if (navFirstLayer) navFirstLayer.classList.add('hidden');
177        if (document.getElementById('BackButtonContainer')) {
178          document
179            .getElementById('BackButtonContainer')
180            .classList.add('hidden!');
181        }
182
183        // Hide all second layers
184        document.querySelectorAll('.second-layer').forEach((layer) => {
185          layer.classList.add('hidden');
186        });
187
188        // Show flight info message
189        if (flightInfoMessage) flightInfoMessage.classList.remove('hidden');
190
191        // Change hamburger to close icon
192        if (hamburgerBtn) hamburgerBtn.innerHTML = closeIconHTML;
193
194        // Lock body scroll
195        lockBodyScroll();
196      }
197
198      // Hide the logo and utility buttons on mobile
199      if (logoContainer) logoContainer.classList.add('hidden');
200      if (utilityButtonsContainer)
201        utilityButtonsContainer.classList.add('hidden');
202
203      // Show search form and components
204      if (searchForm) searchForm.style.display = 'flex';
205      if (searchBar) searchBar.classList.remove('hidden');
206      if (searchCancelButton) searchCancelButton.classList.remove('hidden');
207      if (searchSubmitButton) searchSubmitButton.classList.remove('hidden');
208    } else {
209      // On desktop: normal search behavior
210      // Hide desktop nav and utility buttons
211      if (desktopNav) desktopNav.classList.add('hidden!');
212      if (utilityButtonsContainer)
213        utilityButtonsContainer.classList.add('hidden');
214
215      // Show search form and components
216      if (searchForm) searchForm.style.display = 'flex';
217      if (searchBar) searchBar.classList.remove('hidden');
218      if (searchCancelButton) searchCancelButton.classList.remove('hidden');
219      if (searchSubmitButton) searchSubmitButton.classList.remove('hidden');
220    }
221
222    // Focus on the search input
223    const searchInput = document.querySelector('input[name="searchTerm"]');
224    if (searchInput) searchInput.focus();
225  }
226
227  // Function to hide search form
228  function hideSearchForm() {
229    if (isMobile()) {
230      // On mobile: hide the mobile menu
231      if (mobileMenu) {
232        mobileMenu.classList.add('hidden');
233        // Remove search header gradient class
234        mobileMenu.classList.remove('search-header-gradient');
235
236        // Hide flight info message
237        if (flightInfoMessage) flightInfoMessage.classList.add('hidden');
238
239        // Change back to hamburger icon
240        if (hamburgerBtn) hamburgerBtn.innerHTML = hamburgerIconHTML;
241
242        // Unlock body scroll
243        unlockBodyScroll();
244      }
245
246      // Show the logo and utility buttons again on mobile
247      if (logoContainer) logoContainer.classList.remove('hidden');
248      if (utilityButtonsContainer)
249        utilityButtonsContainer.classList.remove('hidden');
250    } else {
251      // On desktop: normal hide behavior
252      // Show desktop nav and utility buttons
253      if (desktopNav) desktopNav.classList.remove('hidden!');
254      if (utilityButtonsContainer)
255        utilityButtonsContainer.classList.remove('hidden');
256    }
257
258    // Hide search form and components for both mobile and desktop
259    if (searchForm) searchForm.style.display = 'none';
260    if (searchBar) searchBar.classList.add('hidden');
261    if (searchCancelButton) searchCancelButton.classList.add('hidden');
262    if (searchSubmitButton) searchSubmitButton.classList.add('hidden');
263  }
264
265  // Handle search button click
266  if (searchBtn) {
267    searchBtn.addEventListener('click', showSearchForm);
268  }
269
270  // Handle cancel button click
271  if (searchCancelButton) {
272    searchCancelButton.addEventListener('click', hideSearchForm);
273  }
274
275  // Handle Escape key to close search
276  document.addEventListener('keydown', (e) => {
277    if (e.key === 'Escape') {
278      // Check if search is currently visible
279      if (searchBar && !searchBar.classList.contains('hidden')) {
280        e.preventDefault();
281        hideSearchForm();
282      }
283    }
284  });
285
286  // Enhance the clear button functionality to also close search on mobile
287  if (clearSearchButton) {
288    const originalOnClick = clearSearchButton.getAttribute('onclick');
289    clearSearchButton.removeAttribute('onclick');
290
291    clearSearchButton.addEventListener('click', function () {
292      // Clear the input (original functionality)
293      const searchInput = document.querySelector('input[name="searchTerm"]');
294      if (searchInput) searchInput.value = '';
295
296      // On mobile, also close the search
297      if (isMobile()) {
298        hideSearchForm();
299      }
300
301      // Focus back on input if we're still showing the search on desktop
302      if (
303        !isMobile() &&
304        searchInput &&
305        !searchBar.classList.contains('hidden')
306      ) {
307        searchInput.focus();
308      }
309    });
310  }
311
312  // Handle clicks outside the search area to cancel
313  document.addEventListener('click', (event) => {
314    const isSearchButtonClick = searchBtn && searchBtn.contains(event.target);
315    const isSearchFormClick = searchForm && searchForm.contains(event.target);
316    const isSearchCancelClick =
317      searchCancelButton && searchCancelButton.contains(event.target);
318
319    // If search is visible and click is outside search area
320    if (
321      searchBar &&
322      !searchBar.classList.contains('hidden') &&
323      !isSearchButtonClick &&
324      !isSearchFormClick &&
325      !isSearchCancelClick
326    ) {
327      hideSearchForm();
328    }
329  });
330
331  const backBtn = document.getElementById('BackBtn');
332
333  // Only add back-button behavior if it exists
334  if (backBtn) {
335    backBtn.addEventListener('click', () => {
336      // Show the first layer menu:
337      navFirstLayer.classList.remove('hidden');
338      navFirstLayer.classList.add('animate-slide-in-left');
339      // Hide ALL second-layer menus:
340      document.querySelectorAll('.second-layer').forEach((secondLayer) => {
341        secondLayer.classList.add('hidden');
342      });
343      document.getElementById('BackButtonContainer').classList.add('hidden!');
344    });
345  }
346
347  // Handle clicks on the first-layer items
348  const firstLayerItems = document.querySelectorAll(
349    '#nav-first-layer li[data-second-layer]'
350  );
351  firstLayerItems.forEach((li) => {
352    li.addEventListener('click', () => {
353      // 1) Get the ID from data attribute
354      const secondLayerId = li.getAttribute('data-second-layer');
355      if (!secondLayerId) return;
356
357      const secondLayerEl = document.getElementById(secondLayerId);
358      if (!secondLayerEl) return;
359
360      // 2) Show that second-layer while hiding the first
361      secondLayerEl.classList.add('animate-slide-in-right');
362      secondLayerEl.classList.remove('hidden');
363      navFirstLayer.classList.add('hidden');
364
365      // 3) Reveal the back button
366      if (document.getElementById('BackButtonContainer')) {
367        document
368          .getElementById('BackButtonContainer')
369          .classList.remove('hidden!');
370      }
371    });
372  });
373});

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.