PageSourceSearch

https://www.digitalgravity.ae/assets/js/vendors/form-validation.js?v=2898

js digitalgravity.ae collected 2026-09-24 19:00:50 UTC 10,482 bytes, 317 lines download raw bytes

1document.addEventListener("DOMContentLoaded", function () {
2  const forms = document.querySelectorAll("form.needs-validation");
3
4  forms.forEach((form) => {
5    form.addEventListener("submit", function (e) {
6      let valid = true;
7
8      // Full Name
9      const fullName = form.querySelector("[name='fullname']");
10      if (fullName) {
11        const value = fullName.value.trim();
12        if (!value) {
13          showError(fullName, "Name is required.");
14          valid = false;
15        } else if (/^\s/.test(fullName.value)) {
16          showError(fullName, "Name cannot start with a space.");
17          valid = false;
18        } else if (!/^[A-Za-z\s]+$/.test(value)) {
19          showError(fullName, "Name must contain only letters and spaces.");
20          valid = false;
21        } else if (value.length < 2 || value.length > 50) {
22          showError(fullName, "Name must be between 2 and 50 characters.");
23          valid = false;
24        } else {
25          clearError(fullName);
26        }
27      }
28
29      // Company field validation
30      const company = form.querySelector("[name='company']");
31      if (company) {
32        const value = company.value.trim();
33        // Allow letters, numbers, spaces, &, , . ’ -
34        const allowedRegex = /^[A-Za-z0-9&,.’'\-\s]+$/;
35        if (!value) {
36          showError(company, "Company/Organisation name is required.");
37          valid = false;
38        } else if (!allowedRegex.test(value)) {
39          showError(
40            company,
41            "Only letters, numbers, spaces, and (& , . ’ -) allowed."
42          );
43          valid = false;
44        } else if (value.length < 2 || value.length > 100) {
45          showError(company, "Name must be between 2 and 100 characters.");
46          valid = false;
47        } else {
48          clearError(company);
49        }
50      }
51
52      // Email
53      const email = form.querySelector("[name='email']");
54      if (email) {
55        const value = email.value.trim();
56        const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
57
58        if (!value) {
59          showError(email, "Email address is required.");
60          valid = false;
61        } else if (value.length < 5 || value.length > 50) {
62          showError(email, "Email must be between 5 and 50 characters.");
63          valid = false;
64        } else if (!emailPattern.test(value)) {
65          showError(email, "Enter a valid email address (example: [email protected]).");
66          valid = false;
67        } else {
68          clearError(email);
69        }
70      }
71
72      // Phone
73      const phone = form.querySelector("[name='phone']");
74      if (phone) {
75        const value = phone.value.trim();
76        const phonePattern = /^\+?\d{7,15}$/;
77        if (!value) {
78          showError(phone, "Phone number is required.");
79          valid = false;
80        } else if (!phonePattern.test(value)) {
81          showError(phone, "Enter a valid phone number (7–15 digits).");
82          valid = false;
83        } else {
84          clearError(phone);
85        }
86      }
87
88      // Service (dropdown)
89      const service = form.querySelector("[name='interest']");
90      if (service && !service.value) {
91        showError(service, "Selection required");
92        valid = false;
93      } else {
94        clearError(service);
95      }
96
97      // Project Timeline (dropdown)
98      const timeline = form.querySelector("[name='project_timeline']");
99      if (timeline && !timeline.value) {
100        showError(timeline, "Selection required");
101        valid = false;
102      } else {
103        clearError(timeline);
104      }
105
106
107      //// File field validation
108const resume = form.querySelector("[name='resume']"); // change name if needed
109if (resume) {
110  const file = resume.files[0];
111
112  // Allowed file types
113  const allowedTypes = [
114    "application/pdf",
115    "application/msword", // .doc
116    "application/vnd.openxmlformats-officedocument.wordprocessingml.document" // .docx
117  ];
118
119  const maxSize = 5 * 1024 * 1024; // 5MB
120
121  if (!file) {
122    showError(resume, "Resume attachment is required.");
123    valid = false;
124  } else if (!allowedTypes.includes(file.type)) {
125    showError(
126      resume,
127      "Only PDF, DOC, or DOCX files are allowed."
128    );
129    valid = false;
130  } else if (file.size > maxSize) {
131    showError(
132      resume,
133      "File size must be less than 5MB."
134    );
135    valid = false;
136  } else {
137    clearError(resume);
138  }
139}
140
141
142      // Tell us about your project (textarea, optional, max 500)
143      const projectText = form.querySelector("[name='message']");
144      if (projectText && projectText.value.length > 500) {
145        showError(projectText, "Maximum 500 characters allowed");
146        valid = false;
147      } else {
148        clearError(projectText);
149      }
150
151      // Stop form submit if invalid
152      if (!valid) {
153        e.preventDefault();
154        return;
155      }
156
157      // Hide submit button and show loader
158      const submitBtn = form.querySelector("button[type='submit']");
159      const loaderImg = form.querySelector(".form-loader-img");
160      if (submitBtn) submitBtn.style.display = "none";
161      if (loaderImg) loaderImg.style.display = "block";
162      e.preventDefault(); // Prevent default form submission
163
164      grecaptcha.ready(async function () {
165        const showMessage = (msg, isError = true) => {
166          if (loaderImg) loaderImg.style.display = "none";
167          if (submitBtn) submitBtn.style.display = "none";
168
169          let box = form.querySelector(".form-global-message");
170          if (!box) {
171            box = document.createElement("div");
172            box.className = "form-global-message";
173            Object.assign(box.style, {
174              fontSize: "14px",
175              marginTop: "8px",
176            });
177            form.appendChild(box);
178          }
179
180          box.textContent = msg;
181          box.style.color = isError ? "red" : "green";
182          box.style.display = "block";
183
184          setTimeout(() => {
185            box.style.display = "none";
186            if (submitBtn) submitBtn.style.display = "block";
187          }, 3000);
188        };
189
190        const hideLoader = () => {
191          if (loaderImg) loaderImg.style.display = "none";
192        };
193
194        try {
195          // Get token
196          const token = await grecaptcha.execute(
197            "6LcoAZorAAAAABagcN95ntcpzfgpcIlFLA3_4_aV",
198            { action: "submit" }
199          );
200
201          // Build payload
202          const formDataObj = Object.fromEntries(new FormData(form).entries());
203          formDataObj["g-recaptcha-response"] = token;
204
205          // Send
206          const response = await fetch("https://dgapi.digitalgravity.ae/submit/quote", {
207          //const response = await fetch("http://localhost:3005/submit/career", {
208            method: "POST",
209            headers: { "Content-Type": "application/json" },
210            body: JSON.stringify(formDataObj),
211          });
212
213          if (!response.ok) {
214            let msg = "Your message couldn't be delivered. Please try again.";
215            try {
216              const data = await response.json();
217              if (data?.message) msg = data.message;
218            } catch (_) { }
219            throw new Error(msg);
220          }
221
222          // Success
223          hideLoader();
224          form.reset();
225          window.location.href = window.DG_THANKYOU_URL || "/thankyou/";
226        } catch (err) {
227          hideLoader();
228
229          // Map technical errors → user-friendly messages
230          let userMessage = "Something went wrong. Please try again.";
231          if (err.message?.includes("site key")) {
232            userMessage = "Captcha could not be initialized.";
233          } else if (err.message?.toLowerCase().includes("captcha")) {
234            userMessage = "Captcha verification failed. Please try again.";
235          } else if (err.message?.toLowerCase().includes("network")) {
236            userMessage = "Network error. Please check your connection.";
237          } else if (err.message) {
238            userMessage = err.message; // fallback if it's already user-friendly
239          }
240
241          showMessage(userMessage);
242          console.error("Form error (debug):", err); // raw error in console only
243        }
244      });
245    });
246
247    // Error handling functions
248    function showError(input, message) {
249      clearError(input);
250      const error = document.createElement("div");
251      error.className = "error-message";
252      error.style.color = "red";
253      error.style.fontSize = "13px";
254      error.textContent = message;
255      input.insertAdjacentElement("afterend", error);
256    }
257
258    function clearError(input) {
259      if (!input) return;
260      const next = input.nextElementSibling;
261      if (next && next.classList.contains("error-message")) {
262        next.remove();
263      }
264    }
265  });
266
267  // 1️⃣ Function to remove hidden contact person input
268  function removeContactPersonInput() {
269    const popupWrapper = document.querySelector(".formPopupWrapper.active");
270    if (!popupWrapper) return;
271    const form = popupWrapper.querySelector("form.needs-validation");
272    if (!form) return;
273    const hiddenInput = form.querySelector('input[name="contact_person"]');
274    if (hiddenInput) hiddenInput.remove();
275  }
276
277  // 2️⃣ Handle all clicks
278  document.addEventListener("click", function (event) {
279    const target = event.target;
280
281    // ✅ If clicked a contact person button
282    const btn = target.closest(".scheduleCallBtn[data-person]");
283    if (btn) {
284      const popupWrapper = document.querySelector(".formPopupWrapper.active");
285      if (!popupWrapper) return;
286      const form = popupWrapper.querySelector("form.needs-validation");
287      if (!form) return;
288
289      // Create hidden input if not exists
290      let hiddenInput = form.querySelector('input[name="contact_person"]');
291      if (!hiddenInput) {
292        hiddenInput = document.createElement("input");
293        hiddenInput.type = "hidden";
294        hiddenInput.name = "contact_person";
295        form.appendChild(hiddenInput);
296      }
297
298      // Set value from button
299      hiddenInput.value = btn.getAttribute("data-person");
300      return; // stop further processing
301    }
302
303    // ✅ Remove hidden input when clicking close button
304    if (target.closest(".closeFormPopup")) {
305      removeContactPersonInput();
306      return;
307    }
308
309    // ✅ Remove hidden input when clicking outside popup
310    const popupWrapper = document.querySelector(".formPopupWrapper.active");
311    if (popupWrapper && !target.closest(".formPopupContainer")) {
312      removeContactPersonInput();
313    }
314  });
315
316});
317

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.