PageSourceSearch

https://cupertinoconcreterepairandleveling.com/form-validation.js

js cupertinoconcreterepairandleveling.com collected 2026-10-07 05:22:59 UTC 6,588 bytes, 208 lines download raw bytes

1/* Static site forms: validate via worker, then CallTrk.captureForm (Elementor AJAX does not run).
2   Document-level listeners so Elementor popup clones still validate. */
3(function () {
4  const VALIDATION_WORKER_URL =
5    window.FORM_VALIDATION_WORKER_URL ||
6    'https://form-validation-worker.michael-215.workers.dev/';
7
8  const NAME_RE = /^[A-Za-z\s'-]{2,30}$/;
9  const EMAIL_RE = /^\S+@\S+\.\S+$/;
10  const ZIP_RE = /^\d{5}(-\d{4})?$/;
11
12  function captureWithCallRail(form) {
13    form.dataset.fvPass = 'true';
14    try {
15      if (typeof window.submitToCallRail === 'function') {
16        window.submitToCallRail(form);
17        return true;
18      }
19      if (window.CallTrk && typeof CallTrk.captureForm === 'function') {
20        CallTrk.captureForm(form);
21        return true;
22      }
23      form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
24      return false;
25    } finally {
26      delete form.dataset.fvPass;
27    }
28  }
29
30  function fieldsOf(form) {
31    if (!form || !form.querySelector) return null;
32    const submitBtn = form.querySelector('button[type="submit"]');
33    const firstName = form.querySelector('[name="form_fields[ct_fName]"]');
34    const lastName = form.querySelector('[name="form_fields[ct_lName]"]');
35    const phoneField = form.querySelector('[name="form_fields[ct_pNumber]"]');
36    const emailField = form.querySelector('[name="form_fields[ct_email]"]');
37    const zipField = form.querySelector('[name="form_fields[ct_zCode]"]');
38    if (!submitBtn || !firstName || !lastName || !phoneField || !emailField || !zipField) {
39      return null;
40    }
41    return { submitBtn, firstName, lastName, phoneField, emailField, zipField };
42  }
43
44  function prepareForm(form) {
45    const f = fieldsOf(form);
46    if (!f) return null;
47    form.setAttribute('novalidate', 'novalidate');
48    f.phoneField.setAttribute('type', 'text');
49    f.emailField.setAttribute('type', 'text');
50    return f;
51  }
52
53  function removeElementorErrors(form) {
54    form
55      .querySelectorAll(
56        '.elementor-message-danger,' +
57          '.elementor-message,' +
58          '.elementor-error,' +
59          '.elementor-field-error'
60      )
61      .forEach(function (el) {
62        el.remove();
63      });
64  }
65
66  async function validateAndSend(form, e) {
67    if (form.dataset.fvPass === 'true') return;
68    const f = prepareForm(form);
69    if (!f) return;
70
71    if (e) {
72      e.preventDefault();
73      e.stopPropagation();
74      e.stopImmediatePropagation();
75    }
76
77    removeElementorErrors(form);
78
79    const fName = f.firstName.value.trim();
80    const lName = f.lastName.value.trim();
81    const phone = f.phoneField.value.trim();
82    const email = f.emailField.value.trim();
83    const zip = f.zipField.value.trim();
84    const cleanPhone = phone.replace(/\D/g, '');
85
86    if (!NAME_RE.test(fName)) {
87      alert('Enter a valid first name.');
88      f.firstName.focus();
89      return;
90    }
91    if (!NAME_RE.test(lName)) {
92      alert('Enter a valid last name.');
93      f.lastName.focus();
94      return;
95    }
96    if (cleanPhone.length !== 10) {
97      alert('Enter a valid 10-digit phone number.');
98      f.phoneField.focus();
99      return;
100    }
101    if (!EMAIL_RE.test(email)) {
102      alert('Enter a valid email address.');
103      f.emailField.focus();
104      return;
105    }
106    if (!ZIP_RE.test(zip)) {
107      alert('Enter a valid ZIP code.');
108      f.zipField.focus();
109      return;
110    }
111
112    f.submitBtn.disabled = true;
113    const originalText = f.submitBtn.textContent;
114    f.submitBtn.textContent = 'Submitting...';
115
116    var data = null;
117    try {
118      const response = await fetch(VALIDATION_WORKER_URL, {
119        method: 'POST',
120        headers: { 'Content-Type': 'application/json' },
121        body: JSON.stringify({
122          firstName: fName,
123          lastName: lName,
124          phone: cleanPhone,
125          email: email,
126          zip: zip
127        })
128      });
129      if (response.status >= 500) {
130        throw new Error('validation worker returned ' + response.status);
131      }
132      data = await response.json();
133    } catch (workerError) {
134      console.error('Worker unreachable, capturing lead without validation:', workerError);
135      removeElementorErrors(form);
136      captureWithCallRail(form);
137      alert('Thank you! Your request has been submitted successfully.');
138      form.reset();
139      f.submitBtn.disabled = false;
140      f.submitBtn.textContent = originalText;
141      return;
142    }
143
144    try {
145      if (!data || !data.success) {
146        alert((data && data.message) || 'Submission blocked. Please check your details.');
147        f.submitBtn.disabled = false;
148        f.submitBtn.textContent = originalText;
149        return;
150      }
151
152      removeElementorErrors(form);
153      captureWithCallRail(form);
154      alert('Thank you! Your request has been submitted successfully.');
155      form.reset();
156      f.submitBtn.disabled = false;
157      f.submitBtn.textContent = originalText;
158    } catch (error) {
159      console.error('Worker validation failed:', error);
160      alert('Something went wrong. Please try again.');
161      f.submitBtn.disabled = false;
162      f.submitBtn.textContent = originalText;
163    }
164  }
165
166  function boot() {
167    document.querySelectorAll('.elementor-form').forEach(prepareForm);
168  }
169
170  function start() {
171    if (!window.__ctFvDelegate) {
172      window.__ctFvDelegate = true;
173      document.addEventListener(
174        'click',
175        function (e) {
176          const btn = e.target.closest && e.target.closest('.elementor-form button[type="submit"]');
177          if (!btn) return;
178          const form = btn.closest('.elementor-form');
179          if (!fieldsOf(form)) return;
180          validateAndSend(form, e);
181        },
182        true
183      );
184      document.addEventListener(
185        'submit',
186        function (e) {
187          const form = e.target;
188          if (!form || !form.classList || !form.classList.contains('elementor-form')) return;
189          if (!fieldsOf(form)) return;
190          validateAndSend(form, e);
191        },
192        true
193      );
194    }
195    boot();
196    setTimeout(boot, 500);
197    setTimeout(boot, 2000);
198    if (typeof MutationObserver !== 'undefined' && document.body) {
199      new MutationObserver(boot).observe(document.body, { childList: true, subtree: true });
200    }
201  }
202
203  if (document.readyState === 'loading') {
204    document.addEventListener('DOMContentLoaded', start);
205  } else {
206    start();
207  }
208})();

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.