PageSourceSearch

https://vanillasquad.com/assets/themes/apex/js/app.js?v1.2.13

js vanillasquad.com collected 2026-10-05 03:40:55 UTC 6,194 bytes, 199 lines download raw bytes

1// Custom Dropdown Manager
2class CustomDropdownManager {
3  constructor() {
4    this.activeDropdown = null;
5    this.activeToggle = null;
6    this.dropdownsContainer = null;
7    this.retryCount = 0;
8    this.maxRetries = 5;
9    this.init();
10  }
11
12  init() {
13    // Wait for everything to be fully loaded
14    if (document.querySelectorAll(".custom-dropdown-menu").length === 0) {
15      this.retryCount++;
16      if (this.retryCount < this.maxRetries) {
17        setTimeout(() => this.init(), 100);
18      }
19      return;
20    }
21
22    // Create a container for dropdowns at the body level
23    this.dropdownsContainer = document.createElement("div");
24    this.dropdownsContainer.id = "custom-dropdowns-container";
25    this.dropdownsContainer.style.cssText =
26      "position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10000;";
27    document.body.appendChild(this.dropdownsContainer);
28
29    // Move all existing dropdowns into this container
30    const dropdowns = document.querySelectorAll(".custom-dropdown-menu");
31    dropdowns.forEach((dropdown) => {
32      this.dropdownsContainer.appendChild(dropdown);
33    });
34
35    // Manage all custom dropdowns
36    const toggles = document.querySelectorAll(".custom-dropdown-toggle");
37    toggles.forEach((toggle) => {
38      toggle.addEventListener("click", (e) => {
39        e.preventDefault();
40        e.stopPropagation();
41
42        const dropdownId = toggle.id + "Menu";
43        const dropdown = document.getElementById(dropdownId);
44
45        if (dropdown) {
46          if (
47            this.activeDropdown === dropdown &&
48            dropdown.classList.contains("open")
49          ) {
50            this.closeDropdown(dropdown);
51          } else {
52            this.closeAllDropdowns();
53            this.openDropdown(dropdown, toggle);
54          }
55        }
56      });
57    });
58
59    // Close dropdowns by clicking elsewhere
60    document.addEventListener("click", (e) => {
61      if (
62        !e.target.closest(".custom-dropdown-menu") &&
63        !e.target.closest(".custom-dropdown-toggle")
64      ) {
65        this.closeAllDropdowns();
66      }
67    });
68
69    // Close dropdowns with the Escape key
70    document.addEventListener("keydown", (e) => {
71      if (e.key === "Escape") {
72        this.closeAllDropdowns();
73      }
74    });
75
76    // Close dropdowns on scroll
77    window.addEventListener(
78      "scroll",
79      () => {
80        this.closeAllDropdowns();
81      },
82      true,
83    ); // true to capture scroll in all elements
84
85    // Recalculate position on resize
86    window.addEventListener("resize", () => {
87      if (this.activeDropdown && this.activeToggle) {
88        this.positionDropdown(this.activeDropdown, this.activeToggle);
89      }
90    });
91  }
92
93  openDropdown(dropdown, toggle) {
94    this.activeDropdown = dropdown;
95    this.activeToggle = toggle;
96    dropdown.style.display = "block";
97    this.positionDropdown(dropdown, toggle);
98
99    // Force a reflow
100    dropdown.offsetHeight;
101
102    requestAnimationFrame(() => {
103      dropdown.classList.add("open");
104    });
105  }
106
107  closeDropdown(dropdown) {
108    dropdown.classList.remove("open");
109    // Wait for the end of the animation before hiding
110    setTimeout(() => {
111      if (!dropdown.classList.contains("open")) {
112        dropdown.style.display = "none";
113      }
114    }, 300);
115    if (this.activeDropdown === dropdown) {
116      this.activeDropdown = null;
117      this.activeToggle = null;
118    }
119  }
120
121  closeAllDropdowns() {
122    document.querySelectorAll(".custom-dropdown-menu").forEach((dropdown) => {
123      this.closeDropdown(dropdown);
124    });
125  }
126
127  getZoomLevel() {
128    // Get the HTML zoom level (defined by media queries)
129    return parseFloat(getComputedStyle(document.documentElement).zoom) || 1;
130  }
131
132  positionDropdown(dropdown, toggle) {
133    const rect = toggle.getBoundingClientRect();
134    const windowWidth = window.innerWidth;
135    const windowHeight = window.innerHeight;
136    const margin = 10;
137    const zoom = this.getZoomLevel();
138
139    // Make temporarily visible to measure
140    dropdown.style.visibility = "hidden";
141    dropdown.style.display = "block";
142    const dropdownWidth = dropdown.offsetWidth;
143    const dropdownHeight = dropdown.offsetHeight;
144    dropdown.style.display = "";
145    dropdown.style.visibility = "";
146
147    // Compensate zoom for fixed positions
148    // getBoundingClientRect() returns zoomed values, but position: fixed uses non-zoomed coordinates
149    let top = (rect.bottom + 15) / zoom;
150    let left = (rect.left + rect.width / 2) / zoom - dropdownWidth / 2;
151
152    // Check if the dropdown overflows on the left
153    if (left < margin) {
154      left = margin;
155    }
156
157    // Check if the dropdown overflows on the right (windowWidth is non-zoomed for position fixed)
158    if (left + dropdownWidth > windowWidth / zoom - margin) {
159      left = windowWidth / zoom - dropdownWidth - margin;
160    }
161
162    // Check if the dropdown overflows at the bottom
163    if (top + dropdownHeight > windowHeight / zoom - margin) {
164      // Try to position it above
165      top = rect.top / zoom - dropdownHeight - 15;
166
167      // If it overflows at the top
168      if (top < margin) {
169        // Position it at the maximum visible at the bottom
170        top = Math.min(
171          (rect.bottom + 15) / zoom,
172          windowHeight / zoom - dropdownHeight - margin,
173        );
174        // Last resort: at least margin from the top
175        if (top < margin) {
176          top = margin;
177        }
178      }
179    }
180
181    dropdown.style.top = top + "px";
182    dropdown.style.left = left + "px";
183  }
184}
185
186// Initialization on DOM load
187if (document.readyState === "loading") {
188  document.addEventListener("DOMContentLoaded", () => {
189    window.customDropdownManager = new CustomDropdownManager();
190  });
191} else {
192  window.customDropdownManager = new CustomDropdownManager();
193}
194
195// Bootstrap Modal: Scroll to top instantly when modal opens
196document.addEventListener("show.bs.modal", function () {
197  // Scroll to top without smooth animation
198  window.scrollTo({ top: 0, behavior: "auto" });
199});

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.