1//var cspnonce set in above file 2//var visitorIpAddress set in above file 3//var websiteDomain set in above file 4//var aNetPostURL = 'https://test.authorize.net/payment/payment';//sandbox 5//var aNetPostURL = 'https://accept.authorize.net/payment/payment';//prod 6 7document.getElementById('loader').style.display = "none"; 8document.body.className = document.body.className.replace("no-js","js"); 9 10window.onload = function() { 11 if(document.getElementsByTagName('form').length>0) 12 { 13 const forms = document.querySelectorAll("form"); 14 forms.forEach(function(el) { 15 //console.log(el); 16 el.onsubmit = submitted.bind(el); 17 }); 18 } 19} 20 21//to increase performance, load stuff after first interaction 22var isFirstInteraction = true; 23document.body.addEventListener("mousemove", function (evt) { 24 if(isFirstInteraction==true) 25 { 26 onFirstInteraction(); 27 isFirstInteraction = false; 28 } 29}); 30 31document.body.addEventListener("click", function (event) { 32 //console.log('body clicked'); 33 //note evt.target can be a nested element, not the body element, resulting in misfires 34 //console.log(evt.target); 35 //alert("body clicked"); 36 if(isFirstInteraction==true) 37 { 38 onFirstInteraction(); 39 isFirstInteraction = false; 40 } 41 42 //if a youtube video embed 43 // Get the video link 44 let link = event.target.closest('[data-youtube]'); 45 if (link) 46 { 47 // Prevent the URL from redirecting users 48 event.preventDefault(); 49 50 // Get the video ID 51 let id = link.getAttribute('data-youtube'); 52 53 // Create the player 54 let player = document.createElement('div'); 55 player.innerHTML = '<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/'+id+'?autoplay=1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'; 56 57 // Inject the player into the UI 58 link.replaceWith(player); 59 } 60 61 //record clicks 62 var clickedClass = "" 63 if(event.target.className!==undefined) 64 { 65 clickedClass = event.target.className; 66 } 67 var clickedID = ""; 68 if(event.target.id!==undefined) 69 { 70 clickedID = event.target.id; 71 } 72 var clickedTag = ""; 73 if(event.target.nodeName!==undefined) 74 { 75 clickedTag = event.target.nodeName; 76 } 77 var clickedText = ""; 78 if(event.target.innerText!==undefined) 79 { 80 clickedText = event.target.innerText; 81 } 82 var currentURL = window.location.href; 83 var clickedTitle = ""; 84 if(event.target.title!==undefined) 85 { 86 clickedTitle = event.target.title; 87 } 88 var clickedHref = ""; 89 if(event.target.href!==undefined) 90 { 91 clickedHref = event.target.href; 92 } 93 var clickedSrc = ""; 94 if(event.target.src!==undefined) 95 { 96 clickedSrc = event.target.src; 97 } 98 var params = { 99 class:event.target.className, 100 href:clickedHref, 101 id:clickedID, 102 ip:visitorIpAddress, 103 src:clickedSrc, 104 tag:event.target.nodeName, 105 text:event.target.innerText, 106 title:event.target.title, 107 url:window.location.href 108 }; 109 //console.log('params'); 110 //console.log(params); 111 const options = { 112 method: 'POST', 113 body: JSON.stringify(params), 114 //cache: 'no-store', 115 headers: { 116 'Content-Type':'application/json' 117 } 118 } 119 //console.log('options'); 120 //console.log(options); 121 fetch(websiteDomain+'/include/clickHandler.inc.php',options)//define path - doesnt work in subfolders 122 .then(response => { 123 if (!response.ok) {throw response;} 124 return response.json(); 125 }) 126 .then(data => { 127 //console.log('data:', data); 128 if(data.isError==true) 129 { 130 console.warn("fetch success, but isError: "+data.msg); 131 } 132 }) 133 .catch(error => { 134 console.warn(error); 135 }); 136}); 137 138//conditional form fields 139document.body.addEventListener('change', function(evt) { 140 //alert(event.target.value); 141 //if clicked element has a following conditional input, check selected value and maybe show the following input 142 if(event.target.matches('.hasFollowingOther')) 143 { 144 //event.target.parentNode.style.backgroundColor = "red"; 145 if(event.target.value=="Other") 146 { 147 event.target.parentNode.parentNode.nextElementSibling.style.display = "block"; 148 } 149 else 150 { 151 event.target.parentNode.parentNode.nextElementSibling.style.display = "none"; 152 } 153 } 154}); 155 156//highlight current weekday in footer hours table 157var d = new Date(); 158var weekday = new Array(7); 159weekday[0] = "Sunday"; 160weekday[1] = "Monday"; 161weekday[2] = "Tuesday"; 162weekday[3] = "Wednesday"; 163weekday[4] = "Thursday"; 164weekday[5] = "Friday"; 165weekday[6] = "Saturday"; 166var n = weekday[d.getDay()]; 167//console.log(n); 168const matches = document.querySelectorAll(".hours-container table tr td");
169matches.forEach(function(el) { 170 //console.log(el); 171 if(el.innerText.indexOf(n)!==-1) 172 { 173 el.parentElement.classList.add("selected"); 174 } 175}); 176 177//mobileGrippy 178if(document.getElementById('mobileGrippy')) 179{ 180 document.getElementById('mobileGrippy').addEventListener("click", function(e) { 181 //console.log('clicked mobile grippy'); 182 if(e.currentTarget.classList.contains("openNav")) 183 { 184 e.currentTarget.classList.remove("openNav"); 185 e.currentTarget.classList.add("closeNav"); 186 e.currentTarget.innerHTML = "✖";//change icon 187 document.getElementById('site-nav').style.display = "block"; 188 189 } 190 else 191 { 192 e.currentTarget.classList.remove("closeNav"); 193 e.currentTarget.classList.add("openNav"); 194 e.currentTarget.innerHTML = "☰";//change icon 195 document.getElementById('site-nav').style.display = "none"; 196 } 197 }); 198} 199 200function onFirstInteraction() { 201 //change the landing background image 202 if(document.getElementById('home-landing-bg-container')) 203 { 204 document.getElementById('home-landing-bg-container').style.backgroundImage = "url('https://superior-sealcoating.com/uploads/superior-sealcoating-landing-pic-residential-sealcoating-west-chester-pa-1920x729-1.webp')"; 205 } 206 207 //lazy load youtube embed player 208 // Get all of the videos 209 let videos = document.querySelectorAll('[data-youtube]'); 210 211 // Progressively enhance them 212 for (let video of videos) 213 { 214 // Get the video ID 215 let id = new URL(video.href).searchParams.get('v'); 216 217 video.setAttribute('data-youtube', id); 218 video.setAttribute('role', 'button'); 219 video.setAttribute('target','_blank'); 220 video.innerHTML = '<img alt="" src="https://img.youtube.com/vi/'+id+'/maxresdefault.jpg"><br>'+video.textContent; 221 } 222 223 //add recaptcha to every page bc newsletter form is in footer 224 225 var footerEl = document.getElementsByTagName('footer')[0]; 226 var scriptEl = document.createElement('script'); 227 scriptEl.type = 'text/javascript'; 228 if (typeof recaptchaVersion !== 'undefined' && recaptchaVersion==3) 229 { 230 scriptEl.src = 'https://www.google.com/recaptcha/api.js?render='+recaptchaSiteKey; 231 } 232 else//default to v2 233 { 234 scriptEl.src = 'https://www.google.com/recaptcha/api.js' 235 } 236 scriptEl.nonce=cspnonce; 237 footerEl.appendChild(scriptEl); 238 239 isFirstInteraction = false; 240} 241 242function submitted(event) { 243 //console.log('form submitted'); 244 var allowedFileTypes = ['image/jpeg', 'image/png']; 245 var maxFileSize = 2 * 1024 * 1024;//2MB 246 247 //prevent multiple clicks 248 var thisForm = event.target; 249 if (thisForm.classList.contains('submitted')===true) { 250 // Previously submitted - don't submit again 251 console.warn('Form already submitted. Please wait.'); 252 event.preventDefault(); 253 } else { 254 // Mark it so that the next submit can be ignored 255 thisForm.classList.add('submitted'); 256 } 257 //now remove the class submitted after 2 seconds 258 setTimeout(function(){ thisForm.classList.remove('submitted'); }, 2000); 259 260 //remove error divs if this is a subsequent click 261 if(thisForm.getElementsByClassName("error").length>0) 262 { 263 thisForm.querySelectorAll(".error").forEach(e => e.remove()); 264 } 265 var msgtouser_fill=""; 266 var filter = /^[\w\-\.\+]+\@[a-zA-Z0-9\.\-]+\.[a-zA-z0-9]{2,4}$/; 267 var checkboxChecked = 0; 268 var containsRequiredCheckboxes = 0; 269 //loop through input and if required, then validate 270 for (var i = 0; i < thisForm.getElementsByTagName("input").length; i++) { 271 //remove error class from input 272 thisForm.getElementsByTagName("input")[i].classList.remove("inputError"); 273 //if this input type=file 274 if (thisForm.getElementsByTagName("input")[i].type == 'file') { 275 //if a file is chosen 276 if (thisForm.getElementsByTagName("input")[i].files.length > 0) { 277 //validate file extension - files are also validated in the backend 278 if (!allowedFileTypes.includes(thisForm.getElementsByTagName("input")[i].files[0].type)) { 279 msgtouser_fill += "File must be of type jpg or png."; 280 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 281 } 282 //validate file size - files are also validated in the backend 283 if (thisForm.getElementsByTagName("input")[i].files[0].size > maxFileSize) { 284 msgtouser_fill += 'File size must be under 2MB.'; 285 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 286 } 287 } 288 } 289 if(thisForm.getElementsByTagName("input")[i].classList.contains('required')) 290 { 291 //console.log("contains required"); 292 if(thisForm.getElementsByTagName("input")[i].type=='email' || thisForm.getElementsByTagName("input")[i].type=='number' || thisForm.getElementsByTagName("input")[i].type=='text') 293 { 294 //if no value is entered 295 if(thisForm.getElementsByTagName("input")[i].value.length<1) 296 { 297 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 298 msgtouser_fill = "Please complete the required fields."; 299 } 300 //if type email, check for @ 301 if(thisForm.getElementsByTagName("input")[i].classList.contains('email')) 302 { 303 if(thisForm.getElementsByTagName("input")[i].value.length<5 && thisForm.getElementsByTagName("input")[i].value.indexOf('@')==-1) 304 { 305 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 306 msgtouser_fill = "Please complete the required fields."; 307 } 308 } 309 } 310 if(thisForm.getElementsByTagName("input")[i].type=='date') 311 { 312 if(thisForm.getElementsByTagName("input")[i].length<10) 313 { 314 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 315 msgtouser_fill = "Please complete the required fields."; 316 } 317 } 318 else if(thisForm.getElementsByTagName("input")[i].type=='checkbox') 319 { 320 containsRequiredCheckboxes = 1; 321 //ensure at least one checkbox is checked for this input name 322 if(thisForm.getElementsByTagName("input")[i].checked) 323 { 324 checkboxChecked = 1; 325 } 326 } 327 else if(thisForm.getElementsByTagName("input")[i].type=='password') 328 { 329 if(thisForm.getElementsByTagName("input")[i].value.length<5 || thisForm.getElementsByTagName("input")[i].value.length>60) 330 { 331 thisForm.getElementsByTagName("input")[i].classList.add("inputError"); 332 msgtouser_fill = "Please complete the required fields."; 333 } 334 } 335 // else if($(this).attr('type')=='radio') 336 // { 337 // //TODO 338 // } 339 } 340 } 341 for (var i = 0; i < thisForm.getElementsByTagName("select").length; i++) { 342 thisForm.getElementsByTagName("select")[i].classList.remove("inputError"); 343 if(thisForm.getElementsByTagName("select")[i].classList.contains('required')) 344 { 345 if(thisForm.getElementsByTagName("select")[i].options[thisForm.getElementsByTagName("select")[i].selectedIndex].value.length<1) 346 { 347 348 msgtouser_fill = "Please complete the required fields."; 349 } 350 } 351 } 352 for (var i = 0; i < thisForm.getElementsByTagName("textarea").length; i++) { 353 thisForm.getElementsByTagName("textarea")[i].classList.remove("inputError"); 354 if(thisForm.getElementsByTagName("textarea")[i].classList.contains('required')) 355 { 356 if(thisForm.getElementsByTagName("textarea")[i].value.length<1) 357 { 358 thisForm.getElementsByTagName("textarea")[i].classList.add("inputError"); 359 msgtouser_fill = "Please complete the required fields."; 360 } 361 } 362 } 363 //console.log("containsRequiredCheckboxes: "+containsRequiredCheckboxes); 364 //console.log("checkboxChecked: "+checkboxChecked); 365 if(containsRequiredCheckboxes==1 && checkboxChecked==0) 366 { 367 //thisForm.getElementsByTagName("input[type=checkbox]")[i].classList.add("inputError"); 368 msgtouser_fill += "Please check at least one option."; 369 } 370 371 //if recaptcha is present, require it 372 if (thisForm.querySelector('div.g-recaptcha'))//exists 373 { 374 console.log("form contains g-recaptcha"); 375 //make sure w3SpecOpsField is set 376 if (thisForm.querySelector('input[name="w3SpecOpsField"]'))//exists - should be true with v2 377 {
378 console.log("form contains w3SpecOpsField"); 379 if (thisForm.querySelector('input[name="w3SpecOpsField"]').value.length == 0) { 380 msgtouser_fill += "Please check the recaptcha box."; 381 } 382 } 383 } 384 385 if(msgtouser_fill.length>0) 386 { 387 const obj = document.createElement('div'); 388 obj.classList.add('error'); 389 obj.innerHTML = msgtouser_fill; 390 thisForm.prepend(obj); 391 event.preventDefault(); 392 } 393 else 394 { 395 //display loader so user knows its doing something 396 document.getElementById('loader').style.display = "block"; 397 398 //Handle recaptcha v3 asynchronously 399 if (typeof recaptchaVersion !== 'undefined' && recaptchaVersion==3 && thisForm.querySelector('input[name="g-recaptcha-response"]')) 400 { 401 console.log("form contains g-recaptcha-response"); 402 event.preventDefault(); // Stop normal form submit while we fetch token 403 404 if (typeof grecaptcha !== 'undefined') 405 { 406 grecaptcha.ready(function() { 407 grecaptcha.execute(recaptchaSiteKey, {action: 'submit'}).then(function(token) { 408 // Add the token to your form data to send to the backend 409 thisForm.querySelector('input[name="g-recaptcha-response"]').value = token; 410 411 // Now that we have the token, submit the form natively 412 // (We use this specific method so we don't trigger this submit event in an infinite loop) 413 HTMLFormElement.prototype.submit.call(thisForm); 414 }); 415 }); 416 } 417 else 418 { 419 console.warn("reCAPTCHA script failed to load (possibly blocked by browser extension)."); 420 //Either submit the form without reCAPTCHA or remove loader & warn user: 421 HTMLFormElement.prototype.submit.call(thisForm); 422 } 423 } 424 //continue 425 } 426} 427 428//called upon successfully checking the recpatcha 429function w3Callback() { 430 //set the hidden fields 431 var elems = document.getElementsByName("w3SpecOpsField"); 432 var i; 433 for (i = 0; i < elems.length; i++) { 434 elems[i].value = "passedBootCamp"; 435 } 436} 437 438//DOM Ready JQuery 439$(function() { 440// $('.cycle-slideshow').on('cycle-after',function(e, optionHash, outgoingSlideEl, incomingSlideEl, forwardFlag){ 441// //alert("new slide"); 442// //console.log("new slide"); 443// //get height of box with link 444// var ht = $(incomingSlideEl).height(); 445// //console.log(ht); 446// //set the container's height to that of the current slide 447// $(this).animate({height: ht}); 448// }); 449 450 //google ads contact form conversion event 451 $("button[name=w3form_contact]").on('click',function() { 452 gtag_report_conversion(); 453 }); 454 $("div.faq-container .question").on('click',function() { 455 if($(this).hasClass('collapsed')) 456 { 457 $(this).removeClass('collapsed'); 458 $(this).addClass('expanded'); 459 $(this).parent().find('.answer').show('slow'); 460 } 461 else if($(this).hasClass('expanded')) 462 { 463 $(this).addClass('collapsed'); 464 $(this).removeClass('expanded'); 465 $(this).parent().find('.answer').hide('slow'); 466 } 467 }); 468 469// //gallery lightbox adjust html 470// //if gallery page or home page 471 if(window.location.href.indexOf("gallery")>0 || window.location.pathname == '/') 472 { 473 //console.log("on gallery or home page"); 474 var countBoxes = $('div.cssbox').length; 475 $('div.cssbox').each(function(index,value){ 476 $(this).find('a').attr('data-imagelightbox','a'); 477 $(this).find('a').attr('href',$(this).find('span.cssbox_full img').attr('src')); 478 }); 479 } 480 //lightbox 481 $('a[data-imagelightbox="a"]').imageLightbox({overlay: true,button: true, quitOnDocClick: false,arrows:true}); 482 483 //pay page 484 if(window.location.href.indexOf('pay') != -1) 485 { 486 //console.log('on pay page'); 487 488 //var msgtouser_error =""; 489 //var orderContainer = $("div#order-form-container"); 490 //var spanError = $("div#order-form-container").find("span.error"); 491 //var placeOrderBtn = $("button#placeOrderButton"); 492 493 //card connect 494 var validationError = ""; 495 var queryString = "highlightinvalidinput=true&invalidinputevent=true&placeholder=123"; 496 //queryString += "useexpiry=true&usecvv=true&placeholdercvv=123"; 497 var cssStr = "&css=input{background-color:white;border:1px solid gray;border-radius:4px;font-size:16px;padding:10px 16px;}";//works 498 cssStr += "body{margin:0;}"; 499 //cssStr += ".error{color:red;border-color:red;}"; 500 cssStr = encodeURI(cssStr); 501 queryString += cssStr; 502 //$("#tokenframe").attr('src',"https://boltgw-uat.cardconnect.com/itoke/ajax-tokenizer.html?" + queryString); 503 $("#tokenframe").attr('src',"https://boltgw.cardconnect.com/itoke/ajax-tokenizer.html?" + queryString); 504 $("#tokenframe").addClass("w3iFrameStyle"); 505 tokenProperty = scrubInput(getQueryVariable("tokenpropname", "message"), 30, "message", /^[0-9a-zA-Z]+$/); 506 507 function isNumeric(n) { 508 return !isNaN(parseFloat(n)) && isFinite(n); 509 } 510 window.addEventListener('message', function(event) { 511 //console.log(event); 512 if(event.data!=undefined && event.origin!=undefined && event.origin=="https://boltgw.cardconnect.com")//if card connect 513 { 514 token = JSON.parse(event.data); 515 console.log('event'); 516 //console.log(event); 517 console.log('token'); 518 //console.log(token); 519 //document.getElementById('error-container').textContent=token["validationError"]; 520 var mytoken = document.getElementById('mytoken'); 521 //var myexpiry = document.getElementById('myexpiry'); 522 var myerror = document.getElementById('myerror'); 523 mytoken.value = token[tokenProperty]; 524 //tokenVal = token[tokenProperty]; 525 //myexpiry.value = token["expiry"]; 526 myerror.value = token["validationError"]; 527 validationError = "";//reset 528 if(token["validationError"]!=undefined && token["validationError"].length>0) 529 { 530 validationError = token["validationError"]; 531 } 532 //console.log('validationError: '+validationError); 533 //var expiryVal = document.getElementById('myexpiry').value; 534 //console.log(tokenVal); 535 //console.log(expiryVal); 536 } 537 }, false); 538 function getQueryVariable(variable, defaultValue) { 539 var query = window.location.search.substring(1); 540 var vars = query.split("&"); 541 for (var i = 0; i < vars.length; i++) { 542 var pair = vars[i].split("="); 543 if (pair[0] == variable) { 544 return pair[1]; 545 } 546 } 547 return defaultValue ? defaultValue : (false); 548 } 549 function scrubInput(input, maxlen, fallback, pattern) { 550 if (input && input.length !== 0 && input.length <= maxlen) { 551 if(input.match(pattern)){ 552 return input; 553 } 554 } 555 return fallback; 556 } 557 558 $("#submitPayment").click(function () { 559 var thisForm = $(this).parent().parent(); 560 //remove error divs if this is a subsequent click 561 thisForm.find(".error").remove(); 562 thisForm.find("input, select, textarea").removeClass("inputError"); 563 var msgtouser_fill=""; 564 if(thisForm.find("input[name=fName]").val().length<2) 565 { 566 thisForm.find("input[name=fName]").addClass("inputError"); 567 msgtouser_fill += " Please enter your first name."; 568 console.warn("Please enter a first name."); 569 } 570 if(thisForm.find("input[name=lName]").val().length<2) 571 { 572 thisForm.find("input[name=lName]").addClass("inputError"); 573 msgtouser_fill += " Please enter your last name."; 574 console.warn("Please enter a last name."); 575 } 576 if(thisForm.find("input[name=invoiceNum]").val().length<5) 577 { 578 thisForm.find("input[name=invoiceNum]").addClass("inputError"); 579 msgtouser_fill += " Please enter an invoice number with at least 5 digits."; 580 console.warn("Please enter an invoice number with at least 5 characters."); 581 } 582 if(thisForm.find("input[name=paymentAmount]").val().length<4) 583 { 584 thisForm.find("input[name=paymentAmount]").addClass("inputError"); 585 msgtouser_fill += " Please enter a valid payment amount."; 586 console.warn("Please enter a payment amount with at least 4 characters."); 587 } 588 if(thisForm.find("input[name=emailAddress]").val().length<7) 589 { 590 thisForm.find("input[name=emailAddress]").addClass("inputError"); 591 msgtouser_fill += " Please enter a valid email address."; 592 console.warn("Please enter an email address with at least 7 characters."); 593 } 594 if(!thisForm.find("select[name=addressCountry]").val() || thisForm.find("select[name=addressCountry]").val().length<2)//should always pass with only one option 595 { 596 thisForm.find("select[name=addressCountry]").addClass("inputError"); 597 msgtouser_fill += " Please select a country."; 598 console.warn("Please select a country."); 599 } 600 if(thisForm.find("input[name=addressStreet]").val().length<4) 601 { 602 thisForm.find("input[name=addressStreet]").addClass("inputError"); 603 msgtouser_fill += " Please enter a street address."; 604 console.warn("Please enter a street address with at least 4 characters."); 605 } 606 // if(thisForm.find("input[name=addressStreetCont]").val().length<4) 607 // { 608 // thisForm.find("input[name=addressStreetCont]").addClass("inputError"); 609 // msgtouser_fill += " Please enter a street cont with at least 4 characters."; 610 // console.warn("Please enter a street cont with at least 4 characters."); 611 // } 612 if(thisForm.find("input[name=addressCity]").val().length<4) 613 { 614 thisForm.find("input[name=addressCity]").addClass("inputError"); 615 msgtouser_fill += " Please enter a city."; 616 console.warn("Please enter a city with at least 4 characters."); 617 } 618 if(!thisForm.find("select[name=addressState]").val() || thisForm.find("select[name=addressState]").val().length<2) 619 { 620 thisForm.find("select[name=addressState]").addClass("inputError"); 621 msgtouser_fill += " Please select a state."; 622 console.warn("Please select a state."); 623 } 624 if(thisForm.find("input[name=addressZip]").val().length<5) 625 { 626 thisForm.find("input[name=addressZip]").addClass("inputError"); 627 msgtouser_fill += " Please enter a 5-digit zip code."; 628 console.warn("Please enter a zip code with at least 5 characters."); 629 } 630 if(thisForm.find("input[name=totalAmt]").val().length<4 && thisForm.find("input[name=paymentAmount]").val().length>=4) 631 { 632 thisForm.find("input[name=paymentAmount]").addClass("inputError"); 633 msgtouser_fill += " Please enter a valid payment amount."; 634 console.warn("Please enter a payment amount with at least 4 characters."); 635 } 636 if(thisForm.find("input[name=firstName]").val().length<2) 637 { 638 thisForm.find("input[name=firstName]").addClass("inputError"); 639 msgtouser_fill += " Please enter cardholder first name."; 640 console.warn("Please enter a first name with at least 2 characters."); 641 } 642 if(thisForm.find("input[name=lastName]").val().length<2) 643 { 644 thisForm.find("input[name=lastName]").addClass("inputError"); 645 msgtouser_fill += " Please enter cardholder last name."; 646 console.warn("Please enter a last name with at least 2 characters."); 647 } 648 if($('input[name="mytoken"]').val().length==0) 649 { 650 msgtouser_fill += " Please enter a card number."; 651 console.warn("Please enter a card number."); 652 } 653 if(validationError.length>0) 654 { 655 msgtouser_fill += " Please enter a valid card number."; 656 console.warn("Please enter a card number."); 657 } 658 if(thisForm.find("input[name=expirationDate]").val().length!=4) 659 { 660 thisForm.find("input[name=expirationDate]").addClass("inputError"); 661 msgtouser_fill += " Please enter an expiration date with 4 digits (MMYY)."; 662 console.warn("Please enter an expiration date with 4 characters."); 663 } 664 else if(isNumeric($('input[name=expirationDate]').val())===false) 665 { 666 msgtouser_fill += " Please enter a valid expiration date with only 4 digits."; 667 console.warn("Please enter a valid expiration date with only 4 digits"); 668 } 669 if(thisForm.find("input[name=cvv]").val().length<3) 670 { 671 thisForm.find("input[name=cvv]").addClass("inputError"); 672 msgtouser_fill += " Please enter a 3 or 4-digit CVV."; 673 console.warn("Please enter a cvv with at least 3 characters."); 674 } 675 else if(isNumeric($('input[name=cvv]').val())===false) 676 { 677 msgtouser_fill += " Please enter a valid cvv with only digits."; 678 console.warn("Please enter a valid cvv with only digits."); 679 } 680 if(!thisForm.find("input[name=agree]").is(':checked')) 681 { 682 msgtouser_fill += " Please check the agree checkbox."; 683 console.warn("Please check the agree checkbox."); 684 }
685 //recaptcha V3 686 if (typeof recaptchaVersion !== 'undefined' && recaptchaVersion==3 && thisForm.find('input[name="g-recaptcha-response"]').length > 0) { 687 // Handled asynchronously below 688 } else { 689 // Default to v2 validation if v3 is not active 690 if ($('.g-recaptcha').length > 0) { 691 var response = grecaptcha.getResponse(); 692 if (response.length == 0) { 693 msgtouser_fill += " Please check the recaptcha box."; 694 console.warn("Please check the recaptcha box."); 695 } 696 } 697 } 698 699 if(msgtouser_fill.length>0) 700 { 701 //thisForm.prepend('<div class="error" style="margin:0 0 20px 0;">'+msgtouser_fill+'</div>'); 702 $("#submitPayment").before('<div class="error" style="margin:0 0 20px 0;">'+msgtouser_fill.trim()+'</div>'); 703 return false;// Exit here if there are errors 704 } 705 else 706 { 707 //append loader so user knows its doing something 708 document.getElementById('loader').style.display = "block"; 709 //Handle recaptcha v3 asynchronously 710 if (typeof recaptchaVersion !== 'undefined' && recaptchaVersion==3 && thisForm.find('input[name="g-recaptcha-response"]').length > 0) 711 { 712 grecaptcha.ready(function() { 713 grecaptcha.execute(recaptchaSiteKey, {action: 'submit'}).then(function(token) { 714 thisForm.find('input[name="g-recaptcha-response"]').val(token); 715 thisForm[0].submit(); 716 }) 717 .catch(function(error) { 718 console.error("reCAPTCHA execution error:", error); 719 document.getElementById('loader').style.display = "none"; 720 console.warn("Security check could not be completed. Please refresh the page and try again."); 721 }); 722 }); 723 return false; // Prevent native submission while we wait for the token 724 } 725 726 // If not v3, the form will natively submit here 727 //continue 728 } 729 }); 730 $("select[name='addressState'], select[name='addressCountry']").on("change", function() { 731 $(this).removeClass("inputError"); 732 }); 733 //auto populate Total field after user enters Payment Amount 734 $("input#paymentAmount" ).on( "blur", function() { 735 $(this).removeClass("inputError"); 736 var msgtouser_fill=""; 737 // Code to run when the input field loses focus 738 //console.log( "The input field has lost focus." ); 739 // Get the formatted value 740 var formattedValue = $(this).val(); 741 if(formattedValue.length<4) 742 { 743 msgtouser_fill += "Please enter a payment amount with at least 4 digits."; 744 console.warn("Please enter a payment amount with at least 4 digits."); 745 $(this).addClass('inputError'); 746 return; 747 } 748 if(!formattedValue.includes(".")) 749 { 750 msgtouser_fill += "Please enter a payment amount that includes a decimal point."; 751 console.warn("Please enter a payment amount that includes a decimal point."); 752 $(this).addClass('inputError'); 753 return; 754 } 755 // Define the regular expression to search for any letter (case-insensitive) 756 var regex = /[a-zA-Z]/; 757 // Test the input value against the regex 758 if (regex.test(formattedValue)) 759 { 760 msgtouser_fill += "Please enter a payment amount that does not contain letters."; 761 console.warn("Please enter a payment amount that does not contain letters."); 762 $(this).addClass('inputError'); 763 return; 764 } 765 // Remove non-numeric characters (except period and optional negative sign) 766 var numericValue = formattedValue.replace(/[^0-9.-]+/g, ""); 767 // Convert to a number type for calculations 768 var numberForCalculation = parseFloat(numericValue); 769 if(numberForCalculation<0) 770 { 771 msgtouser_fill += "Please enter a payment amount greater than zero."; 772 console.warn("Please enter a payment amount greater than zero."); 773 $(this).addClass('inputError'); 774 return; 775 } 776 else if(numberForCalculation>=1000)//if amount is more than 1k, then add a 3% fee 777 { 778 numberForCalculation = numberForCalculation*1.03; 779 } 780 $("input#totalAmt").val(numberForCalculation.toLocaleString('en-US', {minimumFractionDigits: 2,maximumFractionDigits: 2}));//no dollar sign 781 }); 782 } 783 // $("#btnOpenAuthorizeNetIFrame").click(function () { 784 // var thisContainer = $(this).parent(); 785 786 // //remove error divs if this is a subsequent click 787 // thisContainer.remove("div.error"); 788 789 // var msgtouser_fill=""; 790 // //validate required fields 791 // if($('input[name="firstName"]').val().length<3) 792 // { 793 // msgtouser_fill += ' please enter a first name.'; 794 // $('input[name="firstName"]').addClass('inputError'); 795 // } 796 // if($('input[name="lastName"]').val().length<3) 797 // { 798 // msgtouser_fill += ' please enter a last name.'; 799 // $('input[name="lastName"]').addClass('inputError'); 800 // } 801 // if($('input[name="email"]').val().length<3) 802 // { 803 // msgtouser_fill += ' please enter an email.'; 804 // $('input[name="email"]').addClass('inputError'); 805 // } 806 // if($('input[name="invoiceNumber"]').val().length<3) 807 // { 808 // msgtouser_fill += ' please enter an invoice number.'; 809 // $('input[name="invoiceNumber"]').addClass('inputError'); 810 // } 811 // if($('input[name="amount"]').val().length<2) 812 // { 813 // msgtouser_fill += ' please enter an amount.'; 814 // $('input[name="amount"]').addClass('inputError'); 815 // } 816 // if($('input[name="addressStreet"]').val().length<3) 817 // { 818 // msgtouser_fill += ' please enter a street.'; 819 // $('input[name="addressStreet"]').addClass('inputError'); 820 // } 821 // if($('input[name="addressCity"]').val().length<3) 822 // { 823 // msgtouser_fill += ' please enter a city.'; 824 // $('input[name="addressCity"]').addClass('inputError'); 825 // } 826 // if($('select[name="addressState"]').val().length<2) 827 // { 828 // msgtouser_fill += ' please select a state.'; 829 // $('select[name="addressState"]').addClass('inputError'); 830 // } 831 // if($('input[name="addressZip"]').val().length<3) 832 // { 833 // msgtouser_fill += ' please enter a zip.'; 834 // $('input[name="addressZip"]').addClass('inputError'); 835 // } 836 837 // if(msgtouser_fill=="") 838 // { 839 // //first get token, then POST to show iframe 840 // document.getElementById('loader').style.display = "block"; 841 // var params = { 842 // firstName:$('input[name="firstName"]').val(), 843 // lastName:$('input[name="lastName"]').val(), 844 // email:$('input[name="email"]').val(), 845 // invoiceNumber:$('input[name="invoiceNumber"]').val(), 846 // amount:$('input[name="amount"]').val(), 847 // addressStreet:$('input[name="addressStreet"]').val(), 848 // addressCity:$('input[name="addressCity"]').val(), 849 // addressState:$('select[name="addressState"]').val(), 850 // addressZip:$('input[name="addressZip"]').val() 851 // }; 852 // jQuery.ajax({ 853 // type: "POST", 854 // url: websiteDomain+"/include/getHostedPaymentPageRequest.php", 855 // data: params, 856 // //dataType: "text", 857 // dataType: "json", 858 // cache: false, 859 // async: true, 860 // error: function(xhr, textStatus, error){ 861 // console.warn(xhr.statusText+' '+textStatus+' '+error); 862 // }, 863 // timeout: 3000, 864 // success: function( data ) { 865 // //console.log(data); 866 // //console.log("isError: "+data.isError); 867 // //console.log("msg: "+data.msg); 868 // if(data.isError!==true) 869 // { 870 // //console.log("success"); 871 // //insert token into <form> 872 // $('form#send_token input[name="token"]').val(data.token); 873 // $("#add_payment").show(); 874 // $("#send_token").attr({ "action": aNetPostURL, "target": "add_payment" }).submit(); 875 // //$(window).scrollTop($('#add_payment').offset().top - 50); 876 // $("#add_payment").height = 800;//not working 877 // $("#add_payment").css('height','800'); 878 // document.getElementById('loader').style.display = "none"; 879 // } 880 // else 881 // { 882 // console.warn(data.msg); 883 // alert(data.msg); 884 // } 885 // } 886 // }); 887 // } 888 // else 889 // { 890 // const obj = document.createElement('div'); 891 // obj.classList.add('error'); 892 // obj.innerHTML = msgtouser_fill; 893 // thisContainer.prepend(obj); 894 895 // console.warn(msgtouser_fill); 896 // document.getElementById('loader').style.display = "none"; 897 // } 898 // }); 899}); 900 901// get device info for form submissions, used for audit logging and anti-fraud 902function getDeviceFingerprint() { 903 var gl, debugInfo, gpu = 'Unknown'; 904 try { 905 var canvas = document.createElement('canvas'); 906 gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 907 if (gl) { 908 debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); 909 if (debugInfo) { 910 gpu = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); 911 } 912 } 913 } catch (e) {} 914 return { 915 timezone: Intl.DateTimeFormat ? Intl.DateTimeFormat().resolvedOptions().timeZone : '', 916 tzOffsetMin: new Date().getTimezoneOffset(), 917 language: navigator.language || navigator.userLanguage || '', 918 screen: screen.width + 'x' + screen.height + '@' + (window.devicePixelRatio || 1) + 'x', 919 viewport: window.innerWidth + 'x' + window.innerHeight, 920 colorDepth: screen.colorDepth + '-bit', 921 cpuCores: navigator.hardwareConcurrency || 'Unknown', 922 deviceMemoryGB: navigator.deviceMemory || 'Unknown', 923 touchPoints: navigator.maxTouchPoints || 0, 924 platform: navigator.platform || '', 925 gpu: gpu 926 }; 927} 928 929// Sync device fingerprint with current session on page load 930(function() { 931 function syncFingerprint() { 932 var chEndpoint = (typeof websiteDomain !== 'undefined' && websiteDomain ? websiteDomain : '') + '/include/clickHandler.inc.php'; 933 var fpParams = {
934 action: 'syncSessionFingerprint', 935 fingerprint: getDeviceFingerprint(), 936 url: window.location.href 937 }; 938 var fpOptions = { 939 method: 'POST', 940 body: JSON.stringify(fpParams), 941 headers: { 942 'Content-Type': 'application/json' 943 }, 944 credentials: 'same-origin' 945 }; 946 fetch(chEndpoint, fpOptions) 947 .then(function(response) { 948 if (!response.ok) { throw response; } 949 return response.json(); 950 }) 951 .catch(function(error) { 952 // Silent catch for telemetry 953 }); 954 } 955 956 if (document.readyState === 'loading') { 957 document.addEventListener('DOMContentLoaded', syncFingerprint); 958 } else { 959 syncFingerprint(); 960 } 961})();
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.