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.