PageSourceSearch

https://fakevoice.app/contact.js

js fakevoice.app collected 2026-09-24 11:03:54 UTC 6,301 bytes, 160 lines download raw bytes

1import { analytics, logEvent } from './firebase-analytics.js';
2
3document.addEventListener('DOMContentLoaded', () => {
4    const form = document.getElementById('contactForm');
5    const nameInput = document.getElementById('name');
6    const emailInput = document.getElementById('email');
7    const messageInput = document.getElementById('message');
8    const subjectInput = document.getElementById('subject');
9    const submitBtn = document.getElementById('submitBtn');
10    const statusMessage = document.getElementById('statusMessage');
11
12    // Auto-fill hidden subject with context
13    const setSubject = () => {
14        const time = new Date().toLocaleString();
15        const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
16        subjectInput.value = `New Support Request from Website | ${time} | ${timezone}`;
17    };
18    setSubject();
19
20    const showError = (element, message) => {
21        const errorText = document.getElementById(`${element.id}Error`);
22        element.classList.add('border-red-500');
23        errorText.
23innerText = message;
24        errorText.classList.remove('hidden');
25    };
26
27    const clearError = (element) => {
28        const errorText = document.getElementById(`${element.id}Error`);
29        element.classList.remove('border-red-500');
30        errorText.classList.add('hidden');
31    };
32
33    const validate = () => {
34        let isValid = true;
35        if (!nameInput.value.trim()) {
36            showError(nameInput, 'Name is required');
37            isValid = false;
38        } else {
39            clearError(nameInput);
40        }
41
42        const email = emailInput.value.trim();
43        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
44        if (!email) {
45            showError(emailInput, 'Email is required');
46            isValid = false;
47        } else if (!emailRegex.test(email)) {
48            showError(emailInput, 'Please enter a valid email address');
49            isValid = false;
50        } else {
51            clearError(emailInput);
52        }
53
54        if (!messageInput.value.trim()) {
55            showError(messageInput, 'Message cannot be empty');
56            isValid = false;
57        } else {
58            clearError(messageInput);
59        }
60        return isValid;
61    };
62
63    form.addEventListener('submit', async (e) => {
64        e.preventDefault();
65
66        if (!validate()) return;
67
68        // Robot Check (Honeypot) - if filled, it's a bot
69        const honeypot = document.getElementById('honeypot').value;
70        if (honeypot) {
71            console.log("Bot detected.");
72            return;
73        }
74
75        // UI Loading State
76        const originalBtnText = submitBtn.innerHTML;
77        submitBtn.innerHTML = `<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>`;
78        submitBtn.disabled = true;
79        statusMessage.classList.add('hidden');
80
81        try {
82            const token = await new Promise((resolve) => {
83                grecaptcha.enterprise.ready(() => {
84                    grecaptcha.enterprise.execute('6LeMq6gtAAAAAKi9m2mgG5s4uIcBR9WBBdBwegqa', { action: 'submit' }).then(resolve);
85                });
86            });
87
88            // Verify reCAPTCHA via worker (secret key never leaves the worker)
89            const captchaRes = await fetch('https://fakevoice.zczvwz6pw7.workers.dev/recaptcha', {
90                method: 'POST',
91                headers: { 'Content-Type': 'application/json' },
92                body: JSON.stringify({ token }),
93            });
94            const captchaData = await captchaRes.json();
95            if (!captchaData.success) {
96                throw new Error(captchaData.message || 'Bot detected (score low).');
97            }
98
99            const formData = new FormData(form);
100
101            const response = await fetch('/send_email.php', {
102                method: 'POST',
103                body: formData
104            });
105
106            const result = await response.json();
107
108            if (result.success) {
109                // Log Analytics Event
110                logEvent(analytics, 'contact_form_send', {
111                    page_location: window.location.href,
112                    form_id: 'contactForm'
113                });
114
115                statusMessage.className = 'mt-4 p-4 rounded-xl text-sm font-medium bg-green-50 text-green-700 border border-green-200';
116                statusMessage.innerHTML = '✨ Message sent successfully! We will get back to you soon.';
117                form.reset();
118                setSubject(); // Reset subject timestamp
119            } else {
120                throw new Error(result.message || 'Failed to send message.');
121            }
122        } catch (error) {
123            statusMessage.className = 'mt-4 p-4 rounded-xl text-sm font-medium bg-red-50 text-red-700 border border-red-200';
124            statusMessage.innerHTML = '⚠️ ' + error.message;
125        } finally {
126            statusMessage.classList.remove('hidden');
127            submitBtn.innerHTML = originalBtnText;
128            submitBtn.disabled = false;
129        }
130    });
131
132    // Initial check
133    const updateSubmitButton = () => {
134        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
135        const isValid = nameInput.value.trim() &&
136            emailInput.value.trim() &&
137            emailRegex.test(emailInput.value.trim()) &&
138            messageInput.value.trim();
139
140        submitBtn.disabled = !isValid;
141        if (isValid) {
142            submitBtn.classList.remove('opacity-50', 'cursor-not-allowed');
143            submitBtn.classList.add('hover:bg-blue-700', 'shadow-lg');
144        } else {
145            submitBtn.classList.add('opacity-50', 'cursor-not-allowed');
146            submitBtn.classList.remove('hover:bg-blue-700', 'shadow-lg');
147        }
148    };
149
150    // Real-time validation on input
151    [nameInput, emailInput, messageInput].forEach(input => {
152        input.addEventListener('input', () => {
153            clearError(input);
154            updateSubmitButton();
155        });
156    });
157
158    // Initialize button state
159    updateSubmitButton();
160});

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.