PageSourceSearch

https://sysagent.ai/script.js?v=2.3

js sysagent.ai collected 2026-10-05 12:42:56 UTC 25,138 bytes, 555 lines download raw bytes

1// Version: 2.1
2console.log('AI System Agent Landing Page v2.1 Loaded');
3
4document.addEventListener('DOMContentLoaded', () => {
5
6    // Animation Observer
7    const observerOptions = {
8        root: null,
9        rootMargin: '0px',
10        threshold: 0.1
11    };
12
13    const observer = new IntersectionObserver((entries, observer) => {
14        entries.forEach(entry => {
15            if (entry.isIntersecting) {
16                entry.target.classList.add('animate-fade-in-up');
17                entry.target.style.opacity = '1';
18                observer.unobserve(entry.target);
19            }
20        });
21    }, observerOptions);
22
23    document.querySelectorAll('.animate-on-scroll').forEach(el => {
24        el.style.opacity = '0';
25        observer.observe(el);
26    });
27
28    // Mobile Menu Toggle
29    const btn = document.getElementById('mobile-menu-btn');
30    const menu = document.getElementById('mobile-menu');
31
32    if (btn && menu) {
33        btn.addEventListener('click', () => {
34            menu.classList.toggle('hidden');
35        });
36    }
37
38    // Smooth Scroll for anchor links
39    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
40        anchor.addEventListener('click', function (e) {
41            e.preventDefault();
42            menu?.classList.add('hidden'); // Close mobile menu if open
43
44            const href = this.getAttribute('href');
45            if (href === '#') {
46                window.scrollTo({
47                    top: 0,
48                    behavior: 'smooth'
49                });
50                return;
51            }
52
53            const targetElement = document.querySelector(href);
54            if (targetElement) {
55                targetElement.scrollIntoView({
56                    behavior: 'smooth'
57                });
58            }
59        });
60    });
61
62    // Navbar scroll effect
63    const navbar = document.getElementById('navbar');
64    window.addEventListener('scroll', () => {
65        if (window.scrollY > 50) {
66            navbar.classList.add('shadow-lg', 'bg-dark-bg/95');
67        } else {
68            navbar.classList.remove('shadow-lg', 'bg-dark-bg/95');
69        }
70    });
71
72    // Google Consent Mode v2 Logic
73    initCookieConsent();
74
75    // Interactive Demo Animation
76    setupDemoAnimation();
77
78    // Forward marketing attribution (UTM tags / referrer) to the app so we can
79    // track which campaigns and landing-page visits convert into sign-ups.
80    forwardAttributionToAppLinks();
81});
82
83function initCookieConsent() {
84    const banner = document.getElementById('cookie-banner');
85    const btnAcceptAll = document.getElementById('cookie-accept');
86    const btnDeclineAll = document.getElementById('cookie-decline');
87    const btnPreferences = document.getElementById('cookie-preferences');
88
89    const modal = document.getElementById('cookie-modal');
90    const modalContent = document.getElementById('cookie-modal-content');
91    const btnCloseModal = document.getElementById('close-cookie-modal');
92    const btnSavePreferences = document.getElementById('save-cookie-preferences');
93
94    const toggleAnalytics = document.getElementById('toggle-analytics');
95    const toggleMarketing = document.getElementById('toggle-marketing');
96
97    if (!banner) return;
98
99    // Helper: Hide banner
100    const hideBanner = () => {
101        banner.classList.add('translate-y-full');
102        setTimeout(() => { banner.style.display = 'none'; }, 300);
103    };
104
105    // Helper: Show modal
106    const showModal = () => {
107        modal.classList.remove('opacity-0', 'pointer-events-none');
108        modalContent.classList.remove('scale-95');
109    };
110
111    // Helper: Hide modal
112    const hideModal = () => {
113        modal.classList.add('opacity-0', 'pointer-events-none');
114        modalContent.classList.add('scale-95');
115    };
116
117    // Helper: Update Google Consent Mode via gtag
118    const updateGtagConsent = (analyticsGranted, adGranted) => {
119        if (typeof gtag === 'function') {
120            gtag('consent', 'update', {
121                'analytics_storage': analyticsGranted ? 'granted' : 'denied',
122                'ad_storage': adGranted ? 'granted' : 'denied',
123                'ad_user_data': adGranted ? 'granted' : 'denied',
124                'ad_personalization': adGranted ? 'granted' : 'denied'
125            });
126        }
127    };
128
129    // Check existing consent
130    const storedConsent = localStorage.getItem('consentMode');
131
132    if (!storedConsent) {
133        // Show banner after delay
134        setTimeout(() => {
135            banner.style.display = 'block';
136            banner.offsetHeight; // force reflow
137            banner.classList.remove('translate-y-full');
138        }, 1000);
139    } else {
140        // Apply stored consent immediately
141        try {
142            const consentObj = JSON.parse(storedConsent);
143            updateGtagConsent(consentObj.analytics, consentObj.marketing);
144
145            // Sync toggle UI with stored state
146            if (toggleAnalytics) toggleAnalytics.checked = consentObj.analytics;
147            if (toggleMarketing) toggleMarketing.checked = consentObj.marketing;
148        } catch(e) {
149            console.error("Error parsing consent data", e);
150        }
151    }
152
153    // Accept All Click
154    if (btnAcceptAll) {
155        btnAcceptAll.addEventListener('click', () => {
156            const state = { analytics: true, marketing: true };
157            localStorage.setItem('consentMode', JSON.stringify(state));
158            updateGtagConsent(true, true);
159
160            if (toggleAnalytics) toggleAnalytics.checked = true;
161            if (toggleMarketing) toggleMarketing.checked = true;
162
163            hideBanner();
164        });
165    }
166
167    // Decline All Click
168    if (btnDeclineAll) {
169        btnDeclineAll.addEventListener('click', () => {
170            const state = { analytics: false, marketing: false };
171            localStorage.setItem('consentMode', JSON.stringify(state));
172            updateGtagConsent(false, false);
173
174            if (toggleAnalytics) toggleAnalytics.checked = false;
175            if (toggleMarketing) toggleMarketing.checked = false;
176
177            hideBanner();
178        });
179    }
180
181    // Preferences Click
182    if (btnPreferences) {
183        btnPreferences.addEventListener('click', () => {
184            showModal();
185        });
186    }
187
188    // Modal Close
189    if (btnCloseModal) {
190        btnCloseModal.addEventListener('click', hideModal);
191    }
192
193    // Click outside to close modal
194    if (modal) {
195        modal.addEventListener('click', (e) => {
196            if (e.target === modal) {
197                hideModal();
198            }
199        });
200    }
201
202    // Save Preferences Click
203    if (btnSavePreferences) {
204        btnSavePreferences.addEventListener('click', () => {
205            const state = {
206                analytics: toggleAnalytics ? toggleAnalytics.checked : false,
207                marketing: toggleMarketing ? toggleMarketing.checked : false
208            };
209
210            localStorage.setItem('consentMode', JSON.stringify(state));
211            updateGtagConsent(state.analytics, state.marketing);
212
213            hideModal();
214            hideBanner();
215        });
216    }
217}
218
219function setupDemoAnimation() {
220    const inputField = document.getElementById('demo-input');
221    const loadingDiv = document.getElementById('demo-loading');
222    const planContainer = document.getElementById('demo-plan-container');
223    const planContent = document.getElementById('demo-plan-content');
224    const sendBtn = document.getElementById('demo-send-btn');
225    const toast = document.getElementById('demo-status-toast');
226
227    const tabLinux = document.getElementById('tab-linux');
228    const tabWindows = document.getElementById('tab-windows');
229
230    if (!inputField || !loadingDiv || !planContainer || !planContent) return;
231
232    let currentOS = 'linux';
233    let typeTimeout = null;
234    let simTimeout = null;
235    let charIndex = 0;
236
237    const data = {
238        linux: {
239            text: "Install and configure nginx with SSL using Certbot for example.com with root directory /srv/example.com",
240            html: `
241                <!-- Analysis -->
242                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
243                    <div class="bg-gray-800/50 p-3 rounded border border-gray-700">
244                        <h4 class="text-xs font-bold text-gray-400 uppercase mb-2">Problem Description</h4>
245                        <p class="text-sm text-gray-300">Install and configure nginx with SSL using Certbot for example.com with root directory /srv/example.com</p>
246                    </div>
247                    <div class="bg-gray-800/50 p-3 rounded border border-gray-700">
248                        <h4 class="text-xs font-bold text-gray-400 uppercase mb-2">AI Reasoning</h4>
249                        <p class="text-sm text-gray-300">The plan involves installing nginx, setting up the web root directory, configuring a server block, securing with Let's Encrypt SSL via certbot, and adjusting firewall settings.</p>
250                    </div>
251                </div>
252
253                <!-- Recommendations -->
254                <div class="bg-blue-900/10 border border-blue-900/30 p-3 rounded">
255                    <h4 class="text-xs font-bold text-blue-400 uppercase mb-1">Recommendations</h4>
256                    <ul class="list-disc list-inside text-xs text-gray-300 space-y-1">
257                        <li>Ensure DNS records point example.com to your server's IP before starting</li>
258                        <li>Consider taking a snapshot or backup before making system changes</li>
259                        <li>Verify SSL configuration at https://www.ssllabs.com/ssltest/ after completion</li>
260                    </ul>
261                </div>
262
263                <!-- Command List -->
264                <div class="space-y-3">
265                    <h4 class="text-xs font-bold text-gray-400 uppercase border-b border-gray-700 pb-2">Command Plan</h4>
266
267                    <!-- Cmd 1 -->
268                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700">
269                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
270                        <div class="flex-1">
271                            <div class="flex justify-between items-start">
272                                <code class="text-sm font-bold text-neon-green">sudo dnf install nginx</code>
273                                <span class="text-[10px] px-2 py-0.5 rounded bg-green-900/30 text-green-400 border border-green-900">Low Risk</span>
274                            </div>
275                            <p class="text-xs text-gray-400 mt-1">Install nginx web server from default repositories</p>
276                        </div>
277                    </div>
278
279                    <!-- Cmd 2 -->
280                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700">
281                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
282                        <div class="flex-1">
283                            <div class="flex justify-between items-start">
284                                <code class="text-sm font-bold text-neon-green">sudo mkdir -p /srv/example.com</code>
285                                <span class="text-[10px] px-2 py-0.5 rounded bg-green-900/30 text-green-400 border border-green-900">Low Risk</span>
286                            </div>
287                            <p class="text-xs text-gray-400 mt-1">Create the root directory for the website</p>
288                        </div>
289                    </div>
290
291                    <!-- Cmd 3 -->
292                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700">
293                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
294                        <div class="flex-1">
295                            <div class="flex justify-between items-start">
296                                <code class="text-sm font-bold text-neon-green">sudo chown -R nginx:nginx /srv/example.com</code>
297                                <span class="text-[10px] px-2 py-0.5 rounded bg-green-900/30 text-green-400 border border-green-900">Low Risk</span>
298                            </div>
299                            <p class="text-xs text-gray-400 mt-1">Set proper ownership for the web directory</p>
300                        </div>
301                    </div>
302
303                    <!-- Cmd 4 (Highlighted) -->
304                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700 ring-1 ring-neon-purple/30">
305                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
306                        <div class="flex-1">
307                            <div class="flex justify-between items-start">
308                                <code class="text-sm font-bold text-neon-green">sudo certbot --nginx -d example.com</code>
309                                <span class="text-[10px] px-2 py-0.5 rounded bg-yellow-900/30 text-yellow-400 border border-yellow-900">Medium Risk</span>
310                            </div>
311                            <p class="text-xs text-gray-400 mt-1">Obtain and install SSL certificate for example.com</p>
312                        </div>
313                    </div>
314
315                    <!-- More commands indicator -->
316                    <div class="text-center text-xs text-gray-500 pt-2 italic">
317                        + 4 more commands hidden for preview...
318                    </div>
319                </div>
320            `
321        },
322        windows: {
323            text: "Install IIS, create a website for example.com and secure it with Let's Encrypt",
324            html: `
325                <!-- Analysis -->
326                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
327                    <div class="bg-gray-800/50 p-3 rounded border border-gray-700">
328                        <h4 class="text-xs font-bold text-gray-400 uppercase mb-2">Problem Description</h4>
329                        <p class="text-sm text-gray-300">Install IIS, create a website for example.com and secure it with Let's Encrypt</p>
330                    </div>
331                    <div class="bg-gray-800/50 p-3 rounded border border-gray-700">
332                        <h4 class="text-xs font-bold text-gray-400 uppercase mb-2">AI Reasoning</h4>
333                        <p class="text-sm text-gray-300">The plan involves enabling the Windows Web-Server feature, creating the IIS Site bound to port 80, and running win-acme (wacs) to fetch and install the SSL certificate.</p>
334                    </div>
335                </div>
336
337                <!-- Recommendations -->
338                <div class="bg-blue-900/10 border border-blue-900/30 p-3 rounded">
339                    <h4 class="text-xs font-bold text-blue-400 uppercase mb-1">Recommendations</h4>
340                    <ul class="list-disc list-inside text-xs text-gray-300 space-y-1">
341                        <li>Ensure port 80 and 443 are open in Windows Firewall and external security groups</li>
342                        <li>win-acme (wacs.exe) must be installed and available in the system PATH</li>
343                    </ul>
344                </div>
345
346                <!-- Command List -->
347                <div class="space-y-3">
348                    <h4 class="text-xs font-bold text-gray-400 uppercase border-b border-gray-700 pb-2">Command Plan</h4>
349
350                    <!-- Cmd 1 -->
351                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700">
352                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
353                        <div class="flex-1">
354                            <div class="flex justify-between items-start">
355                                <code class="text-sm font-bold text-neon-blue">Install-WindowsFeature -name Web-Server -IncludeManagementTools</code>
356                                <span class="text-[10px] px-2 py-0.5 rounded bg-green-900/30 text-green-400 border border-green-900">Low Risk</span>
357                            </div>
358                            <p class="text-xs text-gray-400 mt-1">Install Internet Information Services (IIS) and management tools</p>
359                        </div>
360                    </div>
361
362                    <!-- Cmd 2 -->
363                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700">
364                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
365                        <div class="flex-1">
366                            <div class="flex justify-between items-start">
367                                <code class="text-sm font-bold text-neon-blue">New-IISSite -Name "example.com" -BindingInformation "*:80:example.com" -PhysicalPath "C:\\inetpub\\wwwroot\\example"</code>
368                                <span class="text-[10px] px-2 py-0.5 rounded bg-green-900/30 text-green-400 border border-green-900">Low Risk</span>
369                            </div>
370                            <p class="text-xs text-gray-400 mt-1">Create the new IIS site and configure bindings</p>
371                        </div>
372                    </div>
373
374                    <!-- Cmd 3 (Highlighted) -->
375                    <div class="group flex items-start gap-3 p-3 rounded bg-gray-800 hover:bg-gray-750 transition border border-gray-700 ring-1 ring-neon-purple/30">
376                        <input type="checkbox" checked class="mt-1 rounded border-gray-600 bg-gray-700 text-neon-blue focus:ring-offset-gray-900">
377                        <div class="flex-1">
378                            <div class="flex justify-between items-start">
379                                <code class="text-sm font-bold text-neon-blue">wacs.exe --source manual --host example.com --installation iis</code>
380                                <span class="text-[10px] px-2 py-0.5 rounded bg-yellow-900/30 text-yellow-400 border border-yellow-900">Medium Risk</span>
381                            </div>
382                            <p class="text-xs text-gray-400 mt-1">Use win-acme to obtain an SSL certificate and configure IIS HTTPS binding</p>
383                        </div>
384                    </div>
385                </div>
386            `
387        }
388    };
389
390    function switchOS(os) {
391        if (currentOS === os) return;
392        currentOS = os;
393
394        // Update tabs styling
395        if (os === 'linux') {
396            tabLinux.className = "flex-1 py-3 text-sm font-bold text-neon-blue border-b-2 border-neon-blue transition-colors bg-gray-800 flex justify-center items-center gap-2";
397            tabWindows.className = "flex-1 py-3 text-sm font-bold text-gray-500 hover:text-gray-300 transition-colors border-b-2 border-transparent hover:bg-gray-800/50 flex justify-center items-center gap-2";
398        } else {
399            tabWindows.className = "flex-1 py-3 text-sm font-bold text-neon-blue border-b-2 border-neon-blue transition-colors bg-gray-800 flex justify-center items-center gap-2";
400            tabLinux.className = "flex-1 py-3 text-sm font-bold text-gray-500 hover:text-gray-300 transition-colors border-b-2 border-transparent hover:bg-gray-800/50 flex justify-center items-center gap-2";
401        }
402
403        // Reset animation state
404        clearTimeout(typeTimeout);
405        clearTimeout(simTimeout);
406        inputField.value = '';
407        planContainer.classList.add('hidden');
408        loadingDiv.classList.add('hidden');
409        charIndex = 0;
410
411        // Start typing
412        typeWriter();
413    }
414
415    if(tabLinux) tabLinux.onclick = () => switchOS('linux');
416    if(tabWindows) tabWindows.onclick = () => switchOS('windows');
417
418    let typeWriterActive = false;
419
420    // Start animation loop
421    function typeWriter() {
422        typeWriterActive = true;
423        const textToType = data[currentOS].text;
424        if (charIndex < textToType.length) {
425            inputField.value += textToType.charAt(charIndex);
426            charIndex++;
427            typeTimeout = setTimeout(typeWriter, 40); // Typing speed
428        } else {
429            // Typing done, wait then simulate execution
430            typeWriterActive = false;
431            simTimeout = setTimeout(simulateExecution, 800);
432        }
433    }
434
435    function simulateExecution() {
436        // Show loading
437        loadingDiv.classList.remove('hidden');
438        planContainer.classList.add('hidden');
439
440        // Simulate processing time
441        simTimeout = setTimeout(() => {
442            loadingDiv.classList.add('hidden');
443            planContent.innerHTML = data[currentOS].html;
444            planContainer.classList.remove('hidden');
445
446            // Add click handler to button
447            if (sendBtn) {
448                sendBtn.onclick = () => {
449                    if (toast) {
450                        toast.classList.remove('translate-y-20', 'opacity-0');
451                        setTimeout(() => {
452                            toast.classList.add('translate-y-20', 'opacity-0');
453                        }, 3000);
454                    }
455                };
456            }
457
458        }, 1500);
459    }
460
461    // Start typing after a slight delay
462    typeTimeout = setTimeout(typeWriter, 1000);
463}
464
465/**
466 * Returns true when a referrer URL points at an EXTERNAL site (not the
467 * marketing site or the app itself). A referrer from ``sysagent.ai`` (any
468 * subdomain) or the current host is internal navigation, not a real traffic
469 * source, so it must not be forwarded as ``landing_referrer``.
470 */
471function isExternalReferrer(referrer) {
472    if (!referrer) {
473        return false;
474    }
475    try {
476        const host = new URL(referrer).hostname.replace(/^www\./, '').toLowerCase();
477        if (!host) {
478            return false;
479        }
480        const currentHost = (window.location.hostname || '').replace(/^www\./, '').toLowerCase();
481        if (currentHost && host === currentHost) {
482            return false;
483        }
484        const internalDomain = 'sysagent.ai';
485        if (host === internalDomain || host.endsWith('.' + internalDomain)) {
486            return false;
487        }
488        return true;
489    } catch (error) {
490        // Malformed referrer URL: treat as not-external (do not forward).
491        return false;
492    }
493}
494
495/**
496 * Forwards marketing attribution to the application so registrations can be
497 * traced back to the campaign / landing-page visit that drove them.
498 *
499 * Standard UTM tags and common click identifiers present on the current URL,
500 * plus the original external referrer, are appended to every call-to-action
501 * link that points at the app (the dashboard / register links). The app then
502 * captures and submits these values during sign-up.
503 */
504function forwardAttributionToAppLinks() {
505    try {
506        const params = new URLSearchParams(window.location.search);
507        const attribution = new URLSearchParams();
508
509        ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'fbclid', 'ref'].forEach((key) => {
510            const value = params.get(key);
511            if (value) {
512                attribution.set(key, value);
513            }
514        });
515
516        // Preserve the original referrer only when it is EXTERNAL: once the
517        // visitor navigates to the app, its document.referrer becomes this
518        // landing page. Forwarding an internal (same-site / sysagent.ai)
519        // referrer would make a direct visit look like a `sysagent.ai` traffic
520        // source, so a direct landing is left unmarked (reported as `direct`).
521        if (document.referrer && isExternalReferrer(document.referrer)) {
522            attribution.set('landing_referrer', document.referrer);
523        }
524
525        if (Array.from(attribution.keys()).length === 0) {
526            return;
527        }
528
529        const isAppLink = (href) => Boolean(href) && (
530            href.includes('register=true')
531            || /app\.sysagent\.ai\/dashboard/i.test(href)
532            || href.includes('/dashboard/')
533        );
534
535        document.querySelectorAll('a[href]').forEach((anchor) => {
536            const href = anchor.getAttribute('href');
537            if (!isAppLink(href)) {
538                return;
539            }
540            try {
541                const url = new URL(href, window.location.href);
542                attribution.forEach((value, key) => {
543                    if (!url.searchParams.has(key)) {
544                        url.searchParams.set(key, value);
545                    }
546                });
547                anchor.setAttribute('href', url.toString());
548            } catch (error) {
549                // Skip malformed hrefs without interrupting the others.
550            }
551        });
552    } catch (error) {
553        // Attribution forwarding is best-effort and must never break the page.
554    }
555}

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.