PageSourceSearch

https://altdorfs.com/spillover/cms/assets/js/form-script.js

js altdorfs.com collected 2026-09-29 07:25:19 UTC 13,793 bytes, 456 lines download raw bytes

1var recaptchaSiteKey = "6LfdYicjAAAAAPzbE2ZekGHeZtBJip1S34mFJtIG";
2
3var isSpillover = /^(.+-)?admin(-.+)?\.spillover\.com$/gi.test(
4  window.location.hostname
5);
6
7var isProto = window.location.href.startsWith("https://legacy-admin.spillover.com/spillover/proto/");
8
9var isEsite = isProto || !isSpillover;
10
11function saveReferrer() {
12  // Save referrer in session storage and remove from URL.
13  if (window.sessionStorage && URL) {
14    const url = new URL(window.location.href);
15
16    if (!url.searchParams.has("r")) return;
17
18    window.sessionStorage.setItem("spillover-referrer", url.searchParams.get("r"));
19
20    url.searchParams.delete("r");
21
22    window.history.replaceState({}, document.title, url.href);
23  } else {
24    console.warn("sessionStorage or URL not supported");
25  }
26}
27
28function addReferrerToForm(form) {
29  if (window.sessionStorage) {
30    const referrer = window.sessionStorage.getItem("spillover-referrer");
31
32    if (!referrer) return;
33
34    if (form.querySelector("input[name='_spillover_referrer']")) return;
35
36    const input = document.createElement("input");
37    input.type = "hidden";
38    input.name = "_spillover_referrer";
39    input.value = referrer;
40
41    form.appendChild(input);
42  }
43}
44
45if (typeof window.spilloverFormScript === "undefined") {
46  window.spilloverFormScript = true;
47
48  var execOnLoad = function () {
49    saveReferrer();
50
51    if (typeof jQuery === "undefined" || jQuery().jquery.indexOf("3.") !== 0) {
52      if (typeof jQuery !== "undefined") {
53        delete jQuery;
54      }
55
56      var headScriptEl = document.createElement("script");
57      headScriptEl.src = "https://code.jquery.com/jquery-3.6.1.min.js";
58      headScriptEl.type = "text/javascript";
59      document.head.append(headScriptEl);
60    }
61
62    waitForJQAndRun(formScriptInitJQ, formScriptInitJQUI, 20);
63    waitForJQAndRun(fillExistingFormData, false, 20);
64  };
65
66  if (
67    document.readyState === "complete" ||
68    document.readyState === "interactive"
69  ) {
70    // load event already fired, this may happen if this script is loaded dynamically.
71    // Run immediately.
72    execOnLoad();
73  } else {
74    // SEE: https://stackoverflow.com/questions/2810825/javascript-event-window-onload-not-triggered
75    window.addEventListener
76      ? window.addEventListener("load", execOnLoad, false)
77      : window.attachEvent && window.attachEvent("onload", execOnLoad);
78  }
79}
80
81function waitForJQAndRun(callable, callableJQUI, tryCount) {
82  if (typeof jQuery === "undefined" || jQuery().jquery.indexOf("3.") !== 0) {
83    if (tryCount-- > 0) {
84      setTimeout(waitForJQAndRun, 200, callable, callableJQUI, tryCount);
85    } else {
86      console.log("failed to load jQuery");
87    }
88  } else {
89    if (typeof callableJQUI === "function") {
90      if (typeof jQuery.datepicker === "undefined") {
91        var headScriptEl2 = document.createElement("script");
92        headScriptEl2.src = "https://code.jquery.com/ui/1.12.1/jquery-ui.js";
93        headScriptEl2.type = "text/javascript";
94        document.head.append(headScriptEl2);
95
96        var headLinkEl1 = document.createElement("link");
97        headLinkEl1.href =
98          "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css";
99        headLinkEl1.rel = "stylesheet";
100        document.head.append(headLinkEl1);
101      }
102
103      if (typeof flatpickr === "undefined") {
104        var flatpickrJS = document.createElement("script");
105        flatpickrJS.src = "https://cdn.jsdelivr.net/npm/flatpickr";
106        flatpickrJS.type = "text/javascript";
107        document.head.append(flatpickrJS);
108
109        var flatpickerCSS = document.createElement("link");
110        flatpickerCSS.href =
111          "https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css";
112        flatpickerCSS.rel = "stylesheet";
113        document.head.append(flatpickerCSS);
114      }
115
116      waitForJQUIAndRun(callableJQUI, 20);
117    }
118
119    callable();
120  }
121}
122
123function waitForJQUIAndRun(callableJQUI, tryCount) {
124  if (typeof jQuery.datepicker === "undefined") {
125    if (tryCount-- > 0) {
126      setTimeout(waitForJQUIAndRun, 200, callableJQUI, tryCount);
127    } else {
128      console.log("failed to load jQueryUI");
129    }
130  } else {
131    callableJQUI();
132  }
133}
134
135function formScriptInitJQ() {
136  if (isEsite) {
137    // Load recaptcha once per page. Each form on the page runs this init, so a
138    // window-level guard + a stable-id <script> (instead of cache-busted
139    // $.getScript) prevents api.js being fetched/executed once per form.
140    if (
141      typeof grecaptcha === "undefined" &&
142      !window.spilloverRecaptchaLoading &&
143      !document.getElementById("spillover-recaptcha-script")
144    ) {
145      window.spilloverRecaptchaLoading = true;
146      var recaptchaJS = document.createElement("script");
147      recaptchaJS.id = "spillover-recaptcha-script";
148      recaptchaJS.src = `https://www.google.com/recaptcha/api.js?render=${recaptchaSiteKey}`;
149      recaptchaJS.type = "text/javascript";
150      recaptchaJS.onerror = function () {
151        window.spilloverRecaptchaLoading = false;
152        var failedScript = document.getElementById("spillover-recaptcha-script");
153        if (failedScript) {
154          failedScript.parentNode.removeChild(failedScript);
155        }
156      };
157      document.head.append(recaptchaJS);
158    }
159  }
160
161  $("form.cmsform.ajax-request").on("submit", function () {
162    var action = $(this).attr("action");
163
164    if (isEsite) {
165      // Submissions from eSites are handled by a different URL.
166      var parts = action.split("/");
167      var formId = parts[parts.length - 2];
168      action = `https://rails-admin.spillover.com/esites-api/v1/form_submissions/${formId}`;
169      // action = `http://localhost:8080/esites-api/v1/form_submissions/8`;
170    }
171
172    ajaxSubmit(this, action);
173    return false;
174  });
175
176  $("form.cmsform")
177    .find("input,select,textarea")
178    .on("change keypress", function () {
179      if (this.name) {
180        if (isInputDynamicValid(this)) {
181          hideFormFieldMsg("form.cmsform", this.name.replace("[]", ""));
182        }
183      }
184    });
185
186  if (typeof mmddMask !== "undefined") {
187    $(".mmdd-mask").each(function () {
188      mmddMask(this);
189    });
190  }
191}
192
193function formScriptInitJQUI() {
194  $('form.cmsform input[id^="date-"]').datepicker();
195}
196
197function ajaxSubmit(formElement, customAction, customData) {
198  if (typeof grecaptcha !== "undefined") {
199    // Add recaptcha token to form data before submitting.
200    grecaptcha.ready(function () {
201      grecaptcha
202        .execute(recaptchaSiteKey, { action: "form_submission" })
203        .then(function (token) {
204          var hiddenInput = document.createElement("input");
205          hiddenInput.setAttribute("type", "hidden");
206          hiddenInput.setAttribute("name", "g-recaptcha-response");
207          hiddenInput.setAttribute("value", token);
208          formElement.appendChild(hiddenInput);
209
210          doSubmit(formElement, customAction, customData);
211        });
212    });
213  } else {
214    // No recaptcha, just submit.
215    doSubmit(formElement, customAction, customData);
216  }
217}
218
219function doSubmit(formElement, customAction, customData) {
220  addReferrerToForm(formElement);
221
222  var formData = customData ? customData : $(formElement).serialize();
223
224  hideFormFieldMsg(formElement);
225
226  var formAction = customAction ? customAction : formElement.action;
227
228  if (!formAction || typeof formAction !== "string") {
229    formAction = $(formElement).attr("action");
230  }
231
232  $(formElement).find("input,select,textarea,button").prop("disabled", true);
233
234  $.ajax({
235    url: formAction,
236    method: formElement.method,
237    data: formData,
238    timeout: 60000,
239    crossDomain: true,
240  })
241    .done(function (data, textStatus, jqXHR) {
242      if (data && data.hasOwnProperty("success")) {
243        if (data.success) {
244          if (data.hasOwnProperty("redirectUrl") && data.redirectUrl) {
245            window.location = data.redirectUrl;
246            return;
247          }
248
249          $(formElement).addClass("so-form-submitted");
250
251          $(formElement)
252            .find(
253              'input[type="text"],input[type="password"],input[type="number"],input[type="email"],select,textarea,button'
254            )
255            .val("");
256          $(formElement)
257            .find('input[type="radio"],input[type="checkbox"]')
258            .prop("checked", false);
259          var successMsg = "Your submission has been received";
260          if (data.hasOwnProperty("message") && data.message) {
261            successMsg = data.message;
262          }
263          showFormMsg(formElement, successMsg, "success");
264        } else {
265          if (data.hasOwnProperty("errors")) {
266            for (var errKey in data.errors) {
267              showFormFieldMsg(formElement, errKey, data.errors[errKey]);
268            }
269          } else {
270            showFormMsg(formElement, "Some error occurred!", "danger");
271          }
272        }
273      } else {
274        showFormMsg(formElement, "Unhandled response!", "danger");
275      }
276      $(formElement)
277        .find("input,select,textarea,button")
278        .prop("disabled", false);
279      $(formElement)
280        .find("input,select,textarea,button")
281        .removeProp("disabled");
282
283      $(formElement).find('.g-recaptcha-response').remove();
284    })
285    .fail(function (jqXHR, textStatus, errorThrown) {
286      $(formElement)
287        .find("input,select,textarea,button")
288        .prop("disabled", false);
289      $(formElement)
290        .find("input,select,textarea,button")
291        .removeProp("disabled");
292      showFormMsg(formElement, "Some error occurred!", "danger");
293
294      $(formElement).find('.g-recaptcha-response').remove();
295    });
296}
297
298if (typeof currentFormData === "undefined") {
299  var currentFormData = null;
300}
301function fillExistingFormData() {
302  if (currentFormData) {
303    for (var dataKey in currentFormData) {
304      setElementValue(
305        "form [name='" + dataKey + "']",
306        currentFormData[dataKey]
307      );
308    }
309  }
310}
311
312function showFormMsg(formElement, msg, type) {
313  var msgContainerTemplate = $(formElement).find(".form-msg-container-sample");
314  if (msgContainerTemplate.length === 0) {
315    alert(msg);
316    return;
317  }
318
319  var msgContainer = msgContainerTemplate.clone();
320  msgContainer.css("display", "block");
321  msgContainer.removeClass("form-msg-container-sample");
322  msgContainer.addClass("form-msg-container");
323  if (msgContainer.hasClass("alert")) {
324    msgContainer.addClass("alert-" + (type ? type : "warning"));
325  }
326  msgContainer.find(".form-msg-body").text(msg);
327  msgContainer.appendTo(msgContainerTemplate.parent());
328}
329
330function showFormFieldMsg(formElement, fieldName, msg, type) {
331  var formErrMsgElement = $(formElement).find(
332    'span.form-errmsg[data-forfield="' + fieldName + '"]'
333  );
334  if (formErrMsgElement.length > 0) {
335    formErrMsgElement.text(msg);
336    formErrMsgElement.css("display", "block");
337  } else {
338    showFormMsg(formElement, msg, type ? type : "danger");
339  }
340}
341
342function hideFormFieldMsg(formElement, fieldName) {
343  if (fieldName) {
344    $(formElement)
345      .find('span.form-errmsg[data-forfield="' + fieldName + '"]')
346      .css("display", "none");
347  } else {
348    $(formElement)
349      .find("span.form-errmsg[data-forfield]")
350      .css("display", "none");
351    $(".form-msg-container").remove();
352  }
353}
354
355function isInputDynamicValid(input) {
356  return 1 != $(input).attr("data-dyn-invalid");
357}
358
359function setInputDynamicValid(input) {
360  $(input).attr("data-dyn-invalid", "");
361  $(input).removeAttr("data-dyn-invalid");
362}
363
364function setInputDynamicInvalid(input) {
365  $(input).attr("data-dyn-invalid", 1);
366}
367
368function setElementValue(element, value) {
369  element = $(element);
370  if (element.hasOwnProperty("length") && element.length < 1) {
371    return;
372  }
373
374  switch (element.prop("tagName")) {
375    case "INPUT":
376      switch (element.prop("type")) {
377        case "checkbox":
378        case "radio":
379          if (element.length === 1) {
380            element[0].checked = value ? true : false;
381          } else {
382            for (var sii = 0; sii < element.length; sii++) {
383              element[sii].checked = Array.isArray(value)
384                ? value.includes(element[sii].value)
385                : element[sii].value === value;
386            }
387          }
388          break;
389        default:
390          element.val(value);
391          break;
392      }
393      break;
394    case "SELECT":
395    case "TEXTAREA":
396      element.val(value);
397      break;
398    case "FRAME":
399    case "IFRAME":
400      element.contents().find("body").html(value);
401      break;
402    default:
403      element.val(value);
404      element.text(value);
405      break;
406  }
407  element.trigger("change");
408}
409
410// Function to mask MM/DD date inputs.
411function mmddMask(element) {
412  var mask = function (value) {
413    // Remove non-numeric chars and trim to 4 chars
414    var v = value.replace(/\D/g, "").slice(0, 4);
415
416    var fullRegex = /^(0[1-9]|1[0-2])(0[1-9]|1\d|2\d|3[01])$/;
417
418    switch (v.length) {
419      case 1:
420        var dateRegex = /^(0|1)$/;
421        if (!dateRegex.test(v)) return "";
422        break;
423      case 2:
424        var dateRegex = /^(0[1-9]|1[0-2])$/;
425        if (!dateRegex.test(v)) return v.slice(0, 1);
426        break;
427      case 3:
428        var dateRegex = /^(0[1-9]|1[0-2])([0-3])$/;
429        if (!dateRegex.test(v)) return v.slice(0, 2) + "/";
430        break;
431      case 4:
432        if (!fullRegex.test(v)) return v.slice(0, 2) + "/" + v.slice(2, 3);
433        break;
434    }
435
436    if (v.length >= 4 && !fullRegex.test(v)) return "";
437
438    if (v.length >= 2) return v.slice(0, 2) + "/" + v.slice(2, 4);
439
440    return v;
441  };
442
443  var handleEvent = function (e) {
444    var key = e.keyCode || e.charCode;
445
446    // Allow backspace and delete.
447    if (key === 8 || key === 46) return true;
448
449    e.target.value = mask(e.target.value);
450  };
451
452  element.addEventListener("keyup", handleEvent);
453
454  // keyup is not fired if a value is pasted using the mouse only.
455  element.addEventListener("change", handleEvent);
456}

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.