PageSourceSearch

https://www.complogix.io/wp-content/themes/Divi-child/complogix-forms.js?ver=1.2.1

js complogix.io collected 2026-10-05 05:09:41 UTC 9,994 bytes, 311 lines download raw bytes

1(function() {
2  'use strict';
3
4  var API_ENDPOINT = 'https://ndlvlwz3z8.execute-api.us-east-1.amazonaws.com/prod/submit';
5  var BLOCKED_DOMAINS_ENDPOINT = 'https://ndlvlwz3z8.execute-api.us-east-1.amazonaws.com/prod/blocked-domains';
6
7  var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8  var blockedDomains = [];
9
10  /*
11   * The homepage "What's your challenge?" recommender collects three dropdown
12   * answers and no contact details, then redirects to a matching case study.
13   * There is no email address, so it cannot create a GHL contact on its own.
14   * Rather than gate it behind a contact form (which would cost conversions on
15   * the homepage), complogix-quiz.js stashes the answers under STORAGE_KEY and
16   * we attach them to whichever clx-form the visitor submits next. The
17   * qualification data then lands on the contact when they actually convert.
18   *
19   * The key and object shape are a contract shared with complogix-quiz.js.
20   */
21  var QUIZ_STORAGE_KEY = 'clxChallengeQuiz';
22  var QUIZ_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
23  var QUIZ_MAX_VALUE_LEN = 120;
24  var QUIZ_FIELDS = [
25    { key: 'position',  label: 'Position' },
26    { key: 'employees', label: 'Employees' },
27    { key: 'challenge', label: 'Biggest challenge' }
28  ];
29
30  function fetchBlockedDomains() {
31    fetch(BLOCKED_DOMAINS_ENDPOINT)
32      .then(function(response) { return response.json(); })
33      .then(function(data) {
34        if (data.domains && Array.isArray(data.domains)) {
35          blockedDomains = data.domains.map(function(d) { return d.toLowerCase(); });
36        }
37      })
38      .catch(function() {
39        blockedDomains = [];
40      });
41  }
42
43  function isBlockedEmailDomain(email) {
44    if (!blockedDomains.length) return false;
45    var atIndex = email.lastIndexOf('@');
46    if (atIndex < 0) return false;
47    var domain = email.substring(atIndex + 1).toLowerCase();
48    if (blockedDomains.indexOf(domain) !== -1) return true;
49    var parts = domain.split('.');
50    for (var i = 1; i < parts.length; i++) {
51      if (blockedDomains.indexOf(parts.slice(i).join('.')) !== -1) return true;
52    }
53    return false;
54  }
55
56  function sanitize(str) {
57    if (!str) return '';
58    var div = document.createElement('div');
59    div.appendChild(document.createTextNode(str));
60    return div.innerHTML;
61  }
62
63  // localStorage throws in private-browsing modes on some browsers, so every
64  // access is guarded. Losing the quiz answers must never break a submission.
65  function safeStorage() {
66    try {
67      var s = window.localStorage;
68      var probe = '__clx_probe__';
69      s.setItem(probe, '1');
70      s.removeItem(probe);
71      return s;
72    } catch (e) {
73      return null;
74    }
75  }
76
77  function readQuizAnswers() {
78    var store = safeStorage();
79    if (!store) return null;
80    var raw;
81    try {
82      raw = store.getItem(QUIZ_STORAGE_KEY);
83    } catch (e) {
84      return null;
85    }
86    if (!raw) return null;
87
88    var data;
89    try {
90      data = JSON.parse(raw);
91    } catch (e) {
92      clearQuizAnswers();
93      return null;
94    }
95    if (!data || typeof data !== 'object') return null;
96
97    if (!data.at || (new Date().getTime() - data.at) > QUIZ_TTL_MS) {
98      clearQuizAnswers();
99      return null;
100    }
101    return data;
102  }
103
104  function clearQuizAnswers() {
105    var store = safeStorage();
106    if (!store) return;
107    try {
108      store.removeItem(QUIZ_STORAGE_KEY);
109    } catch (e) { /* nothing useful to do */ }
110  }
111
112  /*
113   * Build the block appended to the submitted message. Values come back out of
114   * localStorage, which the visitor can edit, so they are trimmed, length
115   * capped and escaped exactly like any other user-supplied field.
116   */
117  function formatQuizAnswers(data) {
118    var lines = [];
119    for (var i = 0; i < QUIZ_FIELDS.length; i++) {
120      var value = data[QUIZ_FIELDS[i].key];
121      if (typeof value !== 'string') continue;
122      value = value.trim();
123      if (!value) continue;
124      if (value.length > QUIZ_MAX_VALUE_LEN) value = value.substring(0, QUIZ_MAX_VALUE_LEN);
125      lines.push(QUIZ_FIELDS[i].label + ': ' + sanitize(value));
126    }
127    if (!lines.length) return '';
128    return 'Homepage challenge quiz\n' + lines.join('\n');
129  }
130
131  function showError(field, message) {
132    field.classList.add('clx-error');
133    var errorEl = field.parentElement.querySelector('.clx-field-error');
134    if (errorEl) {
135      errorEl.textContent = message;
136      errorEl.classList.add('clx-visible');
137    }
138    field.setAttribute('aria-invalid', 'true');
139  }
140
141  function clearError(field) {
142    field.classList.remove('clx-error');
143    var errorEl = field.parentElement.querySelector('.clx-field-error');
144    if (errorEl) {
145      errorEl.textContent = '';
146      errorEl.classList.remove('clx-visible');
147    }
148    field.setAttribute('aria-invalid', 'false');
149  }
150
151  function clearAllErrors(form) {
152    var fields = form.querySelectorAll('input, textarea');
153    for (var i = 0; i < fields.length; i++) {
154      clearError(fields[i]);
155    }
156  }
157
158  function validateForm(form) {
159    var valid = true;
160    clearAllErrors(form);
161
162    var required = form.querySelectorAll('[required]');
163    for (var i = 0; i < required.length; i++) {
164      if (!required[i].value.trim()) {
165        showError(required[i], required[i].getAttribute('data-label') + ' is required');
166        valid = false;
167      }
168    }
169
170    var emailField = form.querySelector('input[type="email"]');
171    if (emailField && emailField.value.trim()) {
172      if (!EMAIL_REGEX.test(emailField.value.trim())) {
173        showError(emailField, 'Please enter a valid email address');
174        valid = false;
175      } else if (isBlockedEmailDomain(emailField.value.trim())) {
176        showError(emailField, 'Please use your business email address');
177        valid = false;
178      }
179    }
180
181    return valid;
182  }
183
184  function showMessage(form, type, text) {
185    var msgEl = form.querySelector('.clx-form-message');
186    if (!msgEl) return;
187    msgEl.className = 'clx-form-message clx-visible ' + (type === 'success' ? 'clx-success' : 'clx-error-msg');
188    msgEl.textContent = text;
189    msgEl.setAttribute('role', 'alert');
190  }
191
192  function hideMessage(form) {
193    var msgEl = form.querySelector('.clx-form-message');
194    if (msgEl) {
195      msgEl.className = 'clx-form-message';
196      msgEl.textContent = '';
197    }
198  }
199
200  function submitForm(form) {
201    if (!validateForm(form)) return;
202
203    var formId = form.getAttribute('data-form-id');
204    var btn = form.querySelector('.clx-submit-btn');
205    var btnText = btn.textContent;
206
207    var payload = {
208      formId: formId,
209      firstName: sanitize(form.querySelector('[name="firstName"]').value.trim()),
210      lastName: sanitize(form.querySelector('[name="lastName"]').value.trim()),
211      email: form.querySelector('[name="email"]').value.trim().toLowerCase(),
212      sourcePage: window.location.pathname
213    };
214
215    var phoneField = form.querySelector('[name="phone"]');
216    if (phoneField && phoneField.value.trim()) {
217      payload.phone = sanitize(phoneField.value.trim());
218    }
219
220    var companyField = form.querySelector('[name="company"]');
221    if (companyField && companyField.value.trim()) {
222      payload.company = sanitize(companyField.value.trim());
223    }
224
225    var messageField = form.querySelector('[name="message"]');
226    if (messageField && messageField.value.trim()) {
227      payload.message = sanitize(messageField.value.trim());
228    }
229
230    // Carry any earlier homepage quiz answers through on this submission. The
231    // visitor's own message stays first so it is what a human reads first.
232    var quizAnswers = readQuizAnswers();
233    var quizBlock = quizAnswers ? formatQuizAnswers(quizAnswers) : '';
234    var quizAttached = false;
235    if (quizBlock) {
236      payload.message = payload.message ? payload.message + '\n\n' + quizBlock : quizBlock;
237      quizAttached = true;
238    }
239
240    btn.disabled = true;
241    btn.classList.add('clx-loading');
242    hideMessage(form);
243
244    fetch(API_ENDPOINT, {
245      method: 'POST',
246      headers: { 'Content-Type': 'application/json' },
247      body: JSON.stringify(payload)
248    })
249    .then(function(response) { return response.json(); })
250    .then(function(data) {
251      btn.disabled = false;
252      btn.classList.remove('clx-loading');
253
254      if (data.success) {
255        showMessage(form, 'success', 'Thank you! We\'ll be in touch shortly.');
256        form.reset();
257        clearAllErrors(form);
258
259        // Only drop the answers once they have actually reached the CRM, so a
260        // failed or retried submission does not lose them.
261        if (quizAttached) clearQuizAnswers();
262
263        if (typeof window.dataLayer !== 'undefined') {
264          window.dataLayer.push({
265            event: 'form_submission',
266            form_id: formId,
267            form_type: formId.replace(/-/g, '_'),
268            quiz_answers_attached: quizAttached
269          });
270        }
271      } else {
272        var errorText = data.errors ? data.errors.join('. ') : data.message;
273        showMessage(form, 'error', errorText || 'Something went wrong. Please try again.');
274      }
275    })
276    .catch(function() {
277      btn.disabled = false;
278      btn.classList.remove('clx-loading');
279      showMessage(form, 'error', 'Network error. Please check your connection and try again.');
280    });
281  }
282
283  function initForms() {
284    var forms = document.querySelectorAll('.clx-form');
285    if (!forms.length) return;
286
287    fetchBlockedDomains();
288    for (var i = 0; i < forms.length; i++) {
289      (function(form) {
290        form.addEventListener('submit', function(e) {
291          e.preventDefault();
292          submitForm(form);
293        });
294
295        var inputs = form.querySelectorAll('input, textarea');
296        for (var j = 0; j < inputs.length; j++) {
297          inputs[j].addEventListener('input', function() {
298            clearError(this);
299            hideMessage(form);
300          });
301        }
302      })(forms[i]);
303    }
304  }
305
306  if (document.readyState === 'loading') {
307    document.addEventListener('DOMContentLoaded', initForms);
308  } else {
309    initForms();
310  }
311})();

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.