PageSourceSearch

https://sustainableurbansystems.com/assets/js/nav-toggle.js?v=141bd79a73ebc0f0b6ea58e440ae79fc

js sustainableurbansystems.com collected 2026-09-28 06:50:12 UTC 2,997 bytes, 102 lines download raw bytes

1document.addEventListener("DOMContentLoaded", () => {
2  const navToggles = Array.from(document.querySelectorAll("[data-nav-toggle]"));
3
4  const closeNav = (toggle) => {
5    const targetId = toggle.getAttribute("data-nav-toggle") || toggle.getAttribute("aria-controls");
6    if (!targetId) {
7      return;
8    }
9
10    const panel = document.getElementById(targetId);
11    if (!panel) {
12      return;
13    }
14
15    panel.classList.remove("show");
16    toggle.classList.add("collapsed");
17    toggle.setAttribute("aria-expanded", "false");
18  };
19
20  navToggles.forEach((toggle) => {
21    const targetId = toggle.getAttribute("data-nav-toggle") || toggle.getAttribute("aria-controls");
22    if (!targetId) {
23      return;
24    }
25
26    const panel = document.getElementById(targetId);
27    if (!panel) {
28      return;
29    }
30
31    toggle.addEventListener("click", (event) => {
32      event.preventDefault();
33      const isOpen = panel.classList.contains("show");
34      panel.classList.toggle("show", !isOpen);
35      toggle.classList.toggle("collapsed", isOpen);
36      toggle.setAttribute("aria-expanded", String(!isOpen));
37    });
38  });
39
40  const navDropdownToggles = Array.from(document.querySelectorAll("[data-nav-dropdown-toggle]"));
41  const closeAllNavDropdowns = (except) => {
42    navDropdownToggles.forEach((toggle) => {
43      if (except && toggle === except) {
44        return;
45      }
46      const item = toggle.closest(".dropdown");
47      const menu = item?.querySelector(".dropdown-menu");
48      if (!menu) {
49        return;
50      }
51
52      item.classList.remove("show");
53      menu.classList.remove("show");
54      toggle.setAttribute("aria-expanded", "false");
55    });
56  };
57
58  navDropdownToggles.forEach((toggle) => {
59    toggle.addEventListener("click", (event) => {
60      event.preventDefault();
61      event.stopPropagation();
62
63      const item = toggle.closest(".dropdown");
64      const menu = item?.querySelector(".dropdown-menu");
65      if (!item || !menu) {
66        return;
67      }
68
69      const isOpen = menu.classList.contains("show");
70      closeAllNavDropdowns(toggle);
71
72      item.classList.toggle("show", !isOpen);
73      menu.classList.toggle("show", !isOpen);
74      toggle.setAttribute("aria-expanded", String(!isOpen));
75    });
76  });
77
78  document.addEventListener("click", (event) => {
79    navToggles.forEach((toggle) => {
80      const targetId = toggle.getAttribute("data-nav-toggle") || toggle.getAttribute("aria-controls");
81      const panel = targetId ? document.getElementById(targetId) : null;
82      if (!panel || !panel.classList.contains("show")) {
83        return;
84      }
85
86      const clickedInsidePanel = panel.contains(event.target);
87      const clickedToggle = toggle.contains(event.target);
88      if (!clickedInsidePanel && !clickedToggle) {
89        closeNav(toggle);
90      }
91    });
92
93    closeAllNavDropdowns();
94  });
95
96  document.addEventListener("keydown", (event) => {
97    if (event.key === "Escape") {
98      navToggles.forEach(closeNav);
99      closeAllNavDropdowns();
100    }
101  });
102});

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.