PageSourceSearch

https://superior-sealcoating.com/js/scripts.js?v=20260921_1

js superior-sealcoating.com collected 2026-10-04 21:31:43 UTC 34,306 bytes, 961 lines download raw bytes

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 = "&#10006";//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 = "&#9776;";//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.