PageSourceSearch

https://cosmoswebtech.com.au/js/cosmos-forms.js

js cosmoswebtech.com.au collected 2026-10-02 00:14:03 UTC 4,465 bytes, 118 lines download raw bytes

1/*
2 * Shared form client for cosmoswebtech.com.au.
3 * Every lead form posts to /api/submit, which verifies Turnstile and hands the submission to the
4 * shared forms Worker. Nothing here talks to Mautic, and nothing reports success until the server
5 * has actually accepted the submission.
6 */
7(function () {
8  'use strict';
9
10  var ATTR_KEY = 'cosmos_attr';
11  var ATTR_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'gclid', 'li_fat_id'];
12  var PENDING_MESSAGE = 'Almost done. Check your inbox and click the confirmation link to complete this.';
13
14  function readAttr() {
15    try { return JSON.parse(sessionStorage.getItem(ATTR_KEY) || '{}'); } catch (e) { return {}; }
16  }
17
18  // First touch wins for the session. sessionStorage only, so nothing persists past the tab.
19  function captureAttribution() {
20    try {
21      var qs = new URLSearchParams(window.location.search);
22      var cur = readAttr();
23      var changed = false;
24      ATTR_PARAMS.forEach(function (p) {
25        var v = qs.get(p);
26        if (v && !cur[p]) { cur[p] = v.slice(0, 200); changed = true; }
27      });
28      if (changed) sessionStorage.setItem(ATTR_KEY, JSON.stringify(cur));
29    } catch (e) { /* storage blocked: attribution is best effort */ }
30  }
31  captureAttribution();
32
33  function turnstileToken(form) {
34    var widget = form.querySelector('.cf-turnstile');
35    if (!widget || !window.turnstile) return null;
36    return window.turnstile.getResponse(widget) || null;
37  }
38
39  function resetTurnstile(form) {
40    var widget = form.querySelector('.cf-turnstile');
41    if (widget && window.turnstile) window.turnstile.reset(widget);
42  }
43
44  function showError(form, message) {
45    var el = form.querySelector('.form-error');
46    if (!el) {
47      el = document.createElement('p');
48      el.className = 'form-error';
49      el.setAttribute('role', 'alert');
50      el.style.cssText = 'color:#b91c1c;font-size:0.9rem;margin:0.5rem 0 0;';
51      var btn = form.querySelector('button[type="submit"]');
52      if (btn && btn.parentNode) btn.parentNode.insertBefore(el, btn.nextSibling);
53      else form.appendChild(el);
54    }
55    el.textContent = message;
56  }
57
58  function clearError(form) {
59    var el = form.querySelector('.form-error');
60    if (el) el.textContent = '';
61  }
62
63  /**
64   * Submit a form.
65   * kind:   one of the server-side allowlisted kinds (see functions/api/submit.ts)
66   * form:   the <form> element (holds the Turnstile widget and the honeypot input)
67   * fields: plain object of the visitor's values
68   * Resolves { ok, status, message, error, code }.
69   */
70  function submit(kind, form, fields) {
71    clearError(form);
72    var honeypot = form.querySelector('[name="hp_website"]');
73    var token = turnstileToken(form);
74    if (!token) {
75      return Promise.resolve({ ok: false, error: 'Please complete the security check and try again.', code: 'no_token' });
76    }
77
78    var attribution = {};
79    var stored = readAttr();
80    ATTR_PARAMS.forEach(function (p) { if (stored[p]) attribution[p] = stored[p]; });
81
82    return fetch('/api/submit', {
83      method: 'POST',
84      headers: { 'Content-Type': 'application/json' },
85      body: JSON.stringify({
86        kind: kind,
87        turnstileToken: token,
88        hp: honeypot ? honeypot.value : '',
89        fields: fields,
90        page_url: window.location.origin + window.location.pathname,
91        attribution: attribution
92      })
93    }).then(function (res) {
94      return res.json().catch(function () { return {}; }).then(function (json) {
95        if (res.ok && json.success) {
96          try {
97            window.dataLayer = window.dataLayer || [];
98            window.dataLayer.push({ event: 'generate_lead', brand: 'cosmos', form_kind: kind, verification: 'pending' });
99          } catch (e) { /* analytics must never block the form */ }
100          return { ok: true, status: json.status || 'pending_verification', message: json.message || PENDING_MESSAGE };
101        }
102        resetTurnstile(form);
103        return { ok: false, error: json.error || 'Something went wrong. Please try again, or contact us directly.', code: json.code };
104      });
105    }).catch(function () {
106      resetTurnstile(form);
107      return { ok: false, error: 'We could not reach the server. Please check your connection and try again.', code: 'network' };
108    });
109  }
110
111  window.CosmosForms = {
112    submit: submit,
113    showError: showError,
114    clearError: clearError,
115    resetTurnstile: resetTurnstile,
116    PENDING_MESSAGE: PENDING_MESSAGE
117  };
118})();

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.