PageSourceSearch

https://codegen.on.websim.com/startingPopup.js

js codegen.on.websim.com collected 2026-10-03 05:06:25 UTC 8,590 bytes, 212 lines download raw bytes

1/**
2 * Logic for the starting support popup.
3 *
4 * NOTE: The popup will now only be shown after the user performs an interaction
5 * (mouse move/wheel, click, touchstart, keyboard, or input). Listeners are
6 * removed once the popup is shown to avoid side-effects and to prevent the
7 * popup from appearing in thumbnails/screenshots.
8 */
9export function show() {
10  // If the user has permanently dismissed the popup, don't show it.
11  if (localStorage.getItem('popupNeverShow') === 'true') return;
12
13  // Guard to ensure we only show once per page load
14  if (window.__startingPopupShownOnce) return;
15
16  // Inject animation styles (only once)
17  if (!document.getElementById('popup-animations-style')) {
18    const style = document.createElement('style');
19    style.id = 'popup-animations-style';
20    style.textContent = `
21      @keyframes popupFadeIn { from { opacity: 0; } to { opacity: 1; } }
22      @keyframes popupFadeOut { from { opacity: 1; } to { opacity: 0; } }
23      @keyframes popupScaleIn { 
24        from { transform: scale(0.9) translateY(20px); opacity: 0; } 
25        to { transform: scale(1) translateY(0); opacity: 1; } 
26      }
27      @keyframes popupScaleOut { 
28        from { transform: scale(1) translateY(0); opacity: 1; } 
29        to { transform: scale(0.9) translateY(-20px); opacity: 0; } 
30      }
31      .popup-overlay-entering { animation: popupFadeIn 0.4s ease forwards; }
32      .popup-overlay-exiting { animation: popupFadeOut 0.3s ease forwards; }
33      .popup-modal-entering { animation: popupScaleIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; }
34      .popup-modal-exiting { animation: popupScaleOut 0.3s ease forwards; }
35    `;
36    document.head.appendChild(style);
37  }
38
39  const closedInitially = localStorage.getItem('popupClosedInitally') === 'true';
40
41  // Create a lazy-show function that builds and appends the modal when triggered
42  function createAndShowPopup() {
43    // Prevent double-show
44    if (window.__startingPopupShownOnce) return;
45    window.__startingPopupShownOnce = true;
46
47    // Create the overlay container
48    const overlay = document.createElement('div');
49    overlay.id = 'starting-popup-overlay';
50    overlay.className = 'popup-overlay-entering';
51    overlay.style.cssText = `
52      position: fixed;
53      top: 0;
54      left: 0;
55      width: 100%;
56      height: 100%;
57      background-color: rgba(0, 0, 0, 0.85);
58      display: flex;
59      justify-content: center;
60      align-items: center;
61      z-index: 10000; /* Ensure it's above everything else */
62      backdrop-filter: blur(8px);
63      -webkit-backdrop-filter: blur(8px);
64      opacity: 0;
65    `;
66
67    // Create the modal content using existing mode-styled classes
68    const modal = document.createElement('div');
69    modal.className = 'modal-content popup-modal-entering';
70    modal.style.maxWidth = '420px';
71    modal.style.padding = '30px';
72    modal.style.opacity = '0';
73
74    // Conditional checkbox for "Never show again"
75    let checkboxHtml = '';
76    if (closedInitially) {
77      checkboxHtml = `
78        <div class="checkbox-group" style="justify-content: center; margin-top: 20px; padding-top: 15px; border-top: 1px solid var(--mode-form-border);">
79          <input type="checkbox" id="never-show-starting-popup">
80          <label for="never-show-starting-popup" style="margin-bottom: 0; font-size: 0.9em; cursor: pointer;">Don't show this again</label>
81        </div>
82      `;
83    }
84
85    modal.innerHTML = `
86      <h2 style="color: var(--mode-primary); margin-top: 0; font-size: 1.8em;">Support the Project!</h2>
87      <p style="color: var(--mode-label); line-height: 1.6; font-size: 1.05em; margin-bottom: 25px;">
88        If you find this tool helpful, please consider giving it a <b>Like</b>! <br><br>
89        Your support helps us reach the <b>Hot Page</b> so more developers can discover and use these tools.
90      </p>
91      <div style="display: flex; gap: 12px; justify-content: center;">
92        <button id="btn-liked" class="modal-close-button" style="flex: 1; padding: 12px;">I Liked!</button>
93        <button id="btn-later" class="modal-close-button" style="flex: 1; padding: 12px; background: transparent; border: 1px solid var(--mode-primary); color: var(--mode-primary);">Maybe later</button>
94      </div>
95      ${checkboxHtml}
96    `;
97
98    overlay.appendChild(modal);
99    document.body.appendChild(overlay);
100
101    // Common close logic
102    const handleDismiss = (shouldSaveInitial) => {
103      if (shouldSaveInitial) {
104        localStorage.setItem('popupClosedInitally', 'true');
105      }
106
107      const neverCheckbox = document.getElementById('never-show-starting-popup');
108      if (neverCheckbox && neverCheckbox.checked) {
109        localStorage.setItem('popupNeverShow', 'true');
110      }
111
112      // Exit animations
113      overlay.classList.remove('popup-overlay-entering');
114      modal.classList.remove('popup-modal-entering');
115      overlay.classList.add('popup-overlay-exiting');
116      modal.classList.add('popup-modal-exiting');
117
118      overlay.addEventListener('animationend', () => {
119        overlay.remove();
120      }, { once: true });
121    };
122
123    // Button Listeners
124    document.getElementById('btn-liked').addEventListener('click', () => {
125      const neverCheckbox = document.getElementById('never-show-starting-popup');
126      if (neverCheckbox && neverCheckbox.checked) {
127        localStorage.setItem('popupNeverShow', 'true');
128      }
129
130      // Change content to thank the user
131      modal.innerHTML = `
132        <h2 style="color: var(--mode-primary); margin-top: 0; font-size: 1.8em;">Thank You!</h2>
133        <p style="color: var(--mode-label); line-height: 1.6; font-size: 1.1em;">
134          We really appreciate your support! <br><br>
135          Closing in a moment...
136        </p>
137        <div style="font-size: 2em; margin-top: 10px;">❤️</div>
138      `;
139
140      // Set flag and close after delay
141      localStorage.setItem('popupClosedInitally', 'true');
142      setTimeout(() => {
143        overlay.classList.remove('popup-overlay-entering');
144        modal.classList.remove('popup-modal-entering');
145        overlay.classList.add('popup-overlay-exiting');
146        modal.classList.add('popup-modal-exiting');
147
148        overlay.addEventListener('animationend', () => {
149          overlay.remove();
150        }, { once: true });
151      }, 2500);
152    });
153
154    document.getElementById('btn-later').addEventListener('click', () => handleDismiss(false));
155  }
156
157  // Remove registered listeners once triggered
158  function removeTriggerListeners() {
159    triggerEvents.forEach(({ target, type, handler }) => {
160      try { target.removeEventListener(type, handler, listenerOptions); } catch (e) { /* ignore */ }
161    });
162  }
163
164  // When any of these interactions happen, show the popup once and cleanup listeners.
165  const listenerOptions = { passive: true, capture: true };
166  let triggered = false;
167
168  function interactionHandler(e) {
169    if (triggered) return;
170    triggered = true;
171    removeTriggerListeners();
172    // Small delay to avoid interfering with immediate UI events; also prevents popup in thumbnails.
173    setTimeout(createAndShowPopup, 100);
174  }
175
176  // Define a variety of reasonable user interactions to listen for
177  const triggerEvents = [
178    { target: window, type: 'mousemove', handler: interactionHandler },
179    { target: window, type: 'wheel', handler: interactionHandler },
180    { target: window, type: 'keydown', handler: interactionHandler },
181    { target: window, type: 'touchstart', handler: interactionHandler },
182    { target: document, type: 'click', handler: interactionHandler },
183    { target: document, type: 'input', handler: interactionHandler }
184  ];
185
186  // Attach listeners
187  triggerEvents.forEach(({ target, type, handler }) => {
188    try { target.addEventListener(type, handler, listenerOptions); } catch (e) { /* ignore */ }
189  });
190
191  // Safety: also ensure we don't wait forever — show popup after a long delay (e.g., 30s)
192  const LONG_TIMEOUT_MS = 30000;
193  const fallbackTimer = setTimeout(() => {
194    if (!window.__startingPopupShownOnce && !localStorage.getItem('popupNeverShow')) {
195      removeTriggerListeners();
196      createAndShowPopup();
197    }
198  }, LONG_TIMEOUT_MS);
199
200  // If user disabled popup permanently while on-page (rare), ensure listeners removed
201  const checkNeverShowInterval = setInterval(() => {
202    if (localStorage.getItem('popupNeverShow') === 'true') {
203      removeTriggerListeners();
204      clearInterval(checkNeverShowInterval);
205      clearTimeout(fallbackTimer);
206    }
207    if (window.__startingPopupShownOnce) {
208      clearInterval(checkNeverShowInterval);
209      clearTimeout(fallbackTimer);
210    }
211  }, 1000);
212}

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.