1<!doctype html> 2<html lang="en"> 3 4<head> 5 <!-- Performance Optimizations --> 6 <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin> 7 <link rel="preconnect" href="https://code.jquery.com" crossorigin> 8 <link rel="dns-prefetch" href="https://cdn.jsdelivr.net"> 9 <link rel="dns-prefetch" href="https://code.jquery.com"> 10 <link rel="dns-prefetch" href="https://maxcdn.bootstrapcdn.com"> 11 <link rel="preload" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" as="style"> 12 <!-- Required meta tags --> 13 <meta charset="utf-8"> 14 <title>Contact Us</title> 15 <meta name="viewport" content="width=device-width, initial-scale=1"> 16 17 18 <meta name="keywords" content="SWVA sun, Local News, Swva Sports, Breaking News, Obituaries, Public Notices"> 19 <meta name="description" content="SWVA Local Online Newspaper"> 20 21 <link rel="shortcut icon" href="/favicon.ico?v=1790187371"> 22
22<script type="text/javascript" language="javascript" src="/lib/bl2.js"></script>
22 23 24 <!-- bootstrap --> 25 <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> 26
26<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
26 27 <link type="text/css" rel="stylesheet" href="https://bulletlink.com/bootstrap.css"> 28 <!-- /bootstrap --> 29 30 <!-- jQuery --> 31
31<script src="https://code.jquery.com/jquery-1.7.min.js" integrity="sha256-/05Jde9AMAT4/o5ZAI23rUf1SxDYTHLrkOco0eyRV84=" crossorigin="anonymous"></script>
31 32 33 <!-- system --> 34
34<script type="text/javascript" language="javascript" src="/settings.js?v=1790187371"></script>
34 35
35<script>function SParameters(){return '1';}</script>
35 36
36<script type="text/javascript" language="javascript" src="/articles.js?v=1790187371"></script>
36 37
37<script type="text/javascript" language="javascript" src="/ads.js?v=1790187371"></script>
37 38
38<script type="text/javascript" language="javascript" src="/menus.js?v=1790187371"></script>
38 39
39<script type="text/javascript" language="javascript" src="/sections.js?v=1790187371"></script>
39 40
40<script type="text/javascript" language="javascript" src="/polls.js?v=1790187371"></script>
40 41
41<script type="text/javascript" language="javascript" src="/menu_horizontal.js?v=1790187371"></script>
41 42 <!-- /system --> 43 44 <!-- fontAwesome--> 45 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" /> 46 <!-- /fontAwesome--> 47 48 <!-- general --> 49
49<script type="text/javascript" language="javascript" src="/jscript1.js?v=1790187371"></script>
49 50 <link type="text/css" rel="stylesheet" href="/custom.css?v=1790187371"> 51
51<script type="text/javascript" language="javascript" src="/custom.js?v=1790187371"></script>
51 52 <!-- /general --> 53 54 <!--StartMH--> 55 <!--MetaSharing--> 56 <!--EndMH--> 57 58 <!-- live-Builder --> 59
59<script type="text/javascript" src="/ufields.js"></script>
59 60 <!-- /live-Builder --> 61 <meta property="og:image" content="https://swvasun.com/logo.jpg"/> 62<!-- Google tag (gtag.js) -->
vendor: 65 bytes, lines 62-63
62 63<script async src="https://www.googletagmanager.com/gtag/js?id=
63G-WJW2XMLVK9
vendor: 19 bytes, line 63
63" defer></script>
64<script>
vendor: 130 bytes, lines 65-68
65window.dataLayer = window.dataLayer || []; 66function gtag(){dataLayer.push(arguments);} 67gtag('js', new Date()); 68gtag('config', '
68G-WJW2XMLVK9
vendor: 5 bytes, line 68
68');
69</script>
vendor: 2 bytes, line 69
69
70<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1521277811821952" 71crossorigin="anonymous" defer></script>
71 72<meta name="google-site-verification" content="ZWleqlOYPt0wm05jtSHIjposIojO4ID1mzFoLwoHm8M" /> 73<!-- This site is converting visitors into subscribers and customers with OptinMonster - https://optinmonster.com -->
74<script>(function(d,u,ac){var s=d.createElement('script');s.type='text/javascript';s.src='https://a.omappapi.com/app/js/api.min.js';s.async=true;s.dataset.user=u;s.dataset.account=ac;d.getElementsByTagName('head')[0].appendChild(s);})(document,171153,185538);</script>
74 75<!-- / https://optinmonster.com --> 76 77<style>:root{--cms-theme-color: #555;}</style><link rel="stylesheet" href="/sys-popup.css?v=1790187371"><style id="universal-submit-spinner"> 78.form-submit-spinner{display:inline-block;width:16px;height:16px;border:2px solid rgba(255,255,255,.4);border-top-color:#fff;border-radius:50%;animation:frmSpin .7s linear infinite;vertical-align:middle;margin-right:6px} 79@keyframes frmSpin{to{transform:rotate(360deg)}} 80</style></head> 81 82<body id="blbodymain" class="blbody" data-submenu="{{SubMenuId}}"><a href="https://swvasun.com/cdn-cgi/content?id=tq3ehKJP5JSzaoki7KRORT4VIDd1gx2tRQw_Cxal_2k-1790658882.4701972-1.2.1.1-ublGHNrAq4l7JMRrmr2TjZSer0HMBBqi3qFoLVTdSO_9mDEZXxEPGKCCQ1qqWzQX" aria-hidden="true" rel="nofollow noopener" style="display: none !important; visibility: hidden !important"></a> 83 <div class="mainbody"> 84 85<!-- Popup - start --> 86<div class="Ad22"><div data-cms-block="adgroup-22" data-cms-block-label="Advertisement 22" data-cms-el="adgroup" data-cms-adgroup="22">
86<script type="text/javascript" language="JavaScript">AdGroup22();</script>
86</div></div>
87<script> 88if (readCookie("subs-popup-" + ReadSubmenu())) { 89 // document.querySelector("iframe[src*='&pform={{NewsViaEmail2}}']").closest("#f_Popup4").classList.add("d-none"); 90 // document.body.style.overflow = "visible"; 91} else { 92 createCookie("subs-popup-" + ReadSubmenu(), "Yes"); 93} 94</script>
94 95<!-- Popup - end --> 96 97 <!-- stycky ads--> 98 <span class="cus-sticky-ads"><div data-cms-block="adgroup-1" data-cms-block-label="Advertisement 1" data-cms-el="adgroup" data-cms-adgroup="1">
98<script type="text/javascript" language="JavaScript">AdGroup1();</script>
98</div><div data-cms-block="adgroup-2" data-cms-block-label="Advertisement 2" data-cms-el="adgroup" data-cms-adgroup="2">
98<script type="text/javascript" language="JavaScript">AdGroup2();</script>
98</div><div data-cms-block="adgroup-3" data-cms-block-label="Advertisement 3" data-cms-el="adgroup" data-cms-adgroup="3">
98<script type="text/javascript" language="JavaScript">AdGroup3();</script>
98</div></span> 99 <!-- /stycky ads--> 100 <div class="container-xxl mt-2"> 101 <!-- top-links --> 102 <div class="row mb-3 border-bottom pb-2 border-2"> 103 <div class="bl-top-links col-sm-4 float-start"> 104 <div class="float-start"> 105 <!-- search--> 106
106<script type="text/javascript" language="JavaScript">GetGoogleCustomSearch();</script>
106 107 <a class="search-link text-decoration-none text-black fs-6 fw-bold me-3 border rounded p-1 px-2 bl-toplink-1-133-searchlink" href="javascript:void(0)" data-bs-toggle="tooltip" data-bs-placement="right" title="Click to Search Articles..."><i class="fa fa-search" aria-hidden="true"></i> SEARCH</a> 108 <div class="search_content"> 109 <div class="search_box"><gcse:searchbox></gcse:searchbox></div> 110 <div class="search_results"><gcse:searchresults></gcse:searchresults></div> 111 <div class="search_remove"> 112 <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16"> 113 <path fill-rule="evenodd" d="M13.854 2.146a.5.5 0 0 1 0 .708l-11 11a.5.5 0 0 1-.708-.708l11-11a.5.5 0 0 1 .708 0Z"/> 114 <path fill-rule="evenodd" d="M2.146 2.146a.5.5 0 0 0 0 .708l11 11a.5.5 0 0 0 .708-.708l-11-11a.5.5 0 0 0-.708 0Z"/> 115 </svg> 116 </div> 117 </div> 118
118<script> 119 document.getElementsByClassName('search-link')[0].addEventListener("click", function() { 120 document.getElementById("gsc-i-id1").setAttribute("placeholder", "Search for..."); 121 document.getElementsByClassName('search_content')[0].classList.add("search-flex"); 122 document.getElementsByClassName('search_remove')[0].style.display = "block"; 123 document.getElementsByClassName('gsc-results-close-btn')[0].addEventListener("click", function() { 124 document.getElementsByClassName('search_content')[0].classList.remove("search-flex"); 125 }); 126 }); 127 document.getElementsByClassName('search_remove')[0].addEventListener("click", function() { 128 document.getElementsByClassName('search_content')[0].classList.remove("search-flex"); 129 document.getElementsByClassName('search_remove')[0].style.display = "none"; }); 130 </script>
130 131 <style> 132 .gsc-adBlock {display: none !important;} 133 #gsc-i-id1{color:#000!important} 134 #gsc-i-id1::placeholder{color:#555!important;opacity:1;font-size:inherit!important} 135 .search_content{display:flex;display:-webkit-flex;-ms-display:flex;align-items:center;-webkit-align-items:center;-ms-align-items:center;justify-content:center;-webkit-justify-content:center;-ms-justify-content:center;background-color:rgba(255,255,255,.98);height:100%;position:fixed;top:100%;opacity:0;left:0;width:100%;z-index:2147483647;transition: 400ms linear;} 136 .search-flex{top: 0;opacity: 1;display:flex;display:-webkit-flex;-ms-display:flex;align-items:center;-webkit-align-items:center;-ms-align-items:center;justify-content:center;-webkit-justify-content:center;-ms-justify-content:center;transition: 300ms linear;} 137 .search_remove{cursor:pointer;user-select:none;position:absolute;top:10px;left:3%;} 138 .search_remove:hover{color:gray} 139 .gsc-control-cse.gsc-control-cse-en{background-color:transparent;border-width:0;position:absolute}.search_box{border-bottom:2px solid #000;width:70%;padding-bottom:6px} 140 .gsib_b,.gsst_a{display:none!important} 141 .gscb_a{display:none!important} 142 .gsc-input{background-image:none!important} 143 .gsc-input{background-color:transparent!important} 144 .gsc-input-box{background-color:transparent!important;border-bottom:0 solid #ff4500!important;border-width:0!important} 145 .gsc-input{border-width:0!important;color:#fff!important;font-size:20px!important} 146 .gsc-adBlock{display:none!important} 147 .search-icon-desk{cursor:pointer} 148 .search-icon-desk{float:right;height:54px;margin-right:10px} 149 .search-icon-desk i{border:1px solid #ddd;border-radius:4px;color:#000;display:block;line-height:35px;margin-top:9px;padding:0 12px} 150 .search-icon-desk i:hover{color:#79655d} 151 .gsc-results-wrapper-overlay.gsc-results-wrapper-visible {width: 100%;left: 0;top: 0;height: 100%;} 152 .gcsc-branding {display: none;} 153 .gsc-search-button.gsc-search-button-v2{padding:0 18px;line-height:35px;display:inline-block!important;background:#ddd;border-radius:5px;border-color:#555} 154 .gsc-search-button{width:auto!important;float:right!important}.gsc-search-button.gsc-search-button-v2:hover{background:#ddd!important} 155 .gsc-search-button-v2 svg{fill:#000!important} 156 .gsc-search-button.gsc-search-button-v2:hover{background:#05a2d3!important;border-color:#006899!important} 157 .gsc-search-button.gsc-search-button-v2:hover svg{fill:#fff!important} 158 </style> 159 <!-- /search--> 160 <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/about-us-p374-25.htm">ABOUT</a> 161 <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/ContactUs">CONTACT</a> 162 <a class="text-decoration-none text-black fs-6 me-3 bl-toplink-1-133-link" href="/SWVAsun">SIGN ME UP</a> 163 164 165 </div> 166 </div> 167 <div class="cus-social-links col-sm-4 float-sm-end d-flex justify-content-center d-sm-inline-block"> 168 <div class="float-sm-start"> 169 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.facebook.com/swvasun/" target="_blank"><i class="fa fa-facebook-square" aria-hidden="true"></i></a> 170 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://twitter.com/swvasun" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16" style="padding: 3px;background: #212529;fill: #fff;border-radius: 4px;display: inline-block;transform: translateY(-2px);" width="20" height="22"><path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"></path></svg></a> 171 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.linkedin.com/company/swva-media-llc/" target="_blank"><i class="fa fa-linkedin-square" aria-hidden="true"></i></a> 172 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://youtube.com/@southwestvirginiasun8601" target="_blank"><i class="fa fa-youtube-play" aria-hidden="true"></i></a> 173 </div> 174 </div> 175 </div> 176 <!-- /top-links --> 177 <!-- logo--> 178 <!-- Logo + AdGroup16 (centered) --> 179<div class="row mb-3 align-items-center"> 180 <!-- Left section: Logo + Ad16 --> 181 <div class="col-md-12 text-center"> 182 <div class="mb-2"> 183 <div data-cms-el="logo">
183<script type="text/javascript" language="JavaScript">GetLogo();</script>
183</div> 184 </div> 185 <div> 186 <div data-cms-block="adgroup-16" data-cms-block-label="Advertisement 16" data-cms-el="adgroup" data-cms-adgroup="16">
186<script type="text/javascript" language="JavaScript">AdGroup16();</script>
186</div> 187 </div> 188 </div> 189 190 <!-- Right section: Ad29 --> 191 192</div> 193 194 195 <div class="row mb-3"> 196 <div class="col-12 px-0 d-flex justify-content-center border-top border-bottom border-2"> 197
197<script type="text/javascript" language="JavaScript"> 198 GetMenuH(); 199 </script>
199 200 </div> 201 </div> 202 <!-- /menu --> 203<div class="cus-column-133-row row mb-3 border-bottom"> 204 <div class="cus-column-133-1 col-lg-9 pe-lg-3"> 205<div data-cms-popup-root="1"><style> 206#CustomForm {background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='32' viewBox='0 0 16 32'%3E%3Cg fill='%23c9d1cb' fill-opacity='0.16'%3E%3Cpath fill-rule='evenodd' d='M0 24h4v2H0v-2zm0 4h6v2H0v-2zm0-8h2v2H0v-2zM0 0h4v2H0V0zm0 4h2v2H0V4zm16 20h-6v2h6v-2zm0 4H8v2h8v-2zm0-8h-4v2h4v-2zm0-20h-6v2h6V0zm0 4h-4v2h4V4zm-2 12h2v2h-2v-2zm0-8h2v2h-2V8zM2 8h10v2H2V8zm0 8h10v2H2v-2zm-2-4h14v2H0v-2zm4-8h6v2H4V4zm0 16h6v2H4v-2zM6 0h2v2H6V0zm0 24h2v2H6v-2z'/%3E%3C/g%3E%3C/svg%3E");background-attachment: fixed;} 207.form-check-label:hover {color: #0d6efd;cursor: pointer;} 208.cus-form-loader .cus-form-loading {display: block !important;} 209.cus-form-loader .formbody fieldset, .cus-form-loader .system_text.footer_text {opacity: 0.5;pointer-events: none;} 210#cus-form-button-loader {display:none;} 211.cus-form-loader footer, .cus-form-loader button {cursor: no-drop;} 212.fast-spin {-webkit-animation: fa-spin 1s infinite linear;animation: fa-spin 1s infinite linear;} 213.cus-form-loader #SUBMIT1 {display:none;} 214.cus-form-loader #cus-form-button-loader {display:inline-block;} 215.form-floating > .form-control:focus ~ label {color: #0d6efd !important;} 216.form-control:not(:disabled):not([readonly])::file-selector-button {position: absolute;right: 0;border-radius: 5px !important;} 217.input-file-preview {display: none;} 218.input-file-preview img {max-width: 30%;max-height: 200px;border: 1px solid #ced4da;border-radius: 5px;padding: 5px;box-sizing: border-box;cursor: zoom-in;} 219.w-96 {width: 96% !important;} 220.p-valid {color: green;} 221.p-valid:before {position: relative;left: -10px;content: "â";} 222.p-invalid {color: red;} 223.p-invalid:before {position: relative;left: -10px;content: "â";font-size: 0.875rem !important;} 224.open-eye, .close-eye {position: absolute;right: 2%;top: 50%;transform: translate(0,-50%);cursor: pointer;} 225.open-eye i:hover, .close-eye i:hover {color: gray !important;} 226.cus-form-help:before {content: "\f05a";font-family: FontAwesome;padding-left: 0.5rem;padding-right: 0.5rem;} 227.cus-form-help {display: none;} 228.cus-form-help:empty {display: none !important;} 229.form-control:focus ~ .cus-form-help {display: block;} 230.formdivider.formdivider_custom {border-left: 5px solid #ffc107;} 231.form-floating > .form-control {caret-color: #0d6efd;} 232</style> 233<style id="force-form-styles" data-version="2026-01-26-v2"> 234 textarea, textarea.form-control, textarea.formfieldinput { min-height: 120px !important; height: 120px !important; } 235 236 /* Enhanced Spinner Styles - More visible and animated */ 237 .form-spinner-ring { 238 display: inline-block; 239 width: 18px; 240 height: 18px; 241 border: 3px solid rgba(255,255,255,0.4); 242 border-top-color: #fff; 243 border-radius: 50%; 244 animation: formSpin 0.7s linear infinite; 245 margin-right: 8px; 246 vertical-align: middle; 247 } 248 @keyframes formSpin { 249 0% { transform: rotate(0deg); } 250 100% { transform: rotate(360deg); } 251 } 252 253 /* Submit Button Loading State */ 254 button.btn-loading, input.btn-loading { 255 position: relative; 256 pointer-events: none; 257 opacity: 0.85; 258 } 259 button.btn-loading::after { 260 content: ''; 261 position: absolute; 262 width: 18px; 263 height: 18px; 264 top: 50%; 265 left: 12px; 266 margin-top: -9px; 267 border: 3px solid rgba(255,255,255,0.4); 268 border-top-color: #fff; 269 border-radius: 50%; 270 animation: formSpin 0.7s linear infinite; 271 } 272 273 /* Form Overlay during submission */ 274 .form-loading-overlay { 275 position: absolute; 276 top: 0; 277 left: 0; 278 right: 0; 279 bottom: 0; 280 background: rgba(255,255,255,0.7); 281 display: flex; 282 align-items: center; 283 justify-content: center; 284 z-index: 9999; 285 border-radius: inherit; 286 } 287 .form-loading-overlay .spinner-container { 288 display: flex; 289 flex-direction: column; 290 align-items: center; 291 gap: 12px; 292 padding: 30px; 293 background: white; 294 border-radius: 12px; 295 box-shadow: 0 4px 20px rgba(0,0,0,0.15); 296 } 297 .form-loading-overlay .spinner-large { 298 width: 40px; 299 height: 40px; 300 border: 4px solid #e0e0e0; 301 border-top-color: #007bff; 302 border-radius: 50%; 303 animation: formSpin 0.8s linear infinite; 304 } 305 .form-loading-overlay .loading-text { 306 color: #333; 307 font-size: 14px; 308 font-weight: 500; 309 } 310 311 /* Bootstrap 5 Floating Label Fix - Force proper behavior */ 312 .form-floating > .form-control:focus ~ label, 313 .form-floating > .form-control:not(:placeholder-shown) ~ label, 314 .form-floating > .form-select ~ label { 315 opacity: 0.65 !important; 316 transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem) !important; 317 } 318 .form-floating > .form-control::placeholder { 319 color: transparent !important; 320 } 321 .form-floating > label { 322 position: absolute !important; 323 top: 0 !important; 324 left: 0 !important; 325 height: 100% !important; 326 padding: 1rem 0.75rem !important; 327 pointer-events: none !important; 328 border: 1px solid transparent !important; 329 transform-origin: 0 0 !important; 330 transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out !important; 331 } 332 </style> 333<style>.grecaptcha-badge{visibility:hidden!important;position:fixed!important;bottom:14px!important;right:14px!important;}</style>
334<script src="/iframe-resizer-master/js/iframeResizer.contentWindow.js"></script>
vendor: 1 bytes, line 334
334
335<script type="text/javascript" language="javascript" src="https://swvasun.com/lib/bl.js"></script>
vendor: 1 bytes, line 335
335
336<script> 337function ChangeInputFocus(obj, evt, cname) { 338/*if (evt.type == "focus") 339obj.className = cname; 340else if (evt.type == "blur") 341/*obj.className = cname;*/ 342} 343</script>
vendor: 1 bytes, line 343
343
344<script src="https://recaptcha.net/recaptcha/api.js?render=6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM"></script>
vendor: 1 bytes, line 344
344
345<script> 346(function(){ 347 function setToken(t){ 348 var els = document.querySelectorAll('.g-recaptcha-token'); 349 for (var i = 0; i < els.length; i++) els[i].value = t; 350 } 351 function refresh(){ 352 if (typeof grecaptcha === 'undefined') return; 353 grecaptcha.ready(function(){ 354 grecaptcha.execute('6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM', {action: 'submit'}).then(setToken); 355 }); 356 } 357 // Submit-time minter. Login pages call this in their submit handler and await it, 358 // so the request always carries a freshly-minted token instead of reading the 359 // pre-filled hidden field â which may still be empty when a user with saved 360 // credentials presses Enter before the page-load mint resolves (-> recaptcha_missing, 361 // a hard block). Resolves to a fresh token, or falls back to whatever's already in 362 // the field (and never hangs the form: 3s timeout, and resolves if grecaptcha is 363 // unavailable). Also updates the hidden field so FormData-based submits pick it up. 364 window.cmsMintCaptchaToken = function(){ 365 return new Promise(function(resolve){ 366 var els = document.querySelectorAll('.g-recaptcha-token'); 367 var fallback = (els[0] && els[0].value) || ''; 368 var done = false; 369 var to = setTimeout(function(){ if(!done){ done = true; resolve(fallback); } }, 3000); 370 function fin(v){ if(done) return; done = true; clearTimeout(to); setToken(v); resolve(v); } 371 function attempt(){ 372 if (typeof grecaptcha === 'undefined' || !grecaptcha.execute) return false; 373 try { 374 grecaptcha.ready(function(){ 375 grecaptcha.execute('6LdXmfgUAAAAAJNuEHBxVCY0w9eA73hvaWsdgPlM', {action: 'submit'}) 376 .then(function(t){ fin(t || fallback); }) 377 .catch(function(){ fin(fallback); }); 378 }); 379 return true; 380 } catch(_) { return false; } 381 } 382 // grecaptcha may still be loading -- common when the form is rendered 383 // inside a popup, where api.js was injected only moments ago. Poll until 384 // it's ready (or the 3s timeout fires) instead of bailing immediately. 385 if (!attempt()) { 386 var iv = setInterval(function(){ 387 if (done) { clearInterval(iv); return; } 388 if (attempt()) clearInterval(iv); 389 }, 150); 390 } 391 }); 392 }; 393 refresh(); 394 setTimeout(refresh, 5000); 395 setInterval(refresh, 120000); 396})(); 397</script>
397 398<div class="formbody"> 399<div class="body p-2"> 400<!--FormError--> 401<!--FormMessage--> 402<form class="custom-form p-3 mx-0 border rounded" enctype="multipart/form-data" autocomplete="off" id="CustomForm" method="post" name="CustomForm" onsubmit="return unifiedSubmitForm(event)"> 403<div aria-hidden="true" style="position:absolute;left:-9999px;top:-9999px;height:0;width:0;overflow:hidden;"> 404 <label for="website_url_hp">Website (leave blank)</label> 405 <input type="text" name="website_url" id="website_url_hp" tabindex="-1" autocomplete="off" value=""> 406</div> 407<input type="hidden" name="token" class="g-recaptcha-token" value=""> 408<!--FKeys--> 409<input type="hidden" name="SystemEmailSubject" value="SWVAsun.com: Contact Us"> 410<input type="hidden" name="SystemEmailConfirmMessage" value="<center><h2>Your message has been forwarded to appropriate personnel. <br><br>Thank you.</h2></center>"> 411<input type="hidden" name="SystemEmailFormat" value="HTML"> 412<input type="hidden" name="SystemEmailTo" value="[email protected]"> 413<input type="hidden" name="SystemEmailCC" value=""> 414<center class="fw-bold mb-3 fs-3 text-secondary">Contact Us</center> 415<fieldset> 416<div class="system_text Header_text mb-5 text-center"><CENTER><B>Use the form below for comments or questions.</B></CENTER></div> 417 418 419 420<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title=""> 421<div class="input w-100 form-floating"> 422<INPUT NAME="Subject" VALUE="" id="Subject" maxlength="" placeholder=" " type="Text" class="formfieldinput formfieldinput_custom form-control" required> 423<label for="Subject" class="form-label text-muted">* Subject</label> 424</div> 425</section> 426 427 428 429 430<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title="Be sure to enter proper email so we can contact you."> 431<div class="input w-100 form-floating"> 432<INPUT NAME="Email" VALUE="" id="Email" maxlength="" placeholder=" " type="Email" class="formfieldinput formfieldinput_custom form-control" required> 433<label for="Email" class="form-label text-muted">* Email</label> 434</div> 435</section> 436 437 438 439 440<section class="w-96 mb-3" style="margin-left:2%" data-bs-toggle="tooltip" data-bs-placement="auto" title=""> 441<div class="input w-100 form-floating"> 442<textarea name="Message" id="Message" placeholder=" " class="formfieldinput formfieldinput_custom form-control" required rows="3" style="min-height: 120px !important; height: 120px !important; line-height: 1.5 !important; display: block !important; width: 100% !important; padding-top: 1.5rem !important;; min-height: 120px !important; height: 120px !important;" rows="3"></textarea> 443<label for="Message" class="form-label text-muted">* Message</label> 444</div> 445</section> 446 447 448 449 450</fieldset> 451<footer class="text-center mt-3"> 452<button class="button btn btn-primary shadow text-capitalize" id="SUBMIT1" type="submit" onclick="document.getElementById('CustomForm').classList.add('was-validated');"><span class="py-1 px-3 d-inline-block">submit</span></button> 453<i id="cus-form-button-loader" class='fa fa-circle-o-notch fa-spin fast-spin fs-1 text-primary'></i> 454</footer> 455<div class="system_text footer_text mt-5 mb-3 text-center"></div> 456</form> 457</div>
458<script language="javascript">GetGoogleAnalytics('FORM:ContactUs');</script>
458 459<!-- Tooltip -->
460<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
vendor: 2 bytes, line 460
460
461<script> 462var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) 463var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { 464return new bootstrap.Tooltip(tooltipTriggerEl) 465}) 466</script>
466 467<!-- /Tooltip --> 468</div> 469 470<!-- Form Script v2026-01-26-v2 --> 471<!-- Modern Popup Modal for Messages --> 472<div id="form-message-modal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.7); z-index:99999; align-items:center; justify-content:center; padding:20px;"> 473 <div style="background:#fff; min-width:320px; max-width:400px; border-radius:12px; padding:24px; box-shadow:0 25px 50px -12px rgba(0,0,0,0.5);"> 474 <div id="form-message-text" style="margin-bottom:20px; font-size:15px; color:#333; text-align:center; line-height:1.5;"></div> 475 <button id="form-message-ok" style="width:100%; padding:12px 20px; font-size:14px; font-weight:600; background:#007bff; color:white; border:none; border-radius:8px; cursor:pointer; transition:all 0.2s ease;" onmouseover="this.style.background='#0056b3'" onmouseout="this.style.background='#007bff'">OK</button> 476 </div> 477</div> 478
479<script> 480// Resolve the URL to POST the form to. On a normal page load the form lives at 481// its own URL, so window.location.pathname is correct. But when the form is 482// opened inside a popup (sys-popup.js), the page URL is the HOST page, not the 483// form's URL -- sys-popup stamps the real form path onto the form as 484// data-form-action, which we prefer when present. 485function cmsResolvePostUrl(form) { 486 var f = form || document.querySelector('form'); 487 var a = f && f.getAttribute && f.getAttribute('data-form-action'); 488 return a || window.location.pathname; 489} 490 491// Modern message popup function 492// onClose callback is called when user clicks OK (useful for resetting button on error) 493function showFormMessage(text, isSuccess = false, onClose = null) { 494 const modal = document.getElementById('form-message-modal'); 495 const modalMsg = document.getElementById('form-message-text'); 496 const modalOk = document.getElementById('form-message-ok'); 497 498 if (modal && modalMsg && modalOk) { 499 modalMsg.innerHTML = isSuccess 500 ? '<div style="display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:20px;"><div style="width:50px;height:50px;background:linear-gradient(135deg,#28a745,#20c997);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 15px rgba(40,167,69,0.3);flex-shrink:0;"><span style="color:white;font-size:28px;font-weight:bold;">â</span></div><span style="color:#28a745;font-size:28px;font-weight:600;">Success</span></div>' + text 501 : text; 502 modal.style.display = 'flex'; 503 modalOk.onclick = function() { 504 modal.style.display = 'none'; 505 if (isSuccess) { 506 (window.top || window).location.href = '/'; 507 } else if (onClose) { 508 onClose(); 509 } 510 }; 511 // Also close on backdrop click 512 modal.onclick = function(e) { 513 if (e.target === modal) { 514 modal.style.display = 'none'; 515 if (isSuccess) { 516 (window.top || window).location.href = '/'; 517 } else if (onClose) { 518 onClose(); 519 } 520 } 521 }; 522 } else { 523 // Fallback to alert if modal not found 524 alert(text); 525 if (isSuccess) { 526 (window.top || window).location.href = '/'; 527 } else if (onClose) { 528 onClose(); 529 } 530 } 531} 532 533// Accept.js card input modal for Authorize.net 534async function showAcceptJsCardInput(clientConfig, form, originalFormData, paymentResult) { 535 console.log('[forms] Loading Accept.js for card tokenization'); 536 537 // Load Accept.js script dynamically 538 if (!window.Accept) { 539 await new Promise((resolve, reject) => { 540 const script = document.createElement('script'); 541 script.src = clientConfig.jsUrl; 542 script.onload = resolve; 543 script.onerror = () => reject(new Error('Failed to load Accept.js')); 544 document.head.appendChild(script); 545 }); 546 } 547 548 // Create card input modal 549 const modalHtml = ` 550 <div id="acceptjs-modal" style="position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px;"> 551 <div style="background:#fff;max-width:400px;width:100%;border-radius:12px;padding:30px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5);"> 552 <h3 style="margin:0 0 20px;color:#333;">Payment Information</h3> 553 <p style="margin:0 0 20px;color:#666;">Amount: <strong>$${paymentResult.amount}</strong></p> 554 555 <div style="margin-bottom:15px;"> 556 <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Card Number *</label> 557 <input type="text" id="ajs-card-number" placeholder="4111 1111 1111 1111" maxlength="19" 558 style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;box-sizing:border-box;"> 559 </div> 560 561 <div style="display:flex;gap:15px;margin-bottom:15px;"> 562 <div style="flex:1;"> 563 <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Exp Month *</label> 564 <select id="ajs-exp-month" style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;"> 565 <option value="">MM</option> 566 ${[...Array(12)].map((_,i) => '<option value="'+String(i+1).padStart(2,'0')+'">'+String(i+1).padStart(2,'0')+'</option>').join('')} 567 </select> 568 </div> 569 <div style="flex:1;"> 570 <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">Exp Year *</label> 571 <select id="ajs-exp-year" style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;"> 572 <option value="">YYYY</option> 573 ${[...Array(15)].map((_,i) => { const y=new Date().getFullYear()+i; return '<option value="'+y+'">'+y+'</option>'; }).join('')} 574 </select> 575 </div> 576 <div style="width:100px;"> 577 <label style="display:block;margin-bottom:5px;font-weight:600;color:#333;">CVV *</label> 578 <input type="text" id="ajs-cvv" placeholder="123" maxlength="4" 579 style="width:100%;padding:12px;border:1px solid #ccc;border-radius:4px;font-size:16px;box-sizing:border-box;"> 580 </div> 581 </div> 582 583 <div id="ajs-error" style="color:#dc3545;margin-bottom:15px;
583display:none;"></div> 584 585 <div style="display:flex;gap:10px;"> 586 <button type="button" id="ajs-cancel" style="flex:1;padding:12px;background:#6c757d;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:16px;">Cancel</button> 587 <button type="button" id="ajs-submit" style="flex:1;padding:12px;background:#28a745;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:16px;">Pay Now</button> 588 </div> 589 </div> 590 </div> 591 `; 592 593 document.body.insertAdjacentHTML('beforeend', modalHtml); 594 595 const modal = document.getElementById('acceptjs-modal'); 596 const cardInput = document.getElementById('ajs-card-number'); 597 const cvvInput = document.getElementById('ajs-cvv'); 598 const errorDiv = document.getElementById('ajs-error'); 599 600 // Format card number 601 cardInput.addEventListener('input', function(e) { 602 let v = e.target.value.replace(/\s+/g, '').replace(/[^0-9]/g, ''); 603 let f = ''; 604 for (let i = 0; i < v.length; i++) { if (i > 0 && i % 4 === 0) f += ' '; f += v[i]; } 605 e.target.value = f; 606 }); 607 608 // Only numbers in CVV 609 cvvInput.addEventListener('input', function(e) { 610 e.target.value = e.target.value.replace(/[^0-9]/g, ''); 611 }); 612 613 // Cancel button - also reset the submit button 614 document.getElementById('ajs-cancel').onclick = function() { 615 modal.remove(); 616 // Reset the original form's submit button 617 const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]'); 618 if (submitBtn) { 619 submitBtn.disabled = false; 620 if (submitBtn.tagName === 'INPUT') { 621 submitBtn.value = 'Submit'; 622 } else { 623 submitBtn.innerHTML = 'Submit'; 624 } 625 } 626 }; 627 628 // Submit button 629 document.getElementById('ajs-submit').onclick = async function() { 630 const cardNumber = cardInput.value.replace(/\s+/g, ''); 631 const expMonth = document.getElementById('ajs-exp-month').value; 632 const expYear = document.getElementById('ajs-exp-year').value; 633 const cvv = cvvInput.value; 634 635 // Validate 636 if (!cardNumber || cardNumber.length < 13) { 637 errorDiv.textContent = 'Please enter a valid card number'; 638 errorDiv.style.display = 'block'; 639 return; 640 } 641 if (!expMonth || !expYear) { 642 errorDiv.textContent = 'Please select expiration date'; 643 errorDiv.style.display = 'block'; 644 return; 645 } 646 if (!cvv || cvv.length < 3) { 647 errorDiv.textContent = 'Please enter CVV'; 648 errorDiv.style.display = 'block'; 649 return; 650 } 651 652 errorDiv.style.display = 'none'; 653 this.disabled = true; 654 this.innerHTML = '<span class="form-spinner-ring" style="border-color:rgba(255,255,255,0.4);border-top-color:#fff;"></span> Processing...'; 655 656 // Test card detection - skip Accept.js tokenization 657 var TEST_CARDS_V3 = ['4111111111111111', '4000000000000000', '5500000000000004', '370000000000002', '6011000000000004']; 658 if (TEST_CARDS_V3.indexOf(cardNumber) !== -1) { 659 console.log('[forms] Test card detected - skipping tokenization'); 660 originalFormData.append('dataDescriptor', 'TEST_CARD'); 661 originalFormData.append('dataValue', 'TEST'); 662 originalFormData.append('pf-card-number-input', cardNumber); 663 664 try { 665 const testResp = await fetch(cmsResolvePostUrl(), { 666 method: 'POST', 667 body: originalFormData, 668 headers: { 'Accept': 'application/json' } 669 }); 670 const testResult = await testResp.json(); 671 modal.remove(); 672 if (testResult.success && testResult.isPaymentComplete) { 673 if (testResult.subscriberToken) { 674 try { 675 var tp2 = JSON.parse(atob(testResult.subscriberToken.split('.')[1])); 676 var ma2 = tp2.exp ? (tp2.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60); 677 document.cookie = 'subscriber_token=' + encodeURIComponent(testResult.subscriberToken) + '; path=/; max-age=' + ma2 + '; SameSite=Lax'; 678 } catch (e) { /* ignore */ } 679 } 680 var msg = testResult.confirmMessage 681 ? testResult.confirmMessage + '<br><br><small>Transaction ID: ' + testResult.transactionId + '</small>' 682 : 'Test payment successful! Transaction ID: ' + testResult.transactionId; 683 var retUrl = null; 684 try { retUrl = localStorage.getItem('subscriber_return_url'); } catch (e) { /* ignore */ } 685 if (!retUrl) { try { retUrl = new URLSearchParams(window.location.search).get('return_url'); } catch (e) { /* ignore */ } } 686 if (retUrl) { 687 try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ } 688 msg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>'; 689 showFormMessage(msg, true); 690 setTimeout(function() { window.location.href = retUrl; }, 2000); 691 } else { 692 showFormMessage(msg, true); 693 } 694 form.reset(); 695 } else { 696 showFormMessage(testResult.error || 'Payment failed.', false); 697 } 698 } catch (e) { 699 modal.remove(); 700 showFormMessage('Network error. Please try again.', false); 701 } 702 return; 703 } else { 704 // Build Accept.js request 705 const secureData = { 706 authData: { 707 clientKey: clientConfig.clientKey, 708 apiLoginID: clientConfig.apiLoginId 709 }, 710 cardData: { 711 cardNumber: cardNumber, 712 month: expMonth, 713 year: expYear, 714 cardCode: cvv 715 } 716 }; 717 718 // Tokenize via Accept.js 719 Accept.dispatchData(secureData, async function(response) { 720 if (response.messages.resultCode === 'Error') { 721 const errorText = response.messages.message.map(m => m.text).join(', '); 722 errorDiv.textContent = errorText; 723 errorDiv.style.display = 'block'; 724 const ajsBtn = document.getElementById('ajs-submit'); 725 ajsBtn.disabled = false;
726 ajsBtn.innerHTML = 'Pay Now'; 727 return; 728 } 729 730 console.log('[forms] Card tokenized successfully, resubmitting form'); 731 732 // Add token to form data and resubmit 733 originalFormData.append('dataDescriptor', response.opaqueData.dataDescriptor); 734 originalFormData.append('dataValue', response.opaqueData.dataValue); 735 736 try { 737 const response2 = await fetch(cmsResolvePostUrl(), { 738 method: 'POST', 739 body: originalFormData, 740 headers: { 'Accept': 'application/json' } 741 }); 742 const result2 = await response2.json(); 743 744 modal.remove(); 745 746 // Get the submit button to reset on error 747 const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]'); 748 const resetButton = function() { 749 if (submitBtn) { 750 submitBtn.disabled = false; 751 if (submitBtn.tagName === 'INPUT') { 752 submitBtn.value = 'Submit'; 753 } else { 754 submitBtn.innerHTML = 'Submit'; 755 } 756 } 757 }; 758 759 if (result2.success && result2.isPaymentComplete) { 760 // Auto-login: set subscriber_token cookie if provided 761 if (result2.subscriberToken) { 762 try { 763 var tp = JSON.parse(atob(result2.subscriberToken.split('.')[1])); 764 var ma = tp.exp ? (tp.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60); 765 document.cookie = 'subscriber_token=' + encodeURIComponent(result2.subscriberToken) + '; path=/; max-age=' + ma + '; SameSite=Lax'; 766 console.log('[forms] Auto-login: subscriber_token cookie set (Accept.js path)'); 767 } catch (e) { console.warn('[forms] Auto-login cookie error', e); } 768 } 769 // Build success message with custom confirm message if available 770 let successMsg = 'Payment successful! Transaction ID: ' + result2.transactionId; 771 if (result2.confirmMessage) { 772 successMsg = result2.confirmMessage + '<br><br><small>Transaction ID: ' + result2.transactionId + '</small>'; 773 } 774 var retUrl2 = null; 775 try { retUrl2 = localStorage.getItem('subscriber_return_url'); } catch (e) { /* ignore */ } 776 if (!retUrl2) { try { retUrl2 = new URLSearchParams(window.location.search).get('return_url'); } catch (e) { /* ignore */ } } 777 if (retUrl2) { 778 try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ } 779 successMsg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>'; 780 showFormMessage(successMsg, true); 781 setTimeout(function() { window.location.href = retUrl2; }, 2000); 782 } else { 783 showFormMessage(successMsg, true); 784 } 785 form.reset(); 786 } else { 787 showFormMessage(result2.error || 'Payment failed. Please try again.', false, resetButton); 788 } 789 } catch (e) { 790 modal.remove(); 791 const submitBtn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]'); 792 showFormMessage('Network error. Please try again.', false, function() { 793 if (submitBtn) { 794 submitBtn.disabled = false; 795 if (submitBtn.tagName === 'INPUT') { 796 submitBtn.value = 'Submit'; 797 } else { 798 submitBtn.innerHTML = 'Submit'; 799 } 800 } 801 }); 802 } 803 }); 804 } // end else (real card tokenization) 805 }; 806} 807 808// Initialize File Uploader on load to handle image cropping/uploading. 809// Defined as a named function (not an inline DOMContentLoaded callback) so it can 810// also run immediately when the DOM is ALREADY parsed -- e.g. when this form is 811// injected into a popup after DOMContentLoaded has already fired. Without that, 812// the uploader (image cropper) never initializes inside the modal. 813async function cmsInitFormUploader() { 814 // Restore form data if user cancelled payment 815 try { 816 const pendingData = sessionStorage.getItem('_pendingFormData'); 817 if (pendingData) { 818 const formData = JSON.parse(pendingData); 819 const form = document.getElementById('CustomForm') || document.querySelector('form'); 820 if (form) { 821 for (const [key, value] of Object.entries(formData)) { 822 if (value && typeof value === 'string') { 823 const field = form.querySelector('[name="' + key + '"]'); 824 if (field) { 825 if (field.tagName === 'SELECT') { 826 field.value = value; 827 } else if (field.type !== 'file' && field.type !== 'hidden') { 828 field.value = value; 829 } 830 } 831 } 832 } 833 console.log('[forms] Restored form data from cancelled payment'); 834 } 835 // Clear the stored data 836 sessionStorage.removeItem('_pendingFormData'); 837 sessionStorage.removeItem('_pendingFormUrl'); 838 } 839 } catch (e) { 840 console.error('[forms] Error restoring form data:', e); 841 } 842 843 try { 844 const { UnifiedFileUploader } = await import('/sys-components-UnifiedFileUploader.js?v=' + Date.now()); 845 window.unifiedUploader = new UnifiedFileUploader(); 846 window.unifiedUploader.init(); 847 } catch (e) { 848 console.error('Failed to init uploader:', e); 849 } 850}
851// Normal page load: wait for DOMContentLoaded. Popup: the form is injected after 852// DOMContentLoaded already fired, so run immediately when the DOM is already 853// parsed -- otherwise the uploader (image cropper) never initializes in the 854// modal. The earlier empty-box / stuck-button symptom was FontAwesome 4 vs 6 855// (the FA6 spinner glyph was missing); the form now injects FA6, so the cropper 856// controls and spinner render and the uploader completes normally here too. 857if (document.readyState === 'loading') { 858 document.addEventListener('DOMContentLoaded', cmsInitFormUploader); 859} else { 860 cmsInitFormUploader(); 861} 862 863async function unifiedSubmitForm(event) { 864 console.log('[forms] unifiedSubmitForm called v5 - 2026-01-26 Enhanced spinner'); 865 if (event) { event.preventDefault(); event.stopPropagation(); } 866 867 // Use event.target if available (most reliable) 868 const form = (event && event.target && event.target.tagName === 'FORM') ? event.target : (document.getElementById('CustomForm') || document.querySelector('form')); 869 870 // Enforce native constraint validation (required / type=email / pattern ...) 871 // BEFORE anything else. This handler preventDefault()s the native submit on its 872 // first line, which also bypasses the browser's own required-field check -- so 873 // without this call a blank required field would fetch-POST straight through 874 // (legacy custom forms had no client-side guard at all). reportValidity() focuses 875 // the first invalid field, shows the browser's validation bubble, and bails. 876 // Runs before the submit button is put into its spinner/disabled state so a 877 // blocked submit leaves the form fully usable. 878 if (form && typeof form.reportValidity === 'function' && !form.reportValidity()) { 879 return false; 880 } 881 882 // Ensure any processed files are in the form before submission 883 if (window.unifiedUploader && window.unifiedUploader.ensureProcessedFilesInForm) { 884 window.unifiedUploader.ensureProcessedFilesInForm(); 885 } 886 887 const btn = form.querySelector('button[type="submit"], #SUBMIT1, input[type="submit"]'); 888 const originalHtml = btn ? (btn.tagName === 'INPUT' ? btn.value : btn.innerHTML) : 'Submit'; 889 890 // Show loading state with spinner 891 if (btn) { 892 btn.disabled = true; 893 if (btn.tagName === 'INPUT') { 894 // Inputs can't hold an inline span, so use the class-based ::after spinner. 895 btn.classList.add('btn-loading'); 896 btn.value = ' Processing...'; 897 btn.style.paddingLeft = '35px'; 898 } else { 899 // Buttons: the inline ring span is the single spinner. Do NOT add the 900 // btn-loading class here -- button.btn-loading::after renders a SECOND 901 // spinner, which produced two spinning icons at once on submit. Keep the 902 // disabled look via inline styles instead (robust even in popups where the 903 // injected spinner CSS may be absent). 904 btn.style.pointerEvents = 'none'; 905 btn.style.opacity = '0.85'; 906 btn.innerHTML = '<span class="form-spinner-ring"></span> Processing...'; 907 } 908 } 909 910 // Single spinner only: the disabled submit button already shows a spinner 911 // ("Processing..."), so we intentionally do NOT add the full-form overlay 912 // spinner here -- it produced two spinning icons at once (most visible in the 913 // popup). overlay stays null; the cleanup checks below are guarded for it. 914 let overlay = null; 915 try { 916 // Mint a fresh reCAPTCHA token before reading the form. On popup-rendered 917 // forms the on-load mint may not have fired yet (api.js was injected only 918 // moments ago by sys-popup's executeScripts), so without this the hidden 919 // token field is still empty and the server blocks as recaptcha_missing. 920 if (typeof window.cmsMintCaptchaToken === 'function') { 921 try { await window.cmsMintCaptchaToken(); } catch (e) {} 922 } 923 const formData = new FormData(form); 924 925 // Capture display TEXT for all select, radio, and checkbox elements 926 // Add as fieldname_text so email shows human-readable labels instead of raw values 927 form.querySelectorAll('select').forEach(function(select) { 928 if (select.name && select.selectedIndex >= 0) { 929 if (select.multiple) { 930 // Multi-select: join the text of EVERY selected option, not just the first 931 var multiTexts = Array.prototype.map.call(select.selectedOptions, function(o) { return o.text; }); 932 formData.append(select.name + '_text', multiTexts.join(', ')); 933 } else { 934 const selectedOption = select.options[select.selectedIndex]; 935 if (selectedOption) { 936 formData.append(select.name + '_text', selectedOption.text); 937 } 938 } 939 } 940 }); 941 // Capture checked radio button labels 942 var radiosDone = {}; 943 form.querySelectorAll('input[type="radio"]:checked').forEach(function(radio) { 944 if (radio.name && !radiosDone[radio.name]) { 945 radiosDone[radio.name] = true; 946 var label = form.querySelector('label[for="' + radio.id + '"]'); 947 if (label) { 948 formData.append(radio.name + '_text', label.textContent.trim()); 949 } 950 } 951 }); 952 // Capture checked checkbox labels 953 var checkboxTexts = {}; 954 form.querySelectorAll('input[type="checkbox"]:checked').forEach(function(cb) { 955 if (cb.name) { 956 var cleanName = cb.name.replace('[]', ''); 957 var label = form.querySelector('label[for="' + cb.id + '"]'); 958 var text = label ? label.textContent.trim() : (cb.value === 'on' ? 'Yes' : cb.value); 959 if (!checkboxTexts[cleanName]) checkboxTexts[cleanName] = []; 960 checkboxTexts[cleanName].push(text); 961 } 962 });
963 Object.keys(checkboxTexts).forEach(function(name) { 964 formData.append(name + '_text', checkboxTexts[name].join(', ')); 965 }); 966 const response = await fetch(cmsResolvePostUrl(form), { 967 method: 'POST', 968 body: formData, 969 headers: { 'Accept': 'application/json' } 970 }); 971 const result = await response.json(); 972 console.log('[forms] v3 Server response:', JSON.stringify(result)); 973 console.log('[forms] v3 Keys in result:', Object.keys(result).join(', ')); 974 console.log('[forms] v3 requiresCardInput:', result.requiresCardInput, 'clientConfig:', !!result.clientConfig); 975 console.log('[forms] v3 isPaymentRedirect:', result.isPaymentRedirect, 'redirectUrl:', result.redirectUrl); 976 if (result.success) { 977 // PRIORITY 0: Check if payment is already complete (Authorize.net Accept.js) 978 if (result.isPaymentComplete) { 979 console.log('[forms] Payment complete! Transaction:', result.transactionId); 980 981 // Auto-login: set subscriber_token cookie if provided 982 if (result.subscriberToken) { 983 try { 984 var tokenPayload = JSON.parse(atob(result.subscriberToken.split('.')[1])); 985 var maxAge = tokenPayload.exp ? (tokenPayload.exp - Math.floor(Date.now() / 1000)) : (30 * 24 * 60 * 60); 986 document.cookie = 'subscriber_token=' + encodeURIComponent(result.subscriberToken) + '; path=/; max-age=' + maxAge + '; SameSite=Lax'; 987 console.log('[forms] Auto-login: subscriber_token cookie set'); 988 } catch (e) { 989 console.warn('[forms] Auto-login: failed to set cookie', e); 990 } 991 } 992 993 // Build success message with custom confirm message if available 994 let successMsg = 'Payment successful! Transaction ID: ' + result.transactionId; 995 if (result.confirmMessage) { 996 successMsg = result.confirmMessage + '<br><br><small>Transaction ID: ' + result.transactionId + '</small>'; 997 } 998 999 // Redirect back to article if return URL is available (from paywall subscribe flow) 1000 var returnUrl = null; 1001 try { 1002 returnUrl = localStorage.getItem('subscriber_return_url'); 1003 } catch (e) { /* ignore */ } 1004 // Also check URL query param 1005 if (!returnUrl) { 1006 try { 1007 var params = new URLSearchParams(window.location.search); 1008 returnUrl = params.get('return_url'); 1009 } catch (e) { /* ignore */ } 1010 } 1011 1012 if (returnUrl) { 1013 try { localStorage.removeItem('subscriber_return_url'); } catch (e) { /* ignore */ } 1014 // Show brief success message then redirect 1015 successMsg += '<p style="margin-top:15px;color:#666;">Redirecting you back to your article...</p>'; 1016 showFormMessage(successMsg, true); 1017 setTimeout(function() { window.location.href = returnUrl; }, 2000); 1018 } else { 1019 showFormMessage(successMsg, true); 1020 } 1021 form.reset(); 1022 return; 1023 } 1024 1025 // PRIORITY 1: Check for payment redirect FIRST (Stripe Checkout) 1026 // Payment forms MUST redirect to the payment gateway before showing any message 1027 if (result.isPaymentRedirect && result.redirectUrl) { 1028 console.log('[forms] Payment redirect detected, redirecting to:', result.redirectUrl); 1029 window.location.href = result.redirectUrl; 1030 return; 1031 } 1032 1033 // PRIORITY 1.5: Check if card input is required (Authorize.net Accept.js)
1034 console.log('[forms] v3 Checking requiresCardInput:', result.requiresCardInput, 'clientConfig exists:', !!result.clientConfig); 1035 if (result.requiresCardInput && result.clientConfig) { 1036 console.log('[forms] v3 Card input required! Showing Accept.js modal...'); 1037 try { 1038 await showAcceptJsCardInput(result.clientConfig, form, formData, result); 1039 console.log('[forms] v3 showAcceptJsCardInput completed'); 1040 } catch (modalError) { 1041 console.error('[forms] v3 showAcceptJsCardInput error:', modalError); 1042 } 1043 return; 1044 } 1045 1046 // PRIORITY 2: Check for regular redirect URL 1047 if (result.redirectUrl && result.redirectUrl !== window.location.href) { 1048 console.log('[forms] Redirecting to:', result.redirectUrl); 1049 window.location.href = result.redirectUrl; 1050 return; 1051 } 1052 1053 // PRIORITY 3: Show thanks content (email_confirm_message, etc.) 1054 if (result.thanksContent) { 1055 // CRITICAL: Scroll to top BEFORE replacing content to avoid layout shifts 1056 window.scrollTo({ top: 0, behavior: 'smooth' }); 1057 1058 // Check if standalone thank you page is requested 1059 if (result.standaloneThankYou) { 1060 // Replace entire page content with standalone thank you page 1061 document.body.innerHTML = '<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f5f5f5;padding:20px;"><div style="max-width:800px;width:100%;background:#fff;border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.1);padding:40px;">' + result.thanksContent + '</div></div>'; 1062 window.scrollTo({ top: 0, behavior: 'instant' }); 1063 return; 1064 } 1065 1066 // Replace form content with thanks message (embedded mode) 1067 form.innerHTML = '<div id="form-thanks-message" style="padding:40px;text-align:center;min-height:200px">' + result.thanksContent + '</div>'; 1068 1069 // CRITICAL: Force scroll to top using multiple methods for maximum compatibility 1070 const forceScrollToTop = () => { 1071 console.log('[forms] Executing robust scroll-to-top'); 1072 1073 // 1. Standard window scroll 1074 window.scrollTo({ top: 0, behavior: 'smooth' }); 1075 1076 // 2. Element-based scroll (most reliable when embedded) 1077 const thanksEl = document.getElementById('form-thanks-message'); 1078 if (thanksEl) { 1079 thanksEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); 1080 } 1081 1082 // 3. Recursive parent scroll (for nested scrollable containers) 1083 try { 1084 let parent = form.parentElement; 1085 while (parent && parent !== document.body) { 1086 if (parent.scrollHeight > parent.clientHeight) { 1087 console.log('[forms] Resetting parent scroll:', parent); 1088 parent.scrollTop = 0; 1089 } 1090 parent = parent.parentElement; 1091 } 1092 } catch (e) {} 1093 1094 // 4. Fallback for older/mobile browsers 1095 document.body.scrollTop = 0; 1096 document.documentElement.scrollTop = 0; 1097 1098 // 5. Hash trick to force top 1099 if (window.location.hash !== '') { 1100 window.location.hash = ''; 1101 } 1102 1103 // 6. If inside iframe, try to scroll parent window 1104 try { 1105 if (window.parent && window.parent !== window) { 1106 window.parent.scrollTo({ top: 0, behavior: 'smooth' }); 1107 } 1108 } catch (e) {} 1109 }; 1110 1111 // Execute in multiple phases to ensure layout is settled 1112 forceScrollToTop(); 1113 requestAnimationFrame(forceScrollToTop); 1114 setTimeout(forceScrollToTop, 100); 1115 setTimeout(forceScrollToTop, 500); 1116 setTimeout(forceScrollToTop, 1000); 1117 setTimeout(forceScrollToTop, 2000); 1118 } else { 1119 // Default: redirect to home 1120 window.location.href = "/"; 1121 } 1122 } else { 1123 // Remove overlay and reset button on error 1124 if (overlay && overlay.parentNode) overlay.remove(); 1125 showFormMessage(result.error || "Submission failed"); 1126 if (btn) { 1127 btn.disabled = false;
1128 btn.classList.remove('btn-loading'); 1129 btn.style.paddingLeft = ''; 1130 btn.style.pointerEvents = ''; 1131 btn.style.opacity = ''; 1132 if (btn.tagName === 'INPUT') { 1133 btn.value = originalHtml; 1134 } else { 1135 btn.innerHTML = originalHtml; 1136 } 1137 } 1138 } 1139 } catch (e) { 1140 console.error(e); 1141 // Remove overlay and reset button on network error 1142 if (overlay && overlay.parentNode) overlay.remove(); 1143 showFormMessage("Network error. Please check your connection and try again."); 1144 if (btn) { 1145 btn.disabled = false; 1146 btn.classList.remove('btn-loading'); 1147 btn.style.paddingLeft = ''; 1148 btn.style.pointerEvents = ''; 1149 btn.style.opacity = ''; 1150 if (btn.tagName === 'INPUT') { 1151 btn.value = originalHtml; 1152 } else { 1153 btn.innerHTML = originalHtml; 1154 } 1155 } 1156 } 1157 return false; 1158} 1159 1160// ========== STRIPE PAYMENT SUCCESS HANDLER ========== 1161// Check for payment success redirect from Stripe on page load 1162(function checkStripePaymentSuccess() { 1163 const urlParams = new URLSearchParams(window.location.search); 1164 const paymentStatus = urlParams.get('payment'); 1165 const sessionId = urlParams.get('session_id'); 1166 const returnUrl = urlParams.get('return_url'); 1167 1168 if (paymentStatus === 'success' && sessionId) { 1169 console.log('[forms] Detected Stripe payment success, session:', sessionId); 1170 console.log('[forms] Return URL:', returnUrl || 'none'); 1171 1172 // Show loading message 1173 showFormMessage('Processing payment confirmation...'); 1174 1175 // Fetch session details from our API 1176 fetch((typeof __CMS_BASE_PATH !== 'undefined' ? __CMS_BASE_PATH : '') + '/api/payment/session/' + encodeURIComponent(sessionId)) 1177 .then(response => response.json()) 1178 .then(data => { 1179 if (data.success) { 1180 const amount = data.amount ? '$' + parseFloat(data.amount).toFixed(2) : ''; 1181 const txId = data.transaction_id || sessionId; 1182 1183 // Build success message 1184 let msg = '<div style="margin-bottom:15px;font-size:16px;">Your subscription has been activated!</div>'; 1185 msg += '<div style="margin-bottom:15px;font-size:18px;font-weight:600;">Thank you for subscribing.</div>'; 1186 if (amount) { 1187 msg += '<div style="margin-bottom:10px;color:#666;">Amount: <strong>' + amount + '</strong></div>'; 1188 } 1189 if (data.description && data.description !== 'Payment') { 1190 msg += '<div style="margin-bottom:10px;color:#666;">Plan: <strong>' + data.description + '</strong></div>'; 1191 } 1192 msg += '<div style="background:#e8f5e9;padding:10px;border-radius:4px;margin-top:15px;"><strong>Transaction ID:</strong> <span style="font-family:monospace;user-select:all;">' + txId + '</span></div>'; 1193 1194 // If there's a return URL, add redirect notice 1195 if (returnUrl) { 1196 msg += '<div style="margin-top:15px;color:#666;font-size:14px;">Redirecting you back to your article in 3 seconds...</div>'; 1197 } 1198 1199 showFormMessage(msg, true); 1200 1201 // Clean up URL (remove query params) 1202 if (window.history && window.history.replaceState) { 1203 const cleanUrl = window.location.pathname; 1204 window.history.replaceState({}, document.title, cleanUrl); 1205 } 1206 1207 // Redirect to return URL after 3 seconds (back to the article) 1208 if (returnUrl) { 1209 setTimeout(function() { 1210 // Ensure return URL is same origin for security 1211 try { 1212 const returnUrlObj = new URL(returnUrl, window.location.origin); 1213 if (returnUrlObj.origin === window.location.origin) { 1214 window.location.href = returnUrl; 1215 } 1216 } catch (e) { 1217 // If it starts with /, treat as relative URL 1218 if (returnUrl.startsWith('/')) { 1219 window.location.href = returnUrl; 1220 } 1221 } 1222 }, 3000); 1223 } 1224 } else { 1225 showFormMessage('Payment was processed but we could not retrieve details. Your payment was successful.', true); 1226 // Still redirect if return URL exists 1227 if (returnUrl) { 1228 setTimeout(function() { window.location.href = returnUrl; }, 3000); 1229 } 1230 } 1231 }) 1232 .catch(err => { 1233 console.error('[forms] Error fetching session:', err); 1234 // Still show success even if API fails - payment went through 1235 showFormMessage('<div style="margin-bottom:15px;">Payment successful!</div><div style="background:#e8f5e9;padding:10px;border-radius:4px;"><strong>Session ID:</strong> <span style="font-family:monospace;">' + sessionId + '</span></div>', true); 1236 // Still redirect if return URL exists 1237 if (returnUrl) { 1238 setTimeout(function() { window.location.href = returnUrl; }, 3000); 1239 } 1240 }); 1241 } else if (paymentStatus === 'cancelled') { 1242 showFormMessage('Payment was cancelled. You can try again when ready.'); 1243 // Clean up URL 1244 if (window.history && window.history.replaceState) { 1245 const cleanUrl = window.location.pathname; 1246 window.history.replaceState({}, document.title, cleanUrl); 1247 } 1248 } 1249})(); 1250</script>
1250</div> 1251<div class="w-100 d-block"><div data-cms-block="adgroup-30" data-cms-block-label="Advertisement 30" data-cms-el="adgroup" data-cms-adgroup="30">
1251<script type="text/javascript" language="JavaScript">AdGroup30();</script>
1251</div></div> 1252</div> 1253 <div class="cus-column-133-2 col-lg-3 ps-lg-3 border-start"> 1254 <div data-cms-block="adgroup-29" data-cms-block-label="Advertisement 29" data-cms-el="adgroup" data-cms-adgroup="29">
1254<script type="text/javascript" language="JavaScript">AdGroup29();</script>
1254</div><div data-cms-block="adgroup-20" data-cms-block-label="Advertisement 20" data-cms-el="adgroup" data-cms-adgroup="20">
1254<script type="text/javascript" language="JavaScript">AdGroup20();</script>
1254</div><div data-cms-block="poll-1" data-cms-block-label="Poll 1" data-cms-el="poll" data-poll-id="1">
1254<script type="text/javascript" language="JavaScript">GetPoll1();</script>
1254</div> 1255<div data-cms-block="section-file-29" data-cms-block-label="Section 29" data-cms-el="section" data-cms-section="29"><div class="cus-photogallery-133"> 1256 <div class="cus-default-menu-bar"> 1257 <div class="text-uppercase fw-bold px-0 mb-2 text-black cus-133-label"> 1258 <span> 1259 <span class="cus-default-submenu">Swva Sun Video</span> 1260 </span> 1261 </div> 1262 </div> 1263 <div class="row mx-0 hp-parent"> 1264 1265 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="15589" data-cms-article-id="15589" data-url="sun-spotlight-for-feb-p15589-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="02_07_2026"> 1266 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 1267 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true`.trim());"> 1268 <div class="ratio-16-9"> 1269 <div class="img-content cus-dark-img"> 1270 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true/maxresdefault.jpg" data-body="https://open.substack.com/pub/swvasun/p/sun-spotlight-for-the-week-of-feb?r=7ctp54&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true" data-src="/05-02-2026-01-26-25-pm-3456539.jpg" alt="Sun Spotlight for Feb. 1-7" data-was-processed="true" loading="lazy"> 1271 </div> 1272 </div> 1273 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50"> 1274 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i> 1275 </span> 1276 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Sun Spotlight for Feb. 1-7"> 1277 <span class="line-1">Sun Spotlight for Feb. 1-7</span> 1278 </p> 1279 </a> 1280 </div> 1281 </div> 1282 1283 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="8871" data-cms-article-id="8871" data-url="wytheville-parks-and-rec-maroon-silver-vs-rural-retreat-black-p8871-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
128306_10_2024"> 1284 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 1285 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`-2rZwiaTiQ0`.trim());"> 1286 <div class="ratio-16-9"> 1287 <div class="img-content cus-dark-img"> 1288 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/-2rZwiaTiQ0/maxresdefault.jpg" data-body="-2rZwiaTiQ0" data-src="/10-06-2024-08-51-49-pm-2030388.jpg" alt="5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black" data-was-processed="true" loading="lazy"> 1289 </div> 1290 </div> 1291 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50"> 1292 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i> 1293 </span> 1294 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black"> 1295 <span class="line-1">5/6/7 Wytheville Parks and Rec Maroon Silver vs. Rural Retreat Black</span> 1296 </p> 1297 </a> 1298 </div> 1299 </div> 1300 1301 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="111" data-cms-article-id="111" data-url="the-fourth-is-with-wytheville-p111-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
130104_22_2024"> 1302 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 1303 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`rnh5RDc7usk`.trim());"> 1304 <div class="ratio-16-9"> 1305 <div class="img-content cus-dark-img"> 1306 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/rnh5RDc7usk/maxresdefault.jpg" data-body="rnh5RDc7usk" data-src="/no-image.png" alt="The Fourth is with Wytheville" data-was-processed="true" loading="lazy"> 1307 </div> 1308 </div> 1309 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50"> 1310 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i> 1311 </span> 1312 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="The Fourth is with Wytheville"> 1313 <span class="line-1">The Fourth is with Wytheville</span> 1314 </p> 1315 </a> 1316 </div> 1317 </div> 1318 1319 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="112" data-cms-article-id="112" data-url="trail-talk-chasing-waterfalls-in-smyth-county-p112-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
131904_22_2024"> 1320 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 1321 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`UmbIwjjST8E`.trim());"> 1322 <div class="ratio-16-9"> 1323 <div class="img-content cus-dark-img"> 1324 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/UmbIwjjST8E/maxresdefault.jpg" data-body="UmbIwjjST8E" data-src="/no-image.png" alt="Trail Talk: Chasing Waterfalls in Smyth County" data-was-processed="true" loading="lazy"> 1325 </div> 1326 </div> 1327 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50"> 1328 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i> 1329 </span> 1330 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Trail Talk: Chasing Waterfalls in Smyth County"> 1331 <span class="line-1">Trail Talk: Chasing Waterfalls in Smyth County</span> 1332 </p> 1333 </a> 1334 </div> 1335 </div> 1336 1337 <div class="col-sm-12 col-lg-4 px-1 mb-2 hpe-in-video-g-yt-id" data-article="62" data-cms-article-id="62" data-url="wytheville-nd-and-rd-grade-flag-football-p62-29.htm" data-menu="GALLERY" data-submenu="Swva Sun Video" data-date="
133704_15_2024"> 1338 <div class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden"> 1339 <a class="cus-videogallery-29-link position-relative d-block text-decoration-none border rounded overflow-hidden" href="javascript:void(0);" onclick="OpenYouTube(`DZXFNIbWTRQ`.trim());"> 1340 <div class="ratio-16-9"> 1341 <div class="img-content cus-dark-img"> 1342 <img class="cus-videogallery-29-img img-fluid lazy loaded" src="https://img.youtube.com/vi/DZXFNIbWTRQ/maxresdefault.jpg" data-body="DZXFNIbWTRQ" data-src="/no-image.png" alt="Wytheville 2nd and 3rd Grade Flag Football" data-was-processed="true" loading="lazy"> 1343 </div> 1344 </div> 1345 <span class="cus-videogallery-29-play position-absolute translate-middle top-50 start-50"> 1346 <i class="fa fa-play fs-1 text-white text-shadow-1" aria-hidden="true"></i> 1347 </span> 1348 <p class="cus-videogallery-29-heading mb-0 bg-dark text-white p-2 fs-6 lh-lg fw-bold" data-bs-toggle="tooltip" data-bs-placement="bottom" title="" data-bs-original-title="Wytheville 2nd and 3rd Grade Flag Football"> 1349 <span class="line-1">Wytheville 2nd and 3rd Grade Flag Football</span> 1350 </p> 1351 </a> 1352 </div> 1353 </div> 1354 1355 </div> 1356</div> 1357<style> 1358@import url("https://fonts.googleapis.com/css2?family=PT+Sans:wght@700&display=swap"); 1359 1360.cus-29-label { 1361 font-family: pt sans, sans-serif; 1362 border-bottom: 4px solid #bdcde3; 1363} 1364 1365.cus-29-label>span { 1366 position: relative; 1367} 1368 1369.cus-29-label>span:before { 1370 background: #dc3545; 1371 position: absolute; 1372 display: inline-block; 1373 content: ""; 1374 width: 100%; 1375 height: 4px; 1376 bottom: -6px; 1377} 1378 1379.cus-videogallery-29-link:hover .cus-videogallery-29-play i { 1380 transform: scale(1.2); 1381 transition: 200ms linear; 1382} 1383 1384.cus-videogallery-29-play i { 1385 transition: 100ms linear; 1386} 1387</style>
1388<script> 1389/* SubMenu Title */ 1390if (ReadSubmenu() == 29) { 1391 document.title = "Swva Sun Video"; 1392} /* If Full URL used instead of just ID*/ 1393var vu = document.getElementsByClassName("cus-videogallery-29-img"); 1394for (i = 0; i < vu.length; i++) { 1395 if (vu[i].src.split("http").length > 2) { 1396 /*Full URL*/ 1397 var tu = vu[i].getAttribute("data-body"); 1398 if (tu.indexOf("embed") != -1 && tu.indexOf("you") != -1) { 1399 /*YouTube*/ 1400 tu = tu.split("/")[4].slice(0, 11); 1401 vu[i].src = `https://img.youtube.com/vi/${tu}/maxresdefault.jpg`; 1402 } else if (tu.indexOf("watch") != -1 && tu.indexOf("you") != -1) { 1403 /*YouTube*/ 1404 var bx = new URL(tu); 1405 tu = bx.searchParams.get(`v`).slice(0, 11); 1406 vu[i].src = `https://img.youtube.com/vi/${tu}/maxresdefault.jpg`; 1407 } else if (tu.indexOf("vimeo") != -1 && tu.indexOf("player.vimeo") == -1) { 1408 /*Vimeo*/ 1409 tu = tu.split("/")[3]; 1410 vu[i].src = `https://vumbnail.com/${tu}.jpg`; 1411 } else { 1412 /*Other*/ 1413 vu[i].src = vu[i].getAttribute("data-src"); 1414 } 1415 } 1416} 1417 1418function OpenYouTube(id) { 1419 if (!document.getElementById('f_YT_button')) { 1420 if (id.indexOf("/") != -1) { 1421 /*Full URL*/ 1422 if (id.indexOf("embed") != -1 && tu.indexOf("you") != -1) { 1423 /*Youtube Video*/ 1424 id = id.split("/")[4].slice(0, 11); 1425 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`; 1426 } else if (id.indexOf("watch") != -1 && tu.indexOf("you") != -1) { 1427 /*Youtube Video*/ 1428 var bx = new URL(id); 1429 id = bx.searchParams.get(`v`).slice(0, 11); 1430 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`; 1431 } else if (id.indexOf("vimeo") != -1 && id.indexOf("player.vimeo") == -1) { 1432 /*Vimeo Video*/ 1433 id = id.split("/")[3]; 1434 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://player.vimeo.com/video/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`; 1435 } else { 1436 /*Other Video*/ 1437 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`; 1438 } 1439 } else { 1440 var msg = `<iframe id="f-mfp-iframe" class="f-mfp-iframe" allowfullscreen="" src="https://www.youtube.com/embed/${id}?autoplay=1" scrolling="no" frameborder="0"></iframe>`; 1441 } 1442 document.getElementsByTagName("body")[0].insertAdjacentHTML(`beforeend`, `<button id='f_YT_button' type='button' data-bs-toggle='modal' data-bs-target='#f_YT' style='display:none;'></button><div class='f_YT_box modal fade' id='f_YT' data-bs-backdrop='static' data-bs-keyboard='false' tabindex='-1' aria-labelledby='f_YTLabel' aria-hidden='true'><div class='modal-dialog modal-dialog-centered mx-auto'><div class='modal-content' id='YT_parent'><button type='button' class='f_YT_close btn-close position-absolute' data-bs-dismiss='modal' aria-label='Close'><svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='#fff' class='bi bi-x-lg' viewBox='0 0 16 16'><path d='M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z'/></svg></button><div class='modal-body text-center' style='white-space: pre-wrap;'><p id='f_loader'><i class='fa fa-spinner fa-spin' aria-hidden='true'></i></p>${msg}</div></div></div><style>#YT_parent{width:70vw;height:39.43vw}.modal-dialog{max-width:none;display:flex;align-items:center;justify-content:center}
1442.modal-dialog-scrollable .modal-content {max-height: none;overflow:visible;}.modal-body {padding:0;} .f_YT_close{right:-1rem;z-index:999;top:-1rem;background-color:red;border-radius:50px;padding:.5rem;opacity:1;box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.2);background-image:none;color:#fff;display:flex;justify-content:center;align-items:center;font-weight:700;font-size:18px}.f_YT_close:hover{color:#fff;background-color: #333;opacity:1} .f-mfp-iframe {width:100%;height:100%;}#f_loader{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%)}#f_loader i{font-size:4rem;color:red;opacity:0.8}@media (max-width:576px){#YT_parent{width:85vw;height:48vw}}</style></div>`); 1443 document.getElementById('f-mfp-iframe').onload = function () { 1444 document.getElementById("f_loader").remove(); 1445 }; 1446 document.getElementById('f_YT_button').click(); 1447 document.getElementsByClassName('f_YT_close')[0].addEventListener('click', function () { 1448 document.getElementById('f_YT_button').remove(); 1449 document.getElementsByClassName('f_YT_box')[0].remove(); 1450 }); 1451 } 1452} 1453</script>
1453</div> 1454 <div data-cms-block="adgroup-11" data-cms-block-label="Advertisement 11" data-cms-el="adgroup" data-cms-adgroup="11">
1454<script type="text/javascript" language="JavaScript">AdGroup11();</script>
1454</div> 1455 <div data-cms-block="adgroup-28" data-cms-block-label="Advertisement 28" data-cms-el="adgroup" data-cms-adgroup="28">
1455<script type="text/javascript" language="JavaScript">AdGroup28();</script>
1455</div> 1456 <div data-cms-block="adgroup-13" data-cms-block-label="Advertisement 13" data-cms-el="adgroup" data-cms-adgroup="13">
1456<script type="text/javascript" language="JavaScript">AdGroup13();</script>
1456</div> 1457 </div> 1458</div> 1459 1460<footer class="row py-3 mt-3" style="border-top: 2px dotted #dedede; color: #000;"> 1461 <div class="col-3 cus-social-links d-flex justify-content-left position-relative mb-2"> 1462 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.facebook.com/swvasun/" target="_blank"><i class="fa fa-facebook-square" aria-hidden="true"></i></a> 1463 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://twitter.com/swvasun" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-twitter-x" viewBox="0 0 16 16" style="padding: 3px;background: #212529;fill: #fff;border-radius: 4px;display: inline-block;transform: translateY(-2px);" width="20" height="22"><path d="M12.6.75h2.454l-5.36 6.142L16 15.25h-4.937l-3.867-5.07-4.425 5.07H.316l5.733-6.57L0 .75h5.063l3.495 4.633L12.601.75Zm-.86 13.028h1.36L4.323 2.145H2.865z"></path></svg></a> 1464 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://www.linkedin.com/company/swva-media-llc/" target="_blank"><i class="fa fa-linkedin-square" aria-hidden="true"></i></a> 1465 <a class="text-decoration-none text-dark rounded ms-3 fs-4 d-inline-block lh-1 cus-social-1-133-icon" href="https://youtube.com/@southwestvirginiasun8601" target="_blank"><i class="fa fa-youtube-play" aria-hidden="true"></i></a> 1466 </div> 1467 <div class="col-lg-8 col-12 position-relative mb-2"> 1468<div class="col-12 ms-4">Swva Media is a proud member of the following organizations:</div> 1469<div class="col-12"> <div data-cms-block="adgroup-21" data-cms-block-label="Advertisement 21" data-cms-el="adgroup" data-cms-adgroup="21">
1469<script type="text/javascript" language="JavaScript">AdGroup21();</script>
1469</div></div> 1470 </div> 1471 <div class="col-12 d-flex justify-content-center position-relative mb-2"> 1472 <div data-cms-block="section-4" data-cms-block-label="Section 4" data-cms-el="section" data-cms-section="4">
1472<script type="text/javascript">Section4();</script>
1472</div> 1473 </div> 1474 <div class="col-12 d-flex justify-content-center position-relative mb-2">
1475<script type="text/javascript">Section5();</script>
1475 1476 </div> 1477</footer> 1478 1479 1480 </div> 1481 </div> 1482 <!-- go to top --> 1483 <div class="cus-scroll-top position-fixed end-0 bg-dark text-white fs-4 px-2" style="cursor: pointer;bottom: auto;transition: 400ms linear;" data-bs-toggle="tooltip" data-bs-placement="top" title="Go to top"><i class="fa fa-angle-up"></i></div> 1484
1484<script> 1485 if (document.getElementsByClassName("cus-scroll-top")) { 1486 window.addEventListener("scroll", function() { 1487 if (document.body.scrollTop > 600 || document.documentElement.scrollTop > 600) { 1488 document.getElementsByClassName('cus-scroll-top')[0].style.bottom = 0; 1489 } else { 1490 document.getElementsByClassName('cus-scroll-top')[0].style.bottom = "-100px"; 1491 } 1492 }); 1493 document.getElementsByClassName('cus-scroll-top')[0].addEventListener('click', function() { 1494 window.scrollTo(0, 0); 1495 }); 1496 } 1497 </script>
1497 1498 <!-- /go to top --> 1499 <div class="cus-g-analytics-133">
1499<script language="javascript">GetGoogleAnalytics('');</script>
1499</div> 1500 <!-- tooltip --> 1501
1501<script> 1502 var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) 1503 var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { 1504 return new bootstrap.Tooltip(tooltipTriggerEl) 1505 }); 1506 </script>
1506 1507 <!-- /tooltip -->
1508<script defer src="/sys-popup.js?v=1790187371"></script>
vendor: 1 bytes, line 1508
1508
1509<script> 1510// Form data restoration (runs on page load) - reads from COOKIE 1511(function() { 1512 console.log('[forms] Restoration script running...'); 1513 console.log('[forms] Current cookies:', document.cookie); 1514 try { 1515 var cookies = document.cookie.split('; '); 1516 var pendingCookie = null; 1517 for (var i = 0; i < cookies.length; i++) { 1518 if (cookies[i].indexOf('_pendingFormData=') === 0) { 1519 pendingCookie = cookies[i]; 1520 break; 1521 } 1522 } 1523 console.log('[forms] Found pending cookie:', !!pendingCookie); 1524 if (pendingCookie) { 1525 var cookieValue = pendingCookie.substring('_pendingFormData='.length); 1526 console.log('[forms] Cookie value length:', cookieValue.length); 1527 var formData = JSON.parse(decodeURIComponent(cookieValue)); 1528 console.log('[forms] Parsed form data:', Object.keys(formData)); 1529 var form = document.getElementById('CustomForm') || document.querySelector('form'); 1530 console.log('[forms] Found form:', !!form, form ? form.id : 'no id'); 1531 if (form) { 1532 Object.keys(formData).forEach(function(key) { 1533 var value = formData[key]; 1534 if (value && typeof value === 'string') { 1535 var field = form.querySelector('[name="' + key + '"]'); 1536 if (field) { 1537 console.log('[forms] Restoring field:', key, '=', value.substring(0, 20)); 1538 if (field.tagName === 'SELECT') { 1539 field.value = value; 1540 } else if (field.type !== 'file' && field.type !== 'hidden') { 1541 field.value = value; 1542 } 1543 // Trigger change event for floating labels 1544 field.dispatchEvent(new Event('input', { bubbles: true })); 1545 field.dispatchEvent(new Event('change', { bubbles: true })); 1546 } 1547 } 1548 }); 1549 console.log('[forms] Form data restored successfully'); 1550 } 1551 // Clear the cookies 1552 document.cookie = '_pendingFormData=; path=/; max-age=0'; 1553 document.cookie = '_pendingFormUrl=; path=/; max-age=0'; 1554 console.log('[forms] Cookies cleared'); 1555 } 1556 } catch (e) { 1557 console.error('[forms] Error restoring form data:', e); 1558 } 1559})(); 1560</script>
vendor: 1 bytes, line 1560
1560
1561<script> 1562(function(){ 1563 document.addEventListener('submit',function(e){ 1564 var form=e.target;if(!form||form.tagName!=='FORM')return; 1565 var btn=form.querySelector('button[type="submit"],input[type="submit"],.survey-btn-submit,#SUBMIT1'); 1566 if(!btn||btn.disabled)return; 1567 btn.disabled=true; 1568 if(btn.tagName==='INPUT'){btn.value=' Submitting...';} 1569 else{btn.innerHTML='<span class="form-submit-spinner"></span> Submitting...';} 1570 },true); 1571})(); 1572</script>
1572</body> 1573<!--Template-130--> 1574 1575</html>
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.